NICO-FTP 3.0.1.19 — Buffer Overflow SEH with Bypass ASLR

Miguel Méndez Z.
Oct 4, 2018 · 9 min read
Image for post
Image for post

Una breve descripción del binario. Bueno este programa es un cliente para el servicio de FTP el cual tiene las mismas características que cualquier otro cliente, nada de otro mundo :)

Impacto:

Se puede utilizar solo un archivo de configuración con el shellcode ya cargado para que sea ejecutado en cualquier equipo sin un usuario como intermediario que genere el payload.

Probado en:

  • Windows XP sp3 [es]
  • Windows 7 x86 [en] with ASLR

Para empezar lo primero que realiza el binario al ejecutarse es crear un archivo llamado Global.conf el cual guarda la información de cuando se ejecuto el programa por ultima vez, también crea otro archivo que es Sites.conf este archivo guarda toda la informacion y configuración de la lista de servidores.

Ahora aquí es donde se encuentra el problema, ya que podemos inyectar código en las variables del archivo de configuración (Sites.conf) para generar un crash en la aplicación, activando un desbordamiento de buffer.

Image for post
Image for post
archivos creados por el programa

Parámetros del archivo de configuración que son utilizados por el administrador de sitios.

Image for post
Image for post
parametros

Aquí podemos observar que el administrador de sitios muestra un dominio FALSO esto ocurre porque ya se encuentra cargada la configuración mas la shellcode y este simula un dominio dentro del padding para que pase mas desapercibido en el momento de la explotación.

Image for post
Image for post
ejecutando…

Código del exploit *.py que generar un nuevo archivo de configuración mas la shellcode que se aprovecha de un buffer overflow.

Image for post
Image for post
code

Archivo final que sera utilizado por el programa NicoFTP para cargar las configuraciones de conexión , ademas de accionar el BOF.

Image for post
Image for post
config

Después de realizar la conexión al dominio FALSO donde estaba incrustada la shellcode, esta realiza la acción de mostrar la calculadora de windows pero se podria utilizar para generar un reverse shell. (opcional)

Image for post
Image for post

Ahora entramos a lo mas entretenido que es Reverse sobre el binario.

Lo primero que hice fue analizar el binario para saber si tenia algún packer y me encontré con el famoso UPX :) demasiado fácil obtener el código original utilizando algún unpack o desde ollydbg, immunity de forma manual.

Bueno en esta ocasión el paso a paso del unpack no lo mostrare, pero no es complicado sacar este tipo de protección. Así que iremos directamente en busca de la función vulnerable en el binario el cual nos permite desbordar la memoria.

Info:

  • Packer: UPX v0.8x
  • Tools: RDG & Exeinfo
  • Código: Borland C++
Image for post
Image for post
Image for post
Image for post

Después de tener el código legible utilizamos DeDe que es un programa para analizar ejecutables compilados con delphi 2,3,4,5. Este no ayuda a identificar algunas cadenas de texto, direcciones a funciones como también algo del código de las vistas como (botones, textarea, etc…).

Image for post
Image for post

Ya obtenida esta información me dirijo a la dirección 004145E4 con el querido IDAPro. A partir de aquí encontré una función que se encarga de escribir en el archivo Site.conf que renombre como Crea_file_config y otra Show_input que se encarga de mostrar la ventana de “Edición de sitio”.

Image for post
Image for post
Image for post
Image for post

Ya revisado el flujo del programa llego por fin a las rutinas de NameChange.

Image for post
Image for post

Es aquí donde existe una función llamada GetText que toma los bytes del área de texto que se ingresaron como Nombre y los almacena en un buffer, también podemos observar el puntero en el stack con dirección al buffer.

También encontré que calculaba el largo de la entrada con un GetTextLen que sera utilizado como el size (0x111C que es igual a 4380 en decimal) y argumento para GetTextBuf al igual que el puntero al buffer.

Image for post
Image for post

Después de retorno de la función vemos como se llena el buffer con 43h en el cuadro rojo :) ahora debo saber como se están copiando estos byte en la memoria asignada (buffer).

Image for post
Image for post

Después de un largo rato analizando rutinas … y mas rutinas y punteros a de vtable… llegue al punto donde esta copiando la cadena del nombre al buffer, utilizando la función memcpy.

Podemos observar los argumentos enviados a la función memcpy (str_Nombre, Size_largo_nombre, pBuffer) para seguir con la ejecución de un rep movsd que lee y escribe 4 byte en el espacio asignado. Despues de la primera inserción vemos que el puntero al buffer (stack) ya vale 0x45454545…

Image for post
Image for post

Después de este proceso lo que hace es escribir en el archivo de configuración toda esta informacion. hasta el momento todo normal.

¿ Ahora cuando se produce el crash (overflow) ?

El crash se produce cuando se selecciona el host y damos a conectar, pero antes este debe cargar esa data para ser seleccionada así que este es el siguiente paso.

