IceCTF: Analyzing PCAPs and Reversing Encryption
IceCTF started a little while a go and we got a team together to try and grab some of these flags. If you’re doing the challenges right now… well spoiler alert.
One of the challenges provided a PCAP with a conversation between two parties that contained the flag somewhere.
##PCAP Analysis
Alright, let me open this in Wireshark:
Interesting… some IRC chatter. If I right-click and Follow TCP stream
I can read the full conversation:
PRIVMSG Cold_Storm :Hi
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :It's not safe here
PRIVMSG Cold_Storm :What do you mean?
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :Someone is listening
PRIVMSG Cold_Storm :What?!
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :Yea, someone intercepted our last encounter
PING LAG1470558285526987
:irc.glitch.is PONG irc.glitch.is :LAG1470558285526987
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :We need to be careful about what we say on open channels.
PRIVMSG Cold_Storm :Oh 0k, so what do w3 do?
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :I made som3thing...
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :S0mething that will allow us to exchange s3crets secure1y
PING LAG1470558315569084
:irc.glitch.is PONG irc.glitch.is :LAG1470558315569084
:Cold_Storm!~finalC@localhost PRIVMSG Ice_Venom :.DCC SEND encode.pyc 1494322064 1117 1737.
PING LAG1470558345608305
:irc.glitch.is PONG irc.glitch.is :LAG1470558345608305
PRIVMSG Cold_Storm :Ok
PING LAG1470558375659432
:irc.glitch.is PONG irc.glitch.is :LAG1470558375659432
PRIVMSG Cold_Storm :Wmkvw680HDzDqMK6UBXChDXCtC7CosKmw7R9w7JLwr/CoT44UcKNwp7DllpPwo3DtsOID8OPTcOWwrzDpi3CtMOKw4PColrCpXUYRhXChMK9w6PDhxfDicOdwoAgwpgNw5/Cvw==
Looking through there are a bunch of PRIVMSG
IRC messages being exchanged which is just a private conversation between two parties.
Towards the end there’s a base64 encoded string, presumably generated by one of the parties. If you try to base64 decode the data, it’s gibberish.
Wmkvw680HDzDqMK6UBXChDXCtC7CosKmw7R9w7JLwr/CoT4
4UcKNwp7DllpPwo3DtsOID8OPTcOWwrzDpi3CtMOKw4PColrCp
XUYRhXChMK9w6PDhxfDicOdwoAgwpgNw5/Cvw==
Just before this though, there’s a DCC SEND
operation of an encode.pyc
file. DCC is how the IRC protocol handles client to client data transfer. In this case, a tool that probably generated the encoded message above.
##File carving Since I have a packet capture of the whole conversation, I will isolate this file and save it to disk so I can reverse it.
Same as before, right click and follow the TCP stream:
Python compiled files have a specific signature, a version number
(0x16 0x0d
) followed by \r\n
(0x0a 0x0d
). The first 4 bytes of the file confirm this is indeed a .pyc
file (0x16 0x0d 0x0d 0x0a
):
I’m interested in the one way conversation that starts with these bytes. I’ll save it as encode.pyc:
To decompile the pyc and I’ll need uncompyl6. This tool supports all versions of Python (2.7->3.6).
I did try pycdc but in my case, it was decompiling to broken code.
encode.pyc
-> encode.py
:
# Python bytecode 3.5 (3350) disassembled from Python 2.7
# Embedded file name: encode.py
import random
import base64
P = [27, 35, 50, 11, 8, 20, 44, 30, 6, 1, 5, 2, 33, 16, 36, 64, 3, 61, 54, 25, 12, 21, 26, 10, 57, 53, 38, 56, 58, 37, 43, 17, 42, 47, 4, 14, 7, 46, 34, 19, 23, 40, 63, 18, 45, 60, 13, 15, 22, 9, 62, 51, 32, 55, 29, 24, 41, 39, 49, 52, 48, 28, 31, 59]
S = [68, 172, 225, 210, 148, 172, 72, 38, 208, 227, 0, 240, 193, 67, 122, 108, 252, 57, 174, 197, 83, 236, 16, 226, 133, 94, 104, 228, 135, 251, 150, 52, 85, 56, 174, 105, 215, 251, 111, 77, 44, 116, 128, 196, 43, 210, 214, 203, 109, 65, 157, 222, 93, 74, 209, 50, 11, 172, 247, 111, 80, 143, 70, 89]
inp = input()
inp += ''.join((chr(random.randint(0, 47)) for _ in range(64 - len(inp) % 64)))
ans = ['' for i in range(len(inp))]
for j in range(0, len(inp), 64):
for i in range(64):
ans[j + P[i] - 1] = chr((ord(inp[j + i]) + S[i]) % 256)
ans = ''.join(ans)
print(base64.b64encode(ans.encode('utf8')).decode('utf8'))
# okay decompiling ../../../encode.pyc
##Reversing the Algorithm It’s a bit convoluted, but basically the algorithm will:
- let
I
represent the user input - split
I
into 64 byte chunks - let
c
represent each chunk - let
n[0..63]
represent each byte in a given chunk - add
S(c+n)
to the numeric value ofI(c+n)
- perform mod 256 operation on the result
- ie:
127 % 256 = 127
,321 % 256 = 65
- store the result in the return variable at position
c+P(n)-1
.
The algorithm takes each character, adds a number from S
to it and stores the result in the encrypted string array at an arbitrary position defined by P
.
To decrypt the resulting string, I will walk backwards:
- iterate through each byte in each 64 byte chunk
- jump to
c+P(n)-1
and get the numerical value - subtract
S(c+n)
- if that number is below
0
, add256
before converting to string - store the result in the return variable at position
n
.
The decryption algorithm:
enc = base64.b64decode("Wmkvw680HDzDqMK6UBXChDXCtC7CosKmw7R9w7JLwr/CoT44UcKNwp7DllpPwo3DtsOID8OPTcOWwrzDpi3CtMOKw4PColrCpXUYRhXChMK9w6PDhxfDicOdwoAgwpgNw5/Cvw==").decode('utf8')
dec = ['' for i in range(len(enc))]
for c in range(0, len(enc), 64):
for n in range(64):
i = ord(enc[c + P[n] - 1]) - S[n]
if i < 0:
i += 256
dec[c + n] = chr(i)
print ''.join(dec)
And the decrypted flag:
IceCTF{4Lw4y5_US3_5s1_AnD_n3VR4r_mAKe_Y0ur_0wN_cRyp70}........'