Date: Tue, 23 Feb 1999 14:59:14 -0400
From: Julien Nadeau <sw3wn@csoft.net>
To: PacketStorm@genocide2600.com
Subject: NcFTPd remote buffer overflow

Proof of Concept - Security Advisory                        02/23/99
http://poc.csoft.net                                     Released by
poc@csoft.net                                    sw3wn@poc.csoft.net

---

Affected Program        NcFTPd <http://www.ncftp.com>
Description             FTP server (commercial)
Severity                Theoretical root compromise, logs compromise


Synopsis:

NcFTPd is a commercial FTP (File Transfer Protocol) server, in the
NcFTP product line.  The source code is not publicly released.  This
was tested on Linux with libc5 (there's a glibc2 specific version
available).

Problem:

NcFTPd's PORT parsing function has a stack buffer overflow
problem, which would basically allow a user to remotely execute
arbitrary code - the thing here is that the PORT parsing function
seem to change characters, that are not in the range 0x30-0x39
(ASCII '0'-'9'), into 0x20 (ASCII space), hence making an exploit
almost impossible (note that, if ascii 0x40 would be allowed that
would be a different story =p).

The program only parses for characters out of the 0-9 range in a
specific area in memory (the one that contains return address heh)
- the rest is kept unchanged, and you can't really go further in
memory, input line size is restricted.

Like with most buffer overflows there are probably work-arounds to
exploit it - this could have been a particulary neat exploit, since
it runs as a child and one could gain access transparently without
crashing the parent.

The current bug is not really a problem, it can crash the child process
with a segfault, the parent process receives a signal 6 (abort) and the
child process stay zombie for a few seconds and a brand new one is
created.
A few minor DoS attacks are possible but, who cares.  Oh and this could
be
used to not get listed in the logs too.

Example:

--
evil:$ nc victim ftp
220 victim NcFTPd Server (unregistered copy) ready.
user anonymous
331 Guest login ok, send your complete e-mail address as password.
pass some@thing
230-You are user #1 of 50 simultaneous users allowed.
230-
230 Logged in anonymously.
port 00000000000000000000000000000000000000000000 (...)
501 Syntax error in parameters.
evil:$
--


Status:

I contacted the authors, nice enough to send me back the piece
of code that causes the problem - here goes:

static int
ftp_aton(const char *cp, struct sockaddr_in *sinaddr)
{
        char buf[64];
        char *dst;
        char *dstlim;
        int i, c;
        unsigned int octets[6], u;

        memset(sinaddr, 0, sizeof(struct sockaddr_in));
        dst = buf;
        dstlim = dst + sizeof(buf);

        for ( ; ; ) {
                c = *cp++;
                if (c == '\0')
                        break;
                if (! isdigit(c))
                        c = ' ';
                if (dst < dstlim)
                        *dst++ = c;
        }
        *dst = '\0';

        if (sscanf(buf, "%u%u%u%u%u%u",
                &octets[0],
                &octets[1],
                &octets[2],
                &octets[3],
                &octets[4],
                &octets[5]
        ) != 6) {
                return (-1);
        }

        for (i=0; i<6; i++) {
                if (octets[i] > 0xFF)
                        return (-1);
        }

        sinaddr->sin_family = AF_INET;
        u = (octets[0] << 24)
                | (octets[1] << 16)
                | (octets[2] << 8)
                | (octets[3]);
        sinaddr->sin_addr.s_addr = htonl(u);
        u = (octets[4] << 8) | (octets[5]);
        sinaddr->sin_port = htons((unsigned short) u);
        return (0);
}       /* ftp_aton */


void
Port(char *line)
{
         if (gLoggedIn == 0) {
                NotLoggedIn();
                return;
        }
        if (gAllowPORT == 0) {
                Reply("550 This site does not permit PORT.  Please use
PASV
instead.\r\n");
                return;
        }

        if (ftp_aton(line, &gRemoteDataAddr) < 0) {
                Reply("501 Syntax error in parameters.\r\n");
                return;
        }
        /* ... */
}

