Skip to content

Instantly share code, notes, and snippets.

@ngshaohui
Created June 18, 2019 02:27
Show Gist options
  • Save ngshaohui/eca0bc5c3b8a750654707b0032dfb256 to your computer and use it in GitHub Desktop.
Save ngshaohui/eca0bc5c3b8a750654707b0032dfb256 to your computer and use it in GitHub Desktop.
ctf writeup for cddc

Just Console Log

Aspects of this challenge

  • reverse engineering + crypto
  • re: source is obfuscated with bad variable and function names
  • crypto: aes library is imported, and CBC method is called
  • need to understand javascript scoping

Stepping through the problem

1. Identify unused code

  • I used vscode instead of sublime text since I'm much more used to it
  • vscode automatically grays out the unused variables and functions
  • alternatively can do a search to see who is using them

2. Remove unused code

  • don't be afraid to do this
  • if it's not being called at all it can't possibly break anything if nothing uses it

3.1 Find out what each function does

  • one way is to copy and paste the relevant code into a console and try to run it
  • for imperative languages like javascript, we also need to find out what side effects running a function might trigger, for example any global variables being modified
  • write comments to help you keep track

3.2 Rename variables and functions

  • I opted to rename the variables before functions, but there is no specific order it can be done, sometimes it's just easier to do one before the other

4. Understand the program

  • now that the code is cleaner, it should be much more obvious what each line of code does in relation to others, giving you an overall gist of the program
  • we can do this by tracing from the entry point
  • once again, add in comments to help you understand the overall program flow

5. Identify a possible area where the flag is hidden

  • for a crypto challenge, a flag would most likely be hidden as some encrypted text that needs to be decrypted

How I approached it (verbose)

  • read through the description and try to make sense of what to do

  • in this case, it wasn't very informative at all so I opted to just look at the file instead

  • first thing that caught my attention was how vscode grayed out some variables and a function

  • in this case, lines 2-6, 12, 100-102 were all not being used

  • i initially opted to write a comment to take note, but later simply removed them after looking through the entire program and determining that it wasn't necessary at all

  • a few groups i went to talk to were like wtf why do you just delete it but hey don't be scared if it breaks something just copy it from the source again right?

  • i also tried to analyse the array of strings and found out that they were md5 hashes

  • but i opted not to go down that route since it is unlikely you need to do a hash reversal

  • next, you need to understand how a node server works

  • I think we can split this up into a few parts, namely

1. imports

  • imports are pretty straightforward to understand
  • in this case, once you see express you know that it's a javascript backend framwork
  • that's how you know that you will have app.listen and routes

