Health is a medium machine from Hack The Box that involves performing Server Side Request Forgery (SSRF) via redirect to access the Gogs (Go Git Service) which is accessible internally and vulnerable to SQL injection, giving us the credentials, which can be used to login via SSH. Then, exploiting the cron running as root to directly modify the webhook tasks from database to bypass the URL filtering and access the private key of root user.
NMAP
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 32b7f4d42f45d330ee123b0367bbe631 (RSA)
| 256 86e15d8c2939acd7e815e649e235ed0c (ECDSA)
|_ 256 ef6bad64d5e45b3e667949f4ec4c239f (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: HTTP Monitoring Tool
3000/tcp filtered ppp
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Port 80 (HTTP)
The webpage provides a webhook service that monitors the health of given url.
I set up a server that will be monitored and a listener to receive the status.
And configure the webhook.
In response we get that host healthy and on listener we get the content of our server which is directory listing.
If we try to change the monitored url to localhost, we get an error.
We can try to bypass this using a redirect. The following code redirects the received request to a given url.
In our nmap scan, port 3000 was blocked, so we can try to access it via SSRF. In response, we can see that Gogs (Go Git Service) is running on port 3000.
Further, we can see the version.
CVE-2014–8682
Google gives that this version of Gogs is vulnerable to unauthenticated SQL injection (CVE-2014–8682). But the exploit available publicly doesn’t seems to work for us.
So, i downloaded the source code of same version of gogs. According to the advisory, following function is vulnerable to SQL injection.
I installed the same version of gogs locally with the default database which was SQLite3 and created two users for testing.
I started constructing exploit locally. Adding the single quote in vulnerable parameter, we can confirm that it breaks the sql query.
Now adding the bracket and commenting the rest of query, it works properly.
Now the next step is to determine the number of columns being returned. According to the public exploit, it filters the spaces and we can also see that in the function code above. But this can be bypassed using the multi-line comment between keywords instead of space. Using the order by clause, we confirm that there are 27 columns being returned.
Next, step is to determine the column being returned in response and it’s datatype.
Now in the source code of Gogs, we can see the user model. Here, we can see all the columns available in user table. So, we can try to dump it.
Trying to extract the user name, we successfully get user names on our local instance.
Now, trying the crafted payload on target machine, we get at username susanne
.
http://0.0.0.0:3000/api/v1/users/search?q=t')/**/UNION/**/ALL/**/SELECT/**/NULL,NULL,name,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL/**/FROM/**/user--
Next, we extract the password for susanne user. But it is hashed.
http://0.0.0.0:3000/api/v1/users/search?q=t')/**/UNION/**/ALL/**/SELECT/**/NULL,NULL,passwd,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL/**/FROM/**/user--
Looking at the source code of Gogs, we can see how it hashes the password. It uses PBKDF2 algorithm with 10000 iterations.
Looking at the hashcat forums to how to crack this hash, it tells that we also need salt.
Fortunately, in the user model above, we can see it has salt column also, so we can extract it using following sqli payload.
http://0.0.0.0:3000/api/v1/users/search?q=t')/**/UNION/**/ALL/**/SELECT/**/NULL,NULL,salt,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL/**/FROM/**/user--
As the post on hashcat forums also mentions that we need to encode salt and digest into base64. So, we get the final format.
Using hashcat, we crack the hash and get the password for susanne user.
hashcat -m 10900 'sha256:10000:c08zWEliZVcxNA==:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=' /usr/share/wordlists/rockyou.txt
Using the found credentials, we get successful login on SSH.
Privilege Escalation
The .env file in the web directory, contains MYSQL db credentials.
Connecting to mysql, there’s nothing in the tables.
Using pspy, it can be seen that some crons are running as root. One is artisan, which is a cli utility to interact with laravel applications. The other is MySQL which is clearing the tasks table in laravel db.
So, looking at the schedule task in the kernel.php file. It first gets all webhooks created from Task table and passes the URLs to check function.
Looking at the check function, it gets the monitored url using file_get_contents and coverts the output to json and sends it to webhook url.
So, we can try to read /etc/passwd file using webhook. But it gives error so there’s some kind of filtering on backend.
Looking at the function which creates webhook, we can see that it filters the URLs by applying SafeUrlRule. And if the URL is safe and action is test then it directly passes URLs to check function. Otherwise it saves the URLs into database using Task Model.
We can also see the SafeUrlRule.
Now that we know that webhook urls are saved in the database and we have access to database creadentials. We can change the monitored URL to read file directly from database.
First create a webhook. Then, connect to database and we can see the webhook in the tasks table.
Next, update the monitored URL and point it to /etc/passwd file.
UPDATE tasks SET monitoredUrl = 'file:///etc/passwd';
Now, in the output, we can see that it returns the content of /etc/passwd file.
Since, this task is running as root, we can try to read the private ssh key of root user. So, we create another webhook and update the monitored url.
UPDATE tasks SET monitoredUrl = 'file:///root/.ssh/id_rsa';
And in the response we get the private key of root.
Now, we just copy the key and format it correctly and change its permissions. and login as root.
References
- SSRF: https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery
- Gogs SQLi Advisory: https://github.com/advisories/GHSA-g6xv-8q23-w2q3
- Gogs 0.5.5: https://github.com/gogs/gogs/releases/tag/v0.5.5
- CVE-2014–8682: https://www.exploit-db.com/exploits/35238
- PBKDF2: https://cryptobook.nakov.com/mac-and-key-derivation/pbkdf2
- PHP Artisan: https://laravel.com/docs/9.x/artisan#introduction