What is the New Zealand Cyber Security Challenge?

Each year the University of Waikato holds a "cybersecurity" competition which involves CTF for rounds 1 and 2, policy creation/identification for round 3 and attack/defend servers for the top 5 teams. Side Challenges are also held which varies with the theme per year.

Unfortunately, I was not able to solve all challenges in round 0 this year so this solution guide will be incomplete unless extra contributions are provided.


Challenge 1

Presented with a login which is in HTML form. Looking at the DOM, the HTML forms onsubmit attribute is login(event). Going to the console in Chrome DevTools and executing login shows a snippet of the function, click on this will then bring us to the source.

This function simply takes the username and password and posts it to ./login.php using a XMLHttpRequest. The onload is defined which when the responseText is equal to 'yes', it will then go to ./done.php

So this means we can skip the login completely and go to the page ./done.php. An easy way to get this page is to create an anchor tag on the current page: <a href="./done.php">done</a>.


Challenge 2

We are told that a simple XOR cipher is used to "protect our communications" and to check we have the correct key to validate against the examples, which are:

Ciphertext in base64 Plaintext
bVQwJ2M3K0pCIjQm Test message
ekghNjF6HVxSNiEqLjcZcisyLzYrV1Ym Cyber Security Challenge
bVkmcyU2L14RKiBjMiddVSY9YzgrVV40 The flag is hidden below

It was pretty easy to guess that the last row contained the flag and we could assume all the other rows used the same key. Since [plaintext XOR key = cipher-text] then [cipher-text XOR plaintext = key]; this means we only need one cipher-text and it's corresponding plain text to get the key. Using Python, I could get the key using this method:

import base64

base64_ciphertext = 'bVkmcyU2L14RKiBjMiddVSY9YzgrVV40'
plaintext = 'The flag is hidden below'

def bxor(ba1, ba2):
    """ XOR two byte strings """
    return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])

# Decode the cipher-text to a byte string and make the plaintext a byte string
key = bxor(base64.b64decode(base64_ciphertext), plaintext.encode())

The key produced from this was 91CSCZN91CSCZN91CSCZN91C. We can see this repeats so we could say 91CSCZN is the "base key" that repeats. Now to use this key on the final row, since bxor will zip to whatever byte string is the shortest, we do not have to make the key the correct size.

import base64

base64_ciphertext = 'X10iNHk5fQ4HIGBxPnwPU3c='
key = '91CSCZN91CSCZN91CSCZN91C'

def bxor(ba1, ba2):
    """ XOR two byte strings """
    return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])

flag = bxor(base64.b64decode(base64_ciphertext), key.encode())

This gives flag:c376c32d26b4

Challenge 2*

In the DOM of challenge 2, there is another ciphertext hidden: fnQXc211KwwAJ2B2PyoXUyo9. Using the key on this:

import base64

base64_ciphertext = 'fnQXc211KwwAJ2B2PyoXUyo9'
key = '91CSCZN91CSCZN91CSCZN91C'

def bxor(ba1, ba2):
    """ XOR two byte strings """
    return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])

text = bxor(base64.b64decode(base64_ciphertext), key.encode())

This gives text = GET ./e51d35ed.bin. Making an anchor tag on the page again to go to that relative URL as I did previously, this downloaded the file.

This file is 50KB in size and was the end of the line for me unfortunately. No magic bytes match this file and hexedit couldn't tell me the file type. I tried many things with this file but nothing gave a flag. I did notice though that there were no bytes in the file that had a value above 127; not sure if this hinted anything.

Challenge 3

This challenge displayed an input that apparently allowed you to run commands, tying help gave Available commands: file, ls, cat, head, tail. Doing ls in the current directory, we could see flag (not the flag), notflag (not the flag), index.php and some folders/files.

Executing cat index.php gave the source PHP file of the current page, in here we could see inputs were being checked by a regex: ^(ls|file|cat|head|tail)( \-?(\.|\.{3})?\/?\w*| index\.php)?$. Using this in regex101 I found that we could execute ls .... After executing this, I saw there was a file named flag in the directory above.

Trying the regex out a bit more, I found we could execute cat .../flag which then printed the contents


Challenge 4

Challenge 4 simply had the flag in the images EXIF data. I used fotoforensics.com to look at the exit data of the image.


Challenge 4*

I searched quite a bit for this done but didn't get a lead on anything.

Challenge 5

This challenge is a basic SQL injection. Searching the provided name Fitzgerald Kemp returned some information. Searching ' OR '1' = '1 gave all the data and a simply Ctrl+F on the page found the flag.


Challenge 6

This challenge supplies you a .ova file you can import to virtual box. When imported, a small Linux OS is presented with the file validate at /home/tc. When run, it asks you for a flag and waits a few seconds before saying if it's correct for not.

When I was first attempting this, I had written a shell script using vi on the VM (lord help me) to get all the strings out of validate and run them through the program. Obviously this didn't work because they learnt their lesson from last year.

I decided to extract the ova file using 7-Zip on a windows machine and then unzipped VM-disk001.vmdk within. Looking in \home\tc I could then easily access the validate file. I then went to onlinedisassembler.com (no longer available) for a dirty way to look at what this binary contained.

Looking at the strings within the binary on a more familiar interface, I noticed hours later that one of the "flags" (many to put you off - which I had tested them all previously) were reversed; this was the flag.


Challenge 6*

Using the validate I had gotten from the .ova file, I opened up Kali Linux and put it in a proper disassembler. Running the program then stopped as it waited for input. I noticed a few commands above the breakpoint, that there had been a value constructed: https://nzcsc.org.nz/competition/2019/r0/6/challenge/server.php?key=72a145aff16bb741310d7c953070807b.

Requesting this using Python gave the flag:

import requests
r = requests.get('https://nzcsc.org.nz/competition/2019/r0/6/challenge/server.php?key=72a145aff16bb741310d7c953070807b')