Skip to main content

Scanning Web Applications

Introduction#

In this step-by-step tutorial, we will go through all the required stages to set up an in depth configured ZAP Scan against OWASP Juice Shop with the secureCodeBox.

Setup#

For the sake of the tutorial, we assume that you have your Kubernetes cluster already up and running and that we can work in your default namespace. If not, check out the installation for more information.

We will start by installing the ZAP-Advanced scanner:

helm upgrade --install zap-advanced secureCodeBox/zap-advanced

And the juice-shop demo-target.

helm upgrade --install juice-shop secureCodeBox/juice-shop

Creating the ZAP-Advanced scan#

We can first start with a basic zap-advanced scan. We use here our CRD (Custom Resource definition) Scan and a ConfigMap to configure our scan.

apiVersion: v1kind: ConfigMapmetadata:  name: zap-advanced-scan-configdata:  2-zap-advanced-scan.yaml: |-
    # ZAP Contexts Configuration     contexts:      # Name to be used to refer to this context in other jobs, mandatory      - name: scb-juiceshop-context        # The top level url, mandatory, everything under this will be included        url: http://juice-shop.default.svc:3000/      ---apiVersion: "execution.securecodebox.io/v1"kind: Scanmetadata:  name: "zap-authenticated-full-scan-juiceshop-basic"  labels:    organization: "OWASP"spec:  scanType: "zap-advanced-scan"  parameters:    # target URL including the protocol    - "-t"    - "http://juice-shop.default.svc:3000/"  volumeMounts:    - name: zap-advanced-scan-config      mountPath: /home/securecodebox/configs/2-zap-advanced-scan.yaml      subPath: 2-zap-advanced-scan.yaml      readOnly: true  volumes:    - name: zap-advanced-scan-config      configMap:        name: zap-advanced-scan-config

We use volumeMounts and volumes to attach the configMap to our scan in scan.yaml. We also set up a context for our zap-advanced scan. This is where we can input ZAP related parameters.

We can do a test run via:

kubectl apply -f scan.yaml

The ConfigMap right now is minimal. So we can start modifying and adding to it to fit our needs.
For example let's start by setting the scope that we want for our scan. This is done by adding and excluding the paths that the scanner will use. This usually makes the scans faster.
Our ConfigMap will then look like this.

apiVersion: v1kind: ConfigMapmetadata:  name: zap-advanced-scan-configdata:  2-zap-advanced-scan.yaml: |-
    # ZAP Contexts Configuration     contexts:      # Name to be used to refer to this context in other jobs, mandatory      - name: scb-juiceshop-context        # The top level url, mandatory, everything under this will be included        url: http://juice-shop.default.svc:3000/         # An optional list of regexes to include             includePaths:          - "http://juice-shop.default.svc:3000.*"        # An optional list of regexes to exclude        excludePaths:          - ".*socket\\.io.*"          - ".*\\.png"          - ".*\\.jpeg"          - ".*\\.jpg"          - ".*\\.woff"          - ".*\\.woff2"          - ".*\\.ttf"          - ".*\\.ico"

ZAP uses a Spider-Tool that is used to automatically discover new resources (URLs) on a particular Site. We can configure it's mode of operation through the parameter spiders. A possible configuration can look like this :

