Lately, I have been exploring great MITRE ATT&CK framework. Part of their portfolio is ATTACKIQ Academy where one can acquire a great doze of useful knowledge on how to handle various cyber threats in regards to process, organization, taxonomy and daily operations.
Among these great tools (like this absolutely fantastic threat calculator supported by sound and logic methodology) there’s ATT&CK Workbench written and maintained by CTID. An open source tool to operationalize sharing a knowledge base grown amongst blue and red teams in their efforts to keep threats at bay. A very fine tool. However, as being lately into DevOps for sometine, I’ve managed to grow this kind of devops anxiety (or “compulsive neurosis”) which makes me always keep seeking for those aspects of application development associated with proper components management in respect to security and updates through an SDLC of the app.
So, while enjoying the chapter of “Extending ATT&CK with ATT&CK Workbench” I cloned the repositories and fired off the docker-compose up
on my Docker Desktop, just to explore the tool and get this chapter accomplished. First thing which popped up right away was the size of the docker containers of this app (frontend, api and collection manager). Almost 1GB each. Wow! I took some time and searched through countless micro-tutorials on how to dockerize an angular app. None that I found had followed docker best practises. Just install the angular CLI, copy over the whole app, run npm install
and get it into the NGINX base, et voila!. The only prevailing pattern was this NGINX. Apparently the Workbench creators followed those tutorials. So, let’s quickly examine this tool and fix its components Dockerfiles.
attack-workbench-frontend_collection-manager 959MB
attack-workbench-frontend-rest-api 1.06GB
attack-workbench-frontend_frontend 142MB
Well, next step was to see what trivy
was about to say:
attack-workbench-frontend_frontend (debian 10.9)
================================================
Total: 336 (UNKNOWN: 3, LOW: 146, MEDIUM: 62, HIGH: 81, CRITICAL: 44)
attack-workbench-frontend_collection-manager (debian 10.13)
===========================================================
Total: 2028 (UNKNOWN: 0, LOW: 1231, MEDIUM: 463, HIGH: 301, CRITICAL: 33)
attack-workbench-frontend_rest-api:latest (debian 10.13)
========================================================
Total: 2028 (UNKNOWN: 0, LOW: 1231, MEDIUM: 463, HIGH: 301, CRITICAL: 33)
Looks like a friggin lot of the crits. Snyk also confirmed that the culprit was the outdated Debian based NGINX image.
[..]
Docker image: attack-workbench-frontend_frontend:latest
Platform: linux/amd64
Base image: nginx:1.19
Licenses: enabled
Tested 136 dependencies for known issues, found 225 issues.
Base Image Vulnerabilities Severity
nginx:1.19 225 30 critical, 34 high, 26 medium, 135 low
Recommendations for base image upgrade:
Minor upgrades
Base Image Vulnerabilities Severity
nginx:1.22 84 0 critical, 0 high, 0 medium, 84 low
Alternative image types
Base Image Vulnerabilities Severity
nginx:1.23.1-perl 84 0 critical, 0 high, 0 medium, 84 low
The frontend
Further poking around showed up, that the docker wrapping was never updated since the first launch. Hence, my first move was to get this suite wrapped up with my latest alpine-node16 and refresh the NGINX parts. It’s a known docker technique to reduce size of the image - multi-stage build, where the last final image gets only compiled and static web contents of the app, accessible through nginx
web server. However, to further reduce the size I used the base alpine 3.16.2 with nginx added from the distribution.
Modified attack-workbench-frontend/Dockerfile
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
FROM powernuke.nukelab.home:5443/base-alpine-node16:16-3 as build
WORKDIR /home/node/app
COPY ./app/package*.json ./
RUN npm install -g @angular/cli && \
npm install
COPY ./app .
COPY ./docs ../docs
RUN npm run build-prod
FROM powernuke.nukelab.home:5443/base-alpine:3.16-3
RUN apk --update add nginx && \
rm -rf /var/cache/apk/*
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
COPY --from=build /home/node/app/dist/app .
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
Result:
attack-workbench-frontend_frontend:latest 33.4MB
Trivy:
attack-workbench-frontend_frontend:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
The rest (api and collection manager)
Let’s move onward and get the other two cleaned up. Replace old and grumpy node:14
with my freshly built base-alpine-node16:16-3
, address a very bad practise of running an app as local root
- with the USER node
line and see:
Result:
attack-workbench-frontend_rest-api:latest 353MB
Trivy - shows no issues on the OS level, however it looks like the old code has some flawed libs (npm packages):
attack-workbench-frontend_rest-api:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
Node.js (node-pkg)
==================
Total: 7 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 2, CRITICAL: 3)
Well, let’s just run npm update
right before the npm install
that’s hopefully gonna fix buggy npm modules. Ok, how’s now?
attack-workbench-frontend_rest-api:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
Node.js (node-pkg)
==================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
attack-workbench-frontend_collection-manager:latest (alpine 3.16.2)
===================================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
Node.js (node-pkg)
==================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
The ultimate /attack-workbench-rest-api/Dockerfile
(and for the collection manager as well) now is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM powernuke.nukelab.home:5443/base-alpine-node16:16-3
WORKDIR /home/node/app
COPY package*.json ./
RUN npm update && \
npm ci --only=production && \
npm cache clean --force
COPY . .
RUN chown -R node:node /home/node
USER node
CMD [ "npm", "start" ]
Oh, by the way the .dockerignore
should be set in the way so that you won’t copy all the Dockerfiles and other unnecessary stuff into the image with the COPY . .
line.
Results
The size:
attack-workbench-frontend_collection-manager 142MB
attack-workbench-frontend_rest-api 338MB
attack-workbench-frontend_frontend 33.4MB
..down from original:
attack-workbench-frontend_collection-manager 959MB
attack-workbench-frontend-rest-api 1.06GB
attack-workbench-frontend_frontend 142MB
..and from thousands vulnerabilities (with over 100 critical) down to none. Security of the code, secure MongoDB, authentication is another story. Nevertheless, properly maintaining the Dockerfile alone can significantly reduce headaches and ..the attack surface. :)
ps. If anyone wants to use these fixed Dockerfiles, you need to replace my alpine built node16 with regular alpine base node 16 and nginx 1.23 also alpine based.