Reversing the Balong M3/MCU Console – Lightning the Path to Ring 0

This weekend, I spent some time continuing my investigation of a Huawei E5573 modem. In this, I was able to identify and briefly PoC a process by which an attacker can gain privileged code execution on this device, without resorting to the “boot pin” trick used by public jailbreaks of this device.

This method works with an E3372 device, and should work across most, if not all, Huawei 4G modems (if not more Huawei devices). To be clear, this is not “elite hacking” – however, I couldn’t find any documentation on the subject while I was doing this work. Furthermore, this method can be used as a “first principles” primitive: this can be done with no prior knowledge of the device.

If you wish to follow along, some files (binary dump, extractor script, minicom script) are available to help you here, if I know you, ask me for the idb:

file pack

Introduction – Welcome to the E5573

The Huawei E5573 device is a 4G modem, resold by Optus in Australia, and available elsewhere across the world. This device comes in a small, white plastic case, and when opened, looks like this:

The identifiable debug pins are as follows:

  • The pins in the red box up the top are UART. The second and third pins on the top row are receive (data in) and transmit (data out) respectively.
  • The three pins on the left, outlined in yellow, are another set of debug pins: the bottomost pin is the “boot pin” – if this pin is grounded when the device is powered on, it will boot into a “usb dload” mode, which can be used to overwrite the device’s bootloader (and thus, firmware). This is the generally used method for jailbreaking and unbricking the device.
  • The pins on the right are presumably JTAG – this configuration appears common across Huawei’s devices (as alluded to here), though I couldn’t see anything on any of the pins on a logic analyzer.

Observing Boot

My first step was to observe the device booting over UART:

This device then boots Linux, and runs a custom password prompt. A little bit of brute forcing reveals that the password must be 8 characters in length – but no other information is provided.

Inspecting the boot process in further detail, we come across a curious line:

[0000028ms]Heap:0x57d3bbe0 -- 0x57d3c180, 1440Here, I
[0000029ms]Please distribute uart with command L/V/M...
[0000029ms] heap:0x57d3bbe0 -- 0x57d3c180, 1440

We get a clue from 4pda.ru, from the user forth32:

Plugging this into Google Translate, we learn that one can send “m”, “v” or “l” through the UART connection to boot into different modes: the “M3 console”, VxWorks, or the Linux Kernel respectively. There is at least one more mode available (“K”), uncovered through reverse engineering of publicly available firmware.

Selecting the “L” option allows a regular boot, while selecting V gives a non-interactive VxWorks console (on the E3372, this is interactive – I have no idea why this is switched off here, and don’t know enough about the device to re-enable it).

Selecting “m” gives a much more interesting prompt:

This can be done via a script, passed to minicom via the -S option, to make life a bit easier, so you don’t have to hold down the “M” button on boot. the download link to this script is above.

Typing help gives us the following:

[0000029ms]Welcomehelp
[0000726C]exc: help(00000000, 00000000, 00000000, 00000000, 00000000, 00000000) 
[00007270] command list:24
[00007272][0] help 
[00007272][1] d 
[00007273][2] m 
[00007274][3] system_error 
[00007275][4] pm_set_debug 
[00007277][5] pm_print_debug 
[00007278][6] pm_wakeup_acore 
[00007279][7] pm_wakeup_ccore 
[0000727A][8] pm_appa9_wsrc_set 
[0000727C][9] pm_mdma9_wsrc_set 
[0000727D][10] pm_mcu_wsrc_set 
[0000727E][11] pm_appa9_wsrc_unset 
[00007280][12] pm_mdma9_wsrc_unset 
[00007874][13] pm_mcu_wsrc_unset 
[00007876][14] cpufreq_print_debug 
[00007877][15] dump_wdt_hook 
[00007878][16] ios_list_echo 
[0000787A][17] bsp_wdt_print_debug 
[0000787B][18] bsp_wdt_stop 
[0000787C][19] bsp_dump_bus_error_status 
[0000787E][20] set_pm_test_timer 
[0000787F][21] m3_send_a_ipc 
[00007881][22] m3_send_c_ipc 
[00007882][23] set_pm_dfs_profile 
[00007883]exc: help = 0

A little bit of experimentation reveals that “d” allows us to write memory, and “w” allows us to dump memory. Starting from this, I ran a memory dump from 0 to 0xFFFFFFFF, logging the results to file. This generated output in the following form:

