Docker Image Reverse Engineering
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:
1╰─ docker history aci_appcenter_docker_image2IMAGE CREATED CREATED BY SIZE COMMENT362a3eb9c3646 5 years ago /bin/sh -c apk --no-cache add bash … 606MB454ed6729adfb 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756… 81.1MB5d77e3599de56 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B6ec521dc7384d 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.… 0B75339b29ce749 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161… 4.8MBYou 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.
1╰─ docker history aci_appcenter_docker_image --no-trunc2IMAGE CREATED CREATED BY SIZE COMMENT3sha256: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 606MB4sha256:54ed6729adfbb5e0816cdd0d16153032a093d8a203390804e9544989f53e0c72 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles 81.1MB5sha256:d77e3599de5642e16d39a0c9455c7e850ee80ed700cd76cce6ba5deafb0dd616 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B6sha256:ec521dc7384d86f97585c6b4b65a76aef37671e456ec303505bdb3c108b1dee4 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.com 0B7sha256:5339b29ce7493a9dc881571d439e6a2cdc4ba5af3e1874c232296d4e6610b9c8 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161917082cc7242c660c61bb3f3cc1549c7e2dfff2b0de7104 in / 4.8MBWe 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
1alias 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.
1╰─ dfimage -sV=1.36 aci_appcenter_docker_image:latest2
3Unable to find image 'alpine/dfimage:latest' locally4latest: Pulling from alpine/dfimage5df20fa9351a1: Pull complete6820dbffe2156: Pull complete7Digest: sha256:4a271e763d51b7f3cca72eac9bf508502c032665dde0e4c8d5fcf6376600f64a8Status: Downloaded newer image for alpine/dfimage:latest9Analyzing aci_appcenter_docker_image:latest10Docker Version: 1.8.211GraphDriver: overlay212Environment Variables13|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin14
15Image user16|User is root17
18Potential secrets:19|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar20|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar21|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar22|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar23|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar24|Found match etc/udhcpd.conf DHCP server configs dhcpd[^ ]*.conf 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar25Dockerfile:26MAINTAINER gursheno@cisco.com27RUN mkdir /usr/local/cobrafiles28COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles29 usr/30 usr/local/31 usr/local/cobrafiles/32 usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg33 usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg34 usr/local/cobrafiles/ez_setup.py35
36RUN 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.eggFrom 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.