Write-up of the challenge “Inforensic – Invest” of Nuit du Hack 2016 CTF qualifications.
The weekend of 04/01/2016 is pre-qualification for the Nuit du Hack 2016 as a Jeopardy CTF. Having had the opportunity and the time to participate with some colleagues and friends, here’s a write-up resolution of the challenges which we could participate.
- Category: Inforensic
- Name: Invest
- Description : A paranoid guy seems to have secured his file very well. But I am convinced he made a mistake somewhere…
- URL : http://static.quals.nuitduhack.com/invest.pcapng
- Points : 50
The pcapng file recovered, we open it with Wireshark to observe the different frames. This file is provided, let’s look to potential objects related to HTTP requests by exporting (File> Export Objects> HTTP …).
When accessing files that have been exported, several are of interest:
- Contains a string composed only of “0” and “1” as a binary string. The filename is speaking …
- Many files whose content appears in base64 are present, from “encryptaa” to “encryptdc”, or 81 encrypted files.
- This image shows a block diagram of binary processing, consisting of NOT, AND, OR and XOR operators. 8 bit input to provide a single bit in output.
At this stage, we have:
- The message will reveal the flag is encrypted and then base64 encoded before being cut into 81 files ordered and named “encrypt*”.
- The jpeg image of the logical schema presents an algorithm to implement to deal with the binary. 8 bit input to one output.
- The “key.txt” file contains a relatively large binary string. This string is the decryption key of the encrypted message, but it is encoded and we have to decode via the algorithm presented on the image of the logical schema.
Let’s start with the algorithm implementation of the logical schema in Python:
#!/usr/bin/python # Quick'n'dirty logical scheme implementation keyFile = open("key.txt", "r"); resFile = open("keyresult.txt", "w"); def AND(a, b): if a=="1" and b =="1": return "1" else: return "0"; def NOT(a): if a=="1": return "0" else: return "1"; def OR(a, b): if a=="1" or b == "1": return "1" else: return "0"; def XOR(a, b): if (a=="1" or b == "1") and a!=b: return "1" else: return "0"; key = keyFile.read(); i = 0; while i < len(key): a1 = key[i]; a2 = key[i+1]; a3 = key[i+2]; a4 = key[i+3]; a5 = key[i+4]; a6 = key[i+5]; a7 = key[i+6]; a8 = key[i+7]; u13 = NOT(a3) u16 = NOT(a4) u15 = NOT(a5) u14 = NOT(a2) u17 = NOT(a6) u20 = NOT(a8) u1 = AND(a1, u13) u2 = AND(u14, u13); u3 = AND(a1, a2); u18 = XOR(a6, a7); u19 = XOR(u14, u20); u4 = AND(u1, u16); u5 = AND(u16, u2); u6 = AND(u16, u3); u7 = AND(u17, a3); u8 = AND(a3, u19); u9 = AND(u4, u15); u10 = AND(u5, u15); u11 = AND(u15, u6); u12 = AND(u7, u18); u21 = OR(u9, u10); u22 = OR(u11, u12); u23 = OR(u22, u8); u24 = OR(u21, u23); resFile.write(u24); i = i+8
The script is run to produce the file “keyresult.txt” from the binary string in “key.txt”:
ASCII conversion of this new binary string gives us the following key:
Now regenerate an encrypted file as a bundle:
cat encrypt* > encrypt-bundle.txt
Having the key and the encrypted file, proceed to decryption … Yes, but what algorithm? (DES, 3DES, XOR, AES128, AES256 …). It turns out that this is the AES256 gives us a decent result:
openssl enc -aes-256-cbc -d -a -in encrypt-bundle.txt -out decrypt.txt -k 4Ukz95F2YqPi
So we have a file “decrypt.txt”, including portions of its contents appear in clear. A command like “strings decrypt.txt” will display all the displayable text of it, and we note that the last strings are:
[...] word/theme/theme1.xmlPK word/styles.xmlPK word/document.xmlPK docProps/custom.xmlPK docProps/app.xmlPK docProps/core.xmlPK
It seemed to be an RTF file to open with Word! Rename “decrypt.txt” in “decrypt.rtf” and …
Just “remove” or move the image (which is in the foreground), to see the flag appear behind:
Flag : NDH[59rRS57bd5WH8RxgPbRS27q89a5bWrjL]
Sources & ressources :