It should be noted that you cannot obtain a “full” memory dump – the device seems to crash after a few moments of printing memory, and needs to be recovered via a power cycle. Furthermore, this does not seem to follow a sane memory mapping: that is, dumping at 0x0 and 0x10000000 produce the same result. I wrote a Python script to help extract this from a minicom capture file – this script is available above.

Into the Unknown

Running strings across the file reveals that we are either looking at some arbitrary process memory, or more likely, the Balong MCU Console itself:

Unfortunately, the memory dump is not in a sane executable format. Loading this into IDA does not reveal any kind of header. We know from the boot log that the processor is ARM, so we can simply load this into IDA, and begin forcing conversion to ARM code, and see what we get:

The initial instructions don’t look like valid ARM, but if we continue through the code, we can see meaningful ARM functions (wrapped with sane push/pop instructions), as well as data starting to take shape:

Curiously, we see several references to something in the 0x10000000 memory region. On a hunch, I decided to rebase the executable at 0x1000000, and try again. This time, the code comes apart easily, and IDA Pro’s cross-referencing capability shines through: these mysterious DWORDs are mostly function pointers to elsewhere in the code.

Going back to the start of the memory dump, we can also recognize a similar pattern – the code at 0 isn’t code, but is infact an array of dwords.

Half an hour of reversing later, the smell of success is on the air – IDA manages to successfully cross-reference a function to a string reference from within the memory dump, indicating that this is indeed the MCU Console itself:

This appears to be a triggerable number-of-arguments check – and is enough for us to begin proving that we can execute arbitrary code without the boot pin, via the MCU Console alone.

Modifying Code Flow

To prove we can execute arbitrary code, it is enough that we prove we can control code flow. The above check is a fantastic place to start: we can trigger this code flow, and the result is logical – if there are more than 5 arguments, a message will be displayed about there being too many arguments and the command will not execute. If we can lower this limit, we can control the code executing.

With a little experimentation, we can work out that the “m” command allows us to edit memory, but only one dword at a time, as follows (you must supply a full DWORD, or it will assume the missing bytes are zero and overwrite your target address):

m 0x1000F000 0xAABBCCDD

We can use this to modify the check at 0x1000075C, as follows, keeping endianness in mind:

m 0x1000075C 0x0F02F1BB

This should mean that we can supply no more than two arguments to a command, where previously, we could supply a maximum of 5. Firstly, let’s test that “d” with 5 arguments behaves as expected (the test cases below use minicom scripts for automation, so it’s missing some output – but close enough).

Now, we add the “m” command to the script, and try now:

We can edit the error message as well:

Success!

Final Thoughts + Further Work

At this point, we have:

  • Identified an unknown binary, and recovered it into a somewhat sane format for further analysis.
  • Proven we are able to both edit resources in the binary, as well as arbitrarily execute code
  • Demonstrated privileged code execution without the “boot pin” trick / with a fresh bootloader (as opposed to using one from routerunlock.com to jailbreak your device).

This should be enough to load arbitrary privileged code, albeit extremely slowly via UART.

While public jailbreaks exist for this device (and are much faster), this work acts as a fundamental “lego block” for reverse engineering of both the E5573 device, and other devices where a jailbreak may not already exist.

From here, I will aim to continue my exploration of this device, using it as a practice platform to learn hardware security.

Posted in Bards, Computers, Jesting | Tagged , | 4 Comments

Arduino-to-Arduino Voltage Glitching (look ma, no FPGA!)

Recently, I have been delving into the magical world of hardware hacking. One target has given me cause to learn voltage glitching, among other things. On the advice of friends, I ordered a ChipWhisperer kit but while it made it’s merry way across the sea, I wanted to begin experimenting right now.

After giving thanks to Her Majesty the Queen (may she reign eternal) for the long weekend, I decided to follow work done by flawed.net.nz earlier this year, with the hardware I could find on my desk. I will now list the theory behind this work, the process I went through and the final result in this post.

You can download the source files used in this example here and here.

Theory: Voltage Glitching

Voltage glitching is the process of temporarily reducing voltage to a microcontroller as it is performing an operation, causing undefined behaviour. This can be represented as follows:

This can result in surprising and advantageous interactions, such as single CPU instructions not doing the right thing. The impacts of this are immense – it fundamnetally breaks the assumption that computerised systems always do what you tell them to in software.