2. app.listen

  • in this case it was called ___k.listen
  • this is the starting point for an express application
  • basically starts the entire app (it actually starts an event listener if i'm not mistaken)

3. routes

  • / is the root of any web page, and most logic starts from there, such as serving up index.html
  • in this case, there was only 1 route so it was pretty easy to trace since it was an obvious starting point for the app

4. all other functions

  • these are all extras, the app doesn't technically need them to work, they just add functionality

  • but honestly I'm not sure if you even need to know javascript and node servers to attempt this

  • I then went about trying to figure out what function does what

  • starting with the easier looking ones right at the top which was a__ on line 73

  • copy and pasted functions a__ and b__, along with the array __c

  • looking at the for loop on line 75 made it apparent that it was iterating through each string, and looping through each character in the string

  • console logging the end result makes it apparent that all it does is remove the letters and only keeping the numbers, transforming the string

  • this is also reaffirmed by the helpful clue from the variable named numOnly

  • this is essentially a map

  • trying b__ also made it apparent that it was a reverse function

  • I then renamed the functions accordingly

  • next function I moved on to was h__

  • function only takes in one variable __a but seems to use __b as well that is calculated independent of __a

  • simply print out the end result which is a hash

  • "fdd0c313361e4c0b9fe6889f19b098cc"

  • renamed the function to isEqualsToHash

  • can skip this paragraph

  • at this point i actually tried to look at i__

  • "L2Jpbi9zaA==" was clearly base64 (even written right there) and it does /bin/sh

  • the biggest issue here was that "0400.0600.0300.0100" was clearly not a valid ip address so it was unlikely you need to get the flag elsewhere

  • so after 5min of converting everything in this function i realised it was useless and deleted it

  • at this point I felt that i had enough information to start tracing through the program

  • moving on the the entry point of route /

  • line 144 made it apparent that this was where the flag would most likely be, since it suggests that it would normally take in a payload from a user

  • let __c = __a.query.z

  • for line 147 it's another portion i opted to throw into the console again

  • numArr.map(__e =>(__e.length>=c__(3234))?__e.length==d__(11000)? __d=__e: '':'')

  • immediately apparent that c__(3234) and d__(11000) both resolve to "24"

  • regardless, since we were able to establish that function a__ on line 73 gave a constant list we can just evaluate the value of numArr and know that it will be a constant value

  • in this case, the line returns a list with only

  • "234679735362707470949892"

  • and the rest of the list had empty strings

  • i realised that the list was not being assigned anywhere at all so it might have been useless

  • but taking another look, while the lambda function it uses has variable __e, there was one instance of __d that was declared in line 146

  • it became clear that this usage was map was impure and was being done for the side effects

  • we need to know that in javascript if the variable is not in the local context, it will try to find it in the parent context (recursively)

  • console logging __d showed that it had the value "234679735362707470949892"

  • subsequently on line 148, ___j which is actually the aes library was being used so it was time to look up on documentation

  • showed that ___j.utils.utf8.toBytes(__d) was literally just converting a utf8 string to bytes

  • at this point, it was also time for us to look at f__ and g__

  • for function f__ searching up for ModeOfOperation.cbc and looking at the aes-js documentation for CBC made it clear that it was simply creating a cbc cipher object

  • on line 148, we can see that the output of f__ was being passed into g__, which confirms that it is the cipher object

  • this is depicted in the documentation as

var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
var encryptedBytes = aesCbc.encrypt(textBytes);
  • this made it clear that the second argument for g__ is the plaintext, which we have previously established to be __c on line 144 as previously mentioned, reaffirming that it should be the flag we are solving for
  • the first argument for f__ was __d, which we had previously established to contain the string "234679735362707470949892"
  • another argument for was __aa, which was a list of numbers
  • checking on the documentation, this is clearly the key and iv
// An example 128-bit key
var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ];
// The initialization vector (must be 16 bytes)
var iv = [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,35, 36 ];
  • we now know that

  • iv = [102,108,97,103,32,105,115,32,110,111,116,32,116,104,105,115]

  • key = "234679735362707470949892"

  • at this point, we are sure that g__(f__(__d,__aa),___j.utils.utf8.toBytes(__c)) returns a ciphertext

  • this is then converted to hex then sent to the h__, which we had previously established is just checking isEqualsToHash

  • the value it was checking against most probably be the ciphertext that we need at this point

  • ciphertext = "fdd0c313361e4c0b9fe6889f19b098cc"

  • given all 3 pieces of information, I then embarked on a journey of laziness, trying out online aes encrypt decrypt tools since i was lazy to code my own and wasted an hour i think

  • afterwards i decided to not be lazy and simply use an online javascript playground

  • for javascript libraries, you don't actually have to npm install all of them

  • some of them tend to have a cdn you can use, which was how i used it on the playground

  • <script type="text/javascript" src="https://cdn.rawgit.com/ricmoo/aes-js/e27b99df/index.js"></script>

  • didn't save what i did back then, but here is a close replica

  • Edit fiddle - JSFiddle

Other remarks

  • this was the only challenge I solved during the entire CTF
  • while we lost the war, I felt that I won a personal battle and could stand proud because it turns out that the question was set by my senior (jiayi) and we were one of the top 10 solves (can't rmb if it was 8 or 9 solves when I submitted)
  • I think that 2 sems worth of TA for CS1010 has increased my resilience in reading badly written code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment