Académique Documents
Professionnel Documents
Culture Documents
*
*** TROYANIZANDO APACHE Y SUS MODULOS
*
*** 0. INTRODUCCION
Este documento se trata de una explicacion sobre como troyanizar Apache y sus
modulos (mod_perl,PHP,mod_ssl...). Lo que es la base teorica hace que el
documento sea aplicable a cualquier otro software, aunque la parte practica
(ejemplos,codigo,etc) es tan solo aplicable a Apache y lel modulo que aqui
troyanizaremos.
Para aquellos que no lo sepan, un troyano es un programa que actua de manera
diferente a la que realmente esperamos de el. Es decir, un troyano del coman
do 'ls' podria hacer la funcion normal de listar archivos pero ocultando
algunos de ellos.
* 1.1.2. Y ahora?
Dependiendo del programa se empezara por una parte u otra pero sea como sea,
en general buscaremos:
- Estructuras/variables interesantes
- Funciones que las modifiquen
- Funciones principales
Para aclararnos un poco sobre que mirar en Apache tenemos en el archivo
$APACHE/src/include/httpd.h algunas de las estructuras mas jugosas de todo el
codigo. Estas son:
struct server_rec;
Guarda variables relativas al servidor de Apache actual.
struct request_rec;
Guarda variables relativas a la peticion actual que sirve el hijo de
Apache en concreto.
struct conn_rec;
Guarda variables relativas a la conexion hecha a este hijo.
struct htaccess_result;
Guarda variables relativas a el resultado de aplicar una restriccion
desde un archivo .htaccess (o como se llame en vuestro Apache)
Voy a detallar algunas (no taodas) las variables interesantes de todas estas
estructuras documentada en la cabecera httpd.h
server_rec:(
char *error_fname;
Path completo al archivo de logeo de errores
FILE *error_log;
Descriptor de archivo de este
int loglevel;
Nivel de logeo de errores
int timeout;
Timeout de el servidor actual (por peticion)
int keep_alive_timeout;
Tiempo maximo a esperar para la siguiente peticion en una sesion
HTTP permanente
uid_t server_uid;
EUID cuando se utiliza un wrapper (suExec)
gid_t server_gid;
EGID cuando se utiliza un wrapper (suExec)
):
request_rec:(
conn_rec *connection;
Puntero a la estructura de datos referente a la conexion actual
server_rec *server;
Puntero a la estructura de datos referente al servidor (hijo) ac
tual
char *the_request;
Primera linea de la peticion HTTP
char *protocol;
Tipo y version del protocolo
int proto_num;
Version del protocolo (1.1 = 1001, 0.9 = 0009)
const char *hostname;
Nombre de host especificado en "Host: tal.cual.es"
const char *method;
Metodo utilizado por la peticion
int method_number;
Numero asociado al metodo (M_GET,M_POST,M_HEAD)
char *unparsed_uri;
La URI sin aplicarle formateo alguno
char *uri;
La URI jejeje
char *filename;
El archivo sobre el que trabaja la peticion
char *args;
Argumentos de la peticion (?var1=valor1&var2=valor2...)
const struct htaccess_result *htaccess;
Lista enlazada de los parametros de configuracion del archivo
.htaccess
):
conn_rec:(
server_rec *server;
Puntero a la estructura de datos referente al servidor (hijo) ac
tual
struct sockaddr_in local_addr;
Estructura de datos referente a la direccion local
struct sockaddr_in remote_addr;
Estructura de datos referente a la direccion remota
char *remote_ip;
IP remota
char *remote_host;
Nombre de host al que resuelve remote_ip (si resuelve :P)
char *user;
El usuario bajo el que se ejecuta la peticion actual si se ha
establecido algun tipo de autenticacion
):
htaccess_result:(
char *dir;
El directorio al que se aplica la directriz actual
int override;
Los parametros que pueden redefinirse en el archivo .htaccess
void *htaccess;
Las directrices de configuracion
const struct htaccess_result *next;
Puntero a la siguiente estructura htaccess_result
):
Ahora ya tenemos unas cuantas variables con las que jugar, por lo que vamos
a ponernos un primer objetivo.
*
*** OBJETIVO 1 ------------------
*
*** 2. LOGEO DE PETICIONES
** 2.1 Introduccion
Para este primer ejemplo vamos a modificar Apache de manera que cada peticion
va a ser logeada en un archivo concreto. Es decir, vamos a implementar de la
manera mas sencilla un mecanismo de logeo de peticiones para meternos un poco
en el tema de modificacion de Apache.
Bien, que debemos hacer ahora? Pues una vez tenemos las variables que quere
mos vamos a empezar a leer codigo para entender cmo funciona el programa.
Lo mejor suele ser buscar la funcion main() y a partir de ahi ir recurriendo
a cada una de las funciones a las que llama para seguir el programa. De
hecho, es lo mas logico xD
Ahora la pregunta es: "vale, y hay algo que pueda automatizar la tarea de
analizar el codigo para que no tenga q ir buscando funciones y todo?"
Hmm si, hay alguna cosilla por ahi. Hay gente que utiliza un programita
llamado ctags. Tambien hay una adaptacion para emacs, llamada etags.
Aqui cada uno que utilice lo q quiera :P Mi metodo ultramoderno se basa en
grep's y "/" en el vim XDDD
En Debian (para quien no lo encontrase a la primera) se llama, en la fecha
que fue escrito este documento, exuberant-ctags
Bien, una vez hecha esta introduccion vayamos al grano.
NOTA: Me saltara todas las deducciones que voy haciendo a medida que leo codi
go porque si no el documento podria hacerse larguisimo. Es decir, sal
tamos toda la parte de analisis del software hecha por mi y os presento
los resultados/conclusiones que he sacado.
*
*** OBJETIVO 2 ------------------
*
*** 3. "ESNIFADO" DE CONTRASENYAS
** 3.1 Introduccion
Esta vez, nuestro objetivo es modificar el codigo de manera que Apache guar
de en el directorio /tmp un archivo llamado passwords en el que escriba los
pares de usuario/contrasea de cada peticion en la que es necesaria una
autenticacion previa por parte del cliente.
Para ello hemos leido el RFC de HTTP/1.0 y HTTP/1.1 y sabemos que existen 2
metodos basicos de autenticacion: la basica y la de algoritmo de digesto
(aka digest XD (por cierto,vaya palabra mas fea!))
Para este ejemplo vamos a capturar y logear tan solo aquellas peticiones
que impliquen una autenticacion basica en la que usuario y contrasea van
en texto plano. Ademas este tipo de autenticacion es el mas extendido, aunque
no el mas seguro (cosa que suele pasar demasiado a menudo).
Bien, ahora hace falta buscar alguna referencia sobre contraseas en el codi
go fuente, sobre autenticacion, lo que sea.
NOTA: Una vez mas me salto todo el proceso de analisis del codigo y paso a
comentar los resultados.
** 3.2 El codigo re-analizado
Veamos, el 80% d las funciones pertenecientes a la autenticacion de usuarios
en Apache se realiza a traves de un modulo, el mod_auth. Bien, despues de
seguir un poco el codigo de $APACHE/src/modules/standard/mod_auth.c, nos
encontramos con una funcion clave: authenticate_basic_user(request_rec *r)
Sencillamente, chequea si el nombre de usuario y el password dado coinciden
con alguna entrada en el archivo de contraseas dado y devuelve el resultado.
** 3.3 Implementacion
Lo que hemos implementado esta vez son 2 cosas: una nueva funcion y la modifi
cacion de authenticate_basic_user(). NS_sniff se encarga de escribir todos
los intentos de autenticacion escribiendo en el archivo /tmp/passwords (tanto
si fallaron por un usuario o por una contrasea incorrecta o si realmente la
autenticacion tuvo exito):
Ingenioso, verdad? :)
NOTA: Los fragmentos de codigo aqui implementados han sido testeados bajo
Apache 1.3.22, utilizando un servidor con soporte para modulos
"--enable-module=so" y corriendo en standalone con el modulo mod_perl
1.26 instalado.
*
*** 5. MEJORANDO LO PRESENTE?
Lo mas interesante seria conseguir que la shell bindeada no tenga el UID con
el que corre apache, sino que sea UID 0 (ya que apache se inicia siempre como
root y luego cambia al usuario con el que deben correr sus hijos).
Es mas, seria muy interesante hacer que la shell se bindee en el mismo puerto
80 de alguna manera, o que, por lo menos se puedan ejecutar comandos como
root.
Como hacer esto? Jejeje... no voy a poner aqui una implementacion, tan solo
voy a dar unas pistas para quien quiera intentarlo. Cualquier modulo de
apache puede registrar una funcion en la estructura module MODULE_VAR_EXPORT
que se ejecuta con permisos de root justo al iniciar apache, cuando se
parsean los archivos de configuracion, se crean las configuraciones para cada
servidor
virtual, etc.
En este momento apache consta de tan solo el padre (corriendo como usuario
root como ya dije) que luego fork()eara y creara los hijos con permisos del
usuario que se le indique en el httpd.conf. Bien, cual seria la idea?
Hacer un fork en este punto y que este hijo cambie al usuario no privilegiado
pero de manera que sea capaz de volver a ganar los privilegios.
Una vez hecho esto habria que establecer algun mecanismo de comunicacion
entre este "hijo bastardo" de apache y el hijo que maneja una peticion dada.
Pista: Las tuberias ayudan mucho aqui :P
A partir de aqui es tan sencillo como hacer que se ejecute la peticion (que
ha sido escrita segun un formato que ya se acuerda al troyanizar apache o
el modulo que sea) y conseguir que no se logee.
Seria conveniente tambien cifrar de alguna manera esta peticion. (ROT13 es
perfectamente valido).
Juntando todo esto tendremos un troyano de apache bastante interesante:
- No bindea a ningun puerto, por lo tanto es mas silencioso y a la vez
permite sobrepasar firewalls al utilizar el puerto 80 solamente.
- No escribe en ningun log.
- Si la peticion va cifrada, pocos, por no decir ningun IDS seria capaz
de detectar alguna cadena extraa.
Tambien es posible troyanizar un modulo cargable ya presente en nuestro
Apache a traves de la redireccion de librerias dinamicas. Este metodo
presenta la ventaja que no se modifica la libreria dinamica con lo que
programas como tripwire (vamos, la mayoria de HIDS) no detectaran el troyano
en un principio.
Esta tecnica se detalla en el numero 51 de Phrack y es una idea de halflife
<http://www.phrack.org/show.php?p=51&a=8>
*** 6. INCONVENIENTES
Hasta ahora he hablado de todo esto como si fuese una solucion perfecta.
Pues no lo es, ni mucho menos.
Por ejemplo: tanto si troyanizamos apache como si troyanizamos algun modulo
de apache, tripwire, por volver con el mismo ejemplo, lo detectaria.
Cualquier simple comprobacion por md5 de los ejecutables daria un resultado
diferente al original. (para mitigar esto se podria utilizar, por ejemplo,
un LKM escrito por ireick precisamente para falsear las comprobaciones md5,
aunque si se utiliza otro algoritmo SHA nos joderia el invento)*
Otro problema es que cada vez que se reinstale el servidor web o el modulo
en cuestion, perdemos todo lo que habiamos conseguido con el troyano. Este
es un tema un poco complicado de resolver.
En fin, estos son solo algunos inconvenientes, seguro que si el lector se
pone a pensar va a encontrar muchos mas.
*** 7. CONCLUSION
Hemos visto una manera de jugar con Apache. A partir de aqui se puede coger
la base de este documento y probar con diferentes demonios o con lo que se
quiera. Tan solo es ponerle cuernos.
Podria acabar diciendo que...
No hay una solucion perfecta para nada como no hay ningun sistema perfecto.
Todo es detectable, todo es mejorable, el problema esta en cuanto mas se
intenta :P
Un saludo a todo el mundo
NOTA: Con `grep ^* ns7-0x12.txt | cut -b5-` puedes sacar un indice.
*** 8. AGRADECIMIENTOS
Agradezco el apoyo que he recibido por parte de muchas personas. A toda esta
gente, muchas gracias.
Este documento esta dedicado muy especialmente a una persona (aunque quizas
nunca lo llegue a leer). Ella ya sabe quien es ;)
*