DAST – OWASP ZAP docker

Dynamic Application Security Testing (DAST) attempts to identify security vulnerabilities in applications that are running in a near production-like environment. There are many types of DAST scans one of such is the opensource OWASP ZAP scan.

This article is a shallow overview of how DAST can be run hooked up to Jenkins and configured to run an OWASP ZAP scan from a Docker container. We will start by creating a Git repository for storing scripts used to run the scans:

Git repository storing the scan scripts used to run OWASP ZAP docker

Jenkins

Assuming you have already set up a working Jenkins pipeline environment, you can hookup Jenkins to the Jenkinsfile shown above by creating a new ‘Multi-branch Pipeline’ and associate the pipeline to the Jenkinsfile.

In summary, the Jenkins file coordinates the running of scan scripts and does the following steps in sequence:

  • Retrieves script files stored in a Git repository.
  • Runs ‘validate_input.sh’
  • Runs ‘runZapScan.sh’
  • Runs ‘runZapScan.sh’
  • Publishes scan results to Jenkins job

Jenkinsfile.jenkinsfile

def checkoutGitSCM(branch,gitUrl) {
	checkout([$class: 'GitSCM',
		branches: [[name: branch ]],
		doGenerateSubmoduleConfigurations: false,
		extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: '.']],
		submoduleCfg: [],
		userRemoteConfigs: [[url: gitUrl]]
	])
}
pipeline {
	agent {
		node { label 'standard_ubuntu18' }
	}
    options {
		timestamps()
		disableConcurrentBuilds()
		buildDiscarder(logRotator(numToKeepStr: '10'))
		timeout(time: 180, unit: 'MINUTES')
		ansiColor('xterm')
	}
	parameters {
		string(name: 'ZAP_TARGET_URL', defaultValue:'https:planningtasks.com', description:'')
		choice(name: 'ZAP_ALERT_LVL', choices: ['High', 'Medium', 'Low'], description: 'See Zap documentation, default High')
	} 
	stages{
		stage('Initialize'){
			steps{
				script {
					currentBuild.displayName = "ZAP scan='${env.BUILD_NUMBER}''"
					currentWorkspace=pwd()
					cleanWs()					
				}
			}
		}
		stage('ZAP'){
			when { branch 'master' }
			steps{
				sh("echo ${env.WORKSPACE}; ls -l;")
				checkoutGitSCM("master","https://your_git_repo")
				sh("bash -c \"chmod +x ${env.WORKSPACE}/*.sh\"")
				sh("${env.WORKSPACE}/validate_input.sh")
				sh("${env.WORKSPACE}/runZapScan.sh ${params.ZAP_TARGET_URL} ${env.WORKSPACE} ${params.ZAP_ALERT_LVL}")
			}
		}
		stage('Publish'){
			when { branch 'master' }
			steps{
				publishHTML([allowMissing: false,
				alwaysLinkToLastBuild: false,
				keepAll: false,
				reportDir: './reports',
				reportFiles: 'report.html',
				reportName: 'ZAP scan report',
				reportTitles: ''])
			}
		}
	}
	 post {
        always {
            sh("${env.WORKSPACE}/runCleanup.sh")
        }	
	}
}

runZapScan.sh

#!/bin/bash -eux
ZAP_TARGET_URL=$1
ZAP_REPORTS_PATH=$2
ZAP_ALERT_LVL=$3

echo "ZAP_TARGET_URL='$ZAP_REPORTS_PATH'"
echo "ZAP_REPORTS_PATH='$ZAP_REPORTS_PATH'"
echo "ZAP_REPORTS_PATH='$ZAP_ALERT_LVL'"

reportsDirectory="$ZAP_REPORTS_PATH/reports"
mkdir $reportsDirectory

ZAP_CONT_ID=$(docker run --name zap -p 2375:2375 -d owasp/zap2docker-stable zap.sh -daemon \
-port 2375 \
-host 127.0.0.1 \
-config api.disablekey=true \
-config scanner.attackOnStart=true \
-config view.mode=attack \
-config connection.dnsTtlSuccessfulQueries=-1 \
-config api.addrs.addr.name=.* \
-config api.addrs.addr.regex=true)

docker exec $ZAP_CONT_ID zap-cli -v -p 2375 status -t 120
docker exec $ZAP_CONT_ID zap-cli -v -p 2375 open-url $ZAP_TARGET_URL
docker exec $ZAP_CONT_ID zap-cli -v -p 2375 active-scan $ZAP_TARGET_URL
docker exec $ZAP_CONT_ID zap-cli -v -p 2375 spider $ZAP_TARGET_URL
docker exec $ZAP_CONT_ID zap-cli -v -p 2375 active-scan --scanners xss,sqli --recursive $ZAP_TARGET_URL

docker exec $ZAP_CONT_ID zap-cli -p 2375 report -o /home/zap/report.html -f html
docker exec $ZAP_CONT_ID zap-cli -p 2375 report -o /home/zap/report.xml -f xml

docker cp $ZAP_CONT_ID:/home/zap/report.xml $reportsDirectory
docker cp $ZAP_CONT_ID:/home/zap/report.html $reportsDirectory

# Check alerts
ALERT_CNT=$(docker exec $ZAP_CONT_ID zap-cli -p 2375 --verbose alerts --alert-level $ZAP_ALERT_LVL -f json | jq length)

if [[ "${ALERT_CNT}" -gt 0 ]]; then
  echo "Vulnerabilities dectected, Lvl='$ZAP_ALERT_LVL' Alert count='${ALERT_CNT}'"
  exit 1
fi

validate_input.sh

#!/bin/bash
checkInput () {
      if [ -z "$1" ]
      then
            echo "input missing:$2"
            exit 1
      else
            echo "[validate_input] $2=$1"
      fi
  
}
checkInput "${ZAP_TARGET_URL}" "ZAP_TARGET_URL"
checkInput "${ZAP_ALERT_LVL}" "ZAP_ALERT_LVL"

runCleanup.sh

#!/bin/bash -eux
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

Leave a Reply

Your email address will not be published. Required fields are marked *