Vulnerabilidades de seguridad de aplicaciones web de Lua
Auditoría y defensa de las aplicaciones web basadas en Lua. Artículo de Felipe Daragon - 26 de mayo de 2014
Este artículo pretende destacar el riesgo de aportaciones no validadas en aplicaciones web basadas en Lua.
Hace algún tiempo escribí acerca de cómo detectar NoSQL y las vulnerabilidades de inyección de JavaScript del lado del servidor (SSJS) utilizando técnicas basadas en el tiempo. JavaScript es still rising y cada vez más popular como plataforma para el código del lado del servidor. Esta vez quiero cubrir aspectos de seguridad de otro lenguaje/marco que se está adoptando cada vez más para el desarrollo web y que tiene mucho potencial: Lua.
Lua es un lenguaje de gran alcance útil para programadores experimentados pero considerado fácil para programadores inexpertos al mismo tiempo. Aunque Lua se ha utilizado principalmente para el desarrollo de juegos, existe un creciente ecosistema de aplicaciones web y marcos de Lua. Los servidores web maduros, como Apache y Nginx, son la opción preferida para muchos que están creando o pensando en crear sus primeras aplicaciones web basadas en Lua - juntos representan más del 70% de los servidores web del mundo y son opciones sólidas para comenzar. Herramientas de programación web alternativas y pioneras de Lua como CGILua han existido por un tiempo. CGILua se ejecuta en la parte superior de Apache o cualquier servidor web habilitado por CGI.
En Syhunt, hemos estado usando Lua durante bastante tiempo como parte de nuestras herramientas de seguridad de aplicaciones web y un lenguaje de scripting primario, y recientemente empezamos a usar internamente los módulos Lua para los servidores web Apache y Nginx, conocidos como mod_lua y ngx_lua respectivamente. Decidí comprobar por mí mismo cómo las aplicaciones web de Lua con código inseguro podrían ser específicas y cómo fácilmente los servidores en cuestión podrían verse comprometidos. Para realizar las pruebas, creé una pequeña colección de aplicaciones web inseguras con problemas de validación de entrada adaptados a cada software de servidor web.
Los resultados
Mis simulaciones cubrieron Ataques al Sistema de Archivos, Cross-Site Scripting (XSS), Inclusión de Archivo Local (LFI), Inyección de Comandos del SO, Inyección de Código SQL, Inyección de Código Lua, Inyección de CRLF y otras clases de vulnerabilidad. No encontré ninguna manera de realizar ataques de inclusión de archivos remotos como en PHP, pero Lua (en el estado actual) no puede frustrar algunos ataques que sólo son posibles en versiones anteriores de PHP. Cuando Lua Pages estaba disponible, Log Poisoning se utilizó con éxito para actualizar una vulnerabilidad LFI a la ejecución remota de comandos. Se utilizaron técnicas basadas en el tiempo para detectar vulnerabilidades de inyección de código de Lua, y vulnerabilidades de inyección de SQL, que luego se utilizaron para cargar un shell web de Lua (backdoor) a los servidores web que alojan las aplicaciones web vulnerables.
Codificar de forma segura es difícil, incluso para programadores experimentados, sin importar el lenguaje de programación que utilice. Si usted es un desarrollador web que trabaja con Lua, es muy recomendable que aprenda sobre los errores de codificación que afectan a la seguridad y dan lugar a estas vulnerabilidades. Este trabajo pasará por los más comunes e importantes. Si, en cambio, usted es un probador de lápiz encargado de asegurar un sitio web que está utilizando Lua, este documento puede ser igualmente útil para usted.
Este artículo demuestra que los atacantes hábiles e incluso no calificados pueden aprovechar profundamente las debilidades enumeradas anteriormente, y descubrir rápidamente las fallas usando métodos automatizados. Al final también encontrará algunos consejos para endurecer la configuración del servidor web.
Cross-Site Scripting (XSS)
Una de las vulnerabilidades más comunes en las aplicaciones web actuales, los ataques XSS (CWE-79) se pueden utilizar para secuestrar sesiones de usuario, realizar ataques de phishing, ejecutar código malicioso en el contexto de la sesión del usuario, difundir malware y más. Hay diferentes tipos de XSS, como se refleja (se muestra en los ejemplos anteriores) y se almacena en el servidor (por ejemplo, en una base de datos). Para evitar ataques XSS, escape los datos suministrados por el usuario en la salida o valide la entrada del usuario.
Funciones de Impresión
- mod_lua: r:puts(), r:write()
- ngx_lua: ngx.say(), ngx.print()
- CGILua: cgilua.put(), cgilua.print(), <?=somevar?>
''Ejemplos de códigos vulnerables'
-- /vulnerable.lua?name=<script>alert('XSS');</script> -- Apache & mod_lua function handle(r) local name = r:parseargs().name or "" r:puts(name) end -- Nginx & ngx_lua local name = ngx.req.get_uri_args().name or "" ngx.header.content_type = "text/html" ngx.say(name) -- CGILua local name = cgilua.QUERY.name or "" cgilua.htmlheader() cgilua.put(name)
Solución
local name = ngx.req.get_uri_args().name or "" name = htmlescape(name) -- ver un ejemplo al final de este artículo ngx.header.content_type = "text/html" ngx.say(name)
SQL Injection
Los ataques de inyección SQL (CWE-89) son utilizados por los malos para robar información, agregar o cambiar información en una base de datos, cerrar el acceso a una aplicación web, evitar la autenticación, ejecutar comandos arbitrarios, entre otras cosas. Los ataques de inyección de SQL se pueden mitigar eficazmente utilizando una validación de entrada fuerte.
En Lua, la biblioteca LuaSQL permite a las aplicaciones web conectarse a Oracle, MySQL, SQLite, PostgreSQL y otras bases de datos y ejecutar sentencias SQL. mod_lua viene con su propia API de base de datos integrada para ejecutar comandos en MySQL, PostgreSQL, SQLite, Oracle y otras bases de datos. Cualquiera que sea la opción que elija para ejecutar comandos SQL, tenga cuidado con la entrada suministrada por el usuario y utilice la validación de entrada. La documentación mod_lua recomienda el uso de sentencias preparadas siempre que sea posible como una manera de minimizar el riesgo de ataques de inyección SQL. Consulte las notas sobre precauciones al trabajar con bases de datos en:http://www.modlua.org/api/database#database_caveat
SQL funciones de ejecución
- LuaSQL's conn:execute()
- mod_lua's DB API: db:query(), ... (see http://www.modlua.org/api/database)
Ejemplo de código vulnerable:
-- mod_lua DB API function handle(r) r.content_type = "text/html" local username = r:parseargs().username or "" local database, err = r:dbacquire("mysql", "host=localhost,user=user,pass=,dbname=dbname") if not err then local sl = 'SELECT * FROM users WHERE username="'..username..'"' local results, err = database:query(r,sl) -- (...) database:close() else r:puts("Could not connect to the database: " .. err) end end -- CGILua & LuaSQL -- /vulnerable.lua?name=John');SQLBELOW -- /shell.lp?cmd=dir cgilua.htmlheader() local sqlite3 = require "luasql.sqlite3" local name = cgilua.QUERY.name local env = sqlite3.sqlite3() local conn = env:connect('mydb.sqlite') local sql = [[ CREATE TABLE sample ('id' INTEGER, 'name' TEXT); INSERT INTO sample values('1','%s') ]] sql = string.format(sql, name) for l in string.gmatch(sql, "[^;]+") do conn:execute(l) end -- (...) conn:close() env:close() -- SQLLite shell upload example for CGILua -- ATTACH DATABASE 'shell.lp' AS shell; -- CREATE TABLE shell.demo (data TEXT); -- INSERT INTO shell.demo (data) VALUES ('<? os.execute(cgilua.QUERY.cmd) ?>
El ejemplo anterior funciona con mod_lua y ngx_lua con algunos cambios menores si se habilita una extensión de preprocesador LP, pero esto no es exclusivo de SQLLite. Todos los motores SQL pueden ser blanco de ataques similares. El objetivo más común es MySQL: http://www.greensql.com/article/protect-yourself-sqli-attacks-create-backdoor-web-server-using-mysql
Ataques al sistema de archivos
En algunos entornos, las funciones del sistema de archivos Lua aceptan parámetros que contienen bytes nulos (%00), pero no los manejan correctamente, tratando el carácter de byte nulo como terminador de cadena e ignorando todos los caracteres siguientes. La razón es que la propia Lua, como PHP, está programada en C y se basa en las funciones del sistema de archivos del sistema operativo (ver PHP Null Byte Poisoning http://www.madirish.net/?article=436 and http://www.php.net/manual/en/security.filesystem.nullbytes.php).
Tenga esto en cuenta al tratar con la entrada del usuario, especialmente al pasarla a las funciones del sistema de archivos.
''Funciones del sistema de archivos
- Lua's io library: io.open(), ...
- LuaFileSystem (lfs) library functions - http://keplerproject.github.io/luafilesystem/manual.html
- mod_lua: r:mkdir(), r:mkrdir(), r:rmdir(), r:sendfile(), r:touch(), etc
Ejemplo de Vulnerabilidad:
-- Nginx & ngx_lua -- /vulnerable.lua?file=/etc/passwd%00 -- /vulnerable.lua?file=c:\boot.ini%00 ngx.header.content_type = "text/html" local file = ngx.req.get_uri_args().file or "" local f = io.open(file..".txt") local result = f:read("*a") f:close() ngx.say(result)
Una solución eficaz para frustrar este tipo de ataques es evitar pasar la entrada enviada por el usuario a cualquier API del sistema de archivos.
Inclusión de archivos locales (LFI)
Regularmente subestimadas tanto por los probadores de penetración como por los desarrolladores, las vulnerabilidades de inclusión de archivos locales son comúnmente explotadas por los atacantes para causar revelación de código fuente o para obtener la ejecución de comandos a través de envenenamiento de registros y otros métodos.
'Incluir y funciones similares
- Lua's require(), dofile()
- CGILua: cgilua.handlelp(), cgilua.lp.include(), cgilua.doif(), cgilua.doscript()
Ejemplos vulnerables
-- CGILua -- /vulnerable.lua?prod=/var/log/apache/access_log%00 -- after sending request with user-agent "<? os.execute(cgilua.QUERY.cmd) ?>" local prod = cgilua.QUERY.prod or "" cgilua.htmlheader() cgilua.lp.include(prod..".lp") -- CGILua (2) -- /vulnerable.lua?prod=/var/log/apache/access_log%00 local prod = cgilua.QUERY.prod or "" cgilua.handlelp(prod..".lp") -- CGILua (3) -- /vulnerable.lua?prod=somefile.lua%00 local prod = cgilua.QUERY.prod or "" cgilua.htmlheader() cgilua.doif(prod..".lua")
Inyección de código Lua
Este tipo de vulnerabilidad (conocida como CWE-94) ocurre cuando un desarrollador utiliza la función Lua loadstring() y le pasa datos no confiables que un atacante puede modificar. La función loadstring() compilará el código y devolverá una función que cuando se llama tiene el mismo efecto que ejecutar la cadena. Los atacantes pueden usar esto para inyectar el código de arbitraje Lua que luego es ejecutado por la aplicación web.
''Métodos de detección basados en el tiempo
Usando el código Lua:
/vulnerable.lua?name=John")%20c%3Dos.clock t%3Dc() while c()- t<%3D3 do end%20x%3D("`
Usando LuaSocket:
/vulnerable.lua?name=John")%20require"socket".select(nil,nil,3)%20x%3D("
Uso de la función Sleep (ngx_lua only):
/vulnerable.lua?name=John")%20ngx.sleep(10)%20x%3D("
Usando LuaJIT's FFI:
/vulnerable.lua?name=John")%20 ffi%3Drequire"ffi"%20ffi.cdef%20"unsigned%20int%20sleep(unsigned%20int seconds); "%20ffi.C.sleep(10)%20x%3D("
''Ejemplos de códigos vulnerables'
-- ngx_lua -- /vulnerable.lua?name=John")%20os.execute("notepad.exe local name = ngx.req.get_uri_args().name or "" ngx.header.content_type = "text/html" local html = string.format([[ ngx.say("Hello, %s") ngx.say("Today is "..os.date()) ]], name) loadstring(html)() -- CGILua -- /vulnerable.lua?name=<? os.execute('ls -la') ?> -- /vulnerable.lua?name=<? os.execute('dir') ?> -- Print the source of the vulnerable script: -- /vulnerable.lua?name=<? cgilua.put(io.open(cgilua.script_path):read('*a')) ?> local name = cgilua.QUERY.name or "" cgilua.htmlheader() local html = string.format([[ <html> <body> Hello, %s! Today is <?=os.date()?> </body> </html> ]], name) html = cgilua.lp.translate(html) loadstring(html)()
Cuestiones de gestión de las sesiones
Esta es otra área que los desarrolladores web a menudo pasan por alto. Cuando no se implementa correctamente puede conducir a los atacantes secuestrar sesiones activas.
Este es un aspecto que CGILua, por ejemplo, se ha equivocado - genera identificadores de sesión de 9 dígitos basados en el tiempo del sistema operativo y versiones anteriores generan identificaciones secuenciales, no aleatorias. En nuestras simulaciones de ataque contra una aplicación web basada en CGILua vulnerable, pudimos adivinar identificaciones de sesión válidas extremadamente rápidamente a través de ataques de fuerza bruta. Escribí más sobre esta vulnerabilidad en http://www.syhunt.com/? n=Advisories.Cgilua-weaksessionid
Al realizar una revisión de código o una prueba de penetración, se debe prestar especial atención a cómo se generan y manejan los identificadores de sesión, y a los métodos utilizados para cambiar las credenciales de usuario. Algunas herramientas, como el Burp Sequencer, pueden resaltar anomalías en la generación de identificadores de sesión. Si usted está cocinando su propia biblioteca de sesiones, debe utilizar los métodos estándar que ya existen, en lugar de hacer su propia.
Considere usar la biblioteca luuid, que genera ID aleatorios de 128 bits (usando /dev/urandom), o cualquier otra biblioteca Lua que pueda generar ID únicos basados en la aleatoriedad de alta calidad. También puedes preguntar en los foros mod_lua o ngx_lua (toma este correo como ejemplo: https://groups.google.com/forum/#!topic/openresty/iGdgYxd6A64). Lo más probable es que los desarrolladores de módulos de Lua o alguien ya esté usando un generador de ID de sesión seguro.
openssl_random_pseudo_bytes() también está disponible a través de lua-openssl (https://github.com/zhaozg/lua-openssl). Debido a los recientes problemas de seguridad de OpenSSL, es posible que prefiera mantenerse alejado de ella, o no. Ver este post pre-Heartbleed por timoh6 para entender por qué: http://timoh6.github.io/2013/11/05/Secure-random-numbers-for-PHP-developers.html
La matemática de Lua.random(), como la función rand() de PHP, usa la función rand de la biblioteca C subyacente y es un generador de números aleatorios verdaderamente débil. Debido a que es cryptographically insecure, no es adecuado para la generación de tokens.
El OWASP tiene un muy buen artículo sobre las mejores prácticas para la gestión de sesiones, que recomiendo leer: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Length.
Para una inmersión más profunda sobre este tema, véase también: http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
Hashing de contraseña insegura
Cuando mezcle las contraseñas, use sal y evite las funciones MD5 y SHA1 que vienen con los módulos de Lua. MD5 y SHA1 son vulnerables al ataque de colisión, y son demasiado rápidos para calcular y prevenir la fuerza bruta (incluso SHA512 es demasiado rápido). Considere el hashing lento como PBKDF2, bcrypt o scrypt.
Aquí hay dos excelentes artículos sobre cuestiones de hachís por CodingHorror:
OS Command Injection
Los fallos de inyección de comandos del sistema operativo (CWE-78) permiten a los atacantes ejecutar comandos arbitrarios en el servidor remoto. Debido a que una vulnerabilidad de inyección de comandos puede conducir a comprometer el servidor que aloja la aplicación web, a menudo se considera un defecto muy grave. En Lua, este tipo de vulnerabilidad ocurre, por ejemplo, cuando un desarrollador utiliza datos de usuario no validados para ejecutar comandos del sistema operativo a través de las funciones os.execute() o io.popen().
''Ejemplos de códigos vulnerables'
-- /vulnerable.lua?user=demo%20|%20dir%20c:\ -- mod_lua function handle(r) local user = r:parseargs().user or "" local handle = io.popen("dir "..user) local result = handle:read("*a") handle:close() r:puts(result) end -- mod_lua (2) function handle(r) local user = r:parseargs().user or "" os.execute("ls -l /home/"..user) end -- ngx_lua ngx.header.content_type = "text/plain" local user = ngx.req.get_uri_args().user or "" local handle = io.popen("ls -l /home/"..user) local result = handle:read("*a") handle:close() ngx.say(result)
Inyección CRLF
La inyección de CRLF (CWE-93) es una técnica que permite a los atacantes controlar las cabeceras de respuesta HTTP. Es bastante simple, pero extremadamente potente y puede permitir que un atacante realice una miríada de ataques, incluyendo XSS. Una vez más, desinfectar la entrada del usuario antes de usarla en encabezados evita un ataque exitoso.
Nota: Apache HTTPd/mod_lua 2.4.10 (TBA) agrega protección contra este ataque (para más detalles, vea http://www.mail-archive.com/dev@httpd.apache.org/msg59607.html). También contacté al desarrollador de ngx_lua, quien está considerando agregar el mismo tipo de mejora en futuras versiones de ngx_lua.
'Encabezado y redirigir funciones/tablas
- mod_lua: r.headers_out[]
- ngx_lua: ngx.redirect(), ngx.header[]
- CGILua: cgilua.redirect(), cgilua.header()
''Ejemplos de códigos vulnerables'
-- mod_lua -- /vulnerable.lua?user=demo%0d%0aNew-Header:SomeValue -- /vulnerable.lua?user=%0d%0a%0d%0a<script>alert('XSS')</script> function handle(r) local user = r:parseargs().user or "" r.content_type = "text/html" r.headers_out['X-Test'] = user r:puts('Some text') return apache2.OK end -- ngx_lua -- /vulnerable.lua?name=%0d%0aNewHeader:Value local name = ngx.req.get_uri_args().name or "" ngx.header.content_type = "text/html" ngx.redirect("http://www.somehost.com/"..name) -- ngx_lua (2) -- /vulnerable.lua?user=test%0d%0aNewHeader:Value local user = ngx.req.get_uri_args().user or "" ngx.header['X-Test'] = user ngx.say('Some text') -- CGILua -- /vulnerable.lua?url=http://someurl%0d%0aNew-Header:SomeValue local url = cgilua.QUERY.url or "" cgilua.redirect(url) -- CGILua (2) -- /vulnerable.lua?demo=test%0d%0aLocation:http://www.somehost.com local demo = cgilua.QUERY.demo or "" cgilua.header('X-Test',demo) cgilua.htmlheader()
Man in the Middle
Los ataques de Man In The Middle son una amenaza en evolución y ocurren cuando un atacante se las arregla para meterse en tu flujo de tráfico, donde puede alterar o interceptar datos.
Si desea proteger a los usuarios de escenarios de ataque man-in-the-middle:
Implemente HTTPS y configure el encabezado de seguridad de transporte estricto de HTTP (http://dev.chroum.org/sts), y la bandera segura en todas las cookies para reducir este riesgo.
Adopte un ciphersuite TLS/SSL que admita Forward Secrecy (más sobre esto en https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy) - esto hará que las claves privadas comprometidas no puedan descifrar comunicaciones pasadas.
Mantenga su software de servidor y bibliotecas de Lua actualizados con las últimas correcciones de seguridad.
''Instalación de bibliotecas Lua a través de HTTPS
Hablé con el desarrollador de LuaRocks sobre la posibilidad de que un atacante inyectara código Lua malicioso en los paquetes legales de Lua usando MITM. LuaRocks y sus alternativas no han sido diseñadas para defenderse de los ataques de MITM. Al instalar o actualizar módulos Lua a través de estos sistemas, en caso de que se preocupe por esta posibilidad, puede intentar utilizar el espejo GitHub para descargar los módulos Lua a través de SSL. Para lograr esto, puede invocar el comando luarocks usando, por ejemplo, los siguientes parámetros:
luarocks install --only-server=https://raw.github.com/keplerproject/rocks/master/ luasocket
Debido a que la mayoría de los archivos rockspec apuntan a URLs comenzando con https, este método minimizará grandemente este riesgo.
Validando la entrada del usuario
SQL Injection, Code Injection, Command Injection y XSS pueden ser efectivamente mitigados mediante el uso de una fuerte validación de entrada. La mayoría de las aportaciones de los usuarios procederán de:
- mod_lua: r:parseargs(), r:parsebody()
- ngx_lua: ngx.req.get_uri_args(), ngx.req.get_post_args()
- CGILua: cgilua.POST, cgilua.QUERY
En Lua, al igual que en otros lenguajes de programación, es probable que se produzca un bypass de validación debido a patrones débiles utilizados en funciones de coincidencia de patrones, como string.match(). En ngx_lua, hay ngx.re.gmatch(), y ngx.re.match(), que permite trabajar con expresiones PCRE. Con mod_lua, también se pueden usar funciones de análisis de expresiones como r:strcmp_match(), r:expr() y r:regex(). Los desarrolladores deben prestar especial atención a la elaboración de patrones para que coincida con cadenas, porque de lo contrario pueden ser ignorados por atacantes experimentados. Un simple ejemplo de bypass de validación:
-- /vulnerable.lua?email=john@somedomain.com -- /vulnerable.lua?email=<script>alert('john@somedomain.com XSS')</script> local email = ngx.req.get_uri_args().email or "" ngx.header.content_type = "text/html" if email:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w+") then ngx.say(email) end
Ejemplo de escape HTML
function htmlescape(text) local special = { ['<']='<', ['>']='>', ['&']='&', ['"']='"' } return text:gsub('[<>&"]', special) end
Asegúrese de comprobar el proyecto de valor (https://github.com/Etiene/valua), una biblioteca recientemente liberada para realizar la validación de entrada, y http://sputnik.freewisdom.org/lib/xssfilter/ (Todavía tengo que comprobarlo a fondo)
Endureciendo la configuración del servidor
In Apache/mod_lua, asegúrese de limitar el alcance de la directiva SetHandler utilizando la directiva FilesMatch:
<FilesMatch "\.lua$"> SetHandler lua-script </FilesMatch>
Esto evitará que "test.lua.foo" se ejecute como un script Lua.
Ajuste su seguridad asegurándose de tener cadenas PCRE que coincidan perfectamente con las extensiones de Lua. Ejemplo:
LuaMapHandler "\.lp$" versus LuaMapHandler .lp$
El primer ejemplo de expresión regular coincidirá con . lp, mientras que el segundo ejemplo coincidirá con ulp, xlp, . lp, ¡! lp etc, haciendo su configuración menos segura y restrictiva.
Pregunte en los Foros
Detrás de los módulos de Lua hay programadores experimentados - mod_lua es mantenido por Daniel Gruno (miembro de la Apache Software Foundation), y ngx_lua mantenido por Yichun Zhang (CloudFlare). Han estado poniendo mucha energía en mejorar el núcleo de los módulos de Lua, su rendimiento y estabilidad, y añadiendo nuevas características, y están muy abiertos a preguntas, informes de errores y contribuciones de todo tipo.
Copyright © 2014 Syhunt Security
Descargo de responsabilidad: La información en este aviso se proporciona "tal cual" sin garantía de ningún tipo. Los detalles proporcionados son estrictamente con fines educativos y defensivos.
Syhunt no es responsable de los daños causados por el uso directo o indirecto de la información proporcionada por este documento.