This project demonstrates that all Apache Maven™ projects are vulnerable to repository injection.
The POC consists of several modules organized in a single Maven build for practical reasons but would be separate in a read-world attack:
app
module demonstrates a vulnerable build
innocent
moduleinnocent
module is the attacker dependency
malicious
module impersonates Apache Lang3
Note that the commands below use -gs ../settings.xml
to use an alternate settings file, in order to make sure that we don't use the local .m2
cache for the demonstration.
The first thing to do is to populate the malicious repository with the malicious dependency:
cd malicious
mvn -gs ../settings.xml deploy
Then publish the attacker library:
cd innocent
mvn -gs ../settings.xml install
Then go to the app
victim directory and execute the application:
cd ../app
mvn -gs ../settings.xml exec:java
The output will be:
You've been pwned! Hello, world!
Instead of the expected:
Hello, World!
Apache Maven blindly uses repositories defined by transitive dependencies.
Because Maven uses the nearest first strategy, the first dependency it sees in this build is innocent
, which defines a malicious repository.
Then it sees the dependency on org.apache.commons:commons-lang3
and tries to download it.
Because the dependency was made available via the innocent
dependency and its vulnerable repository, the real Apache Commons Lang3 dependency is NOT fetched from Maven Central.
This is a design flaw in Apache Maven: transitive repositories should never be used automatically. Repositories should be redeclared on the consumer side. It is however possible to mitigate by defining a mirror for the malicious repository by pointing the mirror to a sane repository.
It's worth noting that if the dependency was previously fetched into the local .m2
repository, then Maven would not try to download it from the malicious repository.