apiVersion: v1kind: ConfigMapmetadata:  name: zap-advanced-scan-configdata:  2-zap-advanced-scan.yaml: |-
    # ZAP Contexts Configuration     contexts:      # Name to be used to refer to this context in other jobs, mandatory      - name: scb-juiceshop-context        # The top level url, mandatory, everything under this will be included        url: http://juice-shop.default.svc:3000/         # An optional list of regexes to include             includePaths:          - "http://juice-shop.default.svc:3000.*"        # An optional list of regexes to exclude        excludePaths:          - ".*socket\\.io.*"          - ".*\\.png"          - ".*\\.jpeg"          - ".*\\.jpg"          - ".*\\.woff"          - ".*\\.woff2"          - ".*\\.ttf"          - ".*\\.ico"    # ZAP Spiders Configuration     spiders:      - name: scb-juiceshop-spider        # String: Name of the context to spider, default: first context        context: scb-juiceshop-context        # String: Name of the user to authenticate with and used to spider        user: juiceshop-user-1        # String: Url to start spidering from, default: first context URL        url: http://juice-shop.default.svc:3000/        # zapConfiguration.spiders[0].ajax -- Bool: Whether to use the ZAP ajax spider, default: false        ajax: true        # Int: Fail if spider finds less than the specified number of URLs, default: 0        failIfFoundUrlsLessThan: 0        # Int: Warn if spider finds less than the specified number of URLs, default: 0        warnIfFoundUrlsLessThan: 0        # Int: The max time in minutes the spider will be allowed to run for, default: 0 unlimited        maxDuration: 5        # Int: The maximum tree depth to explore, default 5        maxDepth: 10

ZAP also has the option for an Active Scan.
Active scanning attempts to find potential vulnerabilities by using known attacks against the selected targets. Its rules can be modified in the scanners parameter. An example for that would be:

 # ZAP ActiveScans Configuration     scanners:      - name: scb-juiceshop-scan        # String: Name of the context to attack, default: first context        context: scb-juiceshop-context        # String: Name of the user to authenticate with and used to spider        user: juiceshop-user-1        # String: Url to start scaning from, default: first context URL        url: http://juice-shop.default.svc:3000/        # Int: The max time in minutes any individual rule will be allowed to run for, default: 0 unlimited        maxRuleDurationInMins: 1        # Int: The max time in minutes the active scanner will be allowed to run for, default: 0 unlimited        maxScanDurationInMins: 10        # Int: The max number of threads per host, default: 2        threadPerHost: 5        # Int: The delay in milliseconds between each request, use to reduce the strain on the target, default 0        delayInMs: 0        # Bool: If set will add an extra query parameter to requests that do not have one, default: false        addQueryParam: false        # Bool: If set then automatically handle anti CSRF tokens, default: false        handleAntiCSRFTokens: false        # Bool: If set then the relevant rule Id will be injected into the X-ZAP-Scan-ID header of each request, default: false        injectPluginIdInHeader: false        # Bool: If set then the headers of requests that do not include any parameters will be scanned, default: false        scanHeadersAllRequests: false

Some URLs may not be reachable without a privileged user. In this case, it makes sense to provide authentications credentials. This is done through the authentication, users and session parameters in our ConfigMap context. In our case here, we use custom zap scripts to authenticate into juice-shop. The scripts used can be found here.
It can be required to configure your own scripts to fit your scan target: more information on how these scripts integrate into ZAP can be found here.
Our contexts parameter in our scan would then look something like this:

