Docker Image Reverse Engineering

Published

Why?

The other day I was helping some colleagues to develop a container application for Cisco APIC. To build a stateful application you must follow some specific steps and build it according to the APIC specification. If it sounds complex, you are right, is a bit complex.

When reviewing the components needed, I saw, it is need to use a special docker image, which has all the required packages. The downside, the packages were old, for example python2 was used.

As a good engineer I started to look for the dockerfile to have an updated image, but no dockerfile was avalaible, who knows why?. Then I tried to run the image, see what it is inside, but the image was crashing, good.

docker history

I remembered that you could see how a docker layer was built using docker history. So I tried and this was the result:

History of a docker image
1
╰─ docker history aci_appcenter_docker_image
2
IMAGE CREATED CREATED BY SIZE COMMENT
3
62a3eb9c3646 5 years ago /bin/sh -c apk --no-cache add bash 606MB
4
54ed6729adfb 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756… 81.1MB
5
d77e3599de56 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B
6
ec521dc7384d 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.… 0B
7
5339b29ce749 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161… 4.8MB

You can see on line 3, the important part is missing, where I can see how the image is build.

is there a better way?, yep, docker provides the option to use the --no-trunc command which can give us more infomation.

docker --no-trunc option
1
╰─ docker history aci_appcenter_docker_image --no-trunc
2
IMAGE CREATED CREATED BY SIZE COMMENT
3
sha256:62a3eb9c364683751a2fc84227286a7d121a87efca5f043e299a6de62b2846a1 5 years ago /bin/sh -c apk --no-cache add bash python python-dev py-pip py-flask py-requests=2.9.1-r0 ca-certificates openssl-dev openssl gcc libffi-dev build-base && update-ca-certificates && pip install --use-wheel pyopenssl && python /usr/local/cobrafiles/ez_setup.py && easy_install -Z /usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg && easy_install -Z /usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg 606MB
4
sha256:54ed6729adfbb5e0816cdd0d16153032a093d8a203390804e9544989f53e0c72 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles 81.1MB
5
sha256:d77e3599de5642e16d39a0c9455c7e850ee80ed700cd76cce6ba5deafb0dd616 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B
6
sha256:ec521dc7384d86f97585c6b4b65a76aef37671e456ec303505bdb3c108b1dee4 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.com 0B
7
sha256:5339b29ce7493a9dc881571d439e6a2cdc4ba5af3e1874c232296d4e6610b9c8 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161917082cc7242c660c61bb3f3cc1549c7e2dfff2b0de7104 in / 4.8MB

We can see now the commands used but still not very user friendly.

dfimage

So I looked in the internet and found this handy image which can give us the information we want. Check it out here

To use it you can create an alias

Using dfimage
1
alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"

And run dfimage specifying the name and tag of the image you want to reverse engineer.

See complete image history
1
╰─ dfimage -sV=1.36 aci_appcenter_docker_image:latest
2
3
Unable to find image 'alpine/dfimage:latest' locally
4
latest: Pulling from alpine/dfimage
5
df20fa9351a1: Pull complete
6
820dbffe2156: Pull complete
7
Digest: sha256:4a271e763d51b7f3cca72eac9bf508502c032665dde0e4c8d5fcf6376600f64a
8
Status: Downloaded newer image for alpine/dfimage:latest
9
Analyzing aci_appcenter_docker_image:latest
10
Docker Version: 1.8.2
11
GraphDriver: overlay2
12
Environment Variables
13
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
14
15
Image user
16
|User is root
17
18
Potential secrets:
19
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
20
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
21
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
22
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
23
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
24
|Found match etc/udhcpd.conf DHCP server configs dhcpd[^ ]*.conf 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
25
Dockerfile:
26
MAINTAINER gursheno@cisco.com
27
RUN mkdir /usr/local/cobrafiles
28
COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles
29
    usr/
30
    usr/local/
31
    usr/local/cobrafiles/
32
    usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg
33
    usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg
34
    usr/local/cobrafiles/ez_setup.py
35
36
RUN apk --no-cache add bash python python-dev py-pip py-flask py-requests=2.9.1-r0 ca-certificates openssl-dev openssl gcc libffi-dev build-base  \
37
    && update-ca-certificates  \
38
    && pip install --use-wheel pyopenssl  \
39
    && python /usr/local/cobrafiles/ez_setup.py  \
40
    && easy_install -Z /usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg  \
41
    && easy_install -Z /usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg

From the output (lines 28 to the end) you can see we get what we wanted, the packages installed to build this application and also the SDKs installed.

Also on the messages of the potential secrets (line 19) we can see an alpine image was used as a base image.

From this point I was able to create my dockerfile and build my updated image for the APIC container app I was helping.