NahamCon CTF (2020)

Posted on 13 June 2020

I took place in NahamCon CTF. It was a two day event but I was only able to make it for the last 1/2 of the last day. I scored 1075 points which placed me 453 / 2854. It was a fun CTF. Below are some writeups of my favorite challenges:

Rotten

This was a scripting challenge. You connect to a server which sends back rot13 encoded strings. You have to decode the strings and then send them back. Each time you do the server sends back a fragment of the flag. Below is a example network conversation:

RECEIVED:   "send back this line exactly. no flag here, just filler."
SENT:       "send back this line exactly. no flag here, just filler." (0)
RECEIVED:   "zluk ihjr aopz spul lehjasf. johyhjaly 3 vm aol mshn pz 'n'"
SENT:       "send back this line exactly. character 3 of the flag is 'g'" (19)
RECEIVED:   "wirh fego xlmw pmri ibegxpc. rs jpek livi, nywx jmppiv."
SENT:       "send back this line exactly. no flag here, just filler." (22)
RECEIVED:   "fraq onpx guvf yvar rknpgyl. ab synt urer, whfg svyyre."
SENT:       "send back this line exactly. no flag here, just filler." (13)
RECEIVED:   "rdmc azbj sghr khmd dwzbskx. bgzqzbsdq 28 ne sgd ekzf hr 'q'"
SENT:       "send back this line exactly. character 28 of the flag is 'r'" (1)
RECEIVED:   "gsbr poqy hvwg zwbs sloqhzm. bc tzou vsfs, xigh twzzsf."
SENT:       "send back this line exactly. no flag here, just filler." (12)
RECEIVED:   "myhx vuwe nbcm fchy yruwnfs. wbuluwnyl 11 iz nby zfua cm 'o'"
SENT:       "send back this line exactly. character 11 of the flag is 'u'" (6)
RECEIVED:   "nziy wvxf ocdn gdiz zsvxogt. xcvmvxozm 3 ja ocz agvb dn 'b'"
SENT:       "send back this line exactly. character 3 of the flag is 'g'" (5)

Tim was able to put together a really cool node app to process this:

rotten_demo

Trapped

In this challenge you connect to a server and are dropped into a shell, however you cannot run any commands.

[tom@tomandtim ~]$ nc jh2i.com 50019
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
user@host:/home/user$ ls
ls
You are stuck in the trap!
user@host:/home/user$ pwd
pwd
You are stuck in the trap!
user@host:/home/user$ echo hello
echo hello
You are stuck in the trap!
user@host:/home/user$

The “trap” is referring to a shell trap which can be configured to execute a command or function upon receiving a signal via the kill command. I tried used kill -l to list all the signals and then used trap - x to unset every signal but this did not work. Back to the drawing board: man trap. It turns out that bash has a special DEBUG signal which is executed on every command. So running trap - DEBUG unsets the trap and you can read the flag.

user@host:/home/user$ echo hello
echo hello
You are stuck in the trap!
user@host:/home/user$ trap - DEBUG
trap - DEBUG
user@host:/home/user$ ls -l
ls -l
total 4
-rw-r--r-- 1 user user 33 Jun  4 20:12 flag.txt
user@host:/home/user$ cat flag.txt
cat flag.txt
flag{you_activated_my_trap_card}
user@host:/home/user$

Phphonebook

This challenge opens with a dead giveaway. Local file inclusion

phphonebook local file inclusion hint

By using the PHP filter function you can dump the source code of the page:

php base64 filter function

Inspecting the source code it uses the PHP extract method, which takes a variable and parses it into more variables including overwriting the current scope. It amazed me all the “interesting features” PHP has. Using the Firefox dev tools we can forge the request to add a emergency.

    <?php
      extract($_POST);

    	if (isset($emergency)){
    		echo(file_get_contents("/flag.txt"));
    	}
    ?>

firefox dev tools

Official Business

The webpage presents a simple username and password form. You can find the source code under /robots.txt. There is a bug with the Cookie signature validation. See the code below:

def load_cookie():

    cookie = {}
    auth = request.cookies.get("auth")
    if auth:

        try:
            cookie = json.loads(binascii.unhexlify(auth).decode("utf8"))
            digest = cookie.pop("digest")

            if (
                digest
                != hashlib.sha512(
                    app.secret_key + bytes(json.dumps(cookie, sort_keys=True), "ascii")
                ).hexdigest()
            ):
                return False, {}
        except:
            pass

    return True, cookie

The entire check is wrapped in a try catch block so any error will be caught and skipped which passes the cookie validation. So just need to forge a cookie without a signature, which causes cookie.pop("digest") to throw a error and log you into the site.