Skip to content

Commit

Permalink
Implement initial EKS support and first attack technique (closes #374,
Browse files Browse the repository at this point in the history
  • Loading branch information
christophetd committed Jul 5, 2024
1 parent cea6917 commit b2b78ff
Show file tree
Hide file tree
Showing 13 changed files with 410 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Create Admin EKS Access Entry
---

# Create Admin EKS Access Entry




Platform: EKS

## MITRE ATT&CK Tactics


- Lateral Movement

## Description


Uses the EKS Cluster Access Management to assign cluster administrator privileges to an IAM role. This allows the role to perform any action inside the Kubernetes cluster.

<span style="font-variant: small-caps;">Warm-up</span>:

- Create an IAM role

<span style="font-variant: small-caps;">Detonation</span>:

- Create an access entry for the IAM role
- Associate the access entry with the AmazonEKSClusterAdminPolicy access policy

References:

- https://securitylabs.datadoghq.com/articles/eks-cluster-access-management-deep-dive/
- https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html


## Instructions

```bash title="Detonate with Stratus Red Team"
stratus detonate eks.lateral-movement.create-access-entry
```
## Detection


You can use the following CloudTrail events to identify when someone grants access to your EKS cluster:

- **CreateAccessEntry**, when someone creates an access entry for a principal (meaning it's the first this principal is granted privileges in the cluster)':

```json
{
"eventSource": "eks.amazonaws.com",
"eventName": "CreateAccessEntry",
"requestParameters": {
"name": "eks-cluster",
"principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role"
},
"responseElements": {
"accessEntry": {
"clusterName": "eks-cluster",
"type": "STANDARD",
"principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role",
}
}
}
```


- **AssociateAccessPolicy**: when someone assigns an access policy to a principal

```json
{
"eventSource": "eks.amazonaws.com",
"eventName": "AssociateAccessPolicy",
"requestParameters": {
"policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy",
"accessScope": {
"type": "cluster"
},
"name": "eks-cluster",
"principalArn": "arn%3Aaws%3Aiam%3A%3A012345678901%3Arole%2Fstratus-red-team-eks-create-access-entry-role"
}
}
```


10 changes: 10 additions & 0 deletions docs/attack-techniques/EKS/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# EKS

This page contains the Stratus attack techniques for EKS, grouped by MITRE ATT&CK Tactic.
Note that some Stratus attack techniques may correspond to more than a single ATT&CK Tactic.


## Lateral Movement

- [Create Admin EKS Access Entry](./eks.lateral-movement.create-access-entry.md)

1 change: 1 addition & 0 deletions docs/attack-techniques/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution |
| [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution |
| [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration |
| [Create Admin EKS Access Entry](./EKS/eks.lateral-movement.create-access-entry.md) | [EKS](./EKS/index.md) | Lateral Movement |
| [Exfiltrate Compute Disk by sharing it](./GCP/gcp.exfiltration.share-compute-disk.md) | [GCP](./GCP/index.md) | Exfiltration |
| [Exfiltrate Compute Image by sharing it](./GCP/gcp.exfiltration.share-compute-image.md) | [GCP](./GCP/index.md) | Exfiltration |
| [Exfiltrate Compute Disk by sharing a snapshot](./GCP/gcp.exfiltration.share-compute-snapshot.md) | [GCP](./GCP/index.md) | Exfiltration |
Expand Down
9 changes: 9 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,15 @@ AWS:
- Privilege Escalation
platform: AWS
isIdempotent: false
EKS:
Lateral Movement:
- id: eks.lateral-movement.create-access-entry
name: Create Admin EKS Access Entry
isSlow: false
mitreAttackTactics:
- Lateral Movement
platform: EKS
isIdempotent: false
GCP:
Exfiltration:
- id: gcp.exfiltration.share-compute-disk
Expand Down
11 changes: 10 additions & 1 deletion docs/user-guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ For more information, see [Usage](./usage.md), [Examples](./examples.md) and the

## Connecting to your cloud account

Stratus Red Team currently supports AWS and Kubernetes.
Stratus Red Team currently supports AWS, Azure, Kubernetes, and Amazon EKS.

!!! warning

Expand All @@ -108,6 +108,15 @@ In order to use Stratus attack techniques against AWS, you need to be authentica

- Using static credentials in `~/.aws/config`, and setting your desired AWS profile using `export AWS_PROFILE=my-profile`

### EKS

Stratus Red Team does **not** create an EKS cluster for you. It assumes you're already authenticated to an EKS cluster.

To use Stratus attack techniques against Amazon EKS, you need to be authenticated against AWS, as described above. Stratus Red Team will use the current AWS credentials and Kubernetes context to interact with the EKS cluster. It will check that the Kubernetes cluster you're connected to is an EKS cluster, and refuse to run otherwise.

- Authenticate to AWS (for instance, using [`aws-vault`](https://github.com/99designs/aws-vault))
- Run `aws eks update-kubeconfig --name your-cluster-name --region your-region` to update your `~/.kube/config` file with the EKS cluster configuration

### Azure

- Use the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) to authenticate against your Azure tenant:
Expand Down
9 changes: 5 additions & 4 deletions v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0
github.com/aws/aws-sdk-go-v2 v1.26.1
github.com/aws/aws-sdk-go-v2 v1.30.1
github.com/aws/aws-sdk-go-v2/config v1.25.11
github.com/aws/aws-sdk-go-v2/credentials v1.16.9
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4
Expand All @@ -27,7 +27,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2
github.com/aws/aws-sdk-go-v2/service/sts v1.26.2
github.com/aws/smithy-go v1.20.2
github.com/aws/smithy-go v1.20.3
github.com/cenkalti/backoff/v4 v4.2.1
github.com/fatih/color v1.13.0
github.com/golang-jwt/jwt v3.2.2+incompatible
Expand All @@ -51,10 +51,11 @@ require (
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.46.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.8 // indirect
Expand Down
10 changes: 10 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o=
github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA=
github.com/aws/aws-sdk-go-v2/config v1.25.11 h1:RWzp7jhPRliIcACefGkKp03L0Yofmd2p8M25kbiyvno=
Expand All @@ -48,8 +50,12 @@ github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 h1:TUCNKBd4/JEefsZDxo5de
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4/go.mod h1:egDkcl+zsgFqS6VO142bKboip5Pe1sNMwN55Xy38QsM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8=
Expand All @@ -60,6 +66,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2 h1:e3Imv1oXz+W3Tfclflkh72t5TUP
github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2/go.mod h1:d1hAqgLDOPaSO1Piy/0bBmj6oAplFwv6p0cquHntNHM=
github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6 h1:Y0pqdpafA8TdG6AalCMFbbQ5SlO99MAybU0BDPLHbwo=
github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6/go.mod h1:y6fUhf01cjz+VUz+zrmJh3KfIXhefV7dS4STCxgHx7g=
github.com/aws/aws-sdk-go-v2/service/eks v1.46.0 h1:ZPhHHZtAjVohIGIVjXECPfljcPOQ+hjZ1IpgvjPTJ50=
github.com/aws/aws-sdk-go-v2/service/eks v1.46.0/go.mod h1:p4Yk0zfWEoLvvQ4V6XZrTmAAPzcevNnEsbUR82NAY0w=
github.com/aws/aws-sdk-go-v2/service/iam v1.28.2 h1:lax3msAOJ99KL6k9YHehZPZqfxJ7+uFXvtpFWNBgPEo=
github.com/aws/aws-sdk-go-v2/service/iam v1.28.2/go.mod h1:KJbw+8r7gZfjF+OewOVhyEQKiJXJ/OM1F1r3aAvKS9M=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 h1:e3PCNeEaev/ZF01cQyNZgmYE9oYYePIMJs2mWSKG514=
Expand Down Expand Up @@ -98,6 +106,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 h1:fFrLsy08wEbAisqW3KDl/cPHrF43
github.com/aws/aws-sdk-go-v2/service/sts v1.26.2/go.mod h1:7Ld9eTqocTvJqqJ5K/orbSDwmGcpRdlDiLjz2DO+SL8=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package eks

import (
"context"
_ "embed"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/eks"
"github.com/aws/aws-sdk-go-v2/service/eks/types"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
"log"
)

//go:embed main.tf
var tf []byte

// https://docs.aws.amazon.com/eks/latest/userguide/access-policies.html
const ClusterAccessPolicyName = "AmazonEKSClusterAdminPolicy"
const ClusterAccessPolicyARN = "arn:aws:eks::aws:cluster-access-policy/" + ClusterAccessPolicyName

func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "eks.lateral-movement.create-access-entry",
FriendlyName: "Create Admin EKS Access Entry",

Description: `
Uses the EKS Cluster Access Management to assign cluster administrator privileges to an IAM role. This allows the role to perform any action inside the Kubernetes cluster.
Warm-up:
- Create an IAM role
Detonation:
- Create an access entry for the IAM role
- Associate the access entry with the ` + ClusterAccessPolicyName + ` access policy
References:
- https://securitylabs.datadoghq.com/articles/eks-cluster-access-management-deep-dive/
- https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html
`,
Detection: `
You can use the following CloudTrail events to identify when someone grants access to your EKS cluster:
- **CreateAccessEntry**, when someone creates an access entry for a principal (meaning it's the first this principal is granted privileges in the cluster)':
` + codeBlock + `json
{
"eventSource": "eks.amazonaws.com",
"eventName": "CreateAccessEntry",
"requestParameters": {
"name": "eks-cluster",
"principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role"
},
"responseElements": {
"accessEntry": {
"clusterName": "eks-cluster",
"type": "STANDARD",
"principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role",
}
}
}
` + codeBlock + `
- **AssociateAccessPolicy**: when someone assigns an access policy to a principal
` + codeBlock + `json
{
"eventSource": "eks.amazonaws.com",
"eventName": "AssociateAccessPolicy",
"requestParameters": {
"policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy",
"accessScope": {
"type": "cluster"
},
"name": "eks-cluster",
"principalArn": "arn%3Aaws%3Aiam%3A%3A012345678901%3Arole%2Fstratus-red-team-eks-create-access-entry-role"
}
}
` + codeBlock + `
`,
Platform: stratus.EKS,
PrerequisitesTerraformCode: tf,
IsIdempotent: false,
MitreAttackTactics: []mitreattack.Tactic{mitreattack.LateralMovement},
Detonate: detonate,
Revert: revert,
})
}

func detonate(params map[string]string, providers stratus.CloudProviders) error {
//ec2instanceconnectClient := ec2instanceconnect.NewFromConfig(providers.AWS().GetConnection())
eksProvider := providers.EKS()
eksClient := eks.NewFromConfig(eksProvider.GetAWSConnection())
roleArn := params["role_arn"]

log.Println("Using EKS cluster management API to assign administrator privileges to " + roleArn)

_, err := eksClient.CreateAccessEntry(context.Background(), &eks.CreateAccessEntryInput{
ClusterName: aws.String(eksProvider.GetEKSClusterName()),
PrincipalArn: &roleArn,
})
if err != nil {
return fmt.Errorf("failed to create EKS access entry: %w", err)
}
log.Println("Successfully created EKS access entry for role", roleArn)
log.Println("This role is now full EKS cluster admin")

_, err = eksClient.AssociateAccessPolicy(context.Background(), &eks.AssociateAccessPolicyInput{
AccessScope: &types.AccessScope{Type: types.AccessScopeTypeCluster},
ClusterName: aws.String(eksProvider.GetEKSClusterName()),
PolicyArn: aws.String(ClusterAccessPolicyARN),
PrincipalArn: &roleArn,
})
if err != nil {
return fmt.Errorf("failed to associate EKS access policy to role: %w", err)
}
log.Println("Successfully associated EKS access policy", ClusterAccessPolicyName, "to role", roleArn)
return nil
}

func revert(params map[string]string, providers stratus.CloudProviders) error {
eksProvider := providers.EKS()
eksClient := eks.NewFromConfig(eksProvider.GetAWSConnection())
roleArn := params["role_arn"]

_, err := eksClient.DeleteAccessEntry(context.Background(), &eks.DeleteAccessEntryInput{
ClusterName: aws.String(eksProvider.GetEKSClusterName()),
PrincipalArn: &roleArn,
})
if err != nil {
return fmt.Errorf("failed to delete EKS access entry: %w", err)
}
log.Println("Successfully deleted EKS access entry for role ", roleArn)

return nil
}
Loading

0 comments on commit b2b78ff

Please sign in to comment.