Dominik Miklaszewski
by Dominik Miklaszewski

Categories

Tags

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.