apiVersion: v1kind: ConfigMapmetadata:  name: zap-advanced-scan-configdata:  2-zap-advanced-scan.yaml: |-
    # ZAP Contexts Configuration     contexts:      # Name to be used to refer to this context in other jobs, mandatory      - name: scb-juiceshop-context        # The top level url, mandatory, everything under this will be included        url: http://juice-shop.default.svc:3000/        # An optional list of regexes to include        includePaths:          - "http://juice-shop.default.svc:3000.*"        # An optional list of regexes to exclude        excludePaths:          - ".*socket\\.io.*"          - ".*\\.png"          - ".*\\.jpeg"          - ".*\\.jpg"          - ".*\\.woff"          - ".*\\.woff2"          - ".*\\.ttf"          - ".*\\.ico"        # Auth Credentials for the scanner to access the application        # Can be either basicAuth or a oidc token.        # If both are set, the oidc token takes precedent        authentication:          # Currently supports "basic-auth", "form-based", "json-based", "script-based"          type: "json-based"          # json-based requires no further configuration          # zapConfiguration.contexts[0].authentication.json-based -- Configure `type: json-based` authentication (more: https://www.zaproxy.org/docs/api/#json-based-authentication).          json-based:            loginUrl: "http://juice-shop.default.svc:3000/rest/user/login"            # must be escaped already to prevent yaml parser colidations '{"user":{"id":1,"email":"test@test.com"}}''            loginRequestData: '{"email":"admin@juice-sh.op","password":"admin123"}'          # Indicates if the current Zap User Session is based on a valid authentication (loggedIn) or not (loggedOut)          verification:            # isLoggedInIndicator: "\Q<a href="password.jsp">\E"            isLoggedOutIndicator: '\Q{"user":{}}\E'        users:          - name: juiceshop-user-1            username: admin@juice-sh.op            password: admin123            forced: true        session:          # Currently supports "scriptBasedSessionManagement", "cookieBasedSessionManagement", "httpAuthSessionManagement"          type: "scriptBasedSessionManagement"          # scriptBasedSessionManagement configuration details          scriptBasedSessionManagement:            name: "juiceshop-session-management.js"            # -- Enables the script if true, otherwise false            enabled: true            # Script engine values: 'Graal.js', 'Oracle Nashorn' for Javascript and 'Mozilla Zest' for Zest Scripts            engine: "Oracle Nashorn"            # Must be a full path to the script file inside the ZAP container (corresponding to the configMap FileMount)            filePath: "/home/zap/.ZAP_D/scripts/scripts/session/juiceshop-session-management.js"            # A short description for the script.            description: "This is a JuiceShop specific SessionManagement Script used to handle JWT."

For more information on Authentication/Session parameters check out ZAP's documentation on the matter here

Our complete ZAP Scan file is then the following :