De vuelta a la acción:

Esto lo realizare paso a paso. Bueno lo primero que veo es donde se hace una llamada a los archivos de configuración y sigue así hasta obtener el path completo del archivo a leer.

Image for post
Image for post

Después del retorno de File_Read() y un par de funciones mas llegamos a AfterConstruction() y dentro hace una llamada a DoCreate() en el cuadro de color.

Image for post
Image for post

Seguimos analizando varia y varias :) funciones hasta llegar a este punto donde podemos empezar a trasear a donde va a escribir a y que dirección de buffer fue asignado. Vemos que setea una constante string_nombre con 0 para que mas adelante sea ocupada y un GetMem() con un size de 4000h esto retornara en EAX para que luego sea movida a lpReturnedString y mas argumentos que serán enviados a kernel32_GetPrivateProfileStringA().

Image for post
Image for post

Ahora vemos los argumentos para kernel32_GetPrivateProfileStringA(). La flecha negra es una dirección que se genera mediante GetMem() en la función ClassCreate() esta se utiliza como base para tomar el puntero al archivo de configuración después tenemos la flecha amarilla esta punta a una dirección donde esta el archivo a leer y la verde que es el puntero al buffer que sera utilizado para almacenar la strings obtenida del archivo.

Image for post
Image for post

Destripando la función kernel32_GetPrivateProfileStringA() vemos una llamada a kernel32_BaseDllReadWriteIniFile() donde la rutina que llena el buffer se encuentra coloreada.

Image for post
Image for post

Vemos que el buffer ya fue sobrescrito con 0x45.

Seguimos debugeando y dentro de _TMainForm_ConectarClick() obtenemos la rutina que ejecuta la ventana _cls_Conectar_TConnectForm para seleccionar el host a conectar :) al fin.

Image for post
Image for post

Entonces nos queda seleccionar el supuesto host y dar a conectar para seguir con las siguientes instrucciones.

Image for post
Image for post
ventana de conexión

Después entramos a la función _crash_seh y dentro nos topamos con un par de rutinas que saltan de un lado para el otro llegando a SendMessageA().

Image for post
Image for post
inicio del crash :)

Punteros de la función LStrFromPCharLen pisados con los bytes que he inyectado.

Image for post
Image for post
direcciones pisadas

Ahora que ya identifique los punteros que son pisados y ademas son los que ejecutan la excepción me enfoco en seguir traceando los flujos y condiciones hasta llegar a user32_WCSToMBEx.

Image for post
Image for post
Encontrando el camino

Función intermedia!

Image for post
Image for post
obstáculo 1

Ya dentro de la función user32_WCSToMBEx encuentro que hay una llamada a ntdll_RtlUnicodeToMultiByteN y es ahí donde esta el juego.

Image for post
Image for post
Image for post
Image for post
obstáculo 2

Esta es la gran y buscada rutina ntdll_RtlUnicodeToMultiByteN que va sobrescribiendo el Stack.

  • Rutina completa.
Image for post
Image for post
rutina full

La rutina lo que hace es ir tomando los bytes del cuadro amarillo e ir escribiendo en el buffer del Stack cuadro rojo en un ciclo (while) de 0x1311 en decimal es 4881 que es igual al largo del string ingresado, esto llega a pisar todo a su paso hasta los bytes finales del la estructura seh 0xFFFFFFFF y Bummm.

Preparando el ambiente entonces el size 0x1311 lo mueve a EAX mas un desplazamiento al final lo deja en EDX, el buffer a escribir en ECX y el de lectura en EAX.

Image for post
Image for post
contador
Image for post
Image for post
set de buffer

El ultimo cuadro va calculando mediante un SUB la resta del contador del While.

Image for post
Image for post
contador

Empieza a escribir los bytes en el buffer del Stack.

Image for post
Image for post
escritura en buffer

Nos detenemos a 4 bytes antes de pisar el puntero de ExceptioHandler.

Image for post
Image for post
stop a 4 bytes

Seguimos con esos 4 bytes y llegamos a nuestro resultado. Ahora si calculamos la dirección del puntero pisado con el inicio del buffer nos da un largo de 0x1028 bytes que se traduce en 4136 y si restamos 4 quedara en 4132 para pisar SEH jejeje… solo verificar el exploit en python y tendremos lo mismo pero aquí los valide a mas bajo nivel.

Image for post
Image for post
game over

Dato Opcional…

Pasamos como argumento los registros de EDX y EAX a la función Move() pero ya esta frito el Stack :) y pisado los punteros de SEH.

  • EDX=Dirección del buffer a escribir.
  • EAX=Dirección del buffer en Stack.
Image for post
Image for post
Copia byte en Heap

Conclusión:

La vulnerabilidad se produce porque no se valida el largo a escribir en el buffer ya que utiliza un while() que podría ser infinito eso depende del string que ingresemos como nombre y que sera utilizado como size.


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store