Home AceBear CTF 19
Post
Cancel

AceBear CTF 19

I had a lot of fun and a hard time during this CTF, but the challenges are really awesome. I spent the whole time solving one challenge: Incident Response (Misc 1000). In my opinion, this challenge closely resembles a real-life scenario and I’m glad that I’ve learned a lot out of it. So here goes the write up for it!

Memory forensics - Identifying the malicious program

In this challenge, we’re given a network capture and a memory dump of a Windows machine. Opening up the network capture, we can see that the majority of it is network activity between 192.168.182.136 (a local IP address) and 13.229.248.109 on port 443. These activities are identified as SSL because it’s on port 443 but there are some data that doesn’t seem to be SSL/TLS, for example: ASCII data at the beginning of packet #13, and there are patterns in most of other packets. This made me think that it’s not really SSL/TLS traffic but custom encrypted data.

pcap-1

A useful tool for memory forensics is Volatility. Based on the information gathered from the pcap, we can think of looking at network connections in the dump to identify which process was making it.

1
2
3
$ vol.py -f WIN-2VTHJA77HMB-20190313-143547.raw --profile=Win7SP1x86 netscan
...
0x7f8a98d8         TCPv4    192.168.182.136:49158          13.229.248.109:443   ESTABLISHED      1964     explorer.exe   

There goes the IP address of the C2 server, and it’s definitely strange that explorer.exe was making internet connections. This highly indicates that explorer.exe was injected with malicious code. We can dump the memory of explorer.exe to find the malicious code if it’s direct injection, or we can examine the DLLs loaded by explorer.exe if it’s DLL injection. Listing the DLLs, we can see a very suspicious one in an uncommon location

1
2
3
4
$ vol.py -f WIN-2VTHJA77HMB-20190313-143547.raw --profile=Win7SP1x86 dlllist -p 1964
...
0x6eb30000    0x25000        0x1 C:\Users\misc\AppData\Local\Temp\FileProcess.dll
...

We can then dump these DLLs using the following command:

1
$ vol.py -f WIN-2VTHJA77HMB-20190313-143547.raw --profile=Win7SP1x86 dlldump -p 1964 -D explorer_dll/

Now it’s time for some reverse engineering :)

Reverse engineering the DLL

This is probably the hardest part. Now that we’ve identified the malicious code, we need to figure out how it encrypts the data, and the flag is probably in the decrypted data. From the network capture, we can see that traffic mostly comes from the client so there is exfiltration going on. These are good hints to identify possible API imports in the DLL:

  • CryptDecrypt, CryptEncrypt and other crypto APIs
  • socket, connect, send to communicate over the network

2.1. First glance - static analysis

Opening the binary in IDA, we can already see some of these imports. We arrive at the DllMain function and there’s the call to loc_6EB32960. However, this is not a function and IDA failed to identify this as a function. This is the first time that I’ve ever dealt with anti-disassembly and it’s quite interesting to know how this works. If you go to the code being called above and keep going down a little bit, you’ll see an instruction highlighted in red at 6EB32A25. The strange thing is right before that, there a jump to 6EB32A25 + 1, so this can’t be the correct disassembly.

anti-disassembly-1

Hitting P to create a function at 6EB32960 failed, so to fix this, I undefined the instruction highlighted in red, click on 6EB32A26 (6EB32A25 + 1), pressed C to define this as code and patched the only byte left as nop. Now we can go back to the function prologue and hit P again to define a function. Worked like a charm! At that point, I still wasn’t sure if this would impact the code being run, but it’s nice to be able to see the code in graph mode, so I went on and fix all the remaining failed disassembly using this method. There are a few places with random bytes (indicated by the db instruction) that you can also nop out to fix the disassembly.

There’s a few things worth noting in this start function:

  • There are stack strings which looks like base64, but decrypting it yields no readable ascii. base64
  • It gathers some system information and writing it to sysinfo.
  • It creates 3 threads of functions at the following locations: 6EB31AE0 (Thread1), 6EB31D70 (Thread2) and 6EB32230 (Thread3)
    • Thread1 contains more stack strings and call APIs such as GetDC, GetCompatibleBitmap, BitBlt, which indicates that it may be taking screenshots.
    • Thread2 makes networking APIs call such as htons, socket, connect, send.
    • Thread3 also makes networking APIs call including inet_addr. This can be where the C2 IP address is saved.

Diving more into these functions, I figured out more important functions:

  • sub_6EB31050: basically sprintf
  • sub_6EB31090: Write a screenshot to a file.
  • sub_6EB31360: A base64 encode/decode function.
  • sub_6EB31560: Get the current process name
  • sub_6EB31620: A decryption function
  • sub_6EB31820: Send file over the network
  • sub_6EB32180: Run a reverse shell
  • sub_6EB32360: The action is taken based on global boolean variables.
    • Get the IP address of the C2 server and save it into a global variable at 6EB518A0. Get the screen sizes and also store it in global variables.
    • Open directories containing “AppData” in the path.
    • Perform some cryptographic operations.

How did I discover those functions? I basically looking at return values from API calls that are stored in global variables and how those global variables are used. For example:

  • Thread3 opens a socket and saves it to a global variable at 6EB50948. Right after that, a function is called and it uses this socket along with the OpenProcessA API with the parameter to C:\Windows\System32\cmd.exe (as a stack string).
  • Global boolean variables are used in if statements to make decisions about the actions being performed. Also notes to increment of some variables because they are used as counters across the threads to also make decisions about actions.

