/***************************************************************
*
* Proyecto: QueSO ( Que Sistema Operativo ?? )
* Autor: Jordi Murgo <savage@apostols.org>
* Descripcion: Determina el tipo de Sistema Operativo de una
* maquina concreta a partir del comportamiento
* de su pila TCP/IP ante paquetes TCP 'raros'.
* Ver el codigo para mas informacion.
* Licencia: GNU GPL
*
***************************************************************
* Agradecimientos a ToXyN, b0fh y a los colegas del canal #hack
* especialmente a syn por la traduccion del doc
***************************************************************
* CVS: $Id: queso.c,v 1.20 1998/09/22 20:35:43 savage Exp $
***************************************************************/
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#ifdef PCAP
#include <pcap.h>
#endif /* PCAP */
extern char *optarg;
extern int optind, opterr, optopt;
#include "tcpip.h"
static char *id = "$Id: queso.c,v 1.20 1998/09/22 20:35:43 savage Exp $";
#define ACK_HACK 1000
#define RANDOM_ACK (ACK_HACK+666)
#define MAXPKT 6
int PKTDEBUG = 0;
int CONFIGDEBUG = 0;
int SALVAR = 0;
int MAXTIMER = 3;
int VECES = 1;
int Zzz = 100;
int DEFPORT = 80;
char *DEVICE=NULL, DEVBUFF[255];
/*----- CFG_FILE_NAME moved to Makefile -----*/
static char CFGFILE[255] = DFLT_CONFIG_FILE;
/*------------- Prototiping -----------------*/
void debugtcp (unsigned long, tcprec);
void usage (const char *);
int checkos (struct sockaddr_in, short);
/*------------- Print TCP HDR ---------------*/
void
debugtcp (unsigned long myseq, tcprec tcp)
{
fprintf (stderr, "%d->%d S:%1d A:%s%lX W:%04X U:%X F: ",
ntohs (tcp.sport), ntohs (tcp.dport),
(tcp.seqnum?1:0),
tcp.acknum?"+":" ",
(tcp.acknum?(unsigned long) ntohl (tcp.acknum)-myseq:0),
ntohs (tcp.window), ntohs (tcp.urgentptr));
if (tcp.flags & URG)
fprintf (stderr, "URG ");
if (tcp.flags & SYN)
fprintf (stderr, "SYN ");
if (tcp.flags & RST)
fprintf (stderr, "RST ");
if (tcp.flags & FIN)
fprintf (stderr, "FIN ");
if (tcp.flags & ACK)
fprintf (stderr, "ACK ");
if (tcp.flags & PSH)
fprintf (stderr, "PSH ");
if (tcp.flags & XXX)
fprintf (stderr, "XXX ");
if (tcp.flags & YYY)
fprintf (stderr, "YYY ");
fprintf (stderr, "\n");
}
void
usage (const char *progname)
{
fprintf (stderr, "QueSO (c) 1998 savage@apostols.org\n");
#ifdef ENGLISH
fprintf (stderr, "Usage: %s [-options] host.com[/bits][:port] ...\nOptions:\n", progname);
fprintf (stderr, " -v Version\n");
fprintf (stderr, " -d Debug mode, print received PKTs.\n");
fprintf (stderr, " -w Update %s when new OS is found.\n",
CFGFILE);
fprintf (stderr, " -p port Select default remote port. (default=%d)\n",
DEFPORT);
fprintf (stderr, " -f srcIP Select correct In/Out IP.\n");
#ifdef PCAP
fprintf (stderr, " -i iface Select reception interface.\n");
#endif /* PCAP */
fprintf (stderr, " -c file Alternate config file.\n");
fprintf (stderr, " -t seconds Set reception timeout. (default=%d)\n",
MAXTIMER);
fprintf (stderr, " -n times How many times PKTs are sent. (default=%d)\n",
VECES);
fprintf (stderr, " -z usec. To avoid flood. (default=%d)\n",
Zzz);
#else
fprintf (stderr, "Uso: %s [-options] host.com[/bits][:puerto] ...\nOpciones:\n", progname);
fprintf (stderr, " -v Version del Programa\n");
fprintf (stderr, " -d Modo 'debug', imprime PKTs recibidos.\n");
fprintf (stderr, " -w Actualiza %s con el nuevo patron desconocido.\n",
CFGFILE);
fprintf (stderr, " -p puerto Selecciona el puerto remoto. (por defecto=%d)\n",
DEFPORT);
fprintf (stderr, " -f srcIP Selecciona la IP de Entrada/Salida.\n");
#ifdef PCAP
fprintf (stderr, " -i iface Selecciona la Interface de captura.\n");
#endif /* PCAP */
fprintf (stderr, " -c archivo Lee los patrones desde otro archivo.\n");
fprintf (stderr, " -t tiempo Establece el timeout de recepcion (por defecto=%d)\n",
MAXTIMER);
fprintf (stderr, " -n veces Numero de envios de los PKTs (por defecto=%d)\n",
VECES);
fprintf (stderr, " -z useg. Para evitar flood. (por defecto=%d)\n",
Zzz);
#endif /* ENGLISH */
fprintf (stderr, "\n");
exit (EXIT_FAILURE);
}
typedef struct
{
unsigned short set;
unsigned long seq;
unsigned long ack;
unsigned short urg;
unsigned short win;
unsigned short flag;
}
OSRES;
#define SILENT 0
int
check_os (struct sockaddr_in from, struct sockaddr_in dest, short dport)
{
spoofrec spoof;
tcprec tcp;
unsigned short start, s;
int n;
long timeout;
FILE *f;
char line[1024];
unsigned long myseq;
#ifdef PCAP
char fromtxt[16], desttxt[16];
char bpftxt[4096];
#endif
OSRES r[MAXPKT + 1];
for (n = 0; n <= MAXPKT; n++)
{
r[n].set = 0;
}
srand (time (NULL) & 0x0000ffff);
start = s = (rand () % 26000) + 4000;
spoof.seq = myseq = rand ();
spoof.ack = 0;
spoof.from = from;
spoof.dest = dest;
spoof.dport = dport;
if (PKTDEBUG)
{
fprintf (stderr, "Starting %s:%u -> ", inet_ntoa (from.sin_addr), start);
fprintf (stderr, "%s:%u\n", inet_ntoa (dest.sin_addr), dport);
}
#ifdef PCAP
strncpy (fromtxt, inet_ntoa (from.sin_addr), sizeof (fromtxt));
strncpy (desttxt, inet_ntoa (dest.sin_addr), sizeof (desttxt));
sprintf (bpftxt, "src host %s and dst host %s and tcp and src port %d and ( dst port %d",
desttxt, fromtxt, dport, start);
for (n = 1; n <= MAXPKT; n++)
{
sprintf (fromtxt, " or dst port %d", start + n);
strcat (bpftxt, fromtxt);
}
strcat (bpftxt, " )");
if (CONFIGDEBUG)
fprintf (stderr, "BPF: %s\n", bpftxt);
if( init_pcap( bpftxt ) )
return 0;
#endif /* PCAP */
/*-- PKT 0 --*/
spoof.sport = s++;
sendtcp (&spoof, SYN, VECES);
usleep (Zzz);
/*-- PKT 1 --*/
spoof.sport = s++;
sendtcp (&spoof, SYN | ACK, VECES);
usleep (Zzz);
/*-- PKT 2 --*/
spoof.sport = s++;
sendtcp (&spoof, FIN, VECES);
usleep (Zzz);
/*-- PKT 3 --*/
spoof.sport = s++;
sendtcp (&spoof, FIN | ACK, VECES);
usleep (Zzz);
/*-- PKT 4 --*/
spoof.sport = s++;
sendtcp (&spoof, SYN | FIN, VECES);
usleep (Zzz);
/*-- PKT 5 --*/
spoof.sport = s++;
sendtcp (&spoof, PSH, VECES);
usleep (Zzz);
/*-- PKT 6 --*/
spoof.sport = s++;
sendtcp (&spoof, SYN | XXX | YYY, VECES);
usleep (Zzz);
timeout = time (NULL) + MAXTIMER;
while ((timeout > time (NULL)))
{
if (gettcp (&spoof, &tcp))
{
if (ntohs (tcp.sport) == dport)
{
n = ntohs (tcp.dport) - start;
if (n < 0 || n > MAXPKT)
continue; /* ignore invalid pkts */
if (r[n].set == 1)
continue; /* ignore duppes */
if (PKTDEBUG)
{
fprintf (stderr, "IN #%-2u: ", ntohs (tcp.dport) - start);
debugtcp (myseq, tcp);
}
r[n].seq = tcp.seqnum ? 1 : 0;
r[n].ack = tcp.acknum ? (ntohl(tcp.acknum)-myseq+ACK_HACK) : 0;
if(r[n].ack > RANDOM_ACK)
r[n].ack = RANDOM_ACK;
r[n].win = ntohs (tcp.window);
r[n].flag = tcp.flags;
r[n].set = 1;
r[n].urg = tcp.urgentptr ? 1 : 0;
}
}
else
usleep (Zzz);
}
/*---------- CHECK RESULT -----------*/
if ((f = fopen (CFGFILE, "r")))
{
char osname[256]; /* should be smaller than line[], 256 should suffice */
unsigned short flag1 = 0, found = 0, linez = 0;
unsigned short pn = 0, ps = 0, pa = 0, pw = 0, pf = 0, pu = 0;
char *p;
while (fgets (line, sizeof (osname) - 1, f))
{
if (line[0] == '\n')
{
if (flag1 && found == linez)
{
printf ("%s:%d\t%s", inet_ntoa (dest.sin_addr), dport, osname);
fclose (f);
if (osname[1] == '-')
return 0; /* Not accurate response */
else
return 1;
}
}
if (line[0] == '*')
{
strcpy (osname, line);
if (CONFIGDEBUG)
fprintf (stderr, "\n%s", line);
flag1 = 1;
found = 0, linez = 0;
continue;
}
/*------ PARSE LINE ---*/
linez++;
p = strtok (line, " ");
if (p && isdigit (*p))
pn = atoi (p);
else
{
linez = 0;
flag1 = 0;
found = 0;
continue;
}
/*-- seq --*/
p = strtok (NULL, " ");
if (p)
ps = atoi (p);
/*-- ack --*/
p = strtok (NULL, " ");
if (p)
{
if( *p == 'R' )
pa = RANDOM_ACK;
else if( *p == '+' )
pa = atoi (p)+ACK_HACK; /*-- extended ACK field --*/
else
pa = atoi (p);
}
/*-- win --*/
p = strtok (NULL, " ");
if (p)
pw = 0xffff & strtol (p, NULL, 16);
/*-- flags --*/
p = strtok (NULL, " \n");
if (p)
{
if (CONFIGDEBUG)
fprintf (stderr, "%d %d %d %-4X %-7s ", pn, ps, pa, pw, p);
pf = 0;
if (strchr (p, 'S'))
pf |= SYN;
if (strchr (p, 'R'))
pf |= RST;
if (strchr (p, 'A'))
pf |= ACK;
if (strchr (p, 'F'))
pf |= FIN;
if (strchr (p, 'P'))
pf |= PSH;
if (strchr (p, 'X'))
pf |= XXX;
if (strchr (p, 'Y'))
pf |= YYY;
if (strchr (p, 'U'))
pu = 1;
else
pu = 0;
}
if (!r[pn].set)
{
if (pf)
{
if (CONFIGDEBUG)
fprintf (stderr, " ** Not received but configured\n");
found = 0;
flag1 = 0;
continue;
}
if (CONFIGDEBUG)
fprintf (stderr, " ** Ok, not received and not configured\n");
found++;
continue;
}
if (
( pa>=ACK_HACK?(pa==r[pn].ack):pa==(r[pn].ack>0) ) &&
ps == r[pn].seq &&
((pw == r[pn].win)
|| (pw == 1 && r[pn].win)
|| (!pw && !r[pn].win)
) &&
pf == r[pn].flag &&
pu == r[pn].urg )
{
if (CONFIGDEBUG)
fprintf (stderr, " ** Ok, Found\n");
found++;
continue;
}
else
{
if (CONFIGDEBUG)
fprintf (stderr, " ** FAILED %d,%d,%d,%d,%02X != %ld,%ld,%d,%d,%02X\n",
ps, pa, pw, pu, pf,
r[pn].seq,r[pn].ack,r[pn].win,r[pn].urg,r[pn].flag);
found = 0;
flag1 = 0;
continue;
}
}
fseek (f, 0L, SEEK_END);
fclose (f);
if (!SALVAR)
f = stderr;
else
f = fopen (CFGFILE, "a");
if (!f)
{
#ifdef ENGLISH
fprintf (stderr, "Can't open RW %s\n", CFGFILE);
#else
fprintf (stderr, "No puedo abrir %s para Escritura\n", CFGFILE);
#endif
return 0;
}
fseek (f, 0L, SEEK_END);
#ifdef ENGLISH
if (SALVAR)
printf ("%s:%d\t*- Unknown OS, writting new patern %s\n", inet_ntoa (dest.sin_addr), dport, CFGFILE);
else
printf ("%s:%d\t*- Unknown OS, pleez update %s\n", inet_ntoa (dest.sin_addr), dport, CFGFILE);
#else
if (SALVAR)
printf ("%s:%d\t*- SO desconocido, salvando nuevo patron en %s\n", inet_ntoa (dest.sin_addr), dport, CFGFILE);
else
printf ("%s:%d\t*- SO desconocido, actualice %s\n", inet_ntoa (dest.sin_addr), dport, CFGFILE);
#endif
if (PKTDEBUG || SALVAR)
{
#ifdef ENGLISH
fprintf (f, "\n*- Unknown OS @ %s:%d\n",
inet_ntoa (dest.sin_addr),
dport);
#else
fprintf (f, "\n*- SO desconocido en %s:%d\n",
inet_ntoa (dest.sin_addr),
dport);
#endif
for (pn = 0; pn <= MAXPKT; pn++)
{
line[0] = 0;
if (r[pn].urg)
strcat (line, "U");
if (r[pn].flag & SYN)
strcat (line, "S");
if (r[pn].flag & RST)
strcat (line, "R");
if (r[pn].flag & ACK)
strcat (line, "A");
if (r[pn].flag & FIN)
strcat (line, "F");
if (r[pn].flag & PSH)
strcat (line, "P");
if (r[pn].flag & XXX)
strcat (line, "X");
if (r[pn].flag & YYY)
strcat (line, "Y");
if (r[pn].set)
{
fprintf ( f, "%d %ld %s",
pn, r[pn].seq, (r[pn].ack==RANDOM_ACK?"R":((r[pn].ack>=ACK_HACK)?"+":"")) );
if(r[pn].ack!=RANDOM_ACK)
fprintf ( f, "%ld", (r[pn].ack>=ACK_HACK)?r[pn].ack-ACK_HACK:r[pn].ack);
fprintf ( f, " %d %s\n", r[pn].win ? 1 : 0, line);
}
else
fprintf (f, "%d - - - -\n", pn);
}
}
fprintf (f, "\n");
fclose (f);
return 0;
}
#ifdef ENGLISH
fprintf (stderr, "Can't open RO %s \n", CFGFILE);
#else
fprintf (stderr, "No pude abrir %s en Solo-Lectura\n", CFGFILE);
#endif
return -1;
}
/* -------------------------------------------------------- *
* The main function
* -------------------------------------------------------- */
int
main (int argc, char *argv[])
{
struct sockaddr_in dest, from;
unsigned short port;
char *s, *p;
int c;
int accuracy;
int limit = 0;
int bits = 32; /* single host */
unsigned long firsthost, lasthost, netmask, host;
/*
* Unbuffer stdout and stderr
*/
setvbuf (stderr, NULL, _IONBF, 0);
setvbuf (stdout, NULL, _IONBF, 0);
/*-- Init addr --*/
from.sin_addr.s_addr = dest.sin_addr.s_addr = 0;
/*
* Chek Argumentz
*/
while ((c = getopt (argc, argv, "i:p:c:t:n:f:z:vdwkh?")) != EOF)
{
switch (c)
{
case 'p':
DEFPORT = atoi (optarg);
break;
case 'v':
printf ("%s\n", id);
printf ("%s\n", tcpip_id ());
exit (EXIT_SUCCESS);
case 'c':
if (strlen (optarg) >= sizeof (CFGFILE))
fputs ("Filename for config file too long, ignoring.\n", stderr);
else
strcpy (CFGFILE, optarg);
break;
case 't':
MAXTIMER = atoi (optarg);
break;
case 'n':
VECES = atoi (optarg);
break;
case 'f':
if (resolve_host (optarg, &from) < 0)
exit (EXIT_FAILURE);
break;
case 'd':
PKTDEBUG = 1;
break;
case 'k':
CONFIGDEBUG = 1;
break;
case 'w':
SALVAR = 1;
break;
case 'z':
Zzz = atoi (optarg);
break;
case 'i':
strncpy(DEVBUFF,optarg,sizeof(DEVBUFF-1));
DEVICE=DEVBUFF;
break;
default:
usage (argv[0]);
return (EXIT_FAILURE);
}
}
if (optind >= argc)
usage (argv[0]);
/*-- Init raw sockets, we need r00t here --*/
init_tcpip ();
/*-- drops down to original user privileges --*/
if (!geteuid ())
setuid (getuid ());
/*-- Hostname or IP --*/
for(; argv[optind]; optind++) {
s = argv[optind];
limit = 0;
/*-- NetMask --*/
if ((p = strchr (argv[optind], '/')) != 0)
{
*p = 0;
bits = atoi (++p);
if (bits < 0)
{
bits *= -1;
limit = -1;
}
else
{
if (strchr (p, '-'))
limit = 1;
}
}
else
{
p = s;
bits = 32;
}
/*-- Port --*/
if ((p = strchr (p, ':')) != 0)
{
*p = 0;
port = atoi (++p);
}
else
{
port = DEFPORT;
}
if (resolve_host (s, &dest) < 0)
exit (EXIT_FAILURE);
if (!from.sin_addr.s_addr)
from.sin_addr = getlocalip (dest.sin_addr.s_addr);
if (!from.sin_addr.s_addr)
{
#ifdef ENGLISH
fprintf (stderr, "Unable to determine Local IP, use -f parameter\n");
#else
fprintf (stderr, "Imposible determinar la IP local, usa el parametro -f\n");
#endif
exit (EXIT_FAILURE);
}
host = ntohl (dest.sin_addr.s_addr);
netmask = ~(0xFFFFFFFFL >> bits);
/*-- WARNING: 32bit arquitectures have problems when bits==32 --*/
firsthost = (host & netmask) + 1;
lasthost = (host | ~(netmask)) - 1;
if (limit < 0)
lasthost = host;
else if (limit > 0)
firsthost = host;
if (bits < 31)
{
/*-- 0 to 30 bits --*/
for (host = firsthost; host <= lasthost; host++)
{
dest.sin_addr.s_addr = htonl (host);
accuracy = check_os (from, dest, port);
}
}
else
{
/*-- 31 and 32 bits, ignore mask and check only the host --*/
accuracy = check_os (from, dest, port);
}
}
exit (EXIT_SUCCESS);
}
syntax highlighted by Code2HTML, v. 0.9.1