sorenpoulsen.com header

Using OWASP Dependency Check with Maven

Using software components with known security vulnerabilities was ranked at no. 9 on the OWASP Top 10 Security Risks for 2017 and in the recent 2021 update it was promoted to no. 6 with the new title "Vulnerable and outdated components".

Vulnerabilities in this category can sometimes be exploited using generic attacks that do not depend on how users chose to integrate the vulnerable library in their internet facing solutions. This has led to some famous mass exploitations in the past.

In one such events in 2017 the company Equifax fell victim to a massive data breach affecting around 145 mio. customers. Equifax failed to apply a security patch for the Apache Struts2 web framework for several months despite their own policy to patch within 48 hours.

Another infamous vulnerability is Log4Shell from 2021 that had organizations across the world scrambling to patch Log4J because of remote code execution attacks that could be launched through HTTP headers.

There are a lot of commercial tools available to scan software projects for vulnerable libraries in a process known as Software Composition Analysis, but the OWASP foundation also has a free tool OWASP DependencyCheck that's fairly effective and easy to integrate.

In this post we will set up OWASP DependencyCheck in a Maven project and test it against the Apache Struts2 vulnerability CVE-2017-5638 that was the cause of Equifax's predicament.

The OWASP DependecyCheck Maven Plugin

Add dependency-check-maven plugin to the build section of the project's pom.xml file.

<?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>org.test</groupId>
    <artifactId>DependencyCheckTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>6.5.1</version>
                <configuration>
                    <ossindexAnalyzerEnabled>false</ossindexAnalyzerEnabled>
                    <retireJsAnalyzerEnabled>false</retireJsAnalyzerEnabled>
                    <nuspecAnalyzerEnabled>false</nuspecAnalyzerEnabled>
                    <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

By default the plugin's "check" goal is bound to Maven's verify phase:

$ mvn verify

The first time you run the plugin it downloads several years worth of Common Vulnerabilities and Exposures (CVE) records from the National Vulnerability Database (NVD) and saves them in Maven's repository under /.m2/repository/org/owasp/dependency-check-data/. On subsequent runs it only downloads updates.

Following the download it proceeds to analyze the dependencies in the project. If an analyzer finds vulnerable libraries then they are listed in the console output and saved in an HTML report in the project's /target folder.

In this initial run we had no dependencies in the project and nothing was found, but let's spice things up by adding a dangerous dependency to the project!

Enter Apache Struts version 2.5.10

Let's add Apache Struts2 version 2.5.10 as a dependency in our project.

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.5.10</version>
</dependency>

And run dependency check again:

$ mvn verify
...
[WARNING] 

One or more dependencies were identified with known vulnerabilities in DependencyCheckTest:

commons-fileupload-1.3.2.jar (pkg:maven/commons-fileupload/commons-fileupload@1.3.2, cpe:2.3:a:apache:commons_fileupload:1.3.2:*:*:*:*:*:*:*) : CVE-2016-1000031
commons-io-2.4.jar (pkg:maven/commons-io/commons-io@2.4, cpe:2.3:a:apache:commons_io:2.4:*:*:*:*:*:*:*) : CVE-2021-29425
log4j-api-2.7.jar (pkg:maven/org.apache.logging.log4j/log4j-api@2.7, cpe:2.3:a:apache:log4j:2.7:*:*:*:*:*:*:*) : CVE-2017-5645, CVE-2020-9488, CVE-2021-44832, CVE-2021-45046, CVE-2021-45105
struts2-core-2.5.10.jar (pkg:maven/org.apache.struts/struts2-core@2.5.10, cpe:2.3:a:apache:struts:2.5.10:*:*:*:*:*:*:*) : CVE-2017-12611, CVE-2017-15707, CVE-2017-5638, CVE-2017-7525, CVE-2017-7672, CVE-2017-9787, CVE-2017-9793, CVE-2017-9804, CVE-2017-9805, CVE-2018-11776, CVE-2018-1327, CVE-2019-0230, CVE-2019-0233, CVE-2020-17530

This time the dependency checker detects multiple vulnerabilities, including the infamous CVE-2017-5638 that lets an attacker run arbitrary commands on the server through Struts2.

In March 2017 developers were scrambling to patch Struts2. Little did they know that the transitive dependency Log4J version 2 would several years later be revealed to contain an equally serious remote code execution vulnerability of its own dubbed Log4Shell (CVE-2021-44228).

The OWASP DependencyCheck report

We could google each CVE listed in the console, but the dependency checker has already prepared a report with all the details, saved in target/dependency-check-report.html.

screenshot of OWASP DependencyCheck report

If we scroll to a vulnerability such as CVE-2017-5638 we get all the details that are also available from the NVD website.

screenshot of OWASP DependencyCheck report

The suppress buttons in the report are used to deal with false positives.

False positives

The dependency checker detects the use of known vulnerabilities by looking for matches of the dependencies in the Maven project with the software components listed in the CVE records.

This process is a little sketchy because there are different schemes for identifying software. Maven identifies software components using GroupId, ArtifactId and Version (GAV) and CVEs identify them using Common Platform Enumeration (CPE). The JarAnalyzer uses this information but also information from manifests and namespaces in the Jar files to determine if it's a match.

Scrolling back to the list of vulnerable dependencies we find a column named CPE Confidence. This is a score of how confident the dependency checker is that it made a correct match.

In the case of the Struts2-core-2.5.10.jar dependency the CPE Confidence is "Highest". To any human being reading the description it is obvious that this is indeed a match but we will sometimes have false positives. So regardless of the CPE Confidence it is always prudent to read through the CVE description and make our own conclusions.

To prevent false positives from reappearing we can suppress either the software component identified by the CPE or the individual vulnerability by clicking the Suppress button in the report and saving the XML snippet to a file in the project. The first time select the output from "Complete XML Doc". Say we stored the XML in the root of the Maven project in a file named suppressed.xml we would then configure the dependency checker to use this file using the suppressionFiles option in the pom.xml.

<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>6.5.1</version>
    <configuration>         
<ossindexAnalyzerEnabled>false</ossindexAnalyzerEnabled> <retireJsAnalyzerEnabled>false</retireJsAnalyzerEnabled> <nuspecAnalyzerEnabled>false</nuspecAnalyzerEnabled> <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled> <suppressionFiles>${basedir}/suppressed.xml</suppressionFiles> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin>

Automation

To make sure vulnerabilities don't go unnoticed we can set up a Jenkins job to run the OWASP Dependency Check plugin either as a gate that breaks the main build when vulnerabilities are found or as a scheduled side build that reports vulnerabilities for instance through email.

The following pom.xml fails the job on any vulnerability by setting the configuration property failBuildOnAnyVulnerability to true. Alternatively we could use the failBuildOnCVSS property to fail on a CVSS score above a certain threshold.

<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>6.5.1</version>
    <configuration>         
<ossindexAnalyzerEnabled>false</ossindexAnalyzerEnabled> <retireJsAnalyzerEnabled>false</retireJsAnalyzerEnabled> <nuspecAnalyzerEnabled>false</nuspecAnalyzerEnabled> <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled> <suppressionFiles>${basedir}/suppressed.xml</suppressionFiles> <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability> <!--<failBuildOnCVSS>5</failBuildOnCVSS>--> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin>

{{model.usr.name}}
{{cmt.user.name}}
{{cmt.user.name}}
{{childcmt.user.name}}
{{childcmt.user.name}}