Funciones para proteger tus programasVulnerabilidades en programas BATCH por sirdarckcatDado que recientemente me he dado cuenta de que las aplicaciones remotas estan usando cada vez mas aplicaciones BATCH, (de las cuales yo he hecho varias), para generar herramientas, con las cuales se cree que su uso es seguro, he decidido escribir este documento sobre las vulnerabilidades en estos programas, para que se reconsidere su uso en su futuro. Estas vulnerabilidades, las he nombrados segun su semejanza con vulnera- bilidades a nivel web, las cuales son BATCH Injection y BATCH Command Execution. BATCH InjectionLos que estén familiarizados con la seguridad a nivel web, seguramente conocerán el término SQL Injection, o LDAP Injection, que nos permite ejecutar sentencias SQL (o LDAP) en un interprete. Mientras que estos ataques (especialmente SQLi), ya han sido ampliamente estudiados, existe una variable, a la cual he llamado, BATCH Injection. basándonos en el mismo princípio en el cual se basan las vulnerabílidades de SQL Injection, el cual es insertar comandos extra, en una sentencia con el uso de una variable comprometída, BATCH injection, nos permite ejecutar comandos en un programa BATCH, que acepte alguna variable por medio de el comando SET, o a travez de argumentos. Puede que piensen que este ataque no es un vector de ataque remoto, pero temo decirles que estan en un error. Aunque son pocas, existen programas en los cuales, parte del código, utiliza BATCH, normalmente, para tareas de administración via telnet. Un ejemplo, es el archivo en WindowsNT, C:\WINNT\system32\login.cmd el cual aunque solo es una pantalla de bienvenida, la cual nos da shell sin tener que vulnerar nada, demuestra que el uso de archivos BATCH en tareas de administración telnet, es muy común. Podemos ver el contenido a continuación: >> type C:\WINNT\system32\login.cmd @echo off rem rem Secuencia de comandos global predeterminada del inicio de sesión rem para servidor Telnet rem rem En la instalación predeterminada, esta secuencia de comandos es rem ejecutada cuando se ejecuta el comando inicial shell. A cambio, rem intentará invocar la secuencia de comandos del inicio de sesión rem del usuario individual. rem echo *=============================================================== echo Bienvenido al servidor Telnet de Microsoft. echo *=============================================================== cd %HOMEDRIVE%%HOMEPATH% /d Como podémos ver, no es necesário vulnerar nada, dado que se nos da acceso inmediatamente a una shell, sinembargo, que tal que alguien, en un vago intento de "asegurar" este código (alguien que no sabe configurar el servidor telnet), tratará de reescribir este código a algo similar a: >> copy con: login.cmd @echo off echo Bienvenido al sistema de administración via telnet. set /P pass=Por favor ingresa tu contraseña: if "%pass%"=="secreto" ( echo PASSWORD CORRECTO ) else ( echo PASSWORD INCORRECTO call %~nx0 ) ^Z 1 archivos copiados. Se espera que el lector ya entienda la programación en BATCH. en este caso, al ejecutar el comando, nos dará: >> login.cmd Bienvenido al sistema de administración via telnet. Por favor ingresa tu contraseña: incorrecto PASSWORD INCORRECTO Bienvenido al sistema de administración via telnet. Por favor ingresa tu contraseña: secreto PASSWORD CORRECTO Mientras no escriba el password correcto, el codigo se volverá a llamar a si mismo, de esta forma se asegura de que hasta que el usuario envie el password correcto, no pasará de esa pantalla. Ahora, debemos entender como funciona el parser de BATCH en Windows. cuando nosotros escribimos en un código BATCH, algo como.. >> echo %LANG% es El parser de batch, detecta la variable LANG, y la reemplaza por su valor, es decir.. si hacemos algo como.. >> set cmd=ver >> %cmd% Microsoft Windows 2000 [Versión 5.00.2195] Lo que hace es reemplazar %cmd% por "ver", y ejecutar "ver". Ahora que sabemos eso, nos debemos dar cuenta de que, por ejemplo, si tenemos un programa asi: >> copy con: prueba.bat @echo off set /P nombre=¿Como te llamas? echo Tu te llamas %nombre% ^Z 1 archivos copiados. Nosotros podémos hacer que se guarde el resultado en un archivo, de la siguiente forma: >> prueba ¿Como te llamas? sirdarckcat > salida.txt Como vimos, la salida del comando, no se mostro, lo que si hubiera pasado si solo hubieramos puesto el nombre.. mira: >> prueba ¿Como te llamas? sirdarckcat Tu te llamas sirdarckcat Y podemos comprobar que el archivo se escribió >> type salida.txt Tu te llamas sirdarckcat En fin, no solo podemos redirigir la salida de comandos, tambien podemos ejecutar nuestros propios comandos, con el símbolo & >> prueba ¿Como te llamas? sirdarckcat & ver Tu te llamas sirdarckcat Microsoft Windows 2000 [Versión 5.00.2195] Interesante no? para ahora, ya te debes haber dado cuenta que nuestro script login.cmd es vulnerable a algo.. y si, tienes razón. >> login Bienvenido al sistema de administración via telnet. Por favor ingresa tu contraseña: "=="" ( REM PASSWORD CORRECTO Te preguntarás que paso aquí.. porque me dijo PASSWORD CORRECTO? si yo no sabía el password.. para eso, solo debemos de editar nuestro @echo off >> copy con: login.cmd @echo on ¿Sobrescribir login.cmd? (Sí/No/Todo): t echo Bienvenido al sistema de administración via telnet. set /P pass=Por favor ingresa tu contraseña: if "%pass%"=="secreto" ( echo PASSWORD CORRECTO ) else ( echo PASSWORD INCORRECTO call %~nx0 ) ^Z 1 archivos copiados. veamos: >> login >> echo Bienvenido al sistema de administración via telnet. Bienvenido al sistema de administración via telnet. >> set /P pass=Por favor ingresa tu contraseña: Por favor ingresa tu contraseña:"=="" ( REM >> if "" == "" ( REM "=="secreto" ( echo PASSWORD CORRECTO ) else ( echo PASSWORD INCORRECTO call login.cmd ) PASSWORD CORRECTO Lo que hace REM, es comentar una linea, es decir, ignora todo lo que esté despues de este, es el equivalente a "--" en MSSQL y a "/*" en mysql BATCH Command ExecutionAl igual que BATCH Injection, supongamos que queremos que un programa al cual aparentemente no podemos hacer nada, aun con el password correcto ejecute un comando, como por ejemplo.. nos de una shell.. esto se puede hacer con "&" (y despues veremos como con "|") >> prueba ¿Como te llamas? eduardo&cmd Tu te llamas eduardo Microsoft Windows 2000 [Versión 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. >> exit Como vimos, simplemente con &cmd, podemos ejecutar comandos, ahora ya pensarás que solo hay que filtrarlo.. pues, filtremoslo. >> copy con: prueba_seguro.bat @echo off set /P nombre=¿Como te llamas? set nombre=%nombre:&=_% set nombre=%nombre:>=_% set nombre=%nombre:<=_% set nombre=%nombre:|=_% set nombre=%nombre:"=_% echo Tu nombre es %nombre% ^Z 1 archivos copiados. >> prueba_seguro ¿Como te llamas? eduardo&cmd Tu nombre es eduardo_cmd >> prueba_seguro ¿Como te llamas? eduardo|start /B cmd /K REM >> >> Tu nombre es eduardo_start /B cmd /K REM >> >> >> Hey! que paso ahi? bueno, como ya te dije antes, cmd cambia las variables por su contenido, es decir.. cuando hacemos, set nombre=%nombre:&=_% el | es ejecutado.. por lo que CMD lee.. >> set nombre=eduardo|start /B cmd /K REM >> Lo que hace que salga 1 linea extra, aunque nada ahí es ejecutado. ahora, tenemos todavia la posibilidad de ejecutar cosas con | pero, que? cualquier comando, veremos su salida, pero yo quiero una shell y para eso, haremos lo siguiente.. >> prueba_seguro ¿Como te llamas? &cmd ^ Microsoft Windows 2000 [Versión 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. >> exit Microsoft Windows 2000 [Versión 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. >> exit Microsoft Windows 2000 [Versión 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. >> exit Microsoft Windows 2000 [Versión 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. >> exit Esto: "&cmd ^" abrió 4 cmd.. porque? porque el caracter "^" quita lo que esté despues de el, en este caso, un salto de línea.. por lo que al final cmd interpreta.. set nombre=_cmd set nombre= & cmd set nombre= & cmd set nombre= & cmd echo Tu nombre es & cmd Ahora porque no se filtra el caracter ^ tambien? La respuesta es porque ese debería ser el caracter a filtrar primero ya que de lo contrario, arrastra a todos los demas filtros, pero si ese es el primero en filtrarse, entonces podríamos usar & para abrir una shell. Una manera de solucionar eso, es colocando un espacio en cada linea. >> copy con: prueba_mas.bat @echo off set /P nombre=¨Como te llamas? set nombre=%nombre:&=_% set nombre=%nombre:>=_% set nombre=%nombre:<=_% set nombre=%nombre:|=_% echo Tu nombre es %nombre% ^Z 1 archivos copiados. De esta forma.. el ^ solo cancelará un espacio, en ves de un salto de linea y aun asi, despues de tanto, aun podemos hacer una shell.. ya que SET al ser canalizado, no guarda el valor, es decir.. si decimos: >> set dia=1|echo. dia no valdra nada: >> echo %dia% %dia% Asi que.. para poder detener el uso de &, ese debe ser el caracter filtrado primero, pero el caracter | puede anular los filtros, asi que ese tambien debe ser anulado primero, asi que un filtro en batch es irreal.. lo que se debe hacer, es no usar variables (manipulables por el usuario) en forma %var%, ya que siempre es posible ejecutar comandos asi, la forma correcta es la siguiente: >> copy con: seguro.bat @echo off set /P nombre=Escribe tu nombre: set | find "nombre=" ^Z 1 archivos copiados. >> seguro Escribe tu nombre: cmd|cmd&cmd^ nombre=cmd|cmd&cmd^ Para concluir solo quiero decirles, que BATCH solo debe ser usado para automatizar tareas, nunca para una interacción remota, a menos que en el código NUNCA se usen variables expandibles.. Hasta hoy, no he encontrado un filtro realmente seguro, usando FOR's, FIND, canalizaciones, etc. siempre hay alguna forma de ejecutar algun comando, por lo que no se debe usar BATCH en un entorno inseguro. Copyright (c) 2007 sirdarckcat@gmail.com Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is available at: http://www.gnu.org/licenses/fdl.html Tutorial por 0x0309@echo offInyecciones batchLas inyecciones batch son variables que contienen carácteres especiales y/o texto con significado para cmd.exe y que al ser expandidas de manera simple transforma la línea de comandos. Por qué les interesa a algunas personas Porque creen que esto es una materia del "lenguaje archivos por lotes". Valor práctico de evitarlas Se trata el contenido de las variables como texto, y se obtienen los resultados esperados. Problema de seguridad Pueden ser un problema de seguridad si no se las conoce, y se utiliza un archivo por lotes como autenticador de usuarios en una conexión remota en modo terminal, aunque esto es poco común que se realice, y en mi opinión alejado de la realidad. Tipos de expansiones de variables En cmd.exe existen dos tipos de expansiones de variables oficiales, existe una tercera, la del comando set /a: -Expansión simple: es la más común y utilizada. Se utiliza el carácter de porcentaje a cada lado del nombre de la variable para obtener su contenido. Esta expansión es realizada cuando se lee un comando. Por ejemplo, si dentro de un bloque if creamos una variable que no existía antes, asignándole un valor, y la expandimos de manera simple dentro del mismo bloque, la expansión expandirá vacío porque la expansión se realizó cuando se leyó el bloque del comando if y la variable no contenía nada. Con esta expansión el texto del contenido de la variable puede considerarse como parte de la línea de comandos, y no como solamente texto. -Expansión retardada: no es tan utilizada. Se utiliza el carácter exclamación final. Con esta expansión el texto del contenido de la variable es considerado en la mayoría de los casos como solamente texto por lo que en salvo casos especiales no modifica la línea de comandos. Esta expansión es realizada cuando se ejecuta el comando por lo que se obtiene el valor actual de una variable en todo momento. Puede utilizarse en conjunto con la expansión simple para obtener el contenido del contenido de una variable. Ejemplo, si tengo una variable llamada valorUno que contiene valorDos, y quiero obtener el valor de valorDos a través de valorUno se puede hacer: !%valorUno%! Casos improbables, rídiculos, quizás realizados alguna vez.-------------------------------------------------------------- Caso 1. conexión con netcat Equipo servidor: ip:192.168.0.103 comando para escuchar conexiones: nc.exe -L -d -e seguridad.bat -p 123 Equipo cliente: comando para conectarse: nc.exe -vv 192.168.0.103 123 seguridad.bat: Código:
@echo off Si presionamos enter, el archivo por lotes finaliza, y la conexión también. Código:
computador [192.168.0.103] 123 (?) open Si nos volvemos a conectar, y escribimos cualquier palabra por ejemplo:12345 como esta no es la palabra mouse que se asignó como password el archivo por lotes finaliza. Código:
computador [192.168.0.103] 123 (?) open Ahora, veremos una inyección para este caso: Código:
"=="" rem Código:
computador [192.168.0.103] 123 (?) open ¿Qué sucedió? Como se realizó una expansión simple el texto se mezcló con la línea de comandos y la alteró de esta forma: Código:
if not ""=="" rem"=="mouse" (exit) En la primera línea se forma una valor lógico verdadero con not ""=="" y por ello se comenta el resto de la línea con el comando rem, y se continúa hacia abajo, y se ejecuta cmd. De esta forma se obtiene acceso a la shell remota sin conocer la password. -------------------------------------------------------------- Caso 2: Chat de dos administradores de una escuela mediante servidor telnet. Este caso tan imaginativo lo expongo simplemente para mostrar el código del chat que se me ocurrió xD, y para dejar claro que las batch injections son un riesgo de seguridad en casos inimaginables. Se encuentra configurado el servidor telnet de windows, y existen dos cuentas en el sistema con privilegios de administrador. cuenta 1: rockox password de cuenta 1: batch cuenta 2: smart password de cuenta 2 : qwerty Bien, el servidor telnet de windows ejecuta luego de autenticar a los usuarios: Código:
%SYSTEMROOT%\System32\cmd.exe /q /k C:\WINDOWS\system32\login.cmd Y se ha modificado login.cmd con el siguiente contenido: Código:
@echo off Luego se conectan a la hora de costumbre: Código:
telnet.exe -l rockox 192.168.0.103 Código:
telnet.exe -l smart 192.168.0.103 Código:
Cliente Telnet de Microsoft Cada uno escribe su password y se conectan. Al primero se le muestra: Código:
Bienvenido al chat. Y al segundo se le muestra: Código:
Bienvenido al chat. el primero escribe: Código:
hola y al segundo se le muestra: Código:
mensaje del rockox desde SERVIDOR el 08-08-2009 6:45:43 y la conversación funciona bien, sin embargo, un alumno estuvo escaneando la red y obtuvo la password de ambos. Cuando nadie estaba conectado, ingresó al chat, como solo había un usuario conectado, los mensajes no llegaban a ninguna parte porque: esta línea: Código:
if /i "%%b" neq "%username%" ( Ahora escribió lo siguiente: Código:
") y obtuvo: Código:
") En realidad en esta parte: Código:
tlntadmn -m %%a "%msg%" > nul lo que hizo fue lo siguiente: Código:
tlntadmn -m %%a "")" > nul Se puso un cierre de paréntesis, sin haber puesto uno inicial antes, por lo que ante el error de sintaxis, el archivo por lotes finalizó, y se quedó en modo interactivo, debido a que windows ejecuta con el servidor telnet cmd con el parámetro /K. Esto muestra que un error de sintaxis también puede considerarse batch injection. En este caso, la conexión no se pierde, por el parámetro /K. -------------------------------------------------------------- Cómo evitar las batch injectionsUsando expansión retardada. Los códigos quedan sin batch injections de la siguiente manera. Simplemente reemplazo los signos de porcentajes por los signos de exclamación, y añado al comienzo setlocal enableextensions enabledelayedexpansion. enableextensions se agrega de todas maneras, pues hay comandos que solo se ejecutan con esta opción activada, como por ejemplo: goto :eof set /p exit /b y si se ejecuta cmd /e:off se produce error de sintaxis. seguridad.bat: Código:
@echo off login.cmd: Código:
@echo off Casos especiales dónde se produce batch injection con expansión retardada. comando echo on Código:
set var=on Código:
echo.!var! expansión del comando set /a el comando set /a con las extensions habilitadas expande el contenido de una variable solo proporcionando el nombre. Código:
@echo off Este código admite como solución (la coma es un separador dentro del comando set /a permitiendo realizar varios cálculos en una sola línea de izquierda a derecha): Código:
123,p= Ejemplo explicativo Código:
@echo off Esto produce como resultado en la variable suma:1 porque la expansión retardada expande valor como numero, y set /a realiza otra expansión expandiendo numero con su valor -2 Ahora si el código hubiese sido así: Ejemplo Código:
@echo off El resultado en la variable suma es 3 porque set /a expande la variable valor y esta no contiene ningún número por lo que se interpreta como 0. Evitar errores de sintaxis Para evaluar si el contenido de una variable está vacía es a mí gusto mucho más práctico y mejor hacer: Código:
if defined variable que: Código:
if "!variable!"=="" Evitar resultados inesperados En comparaciones númericas con el comando if la única forma de realizarlas correctamente es NO encerrándolas entre comillas, de lo contrario son consideradas como texto, y si se quiere comparar igualdad se debe usar EQU en vez de == porque == es para texto. Tanto equ, neq leq lss geq gtr requieren las extensiones habilitadas. No hacer: Código:
if "5"=="5" (...) Código:
if 5 equ 5 (...) Código:
if 10 equ 0xA (...) Hacer un filtro Para hacer un filtro se requiere ir recorriendo cada carácter, siempre usando expansión retardada, y evalúando si es o no el carácter que queremos. Ejemplo: ejemplo de uso la función clean Código:
@ECHO OFF Evitar interpretar texto dentro del comando set. Cuando recibimos argumentos a una función, para expandirlos usamos %1 %2 %3 %* y no podemos hacer: !1 !2 !* Por eso si queremos hacer esta asignación sin interpretarlo, sino considerarlo como texto, debemos encerrar el set entre paréntesis o entre comillas de esta forma: Código:
set "valor=%1" Código:
(set valor=%1) o si expandimos de manera simple dentro de una expansión retardada: Código:
set "valor=!%contenido%!" Código:
(set valor=!%contenido%!) Se requiere tener las extensions activadas para usar la forma de las comillas, en cambio para los paréntesis no, pero los paréntesis a veces dan problemas dentro de otros paréntesis, como los bloques de código de un comando for. Cómo transformar expresiones lógicas con expansiones simples de variables y crear otro comando. Código:
set /p num=Ingrese numero: batch injection: Código:
not a rem cuando no se puede comentar, pues se llega a una línea hacia abajo de error. Código:
set /p in=Ingrese passw: batch injection: Código:
_" if "1"=="0 rem al revés: Código:
set /p in=Ingrese passw: Código:
1"=="0" if "_ carácteres usados juntos o separados para provocar error de sintaxis: Código:
( Otros textos: Código:
&cmd^ Bugs de programaciónEsto es una observación de cómo un bug del comando for /f permite ignorar parte de un código.El bug del comando for /f y qué no está en la documentación oficial es que la opción eol siempre debe tener un valor, y si no se específica se asume por defecto el ';'. El bug consiste en que no puede decirsele al for /f que no asuma ningún carácter como comentario porque siempre considera el siguiente carácter. Ejemplo: Código:
for /f "eol=" %%a in ("texto") do rem Código:
for /f "eol= tokens=1" %%a in ("texto") do rem Por eso, si uno añade cualquier texto que comience con punto y coma, este se asume como comentario y el contenido de "do" no se ejecuta. Ejemplo: En este código para evitar hacer antes un if defined, se agrega al texto y a la clave el carácter '.'. Por eso "entrar" queda como "entrar." y esto es para que si la variable no está definida, %%a no esté vacío y se ignore la parte del "do". Código:
@echo off Parece funcionar bien, por ejemplo si presionamos enter, sin haber escrito nada, o escribimos un texto distinto de "entrar", se nos dice que está mal. Pero si ingresamos un texto que comience con ; este se asumirá como comentario por la razón antes explicada. batch injection para este caso especial: Código:
; Solución: No usar for /f o usar el mismo carácter de eol como delimitador y usar todos los tokens. De esta forma se eliminan todos los carácteres iniciales del texto que coinciden con el carácter de la opción eol. Código:
@echo off Código:
@echo off Ejemplo, puede ingresarse como password: Código:
;;;entrar Código:
;;;entrar;; Expansión de ! Cuando tenemos la expansión retardada activada, dentro de un archivo por lotes estos son los resultados de la expansión: Código:
! = vacío Cómo escapar el carácter ! con expansión retardada. Cuando tenemos expansión retardada puede ser necesario escapar el carácter ! para que éste no sea expandido como vacío. Por ejemplo dentro del comando set /a existe el operador: ! que significa que el número que procede al operador, si es 0, es convertido a 1, y si el número es distinto de 0, es convertido a 0. Ejemplo: Código:
@echo off Aquí se nos muestra: Código:
n=0 Esto es porque el carácter ! fue expandido a vacío. Para escapar el carácter ! con expansión retardada activada se debe hacer así: Código:
^^! Por eso el código para que funcione correctamente se debe escribir así: Código:
@echo off Ahora, nos muestra los resultados correctos: Código:
n=1 Eso es todo, y espero que ya no se pierda más el tiempo con este tema. Autor: 0x0309 http://foro.elhacker.net/scripting/inyecciones_batch-t263688.0.html |
Programación > BATCH >