You can read more about this subject here:

This must be performed with some measure of accuracy: drop the power for too long, and the target microcontroller may reset.

Target Setup

My target code was a simple counting loop:

void loop() {
 int x = 0;
 int y = 0;
 double sum = 0;
 for(x = 0;x < 500; x++)
 {
 for(y = 0;y < 500;y++)
 {
 sum++;
 }
 }
 Serial.print("500 * 500 = ");
 Serial.println(sum,DEC);
 if(sum != 250000.0000000)
 {
 flashRed(100);
 }
 else
 {
 flashGreen(100);
 }
}
]

Simply put, this should always output 250000: our goal is to influence this result via modifying the voltage.

I ran the above “target” code on an Arduino board which I got as part of the Freetronics Experimenters Kit for Arduino (which you can purchase here). I initially hooked it up to two LED’s and ran it, to confirm it works, which it does – it consistently returns the expected result via the serial monitor.

Glitch Source Setup

The next step was to set up a glitch source. For this, an FPGA is typically used, but as I did not have one, I used the ESP8266 component which came with the BSides 2017 badge, as follows:

(Sorry – the unlabelled resistor-shaped component is actually a resistor).

This used a 2N7000 transistor to intercept the ground connection of the target Arduino device – in it’s unintercepted state, this would always be connected, and thus, providing a steady flow of electrons through the microcontroller.

In this case, we send a continuous high voltage signal to the middle pin, keeping it “on” – however, we occasionally stop sending a signal, causing the transistor to break the circuit and induce a “glitch”.

In practice, it looks like this (don’t worry about the microcontroller inserted into the breadboard up top – for now, it’s not connected):

The leftmost orange connection is our “glitch out” – we can break the connection of anything which uses this as it’s ground. You can test this with a LED – send a HIGH signal to pin 13, and break the connection by sending a LOW signal to the same pin.

Pin 13 is the white wire, connected to the pin labelled D7. The pink wire is connected to a Ground pin.

Note that the transistor is directional – if you are following these steps, orient the transistor exactly as shown. Otherwise: the “gate” is Pin 13/D7, the “source” is the glitch out, and the sink is a Ground pin. If you test with a LED, and get a result where the LED slightly dims but does not turn off when you attempt to drop voltage, you’ve probably messed up the direction of the transistor.

Glitch Timing + Destabilizing

We also need to tweak our source code, so we drop power long enough to meaningfully impact the target, but not long enough to reset the target. To do this, I embraced the oldest and noblest of traditions, brute force best force:

void setup() {
 Serial.begin(115200);
 pinMode(13,OUTPUT);
 digitalWrite(13,HIGH);
 delay(10000);
 Serial.println("GLITCH RDY");
}

int glitchCtr = 0;
int secCtr = 1;

void loop() {
 // put your main code here, to run repeatedly:
 if(glitchCtr > 2000000)
 {
 Serial.print("DUMPING POWER FOR ");
 Serial.println(secCtr);
 digitalWrite(13,LOW);
 // try to pass time.
 int waste = 0;
 for(int i = 0;i < secCtr;i++)
 {
 waste++;
 }
 secCtr *= 2;
 digitalWrite(13,HIGH);
 glitchCtr = 0;
 }
 else
 {
 glitchCtr++; 
 digitalWrite(13,HIGH);
 }
}

You’ll notice that we use a while loop instead of the delay() function: the delay function is too slow. Remember, we’ll want to drop the power for a fraction of a clock cycle on the target only, which is much shorter than 1ms.

At this point, we have almost everything we need to run the attack – unfortunately, the Arduino board itself is unfortunately somewhat resilient to voltage glitching, due to the presence of capacitors on the board itself, providing a stable source of voltage in case of accidental noise on the power supply.

If you attempt the glitching attack at this point, you’ll simply see nothing, until you drop the voltage long enough that the device itself resets.

Following on the flawed.net.nz glitching work, I transplanted the microcontroller onto a breadboard, and restored the connections manually, without capacitors. The wiring should look something like this:

  • With the microcontroller facing upwards, pin count starting from 1
  • Pin 8 is connected to our glitch out
  • Pins 2, 3, 7, 9, 10 and 20 out to their respective positions on the Arduino board itself.

Now, we plug in the ESP8266 and the Arduino board, and open up the serial ports on both. You should see the messages from the ESP indicating when it’s dumping power – after a while, you should see the maths on the glitch target returning unexpected results:

