#include #include /* How well do you know everybody's favorite input scanning/buffer building C function? * Tested on RHEL 4, glibc 2.3.4, 2.6.9-5.EL kernel * satebackire */ int main(int argc, char **argv){ char a[32]; char b[32]; unsigned int ret = 0; ret = /* Uncomment each sscanf line and test it out, bytes printed are user input only (not the format) */ //sscanf(argv[1], "%20.10s:", a); //This doesn't complete correctly (i.e. no copy takes place, ret=0) //for any combination of buffer size or format, NO OVERFLOW //sscanf(argv[1], "%20s:", a); //copies 20 bytes from input buffer and then appends a NULL byte AFTER 20th byte copied, NO OVERFLOW //sscanf(argv[1], "%.20s:", a); //This doesn't complete correctly (i.e. no copy takes place, ret=0) //for any combination of buffer size or format, NO OVERFLOW //sscanf(argv[1], "%32s:%32s", a, b); //Copies data into buffer 'a' first, then 'b' second with input `perl -e 'print "A"x32 .":". "B"x32'` //first off-by-one with the copying into 'a' overwrites the first byte of buffer directly after a //on systems with a tightly packed stack this may be the saved EBP //second off-by-one occurs with the copying of input into 'b', this overwrites the first byte of buffer //'a' effectivly NULLifying the string but the memory has been copied none-the-less (see appendix A) //OFF-BY-ONE BUFFER OVERFLOWS //sscanf(argv[1], "%31s:%31s", a, b); //Buffer copies are limited to 31 bytes + 1 NULL appended to the end, NO OVERFLOW //sscanf(argv[1], "%.32s:%.32s", a, b); //This doesn't complete correctly (i.e. no copy takes place, ret=0) //for any combination of buffer size or format, NO OVERFLOW //sscanf(argv[1], "%10.60s:", a); //This doesn't complete correctly (i.e. no copy takes place, ret=0) //for any combination of buffer size or format, NO OVERFLOW //sscanf(argv[1], "%120.10s:", a); //This doesn't complete correctly (i.e. no copy takes place, ret=0) //for any combination of buffer size or format, NO OVERFLOW //sscanf(argv[1], "%*s:", a); //the '*' character in a format string discards the assignment, ret=-1 (weird), NO OVERFLOW //sscanf(argv[1], "%32c:", a); //copies 32 bytes every time into buffer 'a', without appending a NULL, //and reading past the number of actually bytes available in the buffer //to ensure that the format specified 32 bytes gets copied every time (Appendix C). printf( "argv[1]=[%s][%d] a=[%s][%d]\n", argv[1], strlen(argv[1]), a, strlen(a)); printf("ret=[%d]\n", ret); return 0; } /* Appendix A (gdb) set args `perl -e 'print "A"x32 .":". "B"x32'` (gdb) b 9 (gdb) n 9 ret = (gdb) set *0xbff05120=0x69696969 //think of this as a manually placed stack canary (gdb) x/20x b 0xbff050e0: 0x003d64f8 0x00000000 0x00000000 0x00000000 0xbff050f0: 0x004ff378 0x08049668 0xbff05108 0x080482d9 //boundry of 'b' which starts at 0xbff050f0 0xbff05100: 0x00000000 0x00000000 0xbff05128 0x0804848a 0xbff05110: 0x00000000 0x004fdff4 0x08049588 0x004fdff4 //bountry of 'a' which starts at 0xbff05110 0xbff05120: 0x69696969 0x003d5ca0 0xbff05188 0x003ede33 (gdb) n 31 printf( "argv[1]=[%s][%d] a=[%s][%d]\n", argv[1], strlen(argv[1]), a, strlen(a)); (gdb) x/20x b 0xbff19390: 0x42424242 0x42424242 0x42424242 0x42424242 0xbff193a0: 0x42424242 0x42424242 0x42424242 0x42424242 0xbff193b0: 0x41414100 0x41414141 0x41414141 0x41414141 0xbff193c0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbff193d0: 0x69696900 0x003d5ca0 0xbff19438 0x003ede33 */ /* Appendix B The examples above that use a period character i.e. '.' in their format string actually invalidate the format string format as described when compiling with -Wall. Of course a programmer will often confuse the correct way of doing size limits using format strings in printf() family of functions with the correct way of doing them in scanf() family of functions. warning: unknown conversion type character `.' in format Correct: sprintf(buff, ".256s", input); Incorrect: scanf(buff, ".256s", input); The incorrect case should be caught by functional testing as the return value will never be correct (in the example above it would be 0) */ /* Appendix C $ ./scanf_fun `perl -e 'print "A"x10 .":"'` argv[1]=[AAAAAAAAAA:][11] a=[AAAAAAAAAA:��ï[16] ret=[1] */