The descriptions provided above are really superficial because I also made guesses, but they captures the basic actions being performed.

Now let’s put together the pieces.

2.2. Putting them all together - Dynamic Analysis

The tools that I used for dynamic analysis include Procmon, Process Explorer and x64dbg. Debugging the DLLs can be a little bit difficult because it only runs inside explorer.exe. After trying several ways to inject the DLL into explorer.exe without any luck, I ended up using the AppInit_DLLs registry key to load it when explorer.exe is run.

2.2.1. File system activities

Monitoring file system activities, we can see the folders and files that it’s trying to access. There’s a noticeable amount of activities in C:\Users\<username>\AppData\Roaming\Microsoft\Explorer\ and it’s because it couldn’t find the path. The Explorer folder wasn’t there! This is related to the function all at the very beginning of the DLL. sub_6EB32360 is called to initialized data such as IP addresses and directory to write data to. For some reasons, the function failed to initialized the data as intended because all the boolean variables that I’ve mentioned previously couldn’t be set. Setting breakpoints at CreateFile and WriteFile can reveal the same information in the debugger (which is the actual way that I figured this out.) So I just created the folder myself. If the initialization were successful, a file named sysinfo would have been in the folder, but it was never in there.

procmon1

After that, it runs that initialization function again and finally creates 3 threads with the 3 functions that we’ve found in static analysis. As mentioned before, Thread1 is responsible for taking screenshots and saving them to files. The filenames are unix timestamps, which can also be observed in the pcap. More details are in the next section.

Note: If you encounter the same issue, you will need to create the folder manually. Otherwise, you won’t be able to observe the network traffic because there is no data to be sent.

2.2.2. Networking activities

Observing more closely, around the call to send in Thread2, we can see the string “1” being copied and a function call to sub_6EB31820. Inside sub_6EB31820, we can also see API calls to send and the string “2”. Going back to the pcap, we can see packets starting with “1” and “2”, so these indicate the data type being sent.

sendfile

There are 2 significant counters that are used: A SentFileCounter at 6EB51458 and a CurrentFileCounter at 6EB51460. CurrentFileCounter is incremented in Thread1 every time a screenshot is taken, and SentFileCounter is incremented whenever a file is sent in Thread2. When does the data get sent? The data only get sent when the CurrentFileCounter is at least 5. If the SentFileCounter reaches 0x12C, a reverse shell is spawned by Thread3.

filecount

sentfilecount

It is worth noting that there are 2 separated sockets being used: 1 is for the reverse shell in Thread3 and 1 is for sending the screenshots. Setting breakpoints on htons, inet_addr, socket, connect and send will reveal the information being sent over the network. Following are the 2 types of packets being sent that we’ve observed before: one starts with “1” and one starts with “2”.

send2

send1

The first packet is the unencrypted filename that we can tell from procmon in the previous section. We still have no idea what the second packet is, but the first few bytes are similar to many of those in the pcap, so this highly indicates that the same encryption key is used.

2.2.3. Cryptographic functions

The decryption function is called a lot of times across the program. Setting breakpoints at CryptDecrypt will just reveal the decrypted data. While debugging, I did notice special strings being passed around.

decrypt1

decrypt2

decrypt3

decrypt4

ip1

string1

2.2.4. Adding everything up

Now that we know the second type of packet is encrypted, we also have the data that the program generated. Opening up the files in a hex editor, we can see that it’s an unencrypted bitmap image.

bitmap1

It turns out that the program uses XOR encryption to encrypt the data. The encryption can be observed inside sub_6EB31820 and also by XOR-ing the first few bytes of the encrypted and unencrypted data. The key is stored in a global variable at 6EB51920.

xor1

xor2

xor3

Also notice that there is a huge buffer in this function, which is used to store the data being sent over the network.

databuffer

Now we just need to observe the memory that stores the key and we are able to get it.

databuffer

Notice that the length of sent data is 1028 but the buffer is only 1024, so there is a 4-byte header before the actual data starts. In addition, only the first 8 bytes of the key is used. The job now is to parse all the packets sent to the server from the pcap, identifying the packets of type 2 that we need to decrypt and concat them together and XOR-decrypt them.

My solution was not able to properly decrypt the packets, but the images still showed up and it was sufficient to see the flag. The images were staggered so I made another script to (somewhat) fix the images. They are still really bad but I was lucky enough to see that flag :D

Here is the image before fixing:

imagebefore

And here’s the image after fixing:

imageafter

There’s the flag: AceBear{M3m0ry_f0r3nsic_&_M4lwa3r_an4lysis_i5_cool}

Also if you look carefully, there’s an URL in the picture in Internet Explorer. I tried going to the URL but it was already down at that point: http://mi5cfl4g.chung96vn.cf/fl4g_mi5c

Full solution can be found here: https://github.com/ducphanduyagentp/ourCTFs/tree/master/2019/acebear-ctf-19/misc/incident-response

Future improvements

There are several things that I can improve in the future and would have saved me a lot of time looking at this challenge:

  • A quick way to inject the DLL into the process. I spent many hours debugging explorer.exe inside x64dbg and just manually loaded the DLL every time, which was really time-consuming.
  • A quick way to patch the bytes used for anti-RE. I should probably learn idapython :)

If you have any questions, please feel free to leave a comment below or reach out to me on Twitter. Thanks for reading!

This post is licensed under CC BY 4.0 by the author.