Google CTF Quals 2017 - Moon

| Toshi Piazza

This writeup is for the reversing challenge “Moon” we solved during 2017 Google CTF Quals. This writeup and 3 others were also submitted to the Google CTF Writeup Competition.

Dealing with GLEW

A big problem we noticed early on was the use of GL3W, which generates code to lazily load all OpenGL functions at runtime—at all places where an OpenGL function was used we would simply see a call to some offset in the data section. Unfortunately we couldn’t find a script to re-symbolize function calls, but we plan to make one soon-ish :)

We can see the huge routine which calls LoadLibrary on every OpenGL function, at sub_4032c0.

GLEW lazy loading functions

Instead of symbolizing (by hand) all of the symbols, we only bothered to load ones which had valid XREFs to them, saving a bit of time.

Running the Program

Unfortunately all the RPI-sold computers from our year are not yet reported to support OpenGL 4.3, so first and foremost we had to patch the OpenGL verification check from 4.3 to 4.2. Surprisingly, this “just worked” despite the code making use of Compute Shaders which I had thought to be introduced in OpenGL 4.3.

The program simply opens up a window, and asks for a password. After we’ve entered 32 characters, the program either responds “good” (presumably), or “Nope”.

Running the program

When we XREF the string Nope, we see that it is used when constructing the texture to be printed for this SDL event loop iteration. Not too far from “Nope” do we find “Good”, and we notice that “Good” is only selected if a particular global variable is set. We trace this back to the following code in main:

We want should_compute here …

...Read more

Google CTF Quals 2017 - Web Assembly

| Nick Burnett

This writeup is for the reversing challenge “Web Assembly” we solved during 2017 Google CTF Quals. This writeup was submitted to the Google CTF Writeup Competition and won a $500 prize.

Some parts of this writeup will include background information about the concept.

Challenge Description


This challenge takes place on a very retro looking page the lets you drag assembly instructions from the sidebar into the main page. One button lets us compile the code, and another lets us run it. There are also a list of test cases. A quick glance at the source shows that they have implemented a simple assembly architecture and vm on the client side.

We are given several unminimized javascript files. I will quickly list what each is responsible for:

  • asm.js - This file parses the input, and converts it into a “bytecode”, which encodes the instructions as raw bytes.
  • vm.js - This file contains the VM implementation that decodes the bytecode and runs the instructions.
  • test.js - This file contains code to run a webworker with the VM. It also gives it the testcase input, and compares the worker’s output to the expected output.
  • worker.js - This is run by the webworker. It takes the input, runs the VM, and then responds with the output.

One of the testcases checks our code’s output against the flag. If it matches, it will print the flag out to us. Since we cannot know the flag to output it, we can assume that we need to find a bug in the VM and gain javscript execution.

Bytecode Compilation

The first step is in asm.js, where our data is parsed and compiled to a byte code. This process is fairly straight forward.

There are three datatypes:

  • int is simply a 32 bit integer value …
...Read more

Google CTF Quals 2017 - X Sanitizer

| Nick Burnett

This writeup is for the reversing challenge “X Sanitizer” we solved during 2017 Google CTF Quals. This writeup and 3 others were also submitted to the Google CTF Writeup Competition.

Some parts of this writeup will include background information about the concept.

Challenge Description

Investigation: Index page

The site contains a text box, which we can enter html into. When the button is clicked, it runs some kind of sanitization program, and finally renders the output back to the screen. The page claims that the entire process is client side, and that there is no hidden server logic. From this and description, I would guess that the goal is to preform a Cross Site Scripting (XSS) attack on the page.

Background: Cross Site Scripting

Browsers try to protect users from malicious websites by using something called the Same Origin Policy (SOP). This policy controls what a website can and cannot do. For example a website can access its own cookies and read its own web pages, but it cannot read the cookies or data of another webpage. To define what a webpage is, we use the term origin. A page’s origin in most cases is based on the domain name. So is one origin, while is another.

The fact that SOP blocks cookies is a good thing for the user, because most websites use cookies to tell if you are logged in. Reading another site’s cookie would allow an attacker to log in as you.

However, I mentioned that websites can access their own cookies. Here is where XSS comes into play. If an attacker can run javascript on a website, they will have all the same permissions as the website, even if the script was not originally from the website (hence the name …

...Read more

Google CTF Quals 2017 - Food

| Toshi Piazza

This writeup is for the “Food” challenge found in Google CTF Quals 2017, from the reversing category. This writeup and others were also submitted to the Google CTF Writeup Competition.

JNI-Native Reversing

We are only given a food.apk, and from there we immediately unzip it and run dex2jar, followed by jd-gui to view the source code. Fortunately, the source is very succinct:

Although I’m not well versed in Android development, it looks like it’s loading a library from either one of lib/{armeabi,x86}. We turn our attention to the arm library, and symbolize it using a script we found here. This resolves certain awkward indirect function calls that otherwise would appear as unnamed constant offsets into the JNI struct (e.g. JNI_FindClass).

The bizarre disassembly in JNI_Onload seems to indicate that all useful strings have been encrypted, and are only decrypted at runtime. One such example of this is shown below:


Note that decrypt_string here, as we learn later is a variadic function with the following signature: char *decrypt_string(int a1, ...). We implement the decrypt_string function in python so that we can determine all the strings used by the application:

We can now resolve many …

...Read more

NorthSec 2017 rao_bash

| Toshi Piazza

This is a posthumous writeup of the rao_bash challenge hosted by NorthSec 2017. It features a recompiled and backdoored bash with a broken ELF header. This was solved after the CTF, because a small hint was dropped to us afterwards.

Fixing the ELF Header

First and foremost, we’d like to fix the ELF header of the executable. This ELF header interferes with all of our tools, and even the venerable file is having trouble with it:

$ file ./rao_bash_orig
./rao_bash_orig: ELF 64-bit MSB *unknown arch 0x3e00* (SYSV)

If that’s not enough of a hint, readelf seems to think that the entrypoint is some very large number,

$ readelf -a ./rao_bash_orig
ELF Header:
  Magic:   7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, big endian
  Machine:                           <unknown>: 0x3e00
  Version:                           0x1000000
  Entry point address:               0x300c420000000000

At this point, it should be obvious that the entry point is correct, if interpreted in big endian, and readelf is so nice to show as much that the data itself is all big endian. We simply revert this by flipping the “\x02” to a “\x01” in the 5th byte of the ELF header (see the magic line). Now, all of our tools work as appropriate.


Here is where the hint comes into place: bash is a huge executable, and unfortunately we could not find anything largely different between rao_bash and a bash we compiled ourselves with a similar compiler version, i.e. by using diaphora. However, it seems we missed a very quick-and-dirty xor, as seen below in the main function.

Quick and dirty xor

It looks like argv is checksummed, and if the checksum is valid it moves to sh_login_init(). Obviously, we don’t quite care about the checksum, as it’s very lossy …

...Read more