You’ll see the results get wierder as you drop the power for longer, until the device itself resets (or you get an integer overflow on the ESP8266). Note that sometimes, you won’t glitch an add operation – you’ll glitch some other function call, and the output from the target will freeze – just reset the device if it doesn’t come back after a while.

I hope you enjoyed this read – I know barely anything about hardware hacking, so any feedback / spotted errors are welcome. Special thanks to Silvio and the BSides Canberra 2017 team for the ESP8266 device which served as the glitch source in this exercise.

Appendix: ESP8266 Mini-Board:

While I was going through this exercise, I had contemplated desoldering the pins on the ESP8266 and resoldering them to the be the same as… every other development board I had (female headers). During my research, I stumbled across a clever, less messy solution (source: http://www.cnx-software.com/2015/10/29/getting-started-with-nodemcu-board-powered-by-esp8266-wisoc/):

Posted in Bards, Computers, Jesting | Tagged , | Leave a comment

Writeups – Ping, A Temple Jest (securityfest.ctf.rocks)

One hour ago, I competed in the securityfest.ctf.rocks CTF. In the time I had to participate, I was able to solve two challenges. Without further ado:

Ping

The “ping” challenge was presented as a binary file, which you can download here.

We start by analyzing this in IDA, to get an overview of the program. This appears to be a simple menu-driven application, which allows a user to specify a name (to “login”) and an IP address, a simple check for the IP address, and a popen() block to call ping on the host system to ping the specified IP address, pictured below:

We can quickly notice some irregularities in the program design:

  • Firstly, 0x602060 seems to be re-used to store both the name and the IP address.
  • Secondly, the application allows you to log out, but not exit, indicating that it may be possible to poison the “state” of the application.
  • The variable at 0x602160 stores whether an IP address has been “set” or not, but does not appear to be cleared on logout.

Combining the three assumptions above gives us a viable attack scenario, as follows:

  • Log in, and set a valid IP address
  • Log out, but do not exit
  • Log in, setting 127.0.0.1;cat flag as your username
  • Running the “ping” command

This should execute a ping against localhost, but then also cat the “flag” file in the local directory. A few moments later, and we have success:

A Temple Jest

The “A Temple Jest” challenge was presented as a web challenge. Upon visiting the page, we are met with the following:

Inspecting the source code of the page, we are led to the “/render/404” URL. After a short period of fruitless exploration, I tested other URL’s, such as “/render/200”, which gave a valid response: and more interestingly, so did “/render/2+2”, giving a response like the following:

This immediately indicated server-side code execution of some sort – and given the challenge name (as well as the X-Powered-By: Express header, indicating Node.js), I made the assumption this was template injection.

We can validate this assumption by attempting to access /render/root, which returns the message “[object global] is under construction…”, confirming our suspicion. From here, we can duct tape together a payload to start reading files from the web server, uploading the results via an HTTP POST body, via curl, revealing the flag:

(I’m not sure what this challenge had to do with a memory leak… but sure, I’ll take 200 internet points).

I’d like to thank the organisers of the securityfest.ctf.rocks event for putting together a good number of challenges for everyone to enjoy. I hope that in future, I am less busy when this event is on, so I could have more time to play. See you all in the LabyREnth Challenge, and the Google Capture The Flag events!

Posted in Bards, Computers, Jesting | Leave a comment

Windows 7 x64 Kernel Exploitation – Stack Overflow (3/3)

In this (short) post, we will explore triggering a stack overflow vulnerability in the kernel. This is a classic exploitation case.

The Vulnerability: TriggerStackOverflow

Similar to the last post, we first inspect the vulnerable function in code. This is a classic stack overflow vulnerability, which should be familiar to all of you:

NTSTATUS TriggerStackOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
 NTSTATUS Status = STATUS_SUCCESS;
 ULONG KernelBuffer[BUFFER_SIZE] = {0};

// #define BUFFER_SIZE 512 (common.h)

PAGED_CODE();

__try {
 // Verify if the buffer resides in user mode
 ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));

DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
 DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
 DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer);
 DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer));

#ifdef SECURE
 // Secure Note: This is secure because the developer is passing a size
 // equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence,
 // there will be no overflow
 RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else
 DbgPrint("[+] Triggering Stack Overflow\n");

