Post-processing with Hooks
Introduction
In this step-by-step tutorial, we will go through all the required stages to set up Hooks with the secureCodeBox. Hooks can be used to perform post-processing on findings, for which we'll give a few examples.
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'll start by installing the nmap scanner:
$ helm upgrade --install nmap secureCodeBox/nmap
Next, we'll install two update-field
hooks:
$ helm upgrade --install ufh1 secureCodeBox/update-field-hook --set attribute.name="category" --set attribute.value="first-hook"
$ helm upgrade --install ufh2 secureCodeBox/update-field-hook --set attribute.name="category" --set attribute.value="second-hook"
The first hook will update all secureCodeBox findings such that the field category
is set to the value first-hook
.
The second hook will set the same field to second-hook
.
For a list of all available secureCodeBox hooks, see hooks.
There's no limit to the amount of hooks you can install.
Creating a scan
In practice, you are not required to specify anything to run your hooks.
apiVersion: "execution.securecodebox.io/v1"
kind: Scan
metadata:
name: "nmap-example"
spec:
scanType: "nmap"
parameters:
# We'll just scan for port 80 to speed up the scan.
- "-p"
- "80"
# Scanning an example domain
- "scanme.nmap.org"
For starting scans in Kubernetes, see First Scans.
Once the scan has finished, you will see that two hooks have run on your scan results.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
parse-nmap-example-5p964--1-ctgrv 0/1 Completed 0 18s
scan-nmap-example-gg9kd--1-pjltd 0/2 Completed 0 26s
ufh1-update-field-hook-nmap-example-p8pcb--1-vx25q 0/1 Completed 0 10s
ufh2-update-field-hook-nmap-example-drjmq--1-vzds2 0/1 Completed 0 3s
Inspecting the findings
Looking at the findings, you will notice that the category
field has been set to second-hook
.
This happens because the ufh2
hook was executed after ufh1
, discarding the value first-hook
completely.
[
{
"name": "Open Port: 80 (http)",
"description": "Port 80 is open using tcp protocol.",
"category": "second-hook",
"location": "tcp://45.33.32.156:80",
"osi_layer": "NETWORK",
"severity": "INFORMATIONAL",
"attributes": {
...
},
"id": "9fbda429-478d-4ce0-9a8d-1c4aef4d9b58"
}
]
Hook order
By default, hook order is specified according this definition.
With the hook.priority
field, you can further customize the order of secureCodeBox hooks.
The higher the priority of a hook, the earlier it will execute.
By default, all hooks have a priority of 0
.
If we set ufh2
hook's priority to 1
, we'll observe that it will execute before ufh1
.
$ helm upgrade --install ufh2 secureCodeBox/update-field-hook --set hook.priority="1" --set attribute.name="category" --set attribute.value="second-hook"
$ kubectl get scancompletionhooks.execution.securecodebox.io
NAME TYPE PRIORITY IMAGE
ufh1-update-field-hook ReadAndWrite 0 docker.io/securecodebox/hook-update-field:3.3.1
ufh2-update-field-hook ReadAndWrite 1 docker.io/securecodebox/hook-update-field:3.3.1
Start, the scan and observe orders:
$ kubectl get pods kube minikube
NAME READY STATUS RESTARTS AGE
parse-nmap-example-lrtcl--1-57n9t 0/1 Completed 0 36s
scan-nmap-example-7s2t8--1-gbr6b 0/2 Completed 0 39s
ufh1-update-field-hook-nmap-example-cvzw2--1-x4rcz 0/1 Completed 0 30s
ufh2-update-field-hook-nmap-example-mv57q--1-cvd4k 0/1 Completed 0 33s
Kubernetes sorts the list alphabetically, but notice the age of the jobs.
Looking at the resulting finding, we can see the category is set to first-hook
.
[
{
"name": "Open Port: 80 (http)",
"description": "Port 80 is open using tcp protocol.",
"category": "first-hook",
"location": "tcp://45.33.32.156:80",
"osi_layer": "NETWORK",
"severity": "INFORMATIONAL",
"attributes": {
...
},
"id": "c15d1730-7ca8-4529-b55a-a3412832f309"
}
]
Hook selector
An alternative for more runtime hook control is the scan's HookSelector. This field allows you to define which hooks to run for a scan.
In this case, we select all hooks, except hooks with the label ufh1
.
apiVersion: "execution.securecodebox.io/v1"
kind: Scan
metadata:
name: "nmap-example"
spec:
hookSelector:
matchExpressions:
- key: app.kubernetes.io/instance
operator: NotIn
values: [ "ufh1" ]
scanType: "nmap"
parameters:
# We'll just scan for port 80 to speed up the scan.
- "-p"
- "80"
# Scanning an example domain
- "scanme.nmap.org"
You can find that only ufh2
was executed.
$ kubectl get pods kube minikube
NAME READY STATUS RESTARTS AGE
parse-nmap-example-shkrr--1-2hdt9 0/1 Completed 0 10s
scan-nmap-example-7bllp--1-zx287 0/2 Completed 0 13s
ufh2-update-field-hook-nmap-example-lmljv--1-smgp5 0/1 Completed 0 7s
The following labels are available by default:
app.kubernetes.io/instance
: the Helm release name (e.g.ufh1
,ufh2
)app.kubernetes.io/name
: the Helm chart name (e.g.update-field-hook
)securecodebox.io/internal
: boolean field for whether this hook has internal usages in secureCodeBox (e.g. Cascading Scans hook)
You can also deploy secureCodeBox hooks with your own labels like so:
$ helm upgrade --install ufh2 secureCodeBox/update-field-hook --set hook.labels.securecodebox="rocks" --set attribute.name="category" --set attribute.value="second-hook"
This will add your custom label to the secureCodeBox hook so that you can select is with hookSelector
.
apiVersion: execution.securecodebox.io/v1
kind: ScanCompletionHook
metadata:
labels:
app.kubernetes.io/instance: ufh2
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: update-field-hook
helm.sh/chart: update-field-hook-3.3.1
securecodebox: rocks
name: ufh2-update-field-hook
namespace: default
spec:
env:
- name: ATTRIBUTE_NAME
value: category
- name: ATTRIBUTE_VALUE
value: second-hook
image: docker.io/securecodebox/hook-update-field:3.3.1
ttlSecondsAfterFinished: null
type: ReadAndWrite
Cascading scans
The HookSelector
field is also available in Cascading Rules.
This means that you can selectively disable hooks for certain rules.
Let's say that you're running secureCodeBox with nmap, ncrack, and a DefectDojo persistence provider.
We can imagine that you'd prefer your ncrack passwords to not go directly to DefectDojo, so you could set up the cascading rule such that it filters the DefectDojo hook.
apiVersion: "cascading.securecodebox.io/v1"
kind: CascadingRule
metadata:
name: "ncrack-ftp"
labels:
securecodebox.io/invasive: invasive
securecodebox.io/intensive: high
securecodebox.io/type: bruteforce
spec:
matches:
anyOf:
- category: "Open Port"
attributes:
service: "ftp"
state: open
scanSpec:
hookSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: NotIn
values: [ "persistence-defectdojo" ]
scanType: "ncrack"
parameters:
- -v
- -d10
- -U
- /ncrack/users.txt
- -P
- /ncrack/passwords.txt
- -p
- ftp:{{attributes.port}}
- "{{$.hostOrIP}}"
Note that we use app.kubernetes.io/name
here to filter all releases of the DefectDojo persistence provider.
You can use scan.spec.cascading.inheritHookSelector
on your initial scan definition to pass hookSelector
entries on to cascaded scans.
Selectors defined in cascading rules will only apply to the scan triggered by the rule - if the results of that scan then trigger further cascading scans, the selectors defined in the cascading rule will be dropped and only those from the original scan are kept.
Defining identical entries in both the Scan AND the Cascading Rule resource will lead to undefined behaviour.
See #789 for more details.