PACTF 2018 Writeup: AI

Alex Hong
5 min readMay 26, 2018


I participated in this year’s PACTF (Phillips Academy Capture the Flag challenge), an annual online computer security competition. Although the competition is targeted towards middle/high school students, the problems given varied from easy (10 points) to extremely difficult(100 points). From this other excellent writeup on the problem Skywriting, this problem was not solved by any of the prize-eligible teams. If you are interested (or don’t believe me that some of the problems were actually fairly hard!), you can put your skills to the test here. In the end, our team (composed of university students), managed to solve most of the problems in Round 1 and Round 2, placing 43rd and 133rd respectively out about 1000 teams that scored at least 10 points.

Because this event took place during an extremely busy finals season for me, I did not have time to work on as many problems as I would have liked (the rest of the team pulled a lot of the weight). However, I wanted to write about one particularly interesting problem, AI, which was an 80 point problem in Round 2. Personally, I don’t think it was worth 80 points in terms of difficulty, but only because of how lengthy it was. Here is the problem description:

Our artificial intelligence engineer made a groundbreaking discovery, but left the company unexpectedly… all we have is the obfuscated source.txt

If you want to try your hand at it before you read my solution, please do so! Otherwise, read on.


The first part is quite similar to many CTF problems before. You’re given a long text file with only the following six characters: ()[]+! For those aware of the strange and esoteric nature of JavaScript, the seemingly random string of parentheses and punctuation is actually valid JavaScript. Entering into either Node.js or the browser’s console will show the following console output.

“Congrats! You’ve uncovered the truth. Now go here: URL/”

Unzipping the file gives the following directory structure:

Unzipped AI Directory Contents

So it seems like a fairly basic website with html, css, and js. Opening up index.html reveals a fake MacOS terminal which can’t be interacted in any way whatsoever. Opening up the console doesn’t seem to reveal anything out of the ordinary, other than a missing favicon.ico, which nowadays is fairly common.

Fake terminal

Instead, we can dive into script.js, which seems to be the only relevant file since baffle.min.js and typed.js are third party libraries.

By quickly scanning through script.js, the most relevant line seemed to be on line 160

append_ai(‘The key is now stored securely in (“", get_key_number(6, [16, 23, 16, 15, 42, 8])).’, 30, 300);

Suspicious Function

At this point, I was fairly confident I could just skip through the rest of the file and just find what this function returns based on the input, but I felt like the author of the problem put a decent amount of work into the problem and the rest of the stuff deserved a little attention.

Here’s a summary of what you’re supposed to do. First, uncomment line 3 which makes the fake terminal populate with pseudo shell commands. This launches the “AI” program that can tell the difference between dogs and not dogs.

Much wow, such AI

On line 134, in function launch_artificial_intelligence(stupid) there is an if-statement “if (stupid)” that launches the dog classifier. Change line 102 to launch_artificial_intelligence(false) or just uncomment the if-block on line 134 entirely. Refreshing the page goes through all the animated terminal text again, with a witty jab at Facebook’s recent data scandal.

“AI” throwing some shade at Facebook

This gets to where we already arrived at above, but in a more roundabout way. I imagined that because the code modification wasn’t very hard, it didn’t really make a difference either way but this is another example of why client-side authentication/obfuscation isn’t a very good idea (I’ll discuss this more in a future article). Going back to the function, it seems at first somewhat complex, with variables p and q which can be oddly reminiscent of RSA encryption. Turns out, it’s not the case and all it’s asking is for us to sum up the elements in the array (probably the intent is just to teach students to read the comments carefully).

So we go to the link specified, replacing %d with 120. This gives a link to this file:

Yet another layer, have we gone deep enough yet?

This part is arguably the hardest part of the problem. Essentially, they gave us a string that doesn’t use bytes, but “byfes”, as well as the appropriate conversion between ASCII and PACTFSCII. The way to approach this is to consider the relation between regular ASCII (0–255 or 2⁸) and PACTFSCII (0–32 or 2⁵). Since there are 8 chars in the string given (ignoring terminating \0) and each char is 5 bits in PACTFSCII, by converting to binary using the table, we have 40 bits to work with: “01100 10001 10111 10110 01110 11001 11011 01111”

Next, if you concatenate the bits directly and group them in groups of 8, we have 5 bytes which cleanly translates to 5 bytes of ASCII chars:

“01100100 01101111 01100111 01100111 01101111” = “doggo”

And that’s it! Again, this problem was definitely not as challenging as some of the other problems we encountered (again, go look at the Skywriting writeup), but I found it fairly fun and engaging.

I’ll try to post writeups for some other CTFs in the future so be on the lookout. Thanks for reading!



Alex Hong

Developer Relations Engineer @ Google Cloud