apiVersion: v1kind: ConfigMapmetadata:  name: zap-advanced-scan-configdata:  2-zap-advanced-scan.yaml: |-
    # ZAP Contexts Configuration     contexts:      # Name to be used to refer to this context in other jobs, mandatory      - name: scb-juiceshop-context        # The top level url, mandatory, everything under this will be included        url: http://juice-shop.default.svc:3000/        # An optional list of regexes to include        includePaths:          - "http://juice-shop.default.svc:3000.*"        # An optional list of regexes to exclude        excludePaths:          - ".*socket\\.io.*"          - ".*\\.png"          - ".*\\.jpeg"          - ".*\\.jpg"          - ".*\\.woff"          - ".*\\.woff2"          - ".*\\.ttf"          - ".*\\.ico"        # Auth Credentials for the scanner to access the application        # Can be either basicAuth or a oidc token.        # If both are set, the oidc token takes precedent        authentication:          # Currently supports "basic-auth", "form-based", "json-based", "script-based"          type: "json-based"          # json-based requires no further configuration          # zapConfiguration.contexts[0].authentication.json-based -- Configure `type: json-based` authentication (more: https://www.zaproxy.org/docs/api/#json-based-authentication).          json-based:            loginUrl: "http://juice-shop.default.svc:3000/rest/user/login"            # must be escaped already to prevent yaml parser colidations '{"user":{"id":1,"email":"test@test.com"}}''            loginRequestData: '{"email":"admin@juice-sh.op","password":"admin123"}'          # Indicates if the current Zap User Session is based on a valid authentication (loggedIn) or not (loggedOut)          verification:            # isLoggedInIndicator: "\Q<a href="password.jsp">\E"            isLoggedOutIndicator: '\Q{"user":{}}\E'        users:          - name: juiceshop-user-1            username: admin@juice-sh.op            password: admin123            forced: true        session:          # Currently supports "scriptBasedSessionManagement", "cookieBasedSessionManagement", "httpAuthSessionManagement"          type: "scriptBasedSessionManagement"          # scriptBasedSessionManagement configuration details          scriptBasedSessionManagement:            name: "juiceshop-session-management.js"            # -- Enables the script if true, otherwise false            enabled: true            # Script engine values: 'Graal.js', 'Oracle Nashorn' for Javascript and 'Mozilla Zest' for Zest Scripts            engine: "Oracle Nashorn"            # Must be a full path to the script file inside the ZAP container (corresponding to the configMap FileMount)            filePath: "/home/zap/.ZAP_D/scripts/scripts/session/juiceshop-session-management.js"            # A short description for the script.            description: "This is a JuiceShop specific SessionManagement Script used to handle JWT."
    # ZAP Spiders Configuration     spiders:      - name: scb-juiceshop-spider        # String: Name of the context to spider, default: first context        context: scb-juiceshop-context        # String: Name of the user to authenticate with and used to spider        user: juiceshop-user-1        # String: Url to start spidering from, default: first context URL        url: http://juice-shop.default.svc:3000/        # zapConfiguration.spiders[0].ajax -- Bool: Whether to use the ZAP ajax spider, default: false        ajax: true        # Int: Fail if spider finds less than the specified number of URLs, default: 0        failIfFoundUrlsLessThan: 0        # Int: Warn if spider finds less than the specified number of URLs, default: 0        warnIfFoundUrlsLessThan: 0        # Int: The max time in minutes the spider will be allowed to run for, default: 0 unlimited        maxDuration: 5        # Int: The maximum tree depth to explore, default 5        maxDepth: 10
    # ZAP ActiveScans Configuration     scanners:      - name: scb-juiceshop-scan        # String: Name of the context to attack, default: first context        context: scb-juiceshop-context        # String: Name of the user to authenticate with and used to spider        user: juiceshop-user-1        # String: Url to start scaning from, default: first context URL        url: http://juice-shop.default.svc:3000/        # Int: The max time in minutes any individual rule will be allowed to run for, default: 0 unlimited        maxRuleDurationInMins: 1        # Int: The max time in minutes the active scanner will be allowed to run for, default: 0 unlimited        maxScanDurationInMins: 10        # Int: The max number of threads per host, default: 2        threadPerHost: 5        # Int: The delay in milliseconds between each request, use to reduce the strain on the target, default 0        delayInMs: 0        # Bool: If set will add an extra query parameter to requests that do not have one, default: false        addQueryParam: false        # Bool: If set then automatically handle anti CSRF tokens, default: false        handleAntiCSRFTokens: false        # Bool: If set then the relevant rule Id will be injected into the X-ZAP-Scan-ID header of each request, default: false        injectPluginIdInHeader: false        # Bool: If set then the headers of requests that do not include any parameters will be scanned, default: false        scanHeadersAllRequests: false
---apiVersion: "execution.securecodebox.io/v1"kind: Scanmetadata:  name: "zap-authenticated-full-scan-juiceshop"  labels:    organization: "OWASP"spec:  scanType: "zap-advanced-scan"  parameters:    # target URL including the protocol    - "-t"    - "http://juice-shop.default.svc:3000/"  volumeMounts:    - name: zap-advanced-scan-config      mountPath: /home/securecodebox/configs/2-zap-advanced-scan.yaml      subPath: 2-zap-advanced-scan.yaml      readOnly: true  volumes:    - name: zap-advanced-scan-config      configMap:        name: zap-advanced-scan-config

Let's delete our first test scan and run a new one via :

# Delete the scan created from scan.yaml:kubectl delete -f scan.yaml# Run the scan from scan.yaml:kubectl apply -f scan.yaml

We can check on our scan via:

$ kubectl get scansNAME                                      TYPE                STATE   FINDINGSzap-authenticated-full-scan-juiceshop     zap-advanced-scan   Done    14

If the scan's STATE is set to done, We can see our findings via the S3 bucket. If you've used the default installation method you can follow the guide to access the integrated Minio S3 Bucket to view the findings. And we're done ! Have Fun Scanning :)