Previous posts in this series have demonstrated how unpack.py, when used on a CryptoLocker variant, extracts the malicious PE file injected in to explorer.exe, and how it can also be used to analyse the injected PE file. This post demonstrates how unpack.py can now be used to analyse our CryptoLocker variant’s network communications by dumping the cleartext traffic sent over its HTTPS connections.
Background
I’m investigating whether my automated unpacking and analysis script (unpack.py) is still useful, by using it to analyse a malware sample (a CryptoLocker variant) which was created after my script was. The first two posts in this series (Initial Analysis and The unpacked payload) identified and analysed some unpacked code which was used to overwrite another process. Let’s see if we can do anything with our malware sample’s encrypted network communications.
I’ve been musing, for some time now, about the possibility of extracting cleartext from SSL/TLS connections by examining the memory buffers being passed to the encryption/decryption API calls. My main problem was finding information on what API calls were actually used to encrypt/decrypt traffic for TLS/SSL connections.
It turns out that EncryptMessage() and DecryptMessage() (from secur32.dll) are used to encrypt and decrypt data sent and received over TLS/SSL (and hence over HTTPS) connections. I’ve added extra code to unpack.py to hook these calls and log the cleartext data passed to/received from them — this gives us the cleartext data sent and received over HTTPS connections. Before we start on that though, let’s go back to basics to analyse our malware sample’s network traffic.
First we’ll need to create a fake Internet environment so that our malware sample thinks it has Internet access, without us having to give it Internet access. Before anything can connect to a host on the Internet, it has to either use the destination host’s IP address, or resolve its host name.
Faking Internet servers
If you remember back to part two, we saw this sample spitting out DNS queries for weird looking host names, so before we see any connections from the sample, those DNS queries are going to have to succeed and give the sample an IP address to connect to.
There is a handy way of making these DNS queries succeed without giving the malware sample access to the Internet — dnsspoof. dnsspoof is part of Dug Song’s dsniff package.
dnsspoof will take a hosts file and will respond to DNS queries for any hosts mentioned therein. If a hosts file isn’t specified, then dnsspoof will answer all DNS queries with the local machine’s IP address. This means that we can create a hosts file for the whole DNS domain that the malware was issuing DNS queries for (dnsspoof will take wildcards in the hosts file), and specify an IP address of a host which can handle subsequent connections. Alternatively, we can not specify a hosts file in which case the malware sample will attempt to connect to the local machine. Which approach you take will depend on available hardware/resources and your appetite for risk.
Now, if we get dnsspoof to respond to the malware sample’s DNS queries with the IP address of the local machine, the IP address of an existing machine on the local network, or an IP address that needs to be routed via the local gateway, then we will be able to see what connections the malware sample is trying to make. If we don’t use one of those options for the DNS reply then we will more than likely not see a connection attempt because the malware machine will need to ARP to find the MAC address to which the IP packet should be sent and we won’t see an IP packet unless ARP succeeds.
I’m going to run dnsspoof without a hosts file so that it returns the local machine’s IP address in response to all DNS queries, but I will specify an interface for dnsspoof to listen on so that dnsspoof only responds to DNS queries on my sandbox network. I’m also going to use the Linux iptables command to block all inbound connections on the sandbox network. This is because I don’t yet know what connections the malware sample is going to make. I want to see that first, then I’ll allow the connections once I’ve configured some appropriate software to handle them. I’ll allow the sample to connect to netcat (or some other monitoring software), for instance, but I don’t want it connecting to Apache (or anything real which may give information out, or present vulnerabilities to the malware).
Let’s start dnsspoof and then run the 1300.exe sample again with unpack.py and see what happens when the sample receives a valid DNS response to its DNS queries. Attempt to run dnsspoof as a non-root user, as that’ll limit what dnsspoof can do should it be exploited.
[code autolinks=”false”]
# try as a non-root user
/usr/sbin/dnsspoof -i vnet0
dnsspoof: libnet_open_link(): UID/EUID 0 or capability CAP_NET_RAW required
# give dnsspoof CAP_NET_RAW capability, as root:
setcap cap_net_raw+eip /usr/sbin/dnsspoof
# … and try running it again (non-root user)
/usr/sbin/dnsspoof -i vnet0
dnsspoof: listening on vnet0 [udp dst port 53 and not src 555.25.21.1]
[/code]
That IP address (555.25.21.1 — obviously obfuscated) is the local host’s IP address of the specified network interface. Now let’s run the malware sample again and see what we get. Now I’m running this on Windows 7, which is 64-bit. The malware sample (1300.exe) is 32-bit and the default Python installation is 64-bit, so I need to explicitly run it with the 32-bit Python installation:
c:\Python27_32\python.exe .\unpack.py 1300.exe > inf.log 2> inf-stderr.log
If you launch unpack.py using the 64-bit version of Python, then you’ll see unhandled EXCEPTION_WX86_BREAKPOINT exceptions, presumably because this is a 32-bit sample.
Firstly, let’s look at the network traffic. We notice that dnsspoof logged DNS queries that it saw and responded to. Note that the source address and domain name have been obfuscated, and that the destination address (of the DNS server) has been changed to that of Google’s public DNS server:
555.25.21.3.1026 > 8.8.8.8.53: 42496+ A? itylixelo.domain.malicious 555.25.21.3.1026 > 8.8.8.8.53: 20302+ A? iwybojiju.domain.malicious 555.25.21.3.1026 > 8.8.8.8.53: 15587+ A? ohibac.domain.malicious
Looking at the packet capture we see:
[code autolinks=”false”]
21:27:05.169659 IP 555.25.21.3.1026 > 8.8.8.8.53: 42496+ A? itylixelo.domain.malicious. (41)
21:27:05.304993 IP 8.8.8.8.53 > 555.25.21.3.1026: 42496 1/0/0 A 555.25.21.1 (57)
21:27:05.314298 IP 555.25.21.3.1047 > 555.25.21.1.443: Flags [S], seq 1126513720, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:08.159286 IP 555.25.21.3.1047 > 555.25.21.1.443: Flags [S], seq 1126513720, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:14.168397 IP 555.25.21.3.1047 > 555.25.21.1.443: Flags [S], seq 1126513720, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:31.251326 IP 555.25.21.3.1026 > 8.8.8.8.53: 20302+ A? iwybojiju.domain.malicious. (41)
21:27:31.416999 IP 8.8.8.8.53 > 555.25.21.3.1026: 20302 1/0/0 A 555.25.21.1 (57)
21:27:31.418366 IP 555.25.21.3.1049 > 555.25.21.1.443: Flags [S], seq 2975968976, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:34.397092 IP 555.25.21.3.1049 > 555.25.21.1.443: Flags [S], seq 2975968976, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:40.406222 IP 555.25.21.3.1049 > 555.25.21.1.443: Flags [S], seq 2975968976, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:27:57.470830 IP 555.25.21.3.1026 > 8.8.8.8.53: 15587+ A? ohibac.domain.malicious. (38)
21:27:57.529007 IP 8.8.8.8.53 > 555.25.21.3.1026: 15587 1/0/0 A 555.25.21.1 (54)
21:27:57.530354 IP 555.25.21.3.1051 > 555.25.21.1.443: Flags [S], seq 246367126, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:28:00.434397 IP 555.25.21.3.1051 > 555.25.21.1.443: Flags [S], seq 246367126, win 64240, options [mss 1460,nop,nop,sackOK], length 0
21:28:06.443101 IP 555.25.21.3.1051 > 555.25.21.1.443: Flags [S], seq 246367126, win 64240, options [mss 1460,nop,nop,sackOK], length 0
[/code]
So we can see from that output that it is just dying to open a connection to port 443/tcp on whichever host belongs to those weird host names in the DNS queries. In that packet capture we can see three attempts to resolve a host name and connect to it on port 443/tcp. If we look in unpack.py‘s log output, we see a pattern of two calls to InternetOpen() (from 0x771bafca and from 0x917d5) repeated three times:
[code autolinks=”false”]
[*] <3852:3856> 0x771bafca: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[*] <3852:3856> 0x917d5: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[*] <3852:3856> 0x771bafca: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[*] <3852:3856> 0x917d5: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[*] <3852:3856> 0x771bafca: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[*] <3852:3856> 0x917d5: InternetOpen("",0x0,"","",0x8404c700) = 0xcc0004
[/code]
Those flags that are passed as the fifth argument to InternetOpen() (the 0x8404c700) correspond to the following constants from wininet.h:
INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE
Right, let’s move on to something more interesting and give our malware sample something to connect to. First though, we’ll tweak our security a tad:
[code autolinks=”false”]
# As root, add an iptables NAT rule to redirect port 443/tcp to port 4443/tcp
# because we need root privileges to listen on port 443/tcp but not to listen on
# port 4443/tcp. This way we can run netcat as a non-root user, which is safer
# than running it as root.
iptables -t nat -A PREROUTING -i vnet0 -p tcp –dport 443 -j DNAT –to-destination :4443
# Now add iptables rules to allow an inbound connection to port 4443/tcp, on the sandbox interface (vnet0)
iptables -A INPUT -i vnet0 -p tcp –dport 4443 -m conntrack –ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o vnet0 -p tcp –sport 4443 -m conntrack –ctstate ESTABLISHED -j ACCEPT
[/code]
Then we’ll run netcat to listen on port 4443/tcp which, because of our iptables NAT rules will receive packets destined for port 443/tcp:
nc -l -p 4443 �L3 ��� @db��c��9�!-0�R(��
As expected, we see binary data come out from netcat. This is because our malware sample is attempting to open an HTTPS (port 443/tcp) connection which involves establishing SSL/TLS. SSL and TLS exchange binary data, not ASCII data. The binary data is in fact the SSL client hello message, which can be seen in a packet capture (Wireshark will decode the non-encrypted SSL/TLS data). To get any further with this, we need a valid SSL/TLS server — enter OpenSSL.
OpenSSL has the ability to act as an SSL/TLS client, and as an SSL/TLS server. SSL/TLS servers require a certificate though (to tie a public key used for encryption, with an identity — that of the server and server owner), so we’ll have to create one before we can go much further. Conveniently, OpenSSL can also help us with that. Here’s how we can generate a self-signed certificate for an SSL server — it’s not going to be trusted by anything, but should suffice for what we want to use it for:
[code autolinks=”false”]
# generate a certificate signing request
openssl req -new > cert.csr
# remove the passphrase (careful now)
openssl rsa -in privkey.pem -out key.pem
# sign the csr to produce a certificate
openssl x509 -in cert.csr -out cert.pem -req -signkey key.pem -days 7
# append the key to the certificate
cat key.pem >> cert.pem
# make it server.pem
ln -s cert.pem server.pem
[/code]
C’est bon. Now we can start OpenSSL‘s SSL/TLS server:
[code autolinks=”false”]
# start an SSL server
# note that the underscore may not be visible and that the openssl command is ‘s_server’
openssl s_server -accept 4443 -WWW
[/code]
OpenSSL‘s s_server command defaults to listening on port 4433/tcp, so we could have just translated port 443/tcp to port 4433/tcp and then we wouldn’t have to add ‘-accept 4443’ to OpenSSL‘s command line.
Run the malware sample (1300.exe) again and this time you should see connections to port 443/tcp in the packet capture. These connections are being translated by the iptables NAT rules to port 4443/tcp which is where we’re running OpenSSL‘s SSL/TLS server.
[code autolinks=”false”]
22:01:53.043689 IP 555.25.21.7.61541 > 8.8.8.8.53: 1103+ A? jzed.domain.malicious. (36)
22:01:53.132291 IP 8.8.8.8.53 > 555.25.21.7.61541: 1103 1/0/0 A 555.25.21.1 (52)
22:01:53.137224 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [S], seq 1918958643, win 8192, options [mss 1460,nop,wscale 2,nop,nop,sackOK], length 0
22:01:53.137348 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [S.], seq 3616910783, ack 1918958644, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
22:01:53.137648 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [.], ack 1, win 16425, length 0
22:01:53.240579 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [P.], seq 1:132, ack 1, win 16425, length 131
22:01:53.240708 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [.], ack 132, win 237, length 0
22:01:53.241032 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [P.], seq 1:995, ack 132, win 237, length 994
22:01:53.242788 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [P.], seq 132:458, ack 995, win 16176, length 326
22:01:53.248251 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [P.], seq 995:1054, ack 458, win 245, length 59
22:01:53.452125 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [P.], seq 995:1054, ack 458, win 245, length 59
22:01:53.452381 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [.], ack 1054, win 16161, options [nop,nop,sack 1 {995:1054}], length 0
22:02:08.629109 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [P.], seq 458:767, ack 1054, win 16161, length 309
22:02:08.668158 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [.], ack 767, win 254, length 0
22:04:07.790672 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [F.], seq 1054, ack 767, win 254, length 0
22:04:07.791078 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [.], ack 1055, win 16161, length 0
22:04:07.791419 IP 555.25.21.7.49163 > 555.25.21.1.443: Flags [F.], seq 767, ack 1055, win 16161, length 0
22:04:07.791469 IP 555.25.21.1.443 > 555.25.21.7.49163: Flags [.], ack 768, win 254, length 0
[/code]
Extracting the cleartext data
Now tcpdump can’t see the cleartext data that is sent in that connection because, well, that is the purpose of SSL/TLS. Also, I couldn’t find a way of getting OpenSSL to behave like netcat and to actually dump the cleartext data either. All is not lost however, remember that unpack.py has some new functionality — the ability to dump data buffers before encryption and after decryption. Looking at unpack.py‘s output we notice that it is now logging calls to EncryptMessage() (along with some debug messages which I didn’t remove):
[code autolinks=”false”]
[*] <2636:2668> wininet!GetFileExtensionFromUrl+0x24e2 0x767a3310: EncryptMessage(…)
[/code]
unpack.py has also created some new files:
[code autolinks=”false”]
-rw-r–r– 1 malware musings 280 May 10 22:08 1300.exe.encmsg0x004999ad-2636
-rw-r–r– 1 malware musings 273 May 10 22:02 1300.exe.encmsg0x004d3565-2636
-rw-r–r– 1 malware musings 274 May 10 22:10 1300.exe.encmsg0x004f0a0d-2636
-rw-r–r– 1 malware musings 274 May 10 22:05 1300.exe.encmsg0x004f1355-2636
-rw-r–r– 1 malware musings 277 May 10 22:16 1300.exe.encmsg0x0053ff6d-2636
-rw-r–r– 1 malware musings 550 May 10 22:13 1300.exe.encmsg0x03e6a265-2636
[/code]
Now, these files are the contents of the cleartext memory buffers passed to the EncryptMessage() API call being used to encrypt data destined for an SSL/TLS connection and, if you’re anything like me, you’ll be dying to know what’s in them, so let’s have a look:
POST /topic.php HTTP/1.1 Accept: */* Host: ezutiduduri.domain.malicious Content-Length: 160 Cache-Control: no-cache ̫�qd�U�*�+�T�i����
Hmm — a HTTP request containing binary data. Let’s see if we can extract the binary data from each of those encmsg files:
[code autolinks=”false”]
for f in *.encmsg*; do
binfile="bin-`expr \"$f\" | sed ‘s/^.*encmsg//’`"
sed ‘1,/^\r$/d’ "$f" > "$binfile"
done
[/code]
I should probably explain that. The binfile assignment is simply (or not, depending on your level of UNIX experience I guess) modifying the ‘1300.exe.encmsg0x…’ file names by removing everything up to and including the ‘.encmsg’ string (sed ‘s/^.*encmsg//’), and prepending the string ‘bin-‘ to the result (“bin-). This variable now holds the output file name for this particular encmsg file that the for loop has assigned to the f variable. Oh, I should probably also mention that all of my UNIX scripting is Bourne/bash shell — if you run that same script snippet in a csh, or tcsh, you’ll more than likely encounter problems (that’s something that I should have probably mentioned on a number of other posts too).
As for the second sed command, I’m actually surprised that it worked to be honest. The encmsg files contain the HTTP requests that the malware sample issued, before they were encrypted and sent over the SSL/TLS connection. HTTP requests contain headers which are terminated by a blank line. These are normally easy to remove, and a csplit command can usually be used to separate everything up to and including the first blank line, leaving us with the HTTP data.
These files (HTTP requests) however, contain binary data. The second sed command is an attempt to remove all of the lines up to and including the first line that contains only a carriage return character.
Using ‘/^$/’, that is, the regular expression to match a blank line (enclosed in ‘//’ so that csplit treats it as a regular expression), didn’t work so I examined the encmsg files and noticed that the HTTP headers were using the MS-DOS end-of-line convention of carriage return/line feed. The ‘\r’ matches the carriage return character and is necessary because UNIX uses a single line feed character to denote the end of a line and hence treats the carriage return as just another character that just happens to appear on the end of every line.
I didn’t want to remove carriage-returns with something like ‘tr -d \015’ because that would have also removed any such characters from the binary data, and we don’t want to modify the binary data.
Right — let’s see what that’s given us:
[code autolinks=”false”]# see what ‘bin-‘ files we have
$ ls -l bin-*
-rw-r–r– 1 malware musings 160 May 11 18:36 bin-0x004999ad-2636
-rw-r–r– 1 malware musings 160 May 11 18:36 bin-0x004d3565-2636
-rw-r–r– 1 malware musings 160 May 11 18:36 bin-0x004f0a0d-2636
-rw-r–r– 1 malware musings 160 May 11 18:36 bin-0x004f1355-2636
-rw-r–r– 1 malware musings 160 May 11 18:36 bin-0x0053ff6d-2636
-rw-r–r– 1 malware musings 437 May 11 18:36 bin-0x03e6a265-2636
# are they all the same?
$ md5sum -b bin-*
84f64bafc51066d82079831f99426c56 *bin-0x004999ad-2636
84f64bafc51066d82079831f99426c56 *bin-0x004d3565-2636
84f64bafc51066d82079831f99426c56 *bin-0x004f0a0d-2636
84f64bafc51066d82079831f99426c56 *bin-0x004f1355-2636
84f64bafc51066d82079831f99426c56 *bin-0x0053ff6d-2636
adcd7a89d672255221e5cc4a5bcf1101 *bin-0x03e6a265-2636
[/code]
So all of those files except the last one, are the same. Although unpack.py dumps the EncryptMessage() and DecryptMessage() memory buffers to the encmsg and decmsg files (respectively), it does so by appending the memory buffer to a file which is named based only on the memory buffer address and process id. That means that multiple EncryptMessages() (or DecryptMessages()) that use the same memory buffer in the same process will have their memory buffer contents concatenated together. Is that why the last file in the list above is different to the others, and also larger in size? Let’s have a look at it:
[code autolinks=”false”]
$ hexdump -C bin-0x03e6a265-2636
00000000 cc ab 86 71 64 a5 55 a4 2a b5 2b d7 54 83 69 a7 |…qd.U.*.+.T.i.|
00000010 ff 1b 48 89 1d ea ad 0a 5b 0f 00 93 d4 6e 54 a8 |..H…..[….nT.|
00000020 76 a6 19 c7 c1 1e 1a 6b b3 67 cf 14 0c 19 b7 aa |v……k.g……|
00000030 ac 29 9f 2c e8 4c 65 09 f0 d4 a3 1a cb e3 b5 94 |.).,.Le………|
00000040 b2 ca d4 f6 b0 98 51 b0 2b 7a d2 ae 7f 41 b8 4e |……Q.+z…A.N|
00000050 47 b9 b9 60 f3 27 9a 99 0d 52 6c e8 67 02 0b e0 |G..`.’…Rl.g…|
00000060 1b f7 6e e3 86 ea 29 de ad 60 c8 e4 b4 08 6a da |..n…)..`….j.|
00000070 03 9c a5 ae 5f 0a 48 0b c9 1b e8 05 54 61 06 ff |…._.H…..Ta..|
00000080 fb e0 26 db 96 cc 2e 19 fd 35 b1 be f3 e3 86 d6 |..&……5……|
00000090 58 ec 5c 70 87 32 f8 f6 bc cb 37 0d 51 bf 55 3c |X.\p.2….7.Q.U<|
000000a0 50 4f 53 54 20 2f 74 6f 70 69 63 2e 70 68 70 20 |POST /topic.php |
000000b0 48 54 54 50 2f 31 2e 31 0d 0a 41 63 63 65 70 74 |HTTP/1.1..Accept|
000000c0 3a 20 2a 2f 2a 0d 0a 48 6f 73 74 3a 20 69 62 61 |: */*..Host: iba|
000000d0 6b 65 70 6f 63 2e ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? |kepoc………..|
000000e0 ?? ?? ?? 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e |…..Content-Len|
000000f0 67 74 68 3a 20 31 36 30 0d 0a 43 61 63 68 65 2d |gth: 160..Cache-|
00000100 43 6f 6e 74 72 6f 6c 3a 20 6e 6f 2d 63 61 63 68 |Control: no-cach|
00000110 65 0d 0a 0d 0a cc ab 86 71 64 a5 55 a4 2a b5 2b |e…….qd.U.*.+|
[/code]
Bingo — notice the start of another HTTP request at offset 160 (0xa0)? Also note that 160 is the ‘Content-Length:’ mentioned in the HTTP headers, so anything after offset 160 has been appended to the original request. That is, it will be a second HTTP request that unpack.py has appended to the first because they were encrypted from the same memory buffer in the same process (and were hence written to the same file). Let’s separate the two and process the second request:
[code autolinks=”false”]
# copy ‘if’ to ‘of’ and skip 1 (skip) block of 160 bytes (bs)
# Skipping one block of 160 bytes is more efficient than skipping
# 160 blocks of 1 byte each as the former allows dd to read 160 bytes
# at a time, as opposed to 1 byte at a time
dd if=bin-0x03e6a265-2636 of=bin-0x03e6a265-2636b bs=160 skip=1
# run it through the sed command again to remove the HTTP headers
sed ‘1,/^\r$/d’ bin-0x03e6a265-2636b > binbin-0x03e6a265-2636
# compare the output
md5sum -b binbin-0x03e6a265-2636
84f64bafc51066d82079831f99426c56 *binbin-0x03e6a265-2636
[/code]
Smashing — that is the same as the other ‘bin-‘ files. As for what the contents of the binary data actually means, that is going to require manual analysis of the malware sample.
Conclusion
Now it would be interesting to allow this sample to communicate with the Internet so we can see the response from the server, however, it’s taken me so long to find the time to analyse this sample and now the domain name that it is looking for, while still showing up in whois, no longer resolves (the parent domain does not have an NS record for it).
Still, this final post in the series managed to demonstrate how unpack.py can be used to dump SSL/TLS data before encryption, and how you can trick malware samples in to talking to you rather than to their intended target.
So there you have it. My unpack.py script is still useful. This series has shown how it can:
Part 1: Initial Analysis
- Extract unpacked code and identify the unpacking loop
- Dump memory buffers that were decompressed using RtlDecompressBuffer()
- Identify process hollowing where a process is created in a suspended state, overwritten, and then resumed
Part 2: The unpacked payload
- Show new processes being created along with their command line
- Dump memory buffers that are decrypted using EncryptDecrypt()
- Dump memory buffers that are written to other processes using WriteProcessMemory()
- Provide information useful for further manual analysis using a debugger
Part 3: Network Communications
- Dump SSL/TLS data before it is encrypted (it also dumps received data after decryption but this post was unable to demonstrate that)
What was in the binary files? Don’t leave us hanging!