// Vulnerability Note: This is a vanilla Stack based Overflow vulnerability
 // because the developer is passing the user supplied size directly to
 // RtlCopyMemory()/memcpy() without validating if the size is greater or
 // equal to the size of KernelBuffer
 RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
#endif
 }
 __except (EXCEPTION_EXECUTE_HANDLER) {
 Status = GetExceptionCode();
 DbgPrint("[-] Exception Code: 0x%X\n", Status);
 }

return Status;
}

However, upon inspecting the disassembly, there is a slight discrepancy: instead of calling RtlCopyMemory, the following code sequence is present:

In this case, our exploitation is absolutely no different to a traditional stack overflow, with two key differences.

Exploitation Realities…

Under Win32, it is possible to cleanly return to a nice stack frame. Under 64-bit, this seems more difficult (impossible?) and I’m not sure why – my shellcode doesn’t return cleanly, ever. At the end of the shellcode, we don’t return: we simply enter an infinite loop and lock one CPU.

Due to the above, we can’t spawn a new thread. Instead, we need to spawn a new child process, and elevate *it’s* privileges, instead of our own. This is an easy modification of the last shellcode we used. The observant reader will notice this at the end of our shellcode, represented as \xEB\xFE.

Finishing Touches

Our exploitation approach is, in this case, as follows:

  • Create a cmd.exe child process
  • Prepare our token thief shellcode with our child’s PID
  • Send IOCTL with a buffer long enough to trigger an overflow
  • Return to our shellcode, which gives our child a SYSTEM pid, and then loops infinitely.
  • ??? (probably do bad stuff as SYSTEM)
  • Need to force a shutdown, because process can’t exit otherwise.

In practice:

You’ll notice there’s a delay of 3 seconds, while we wait for a “stable” cmd.exe. This is a lesson learned during my exploitation attempts – if you attempt to modify cmd.exe’s privilege token too quickly, you can get a blue screen.

This exploit is a little (alot!) more stable than the arbitrary overwrite, as we aren’t wrecking HalDispatchTable. As before, the code is available in the download pack with the first post.

I hope you enjoyed this mini-series – the next steps are to upgrade this to Windows 10, and investigate other types of kernel exploitation primitives. I’m sure I’ve missed plenty of stuff – please leave comments if I’ve missed something in my blind stumbling around, and there’s a better way to do things 😀

Posted in Uncategorized | Leave a comment

Windows 7 x64 Kernel Exploitation – Arbitrary Write (2/3)

In this post, we will dive into an actual example of exploitation, against an arbitrary write-what-where vulnerability.

The Vulnerability: TriggerArbitraryOverwrite

The vulnerability we are exploiting is within the TriggerArbitraryOverwrite function. In source code, we can see this in the “TriggerArbitraryOverwrite” function, as follows:

NTSTATUS TriggerArbitraryOverwrite(IN PWRITE_WHAT_WHERE UserWriteWhatWhere) {
 PULONG_PTR What = NULL;
 PULONG_PTR Where = NULL;
 NTSTATUS Status = STATUS_SUCCESS;

PAGED_CODE();

__try {
 // Verify if the buffer resides in user mode
 ProbeForRead((PVOID)UserWriteWhatWhere,
 sizeof(WRITE_WHAT_WHERE),
 (ULONG)__alignof(WRITE_WHAT_WHERE));

What = UserWriteWhatWhere->What;
 Where = UserWriteWhatWhere->Where;

DbgPrint("[+] UserWriteWhatWhere: 0x%p\n", UserWriteWhatWhere);
 DbgPrint("[+] WRITE_WHAT_WHERE Size: 0x%X\n", sizeof(WRITE_WHAT_WHERE));
 DbgPrint("[+] UserWriteWhatWhere->What: 0x%p\n", What);
 DbgPrint("[+] UserWriteWhatWhere->Where: 0x%p\n", Where);

#ifdef SECURE
 // Secure Note: This is secure because the developer is properly validating if address
 // pointed by 'Where' and 'What' value resides in User mode by calling ProbeForRead()
 // routine before performing the write operation
 ProbeForRead((PVOID)Where, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
 ProbeForRead((PVOID)What, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));

*(Where) = *(What);
#else
 DbgPrint("[+] Triggering Arbitrary Overwrite\n");

// Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability
 // because the developer is writing the value pointed by 'What' to memory location
 // pointed by 'Where' without properly validating if the values pointed by 'Where'
 // and 'What' resides in User mode
 *(Where) = *(What);
#endif
 }
 __except (EXCEPTION_EXECUTE_HANDLER) {
 Status = GetExceptionCode();
 DbgPrint("[-] Exception Code: 0x%X\n", Status);
 }

return Status;
}

