Friday, September 25, 2015

CSAW 2015 Quals: Forensic 100 - Transfer write-up

I worked on this challenge during the "CSAW 2015" as part of a CTF team called seven.

We get a PCAP and need to find the hidden flag.
Looking at the traffic in the PCAP, there doesn't seem to be anything interesting ... a bunch of HTTP requests (some HTTPS, hence the TLS).


Seeing as how there were numerous requests towards Google, Facebook, Twitter, csaw.engineering.nyu.edu, and other sites... I tried the next logical thing and took a look at the files that were transferred. This is easily done using Wiresharks file export feature.

Sure enough, there are quite bit of files to be seen. Here is a summary based on only the names and sizes of the files retrieved.


  • 4 files roughly 153KB in size, having the name %5c - probably the HTML source of the sites that were visited
  • 4 files roughly 24KB in size having the same name %5c - probably some CSS/JavaScript that came with the HTML source
  • 123 files roughly 1KB in size having the name object<number> - no idea what that would be...
  • 8 files roughly 1KB in size having the name %5c- probably various redirects (would make sense based on the 4+4 sites that were visited)
Looking at the files named %5c it's easy enough to confirm the above assumptions without taking to much time. Now for the hard part ... analyzing the many object<number> files ...


Luckily, opening up the very first one (in my case it's called object60) reviles a Python script!


import string
import random
from base64 import b64encode, b64decode

FLAG = 'flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}'

enc_ciphers = ['rot13', 'b64e', 'caesar']
# dec_ciphers = ['rot13', 'b64d', 'caesard']

def rot13(s):
 _rot13 = string.maketrans( 
     "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 
     "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")
 return string.translate(s, _rot13)

def b64e(s):
 return b64encode(s)

def caesar(plaintext, shift=3):
    alphabet = string.ascii_lowercase
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = string.maketrans(alphabet, shifted_alphabet)
    return plaintext.translate(table)

def encode(pt, cnt=50):
 tmp = '2{}'.format(b64encode(pt))
 for cnt in xrange(cnt):
  c = random.choice(enc_ciphers)
  i = enc_ciphers.index(c) + 1
  _tmp = globals()[c](tmp)
  tmp = '{}{}'.format(i, _tmp)

 return tmp

if __name__ == '__main__':
 print encode(FLAG, cnt=?)

Well this looks promising! :)
It looks like a simple encoder/decoder, and it even has the flag template flag{xxx} embedded in it. There are four distinct functions here:

  • rot13 - a simple substitution cipher that replaces a letter with the letter 13 letters after it in the alphabet
  • b64e - a function that simply encodes a string to Base64
  • caesar - another simple substitution cipher that replaces a letter with the letter 3 letters after it in the alphabet
  • encode - this seams to be the master function which is used to encrypt the flag (notice a call to the encrypt function at the main part of the script!). It seems to first encode the flag into Base64, but first it ads the number 2 before the encoded string is concatenated. The number 2 seems to align with the index of the b64e function in the enc_ciphers variable. The rest of the function seems to randomly pick a function from the enc_ciphers variable and then apply the chosen algorithm to the entire string, thus forming a new one. Each time, the index of the used algorithm is added before the string. 

Obviously, we now have a way of decrypting the encrypted flag - we just need to find the string which was encoded. Looking at the rest of the object files, we see a bunch of seemingly random letters:

2Mk16Sk5iakYxVFZoS1RsWnZXbFZaYjFaa1prWmFkMDVWVGs1U2IyODFXa1ZuTUZadU1YVldiVkphVFVaS1dGWXlkbUZXTVdkMVprWnJWMlZHYzFsWGJscHVVekpOWVZaeFZsUmxWMnR5VkZabU5HaFdaM1pYY0hkdVRXOWFSMVJXYTA5V1YwcElhRVpTVm1WSGExUldWbHBrWm05dk5sSnZVbXhTVm5OWVZtNW1NV1l4V1dGVWJscFVaWEJoVjF

This seems to be our encrypted flag scattered across a bunch of files. As we can see, the number 2 at the beginning makes the first file (the one with the smallest number in object<number>) the first part of the encrypted string. 

First we need to create two decryption functions: b64d, caesard.
b64d is simple - we just call Python's b64decode to decode the Base64 string back to it's original form.
caesard is also simple - we just use a -3 offset to revert the changes of the original caesar cypher

Adding it all together, we get the flag

flag{li0ns_and_tig3rs_4nd_b34rs_0h_mi}

Here is the Python script I used - enjoy ! :)

import string
from base64 import b64decode
import os

mypath = "D:\CTFs/CSAW_2015/forensic/100/files/"
files = [ f for f in os.listdir(mypath) if os.path.isfile(os.path.join(mypath,f)) ]

encodedParts = []
for file in files:
 if "object" in file:
  encodedParts.append(int(file[6:]))

encodedParts = sorted(encodedParts)

# remove first element since it is the script itself ...
encodedParts.pop(0) 
# print encodedParts

encodedFlag = ""
for file in encodedParts:
 pathToFile = mypath + "object" + str(file)
 encodedFlag += open(pathToFile, "r").read()
 
# print encodedFlag

dec_ciphers = ['rot13', 'b64d', 'caesard']

def rot13(s):
 _rot13 = string.maketrans( 
     "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 
     "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")
 return string.translate(s, _rot13)

def b64d(s):
 return b64decode(s)

def caesard(plaintext, shift=-3):
    alphabet = string.ascii_lowercase
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = string.maketrans(alphabet, shifted_alphabet)
    return plaintext.translate(table)


while(1):
 if (encodedFlag[0] == "1"):
  encodedFlag = rot13(encodedFlag[1:])
 elif (encodedFlag[0] =="2"):
  encodedFlag = b64d(encodedFlag[1:])
 elif (encodedFlag[0] =="3"):
  encodedFlag = caesard(encodedFlag[1:])
 else:
  print "finished..."
  print encodedFlag
  exit()

No comments:

Post a Comment