Writeup – Auth Service, Zippy (ABCTF)

Over the past week, team farmingsimulator2015 participated in ABCTF (among others, which I can’t write about for now as they’re still in progress). This was a high-school level CTF with relatively limited variety (e.g. no binary exploitation), but there were two interesting challenges which I’ll write down for reference.

Authentication Service

Authentication Service was presented as a node.js web application: the source code I’ve mirrored here. The application allows the user to log in with a username and a password, and the goal is to set yourself to admin.

The interesting code starts at around line 53:

app.post('/login', function(req, res) {
 if(req.body.username && req.body.password) {
 var admin = "false";
 if(req.body.username===secrets.username && req.body.password===secrets.password)
 admin = "true";
 var auth = {username: req.body.username, password: req.body.password, admin: admin};
 auth = encrypt(JSON.stringify(auth));
 res.append('Set-Cookie', 'auth='+auth+'; Path=/; HttpOnly');

We can see that unless we know the secret username and password (we don’t), we’re going to get a JSON token with “admin” set to false, which gets encrypted via AES-192 in CBC mode, which gets sent to us in a cookie.

We also have the code which the server uses to parse the cookies when we try to access the “/” page:

app.get('/', function(req, res) {
 if(req.cookies.auth) {
 var auth = decrypt(req.cookies.auth).replace(/[^0-9a-zA-Z{}":, ]+/g, '');
 auth = JSON.parse(auth);
 res.render('index', {auth: auth, flag: secrets.flag});
 else {
 res.render('index', {auth: false, flag: secrets.flag});

The vulnerability here is twofold:

  1. Firstly, the regex which the application uses to parse the authentication token strips out backslashes, allowing us to break the token’s format by setting a username like: 123″, extraparam: “1”
  2. Secondly, we can invalidate existing parameters using bit flips in the CBC-mode encryption.

To summarize, the existing admin token looks like this (after JSON.stringify)

{"username": "a", "password": "Password1", "admin": "false"}

We can leverage the first vulnerability to insert our own “admin” parameter:

{"username": "a\", \"admin\":\"true", "password": "Password1", "admin": "false"}

The backslashes will automatically get stripped by the decode function (line 39, var auth=). We can then use random bit flips to break the final “admin” parameter, so that our injected parameter is the only valid one:

{"username": "a\", \"admin\":\"true", "password": "Password1", "ad_in": "!al*e"}

The only limitation was that the final (real) admin parameter should lie in an AES block of it’s own, such that our bit flips did not affect anything else. We were able to accomplish this by inserting increasingly long passwords until a new AES block was generated (i.e. the cookie suddenly expanded by 32 characters).

The exploitation of the vulnerability was then a simple trip through Burp Suite, using the Intruder functionality to sequentially flip bits in the password/admin blocks until we got our flag back:

Pasted image at 2016_07_17 11_50 PM


Zippy was presented as a small zip file, containing 40 more archives: zippy. Each of the zip files inside was encrypted. Our initial approach was to load them all into jtr via zip2john and brute force while I went to work: unfortunately, a few hours of brute forcing produced nothing meaningful, so we stopped.

We continued our investigation in Wikipedia’s page for the zip format, helpfully supplemented by the 010 editor template for Zip files. A few interesting fields come to attention:


Interestingly, the uncompressed size of data.txt is 4, and the CRC32 checksum is calculated against the unencrypted version of data.txt: so our next approach is to simply brute force the plaintext against a quick CRC32 check.

From here, we quickly notice that many of the zips are using characters only from the Base64 character set (which makes sense, given the filenames are all .txt). Optimising our brute force solution, we arrive at the this script. Running this across the zip files in the original archive generates a base64 chunk.

Decoding the base64 chunk, we realize we have another zip file, which we do successfully crack (“z1P”), for the flag:


As always, thanks to the creators of ABCTF for running a solid CTF event, and thanks for the super fun problems based on a Windows 98 Virtual Machine.

See you all in a week’s time for Trend Micro’s CTF!

About Norman

Sometimes, I write code. Occasionally, it even works.
This entry was posted in Bards, Computers, Jesting and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s