Cyber Apocalypse 2021 CTF

Reading time ~8 minutes


I joined the second HackSouth team a bit late so some of the challenges were already sovled by the time I looked at them. This post is mostly my raw thoughts and flow as I solved the challenges and cleaned up a bit thanks to hindsight.

Reversing - Alienware

Difficulty 2/4

  • windows binary (wine isnt enough) (or maybe if you are better than me its enough)
  • decompile binary with ghidra and use x64dbg to debug it
  • step through and wait for a secondary dll to be dumped to disk
  • decompile this binary and see that its encrpyting files in your users<username>\docs folder
  • restart the debug session step into the code now with the folders created with some test data in the docs folder.
  • extract the key from memory (16 byte , hex qword)
  • start documenting the code for the encryption to understand it better
  • realize there is no decrypt in the binary
  • start writing c++ code to decrypt the file using windows crypto api
  • waste eons getting the params wrong ( thanks to those that helped with this)
    • demo code ripped from here in the hopes it would compile out of the box
    • it did not
    • fix the code so that it compiles
    • continue getting params right
  • finally get the params right , test it on the file we encrypted earlier
  • see that it works and decrypt the pdf file that was along side the binary
  • flag is in the file
  • Having a look at other solutions to this challenge , it looks like you could have patched the call to the encrypt with a call to decrypt, I would have not thought that would have worked as the params are different but that fact that it gave others the solution meant I need to do a bit more testing/research.

Reversing - backdoor

Difficulty 2/4

  • this was a two part challenge since up front it showed that we needed to connect to a docker container to get the flag.
  • running the binary didnt output anything except when hitting ctrl + c you would see a python script
  • basic recon on the binary didnt give to much more away except that it was using embeded python.
  • tried a few existing tools to get the info out
  • turns out you could just use the archive viewer (pyi-archive_viewer) provided by pyinstaller to view the archive and extract the python script called bd
  • the script is in pyc compiled format and while we can see a string that looks like a password from outputting the file to the terminal, using this password on remote server doesnt do anything but close the connection.
  • time to decompile the code
  • none of the decompilers work
  • there is no python header in the pyc file which is why all the decompilers fail.
  • get the correct magic string for the python version being used and add it to the start of the file
  • decompilers now work (decompyler6)
  • we can now understand what the flow is for the remote server
  • provide a password in md5 format
  • then send a command you want to execute in the format they want e.g. ‘command: ls’
  • i intially missed that you needed to specify the lenth of the command so most of my commands failed initially
  • finally sent through the required code to allow for a long buffered command
  • at which point i could just cat the flag.

Misc - input as a service

  • This is a python sandbox escape
  • Took a while to figure out what was allowed or not allowed.
  • Finally realized that the built ins were still availble.
  • Looking at other folks solutions it looks like i could have just used open (the python function) as well to read the flag directly.
    • e.g. print(open(‘flag.txt’).read())
  • simply use the import to import the os name space and call ‘cat flag.txt’ to get the flag (you could do ls first to find the file)
    • import(‘os’).system(‘ls’)
    • import(‘os’).system(‘cat flag.txt’)

