Inter-ACE Challenge Writeup

By Izzy Whistlecroft and David Young

This post is a companion post to our blog post “Inter-ACE Success” and will go into more details on the challenges that the competitors faced over the days. Credit for the challenge solutions goes to the Hapless Techno-Weenies, featuring David Young, Izzy Whistlecroft, Josh Curry, and Laurie Kirkcaldy.

This year’s InterACE had a greater variety of challenges than last year, with most challenges being written by Cambridge’s own one-man-army Graham Rymer — the exceptions were the NFC Challenge which was designed by Michelle Houghton, Con Air which was provided by Context IS, and the Palo Alto Networks set of challenges.

Particular favourite challenges were the NFC Challenge (which encouraged a lot of talking to new people), Critters which had us trying to work out what to do quite a while, and Close Encounters of the Polybius Kind which was unlike anything we had seen before.

Web Stuff
Con Air (3000)

Con Air was the challenge provided by Context IS, it presented itself as a simple web page belonging to a fictitious aerospace company. There was a single text entry field that took in a URL and returned the first 200 characters of the target, but base 64 encoded and only if the target returned a 200 response code.

Our first approaches were to run dirb against the server to try to enumerate its contents, and to try out some file:// URIs, such as file:///etc/passwd. Neither of these approaches were helpful, since the web server didn’t appear to have any other pages, and the application was checking for a 200 status code. File URIs have a very messy history and do not return an HTTP status code (or indeed any headers). Similarly unhelpful were our attempts to “trick” the application by sending it a 302 redirect from a web server on one of our laptops to a file:// URI. This also failed because 302 is not 200.

After some time we managed to obtain a hint that there was some sort of database involved, and having already checked the input box for SQL injections, we knew that there was something sneaky going on.

After thinking for a while about different kinds of databases, we found that if you do a web search for “curl database” the top result tends to be for Apache CouchDB, which has an inbuilt REST API exposed on port 5984. Once we’d established that we could connect to the database the obvious next step was to find out what was on it. By requesting http://localhost:5984/_all_dbs we discovered a database that was suggestively named flag.

From the flag database we could request all documents, with the intuitive request to http://localhost:5984/flag/_all_docs, which returned an entry with ID and value both set to s3cur3v4lu3. This wasn’t the flag, but we were able to request the actual document from http://localhost:5984/flag/s3cur3v4lu3.

This was closer, but we weren’t there yet. Our next request had to be to http://localhost:5984/flag/_changes to see whether there were any earlier revisions that did contain the flag we were after. This time, at last, we were in luck as there was the snappily named 2–13a8515ec99424e3e4d8e066245a7d61 revision. Requesting this with http://localhost:5984/flag/s3cur3v4lu3?rev=2-13a8515ec99424e3e4d8e066245a7d61 we were presented with our flag, which looked like enc_key(some-random-data).

Close Encounters of the Polybius Kind (3000)

Definitely the more interesting of the two cryptography challenges, this challenge provided an audio file which contained an encrypted flag. The name indicated that it would make use of the Polybius Cipher, which makes use of a 5x5 grid of letters (usually I and J share a square so all letters can fit) and then provides pairs of the numbers 1–5 as coordinates for the grid.

We knew that flags generally took the form of FLAG=QXTHEIPAHY , based on that we knew in a normal Polybius grid we would be looking for value pairs potentially looking like “12 13 11 22” (or 21 31 11 22 depending on whether row or column first), which translates into “FLAG”.

A classic Polybius Square

Upon listening to the music file we noted that it started with 5 different high-pitched notes followed by 5 different low-pitched notes, these then repeated but slightly faster (naturally these were the notes from Close Encounters of the Third Kind). After this there were 14 pairs of a high-note followed by a low note, with a short silence between each pair. The 14 pairs formed the column and row values of the Polybius Square, meaning we needed to write down the pitch of each note and work from there.

After doing this, we realised that the first sets of 5 notes indicated the order in which the notes would go in the Polybius Square. Using this knowledge we finally managed to extract the flag from the note pairs we had transcribed.

Our transcription notes for this challenge

Enigma (1000)

The name of this challenge seemed to indicate the type of cryptosystem were were expected to break, and the description told us that it was enciphered on an M3 Enigma machine and that the plugboard was not used. The ciphertext provided was:


Given this, we headed straight for our old friend Practical Cryptography (having encountered it at C2C last year, Graham really likes Enigma!), downloaded their C program, inserted our ciphertext, compiled it, and let it run. After a couple of minutes it produced the following output:

final key: 
indicator=ACE, rotors=135, rings=AAA, plugboard=

The entire plaintext was the flag, and is the first two lines of The New Colossus by Emma Lazarus.

Reverse Engineering
Authentic (2000)

