PCAP Me If You Can (forensics 300)
The hackers have written their own protocol for their MALL-ware. Can you figure out what they’re saying? Hint 1: custom protocols are hard Hint 2: Don’t worry, I won’t make you decrypt anything in this challenge.
Author: hulto pcapmeifyoucan.pcapng
The challenge was about locating a custom protocol among other usual traffic, so the first task was to look for abnormal packets in regular protocols or an unrecognized protocol itself. The first approach was to list all unique protocols captured:
$ tshark -r pcapmeifyoucan.pcapng -T fields -e frame.protocols | sort | uniq
eth:ethertype:arp
eth:ethertype:ip:icmp:ip:udp:data
eth:ethertype:ip:icmp:ip:udp:dns
eth:ethertype:ip:tcp
eth:ethertype:ip:tcp:data
eth:ethertype:ip:tcp:http
eth:ethertype:ip:tcp:http:data-text-lines
eth:ethertype:ip:tcp:ssh
eth:ethertype:ip:tcp:ssl
eth:ethertype:ip:tcp:ssl:ocsp:ssl
eth:ethertype:ip:tcp:ssl:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:pkix1implicit:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:pkcs-1:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:x509ce:x509ce:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:x509ce:x509ce:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:x509ce:pkix1implicit:x509ce:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1implicit:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1implicit:ssl:ocsp
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:ssl:ocsp
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:tcp:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:x509ce:pkix1explicit:pkix1implicit:x509ce:ssl:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509sat:x509ce:x509ce:x509ce:pkix1implicit:x509ce:x509ce:pkix1explicit:x509ce:x509ce:ssl
eth:ethertype:ip:udp:data
eth:ethertype:ip:udp:dns
eth:ethertype:ip:udp:mdns
eth:ethertype:ip:udp:nbns
eth:ethertype:ipv6:udp:mdns
Discarding the ssl protocols a few other regular ones came up. The first choice was the DNS protocol, used for data exfiltration, but nothing out of the ordinary was found. Next the ARP and HTTP protocols were examined. Nothing, except this:
GET /watch?v=dQw4w9WgXcQ HTTP/1.1
Host: www.youtube.com
User-Agent: curl/7.61.0
Accept: */*
Let’s try to get something from the used ports. By building a filter to list the unique destination ports (therefore, identifying servers) and ignoring the ones in the ephemeral range (32768+)…:
$ tshark -r pcapmeifyoucan.pcapng -T fields -e tcp.dstport tcp.dstport lt 32768 | sort | uniq
22
443
80
8888
What is that 8888 port doing there? Looks promising, let’s check these packets.
$ tshark -r pcapmeifyoucan.pcapng tcp.port eq 8888
13990 68.058515 172.16.140.131 45826 172.16.140.1 8888 TCP 74 45826 → 8888 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=2111755176 TSecr=0 WS=128
13991 68.058661 172.16.140.1 8888 172.16.140.131 45826 TCP 78 8888 → 45826 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=32 TSval=676410590 TSecr=2111755176 SACK_PERM=1
13992 68.058814 172.16.140.131 45826 172.16.140.1 8888 TCP 66 45826 → 8888 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=2111755177 TSecr=676410590
13993 68.058874 172.16.140.1 8888 172.16.140.131 45826 TCP 66 [TCP Window Update] 8888 → 45826 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=676410591 TSecr=2111755177
13994 68.058952 172.16.140.131 45826 172.16.140.1 8888 TCP 112 45826 → 8888 [PSH, ACK] Seq=1 Ack=1 Win=29312 Len=46 TSval=2111755177 TSecr=676410590
13995 68.059025 172.16.140.1 8888 172.16.140.131 45826 TCP 66 8888 → 45826 [ACK] Seq=1 Ack=47 Win=131712 Len=0 TSval=676410591 TSecr=2111755177
13998 68.071416 172.16.140.1 8888 172.16.140.131 45826 TCP 74 8888 → 45826 [PSH, ACK] Seq=1 Ack=47 Win=131712 Len=8 TSval=676410603 TSecr=2111755177
13999 68.071475 172.16.140.1 8888 172.16.140.131 45826 TCP 66 8888 → 45826 [FIN, ACK] Seq=9 Ack=47 Win=131712 Len=0 TSval=676410603 TSecr=2111755177
14000 68.071634 172.16.140.131 45826 172.16.140.1 8888 TCP 66 45826 → 8888 [ACK] Seq=47 Ack=9 Win=29312 Len=0 TSval=2111755190 TSecr=676410603
14001 68.071832 172.16.140.131 45826 172.16.140.1 8888 TCP 66 45826 → 8888 [FIN, ACK] Seq=47 Ack=10 Win=29312 Len=0 TSval=2111755190 TSecr=676410603
14002 68.071904 172.16.140.1 8888 172.16.140.131 45826 TCP 66 8888 → 45826 [ACK] Seq=10 Ack=48 Win=131712 Len=0 TSval=676410603 TSecr=2111755190
[...]
It’s a brief but regular TCP communication: a handshake, some data goes, some data comes and finish. Data, huh? The data section was present in the PSH,ACK packets, so these were filtered:
$ tshark -r pcapmeifyoucan.pcapng -T fields -e data tcp.flags eq 0x018 and tcp.port eq 8888
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeadedf3f200
c3d0d0cdd09e1337
566572622e575249544500eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00a0f5e3eaea9ee6edf5e2f7a000
f5e3eaea9ee6edf5e2f79eadf2ebeeadedf3f2881337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeadedf3f200
c3d0d0cdd09e1337
566572622e575249544500eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00cdcbc59ee6edf59edff0e39ef7edf3bd00
cdcbc59ee6edf59edff0e39ef7edf3bd9eadf2ebeeadedf3f2881337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeadedf3f200
cdcbc59ee6edf59edff0e39ef7edf3bd9e881337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00f5e6eddfebe700
c3d0d0cdd09e1337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeadedf3f2b99ef5e6eddfebe700
cdcbc59ee6edf59edff0e39ef7edf3bd9e88e6f3eaf2ed881337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeade2dff2df00
d0c7d2d1c3c1f9d2e6aff1dde7f1ddcbf7ddcedfb3f1f5aef0e2ddd2e6b1f0e3dddff0e3ddcbdfecf7ddeae7e9e3dde7f2dde0f3b5ddf2e6e7b3ddafdde7f1ddebe7ece3fb881337
566572622e575249544500eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00f7aeeaae9eebdfec00
f7aeeaae9eebdfec9eadf2ebeeadedf3f2881337
566572622e5245414400eee3ecf2dface5edece300afb1b1b5e6dff6edf0f1d0e1aeaeea00adf2ebeeadedf3f200
f7aeeaae9eebdfec881337
This output was saved in a file named comm.txt
and analyzed. First things noted were:
1) it looks like a request and a response communication style;
2) the 00
value looks like as a separator;
3) there’s a “1337” (leet) in all the reponses, maybe a breadcrumb hint;
The longer string (the request) was converted to ASCII, using every pair of chars as representation of a byte:
566572622e52454144 eee3ecf2dface5edece3 afb1b1b5e6dff6edf0f1d0e1aeaeea adf2ebeeadedf3f200
Verb.READ îãìò߬åíìã ¯±±µæßöíðñÐá®®ê òëîíóò
“Verb.READ” sounds good. There are three other similar strings with different value for the first part:
566572622e5752495445 eee3ecf2dface5edece3 afb1b1b5e6dff6edf0f1d0e1aeaeea a0f5e3eaea9ee6edf5e2f7a0
Verb.WRITE îãìò߬åíìã ¯±±µæßöíðñÐá®®ê õãêêæíõâ÷
“Verb.WRITE”. Sweet. Now let’s remove everything that’s redundant and work only with the differences:
V E R B . R E A D |
adf2ebeeadedf3f2 00
c3d0d0cdd09e1337
V E R B . W R I T E |
a0 f5e3eaea9ee6edf5e2f7 a0 00
f5e3eaea9ee6edf5e2f7 9eadf2ebeeadedf3f2881337
V E R B . R E A D |
adf2ebeeadedf3f2 00
c3d0d0cdd09e1337
V E R B . W R I T E |
cdcbc59ee6edf59edff0e39ef7edf3bd 00
cdcbc59ee6edf59edff0e39ef7edf3bd 9eadf2ebeeadedf3f2881337
V E R B . R E A D |
adf2ebeeadedf3f2 00
cdcbc59ee6edf59edff0e39ef7edf3bd9e881337
V E R B . R E A D |
f5e6eddfebe7 00
c3d0d0cdd09e1337
V E R B . R E A D |
adf2ebeeadedf3f2b99ef5e6eddfebe7 00
cdcbc59ee6edf59edff0e39ef7edf3bd9e88e6f3eaf2ed881337
V E R B . R E A D |
adf2ebeeade2dff2df 00
d0c7d2d1c3c1f9d2e6aff1dde7f1ddcbf7ddcedfb3f1f5aef0e2ddd2e6b1f0e3dddff0e3ddcbdfecf7ddeae7e9e3dde7f2dde0f3b5ddf2e6e7b3ddafdde7f1ddebe7ece3fb881337
V E R B . W R I T E |
f7aeeaae9eebdfec 00
f7aeeaae9eebdfec 9eadf2ebeeadedf3f2881337
V E R B . R E A D |
adf2ebeeadedf3f2 00
f7aeeaae9eebdfec881337
The first visible pattern is that every “WRITE” receives as response the request itself and some other fixed-value string. Probably a success or error message. Let’s ignore this for now. The response to the sixth “READ” seems off from the others, let’s dig deeper there.
d0 c7 d2 d1 [...]
dec 208 199 210 209
We know that the flag begins with “RITSEC”, right? The decimal distance from the first to the fourth value is 1, from the fourth to the third is also 1 and from the first to the third is 2. You get the picture:
d0 c7 d2 d1
dec 208 199 210 209
R I T S
dec 82 73 84 83
208 - 82 = 199 - 73 = 210 - 84 = 209 - 83 = 126
Seems that we found the secret protocol. The python program decode.py
below decodes all the communication.
# Open and read the file containing all the packets' data field
with open('comm.txt') as f:
data = f.read().strip().split("\n")
f.close()
# Read every two lines as one "communication" (request + response)
for communication in range(0, len(data), 2):
print("Request: ")
# Group the data on a 2-char basis and split it when equal to "00"
request = []
str = ""
for i in range(0, len(data[communication]), 2):
if(data[communication][i:i+2] == "00"):
request.append(str)
str = ""
else:
str += data[communication][i:i+2]
# Now that the request was split into separated parts, we'll convert
# each of these parts to a string, grouping it into pairs of chars todo
# make one byte
for part in range(0, len(request)):
# "Bytefy" the part being parsed
for i in range(0, len(request[part]), 2):
byte = int(request[part][i:i+2], 16)
if byte >= 126:
byte -= 126
# Print the byte's equivalent ASCII char
print(chr(byte), end="")
print(" | ", end="")
print("\n")
print("Response: ")
# The response is less complex. Every strings ends with the "1337" string
# and has no internal separators
response = data[communication + 1].strip("1337")
# "Bytefy" the string
response = [(response[i:i+2]) for i in range(0, len(response), 2)]
for byte in range(0, len(response)):
print(chr(int(response[byte], 16) - 126), end="")
print("")
print("=================================================================")
Result:
$ python3 decode.py
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/out |
Response:
ERROR
=================================================================
Request:
Verb.WRITE | penta.gone | 1337haxorsRc00l | "well howdy" |
Response:
well howdy /tmp/out
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/out |
Response:
ERROR
=================================================================
Request:
Verb.WRITE | penta.gone | 1337haxorsRc00l | OMG how are you? |
Response:
OMG how are you? /tmp/out
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/out |
Response:
OMG how are you?
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | whoami |
Response:
ERROR
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/out; whoami |
Response:
OMG how are you?
hulto
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/data |
Response:
RITSEC{Th1s_is_My_Pa5sw0rd_Th3re_are_Many_like_it_bu7_thi5_1_is_mine}
=================================================================
Request:
Verb.WRITE | penta.gone | 1337haxorsRc00l | y0l0 man |
Response:
y0l0 man /tmp/out
=================================================================
Request:
Verb.READ | penta.gone | 1337haxorsRc00l | /tmp/out |
Response:
y0l0 man
=================================================================