Misc : Build yourself in

  • This was a python shell escape again
  • This challenge really gave me a headache
  • So in theory i was ready for another sandbox escape , except now pythons exec was used and all builtins besides the print function were removed.
  • What this functionally meant is it was very tedious to test anything until you got access to the builtin functions some how.
  • To make matters worse you werent allowed to use quotes in your exploit
  • The solution i took was to find the subclass object locally and then find the correct index to the globals import to get access to the built in functions locally and hope they matched remotely.
  • Once i had them available locally i could in theory run the same code remotely without the indexes changing as long as i used the same python version (which i did).
  • To get things going i first stored all the global functions in a short variable
  • a = ().class.bases[0].subclasses()[94].init.globals.values()
  • I then extract the chr function to convert numbers to chars so that we dont have to worry about indexes
  • chr = [a for a in [x for x in a].pop(5).values()].pop(14)
  • I then wrote a small function that genrated the chr concats required to make a string i needed , this let me setup imports and the os call for example
  • flag = chr(99) + chr(97) + chr(116) + chr(32) + chr(102) + chr(108) + chr(97) + chr(103) + chr(46) + chr(116) + chr(120) + chr(116);os = chr(111) + chr(115);ls = chr(108) + chr(115);imp = chr(95) + chr(95) + chr(105) + chr(109) + chr(112) + chr(111) + chr(114) + chr(116) + chr(95) + chr(95);blt = chr(95) + chr(95) + chr(98) + chr(117) + chr(105) + chr(108) + chr(116) + chr(105) + chr(110) + chr(115) + chr(95) + chr(95);
  • finally once all that was done i could simply index the globals dictionary and use the strings i build to cat the flag out
  • ().class.bases[0].subclasses()[94].init.globals[blt]imp.system(flag);
  • the full exploit had to be concatted to one line as seen below to execute correctly on the remote server.
  • a = ().class.bases[0].subclasses()[94].init.globals.values();chr = [a for a in [x for x in a].pop(5).values()].pop(14);flag = chr(99) + chr(97) + chr(116) + chr(32) + chr(102) + chr(108) + chr(97) + chr(103) + chr(46) + chr(116) + chr(120) + chr(116);os = chr(111) + chr(115);ls = chr(108) + chr(115);imp = chr(95) + chr(95) + chr(105) + chr(109) + chr(112) + chr(111) + chr(114) + chr(116) + chr(95) + chr(95);blt = chr(95) + chr(95) + chr(98) + chr(117) + chr(105) + chr(108) + chr(116) + chr(105) + chr(110) + chr(115) + chr(95) + chr(95);().class.bases[0].subclasses()[94].init.globals[blt]imp.system(flag);

Misc - alien camp

  • question answer service
  • connect to a remote service calculate a value and send the correct answer as fast as possible.
  • The values printed when asking for questions are emoji’s
  • Steps to solves are :
    • Connect using pwn tools
    • request option 1
    • parse the strings and store the emoji -> number mapping
    • request option 2
    • parse the string out and replace the emojis with their correct number mappings
    • pass the string to pythons eval
    • send the response back using pwn tools send_raw
    • let the program run for all 500 questions and then the flag will be printed.
  • This is a fairly common challenge and the biggest time waste for me here was thinking i needed to use the emoji hex values as the numbers, once i realized there was a mapping it was pretty simple.

Hardware : serial logs

  • Recon : Download the file , extract and see whats available
  • Initial display shows its a zip
  • extract shows a binary data file
  • load file into hex editor
  • file has a magic string that points to
  • Download app and load file
  • digital logic analyzer trace shows up in a raw form
  • do more recon to find out how the the logs were captured (i.e. what protocol)
  • confirm that the logs are async serial in the app which effectively translates to uart
  • once the analyzer is selected raw hex is displayed
  • This is not correct , most likely thing that is wrong is the baud rate
  • Sad to say but i initially just brute forced choosing the different baud rates
  • find the correct baud rate by looking at the stream data and seeing raw text being output
  • do some initial analyst to see if the long strings being output are related to the challenge (they are not)
  • see that the stream terminates indicating that the baud rate has changed again.
  • Clear out the old logs to make it easier to analyze
  • check the high voltage edges and measure their time and use this to calculate the baud rate
  • formula used is 1/(high edge) * 10^6 = 1/(13.4) * 10^6 ( not the value here is just an example go check the trace for actual value)
  • this gives you the bit rate which you can round up and enter as a custom baud rate into logic 2’s analyzer
  • you should now see the clean logs and the last line has the flag.

Hardware : Compromised

  • We know that its a logic 2 file this time so skip a lot of intial recon steps
  • this trace has a new protocl which is I2C
  • load the correct analyzer
  • change the output into ascii
  • you should see that there are commands and data being sent to 2 devices
  • export the stream to csv (add screenshot here showing how)
  • import the stream into google sheets or excel
  • filter only on the contents being sent to the ‘,’ device (this is the ascii value)
  • scroll to the bottom of the trace and the flag is listed downwards
  • copy the flag out and paste it into an editor and concat the lines into a single line string

Embracing the ultra

# IntroThis past month marked the latest iteration of the 360one MTB and the culmination of months of training. In the end, it wasn't jus...… Continue reading

Covert Login Alerting

Published on June 01, 2020

Analysing Flutter Applications

Published on January 13, 2020