Authentic was a buffer overflow challenge: we were given a binary which we were told was running on a particular server, and a file called help.c that was supposed to be similar to the challenge binary. Indeed — the comment at the top of help.c suggested that it should have been called authenticator.c, which could mean it was very helpful.

The relevant part of help.c is:

#define PASS 0x000000000B57AC1ELL 
#define FAIL 0x0000000000FFFFFFLL

int main(int argc, char *argv[]) {
unsigned char buff[128]; // 128-byte buffer
int64_t auth_token = FAIL; // 64-bit integer

scanf(“%144c”, buff); // !

if(auth_token == PASS) {

return 0;

This was compiled with -fno-stack-protector to make it a bit easier.

By trial-and-error we discovered that there was an extra 8 bytes separating buff from auth_token in the stack, so the payload we needed was made up of 136 (128+8) “random” bytes, and then the value of PASS (but reversed due to endianness). In the competition we generated the payload with Python, but it could also be generated with Perl, `printf` in Bash, or any number of other ways.

In python:

import sys;

In perl:

print ‘A’x136, \”\x1e\xac\x57\x0b\x00\x00\x00\x00\”

It turned out that the authentication part of the challenge binary was identical to that of help.c, so we were able to pipe this payload straight into netcat to get the flag.

Wee Beastie (2000)

The binary for Wee Beastie turned out to be a tricksy one. When run normally it would print The flag is at memory address: [Address], which led us to try the obvious next step of running it in a debugger and inserting a breakpoint just after the call to printf. This would allow us to dump the contents of the referenced memory address.

It did not turn out to be so simple, however, as the first run inside radare2 did not appear to do anything, and a second attempt triggered an error about the file not existing. Weird.

Closer inspection revealed that the program would check whether it was running inside a debugger and if it was, would call unlink to delete itself. This is a very common technique for malware, and it was fun to come across it in a competition setting. Fun as it was, we still had to solve it, which we did by patching the binary to replace the jne instruction immediately following the call to ptrace with a je. This did mean that the program will immediately delete itself when executed outside of a debugger, but we were only interested in getting the flag to submit, which we were then able to do (by running it in the debugger and printing the memory location specified by the patched executable).

Heartbleed (500)

For this challenge, we were given an IP address of a server which was vulnerable to Heartbleed. To get the flag value we had to exploit Heartbleed so we could read values stored in the server’s memory (which is where the flag was being stored). We did this with Metasploit, which has a handy module for exploiting Heartbleed.

DHCP (500)

The DHCP challenge involved querying the local DHCP server for a particular invalid field. We tried a large number of options on the server until we received a response containing the flag, unfortunately in the heat of the competition we forgot what the exact query that got us the flag was.

Pulsar (1000)

For this challenge there was a server somewhere on the network that would periodically broadcast a packet to the network. We left Wireshark running for about ten minutes, and then used the “search for a string in the packet bytes” function to look for the sting “FLAG”, which immediately pulled out the packets we were looking for and gave us the flag.

Time Crisis (1500)

Time Crisis provided a network service which we could connect to with netcat and it would accept text and return us “Access Denied”. The challenge text informed us that we had to successfully log in to get the flag. Unfortunately, no one on our team managed to guess that the strategy for solving this puzzle was to perform a timing attack on the password verification mechanism.

Had we worked this out, we would hopefully have discovered that the solution was to write a program that tried all single-character passwords, recording the time taken for the server to return its “Access Denied” response. One of these would take a bit longer than the others as the execution pathway following a correct letter was slightly slower than that of an incorrect letter. The process then repeats, next testing all two-letter passwords beginning with the letter discovered by the first iteration. Keep going until “Access granted” appears and you get the flag.

Tower Heist (1500)

Another network service, the goal of Tower Heist was to attain “Level 3 access” to the system. We ran nmap -p1–65535 against the server to try to discover a starting point. The scan informed us that there were services listening on TCP ports 22, 10000, 11000, 12000, and 13000. Assuming that port 22 was for management access over SSH, we turned our attention to the higher numbers.

When we connected to port 10000, the server immediately replied with a message:

Nakatomi Socrates BSD 9.2 
Z-Level Central Core
Preliminary Clearance Approved (TTL: < 1s)


The short validity period for the hash suggested to us that it was derived from the Unix timestamp by some means. Connecting to port 11000, we had the opportunity to enter some text, to which the server said ACCESS DENIED. Guessing that it wanted the Preliminary Clearance hash from port 10000, we piped the netcat output from through grep to pull out just the hash and sent that into a netcat pointed at port 11000. Happily, this gave us exactly what we wanted:

Nakatomi Socrates BSD 9.2 
Z-Level Central Core
Level 1 Clearance Approved (TTL: < 1s)


Having quickly written a script to continue this for the next two levels, we discovered that the challenge was broken and the service on port 11000 was sending us the wrong hash. This was fixed for the second day, and we ran the script to see whether it would get us to the next level. We looked for Clearance Approved messages in the output, and to our surprise found FLAG=XXXXXXXXXX (we didn’t keep a note of the actual flag).

The bash script we used for this challenge was as follows:

nc 10000 | tee -a towerlog | tr ‘\0’ ‘\n’ | grep — only-matching -E ‘[a-z0–9]{32}’ | \
nc 11000 | tee -a towerlog | tr ‘\0’ ‘\n’ | grep — only-matching -E ‘[a-z0–9]{32}’ | \
nc 12000 | tee -a towerlog | tr ‘\0’ ‘\n’ | grep — only-matching -E ‘[a-z0–9]{32}’ | \
nc 13000

Critters (1500)

Critters was a problem which came with the tagline “Evolve or die” and a black and white image which looked like a rather mutilated QR code. We initially worked off the idea that we would need to evolve this using the ruleset of Conway’s Game of Life, but after playing with this idea for a while we came to the conclusion that wasn’t the way to do it.

The start point for Critters

We eventually discovered another algorithm, also focused on evolving an image and which was reversible. This was a cellular automaton called Critters, which reminded us of the name of the challenge and hinted that we might be on the correct path.

The ruleset for Critters is fairly straightforward. The algorithm makes two passes over the image, at each step by breaking it up into 2x2 pixel squares. The even pass takes 2x2 squares whose top left pixel is at even coordinates (e.g. (0,0), (12,8), etc.) and the odd pass takes the odd-indexed 2x2 squares ((1,5), (3,41), etc.) and applies the following rules based on the number of filled pixels in the current 2x2 square.

The rest of the ruleset is as follows:

  • 0 filled: Take the complement (fill all four pixels).
  • 1 filled: Take the complement, i.e. if a pixel is filled then empty it and vice versa.
  • 2 filled: Leave them as they are.
  • 3 filled: Rotate the square through 180 degrees and take the complement.
  • 4 filled: Take the complement (unfill all four pixels).

(We found this ruleset along with some helpful illustrations on

After this realisation we quickly wrote a Python script which would apply the critters algorithm to the given image and after a few generations a QR code was born. As it was nearing the end of the competition (the final half an hour) it was a bit of a rush job, so it’s not the prettiest or most optimised code but it gets the job done and we present it unedited.

This spat out a series of images in the same X PixMap format as the challenge image, which we looked through until we found the QR code.

The QR code produced by the Critters algorithm

As it looked cool, after the event we also converted the image sequence into an animated gif.

The birth of a QR code

Social Engineering
NFC (1000)

As an extra bonus challenge, each of the competitor and visitor badges had an NFC sticker. Scanning these provided clues towards a Zebra Puzzle.

We were given the two questions at the start:

  • Who is using the mobile phone?
  • Who is subjected to a phishing attack?

The clues we recovered were:

  • “Sarah has a smart watch”
  • “The person working in the morning sits at the desk next to the person who has received malware”
  • “The person working in the evening is using a tablet”
  • “The person in HR sits at desk 1”
  • “A social engineering attack happens in the afternoon”
  • “The person in HR sits next to Sue”
  • “Extra clue = Zebra Puzzle”
  • “The person sat in the middle is using a desktop”
  • “The person in marketing has been sent a virus”
  • “There are five desks”
  • “The person working at lunchtime sits at the desk next to the person who has received spam”
  • “Pete works in sales”
  • “The person in corporate is working at midnight”
  • “The person in IT has a laptop”
  • “Sarah sits to the immediate right of Matt”
  • “Bob is working in the morning.”

Once we had collected enough clues we went through them on paper, then on a spreadsheet, and then on paper again for good measure. It turned out that Bob had the mobile phone, and Sarah was subjected to a phishing attack. Our full solution was as follows (in the order Desk Number, Name, Time, Device, Attack, Department):

  • Desk 1: Bob, Morning, Mobile, Spam, HR
  • Desk 2: Sue, Lunch, Laptop, Malware, IT
  • Desk 3: Pete, Afternoon, Desktop, Social Engineering, Sales
  • Desk 4: Matt, Evening, Tablet, Virus, Marketing
  • Desk 5: Sarah, Midnight, Smart Watch, Phishing, Corporate

Padlock (1000)

Each team was given a padlock and a set of picks, with the goal of using the picks to open the lock. It was tough, and we opened it by spending several minutes raking it fairly randomly. Then we showed the opened lock to one of the organisers to claim the points.

WiFi (1000)

Somewhere in the room was a device connected to a WiFi network encrypted with WPA2-PSK. The goal was to work out what the PSK (pre-shared key, i.e. the password) was, and enter it into the flag tracker. We achieved this by using aircrack-ng to send deauthentication packets to trigger the device to reauthenticate to the access point, at which point we could capture the handshake at the start and then crack the password to get the flag.

This challenge was quite difficult for a while because the device was getting a lot of deauthentication packets to the point where it wasn’t reconnecting very often. Eventually the device did manage to reauthenticate, giving us the handshake we needed to crack.

## Analytics
### Snake (1000)

For this challenge we were provided with a text file containing 160 IP address ranges, given in CIDR subnet notation:

We worked out how to solve this one early on, during the first day, but didn’t manage to find the tools we needed until about five minutes before the end of the competition — the flag entry was extremely frantic with only three minutes before the competition ended.

We suspected that the solution would involve drawing the subnets out on a grid, à la XKCD’s map of the internet. The name snake feels as if it was inspired by the way that the Hilbert Curve snakes around the unit square.

We found a GitHub repository that looked promising, so we cloned it to discover first that it came with a Mach-O Macintosh binary, and after compiling it for Linux it segfaulted on our input data, or so we thought. In the heat of the competition we grabbed an older release, compiled it, and ran it again. This time it was more helpful, saying ipv4-heatmap: bad input parsing IP on line 1:

Looking over the documentation again, it transpired that ipv4-heatmap only takes in lists of IP addresses, and we would have to convert our list of subnets before it could be used. By a stroke of either pure genius or sheer dumb luck, we came across an article by Ubuntu Geek describing the prips tool for printing IP addresses in a given range. After hurriedly installing it and discovering that it could only process one subnet at a time as a command line argument, a very hastily written loop generated our list. It was so intense that we forgot how to spell “CIDR”:

while read cird ; do
prips $cird
done < snake.lst > ips.list

This new list could be loaded into ipv4-heatmap to produce an image that looked a bit like a QR code when zoomed in. A phone was out immediately to scan it and then we entered the resulting flag into the tracker.

A much zoomed in version of the QR Code

We discovered afterwards that ipv4-heatmap was only segfaulting as a result of having the -o option set to specify an output filename, so we could have saved ourselves the extra step by running the program without any options from the start.

Rock Star (1000)

This was a set of three salted SHA512 hashes which we suspected came from words in the famous RockYou password list (given the name of the challenge).


We immediately started using hashcat to attack these and recovered the first and third passwords almost immediately, but the third one eluded us for the first day and a half of the competition. This was a bit unusual because a lot of teams had solved it, and those who hadn’t were just as stumped as we were.

It turned out that the current version of hashcat runs a more optimised algorithm for ordering the candidate passwords that skips the longer ones. Toning down the optimisation, we eventually recovered all three passwords, which concatenated to form the flag:

user1: puppetmaster
user2: vladimirvladimirovichputin
user3: poisonrumors

Chiphop (1000)

Chiphop was an MP3 file featuring an interesting song about C programming. When played from one of our team member’s laptops there was also a strange, high-pitched buzzing noise over the top.

We decided to open it up in the Audacity audio editing software, and set the track to spectrogram view. By right clicking on the vertical axis we could zoom out and see more of the spectrum.

The full Chiphop spectogram

Looks a bit like a QR code repeated along the top frequencies! After zooming in cleaning it up a bit we this QR code which gives us the flag:

The Chiphop QR Code, now cleaned up and in the standard colours

The RAID (1000)

We were provided with two hard disk images, image1.img and image2.img. Given the rather suggestive title for this challenge, we guessed that they were two halves of a RAID0 striped array — the only other RAID configuration with two drives has them as a mirrored pair, which wouldn’t make for a particularly interesting challenge. We set these up as loop devices by running the following:

losetup /dev/loop1 image1.img
losetup /dev/loop2 image2.img

The drives were automatically detected as RAID0 and configured as /dev/md127 (otherwise we would have built the array with mdadm). The next stage was to dump the reconstructed disk image to a file and carve out its contents:

cat /dev/md127 > md.img
foremost md.img

During the competition we actually used photorec, but for this challenge foremost is simpler to run. The only file carved out this way was a JPEG image and the flag was some text displayed in the picture.

Palo Alto Networks (3600)

Palo Alto Networks provided us with read-only access to a cloud-based, virtualised firewall appliance which we had to use to analyse a lot of different information to deduce the answers to the 36 questions.

Among other things, we had to find out which user had transferred the most data in the preceding 30 days, where company information was being exfiltrated to, and audit the layer 7 firewall policies. In addition to this, some of the questions were about general firewall configurations and some specific options related to configuring the Palo Alto Networks appliances.

We managed to answer most of these (successfully answering 32/36 of the questions), and were awarded a special prize at the end of the event for getting the highest score on these questions out of all of the teams.


Overall we were very impressed with the variety of the challenges at Inter-ACE this year, each was very different to the others, which kept us guessing. In case you missed the link at the start, perhaps read our other blog post if you haven’t already. Finally, thanks to everyone who helped to write challenges for Inter-ACE, and thank you reader for making it to the end of this post!