We should look in the disassembly to get a more detailed understanding of what we’re exploiting. In IDA, we can see this in the function at 0x140006090:

To translate, we can trigger this vulnerability by calling this function, and passing in a structure argument which looks like the following:

typedef struct _WRITE_WHAT_WHERE{
 PULONG What;
 PULONG Where;
}WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

From here, the function will naively behave as specified in the source code, taking *what and writing it to *where.

Interlude: the Kernel, Drivers, and You

We cannot call a device driver function directly – however, Windows offers the DeviceIoControl function, which passes a controlled interface to device drivers, as a way for user interaction with drivers (and thus, hardware):

The general approach to this is as follows:

  • Use the CreateFile function, with a first argument of “\\\\.\\HackSysExtremeVulnerableDriver”
  • Use DeviceIoControl, passing in the appropriate IOCTL argument (as specified in HackSysExtremeVulnerableDriver.h as HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE), passing in our vulnerable buffer as an argument.
  • This triggers TriggerArbitraryOverwite, passing in our vulnerable buffer.

It also is responsible for coordinating actions such as device IO: an interface to this functionality is offered via the Device Driver mechanism, and functions like DeviceIoControl.

Exploiting the Vulnerability

Unfortunately, this vulnerability does not immediately allow us to control the instruction pointer (or does it?). The way we exploit this is corrupting another code path: specifically, we corrupt the code path of NtQueryIntervalProfile, by overwriting the second element of the HalDispatchTable structure. To understand this, we can delve into the NtQueryIntervalProfile function itself:

Here, we can see that the NtQueryIntervalProfile function, callable from userland, calls KeQueryIntervalProfile, which in turn straight-up calls a predictable address in the kernel. Corrupt the address, and we control the code flow.

Unfortunately, KASLR suddenly rears it’s ugly head. In this specific case, it is not feasible to simply “brute force” KASLR (even if it were brute forceable with the increase in possible entropy provided by 64-bit). Thankfully, we can resolve the real kernel base address by querying a known symbol in memory, and comparing it’s offset-to-base to a copy on disk (this is easier to understand in code – I’ve stolen someone’s example code for simplicity’s sake, but the concept is straightforward).

This is enough to get us arbitrary code exec at a given address, and can be tested by substituting the shellcode in the sample code with int 3’s.

Interlude: The Token and the Thief

In kernel-land, our payload cannot be as simple as “cmd.exe” from shellstorm.org. Given the lack of error handling in the kernel, our payload should be as simple as possible: in this case, we utilize the windows privilege model to give ourselves system privileges.

Each process in Windows is assigned a privilege token, which is visible in it’s _EPROCESS structure. These are effectively joined in a double linked list: so our shellcode is a simple token stealer, which traverses this linked list, stealing the token of the _EPROCESS strucutre with PID 4, and giving it to ourselves.

Unfortunately, due to the recent changes in Visual Studio, it is not permitted to inline assembly. FASM (or a compiler / assembler of your choice) to the rescue.

This is enough to build a complete working privilege escalation exploit.

Finishing Touches

To summarize, our exploitation technique is as follows:

  • Prepare token thief shellcode with our PID.
  • Send IOCTL to overwrite HalDispatchTable[1] with address of our shellcode
  • Call NtQueryIntervalProfile, which triggers our shellcode
  • Steal token from PID 4 to ourselves
  • Create cmd.exe, which spawns under our token (SYSTEM)

Hopefully, this should give us a shell with ntauthority\system privilege (which is what PID 4 naturally has):

Success! The exploit code is available with the download pack in Part 1. If you’re following along, you’ll quickly note that any further (legitimate) calls to NtQueryIntervalProfile / KeQueryIntervalProfile may result in strange behaviour, including blue screens. Of note, this is reliably triggered in the shutdown process: shutting down generally produces a blue screen, as we’re not replacing the original HalDispatchTable[1] after our exploit.

In the next post, we will explore using a stack overflow vulnerability.

Posted in Uncategorized | Leave a comment