Minimal Container Images
A production image ships a compiler, a package manager, and a shell, turning a small application flaw into a working toolkit. Perform the pivot, then cut the runtime down to a minimal stage with none of it.
What Is Minimal Container Images?
Every tool you ship in a production image is a tool an attacker inherits. A runtime image carrying a package manager, a compiler, a shell, and network utilities turns a limited application flaw into a working toolkit, because the attacker no longer has to bring anything with them. This exercise covers that escalation: watch an attacker pivot using the image's own contents, then cut the runtime down to a minimal stage with no shell and no build tooling.
What You'll Learn in Minimal Container Images
- It lets an attacker with limited code execution fetch and install whatever they need, so a small flaw becomes a full foothold without them bringing any tooling.
- The build tooling stays in the build stage, and only the compiled artifact and its runtime dependencies are copied into the final image.
- Many common exploitation and persistence techniques assume a shell to run commands, so removing it breaks the easiest path from code execution to control.
- One is about what the layers still record from the build, and this one is about what the final image hands an attacker at runtime. Same tool, different failure.
Minimal Container Images — Training Steps
-
A foothold in checkout
Bob does not start with a shell. The checkout service builds PDF shipping labels and shells out to do it, and one field in that request is not sanitised. He injects a probe to confirm he can run a command, and to see who and where he is.
-
Everything the image ships
Bob has one-command-at-a-time execution as the app, but no shell yet. That is usually where an attacker stalls, unless the container hands him a toolkit. Before doing anything loud, he checks what is already installed.
-
Curl the cloud credentials
The container runs in the cloud, and every cloud instance exposes a metadata service on a fixed internal address that hands out the machine's temporary role credentials. Reaching it needs an HTTP client. The image shipped one.
-
How one command became a breach
Bob's access to the app was tightly limited.
-
A shell in a runtime container
Skelwyn runs endpoint detection on its container hosts. It does not know what each container is for; it reports behavior that does not fit a runtime.
-
Confirm what shipped
Alice reproduces what the attacker found: she asks the running container which of those tools it actually contains.
-
A 412MB runtime
The toolkit is not free. It is weight, and it is attack surface. Alice checks the size of the image the service actually runs.
-
Where the toolkit gets in
The image is built from one Dockerfile. Alice opens it to see why a runtime container ends up carrying a compiler.
-
One stage, everything ships
The image is built and run in a single stage, so whatever the build needs, the runtime also carries.
-
Split build from runtime
The fix is a multi-stage build: keep the full image and its toolchain as a build stage that compiles the app, then copy only the finished app and its dependencies into a distroless runtime image that has no shell, no package manager, and no compiler.