Holiday Hack 2022: Cloud Ring
Orienting
Continuing down through the tunnels, on the next level is a door to the Cloud Ring:
AWS CLI Intro
Challenge
Jill Underpole stands on the lowest platform in the Cloud Ring next to a Cranberry Pi:
Umm, can I help you?
Me? I’m Jill Underpole, thank you very much.
I’m working on this here smoke terminal.
Cloud? Sure, whatever you want to call it.
Anyway, you’re welcome to try this out, if you think you know what you’re doing.
You’ll have to learn some basics about the AWS command line interface (CLI) to be successful though.
Talking to her unlocks a hint in the badge:
- In the AWS command line (CLI), the Secure Token Service or STS has one very useful function.
The terminal is a split pane, with questions at the top and a command prompt at the bottom:
Solution
Video
I’ll show walking through all four of these in this video:
Q1
You may not know this, but AWS CLI help messages are very easy to access. First, try typing: $ aws help
elf@69c7d985afc6:~$ aws help
Q2
Great! When you’re done, you can quit with q. Next, please configure the default aws cli credentials with the access key AKQAAYRKO7A5Q5XUY2IY, the secret key qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf and the region us-east-1 . https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config
elf@69c7d985afc6:~$ aws configure
AWS Access Key ID [None]: AKQAAYRKO7A5Q5XUY2IY
AWS Secret Access Key [None]: qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf
Default region name [None]: us-east-1
Default output format [None]:
Q3
Excellent! To finish, please get your caller identity using the AWS command line. For more details please reference: $ aws sts help or reference: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/index.html
elf@69c7d985afc6:~$ aws sts help
Q4
Excellent! To finish, please get your caller identity using the AWS command line. For more details please reference: $ aws sts help or reference: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/index.html
elf@69c7d985afc6:~$ aws sts get-caller-identity
{
"UserId": "AKQAAYRKO7A5Q5XUY2IY",
"Account": "602143214321",
"Arn": "arn:aws:iam::602143214321:user/elf_helpdesk"
}
And that completes the terminal!
Trufflehog Search
Hints
Jill is impressed, and offers hints for the next task:
Wait, you got it done, didn’t you?
Ok, consider me impressed. You could probably help Gerty, too.
The first trick’ll be running the Trufflehog tool.
It’s as good at sniffing out secrets as I am at finding mushrooms!
After that, it’s just a matter of getting to the secret the tool found.
I’d bet a basket of portobellos you’ll get this done!
There’s two additional hints in the badge:
- You can search for secrets in a Git repo with
trufflehog git https://some.repo/here.git
.- If you want to look at an older code commit with git, you can
git checkout CommitNumberHere
.
Challenge
On a higher platform, Gerty Snowburrow is hanging out and tasks me with a challenge:
Well now, look who’s venturing down into the caves!
And well, who might you be, exactly?
I’m Gerty Snowburrow, if you need to know.
And, not that I should be telling you, but I’m trying to figure out what Alabaster Snowball’s done this time.
Word is, he committed some secrets to a code repo.
If you’re feeling so inclined, you can try and find them for me.
The badge challenge specifieis what I’m looking for:
Use Trufflehog to find secrets in a Git repo. Work with Jill Underpole in the Cloud Ring for hints. What’s the name of the file that has AWS credentials?
Solution
I’ll download a compiled copy of Trufflehog from the latest release page. I like to save it in a folder in /opt
and then put a symlink to that in /usr/local/bin
so that it is in my path.
Now I just run trufflehog
against the given repo using the syntax given in the hint:
oxdf@hacky$ trufflehog git https://haugfactory.com/asnowball/aws_scripts.git
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found unverified result 🐷🔑❓
Detector Type: AWS
Decoder Type: PLAIN
Raw result: AKIAAIDAYRANYAHGQOHD
Timestamp: 2022-09-07 07:53:12 -0700 -0700
Line: 6
Commit: 106d33e1ffd53eea753c1365eafc6588398279b5
File: put_policy.py
Email: asnowball <alabaster@northpolechristmastown.local>
Repository: https://haugfactory.com/asnowball/aws_scripts.git
Found unverified result 🐷🔑❓
Detector Type: Gitlab
Decoder Type: PLAIN
Raw result: add-a-file-using-the-
Commit: 2c77c1e0a98715e32a277859864e8f5918aacc85
File: README.md
Email: alabaster snowball <alabaster@northpolechristmastown.local>
Repository: https://haugfactory.com/asnowball/aws_scripts.git
Timestamp: 2022-09-06 19:54:48 +0000 UTC
Line: 14
Found unverified result 🐷🔑❓
Detector Type: Gitlab
Decoder Type: BASE64
Raw result: add-a-file-using-the-
Repository: https://haugfactory.com/asnowball/aws_scripts.git
Timestamp: 2022-09-06 19:54:48 +0000 UTC
Line: 14
Commit: 2c77c1e0a98715e32a277859864e8f5918aacc85
File: README.md
Email: alabaster snowball <alabaster@northpolechristmastown.local>
It finds three potential secrets:
- AWS secrets in
put_policy.py
- Two Gitlab secrets in
README.md
put_policy.py
is the answer to solve the task!
Exploitation via AWS CLI
Hints
Gerty is impressed:
Say, you got it done, didn’t you?
Well now, you might just be able to tackle the other AWS terminal down here.
It’s a bit more involved, but you’ve got the credentials to get it started now.
Before you try it, you should know the difference between managed and inline policies.
Short version: inline policies apply to one identity (user, role, group), and managed policies can be attached to many identities.
There are different AWS CLI commands to interact with each kind.
Other than that, the important bit is to know a bit about cloud or IAM privilege escalation.
Sometimes attackers find access to more resources by just trying things until something works.
But if they have access to the
iam
service inside the AWS CLI, they might just be able to ask what access they have!You can do it!
It unlocks new hints in the badge as well:
- AWS inline policies pertain to one identity while managed policies can be attached to many identities.
- You can try
s3api
orlambda
service commands, but Chris Elgee’s talk on AWS and IAM might be a good start!
Challenge
Sulfrod is at the top of the cave with a Cranberry Pi, and demands my help:
Hey! You - come here!
You look like someone who knows how to do this nerd stuff.
I need my terminal to be stronger, like me!
flexes
You’re gonna do that for me so I can bust into this cloud machine thing.
The Pi offers a split terminal with questions and a command line space to work:
Solution
Video
I’ll walk through all the answers in this video:
Q1
Use Trufflehog to find credentials in the Gitlab instance at https://haugfactory.com/asnowball/aws_scripts.git. Configure these credentials for us-east-1 and then run: $ aws sts get-caller-identity
Trufflehog found the creds above with this information:
Found unverified result 🐷🔑❓
Detector Type: AWS
Decoder Type: PLAIN
Raw result: AKIAAIDAYRANYAHGQOHD
Repository: https://haugfactory.com/asnowball/aws_scripts.git
Timestamp: 2022-09-07 07:53:12 -0700 -0700
Line: 6
Commit: 106d33e1ffd53eea753c1365eafc6588398279b5
File: put_policy.py
Email: asnowball <alabaster@northpolechristmastown.local>
I’ll clone the repo:
$ git clone https://haugfactory.com/asnowball/aws_scripts.git
Cloning into 'aws_scripts'...
remote: Enumerating objects: 64, done.
remote: Total 64 (delta 0), reused 0 (delta 0), pack-reused 64
Unpacking objects: 100% (64/64), 23.83 KiB | 677.00 KiB/s, done.
But the keys are obfuscated:
import boto3
import json
iam = boto3.client('iam',
region_name='us-east-1',
aws_access_key_id=ACCESSKEYID,
aws_secret_access_key=SECRETACCESSKEY,
)
# arn:aws:ec2:us-east-1:accountid:instance/*
response = iam.put_user_policy(
PolicyDocument='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ssm:SendCommand"],"Resource":["arn:aws:ec2:us-east-1:748127089694:instance/i-0415bfb7dcfe279c5","arn:aws:ec2:us-east-1:748127089694:document/RestartServices"]}]}',
PolicyName='AllAccessPolicy',
UserName='nwt8_test',
)
Trufflehog called out a commit where it found the files. I can checkout that one:
$ git checkout 106d33e1ffd53eea753c1365eafc6588398279b5
Note: switching to '106d33e1ffd53eea753c1365eafc6588398279b5'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 106d33e added
Now the creds are there:
...[snip]
iam = boto3.client('iam',
region_name='us-east-1',
aws_access_key_id="AKIAAIDAYRANYAHGQOHD",
aws_secret_access_key="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",
)
...[snip]...
Alternatively, instead of checking out the old commit, I can just use git diff
. I’ll get back to the current state first:
$ git checkout main
Already on 'main'
Your branch is up to date with 'origin/main'.
git log
will show the commits and their messages:
$ git log --oneline
2dee61b (HEAD -> main, origin/main, origin/HEAD) added
f7224b1 added
b0ed27c added
8ea431b added
5ca1506 added
3476397 added
106d33e added
c0e38e0 added
3fe41dc added
e88e1d8 added
7ccd1f5 added
3c277c5 added
faf5420 added
a6f3a4a added
919dbb7 added
966b48d added
03a2099 added
4227085 added
1298e07 added
b0b6d01 added
eefd203 added
2c77c1e Initial commit
These are not very detailed, but I know the suspect commit starts with 106d33e
, so I’ll compare that one and the one that comes after it (above it in the log):
$ git diff 106d33e 3476397
diff --git a/put_policy.py b/put_policy.py
index f7013a9..d78760f 100644
--- a/put_policy.py
+++ b/put_policy.py
@@ -4,8 +4,8 @@ import json
iam = boto3.client('iam',
region_name='us-east-1',
- aws_access_key_id="AKIAAIDAYRANYAHGQOHD",
- aws_secret_access_key="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",
+ aws_access_key_id=ACCESSKEYID,
+ aws_secret_access_key=SECRETACCESSKEY,
)
# arn:aws:ec2:us-east-1:accountid:instance/*
response = iam.put_user_policy(
It shows what changed - the secrets were replaced with variables.
Now I’ll just configure with these secrets:
elf@2879dd62e42b:~$ aws configure
AWS Access Key ID [None]: AKIAAIDAYRANYAHGQOHD
AWS Secret Access Key [None]: e95qToloszIgO9dNBsQMQsc5/foiPdKunPJwc1rL
Default region name [None]: us-east-1
Default output format [None]:
elf@2879dd62e42b:~$ aws sts get-caller-identity
{
"UserId": "AIDAJNIAAQYHIAAHDDRA",
"Account": "602123424321",
"Arn": "arn:aws:iam::602123424321:user/haug"
}
Q2
Managed (think: shared) policies can be attached to multiple users. Use the AWS CLI to find any policies attached to your user. The aws iam command to list attached user policies can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/index.html Hint: it is NOT list-user-policies.
elf@157d111060c0:~$ aws iam list-attached-user-policies --user-name haug
{
"AttachedPolicies": [
{
"PolicyName": "TIER1_READONLY_POLICY",
"PolicyArn": "arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY"
}
],
"IsTruncated": false
}
Q3
Now, view or get the policy that is attached to your user. The aws iam command to get a policy can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/index.html
elf@157d111060c0:~$ aws iam get-policy --policy-arn arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY
{
"Policy": {
"PolicyName": "TIER1_READONLY_POLICY",
"PolicyId": "ANPAYYOROBUERT7TGKUHA",
"Arn": "arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 11,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"Description": "Policy for tier 1 accounts to have limited read only access to certain resources in IAM, S3, and LAMBDA.",
"CreateDate": "2022-06-21 22:02:30+00:00",
"UpdateDate": "2022-06-21 22:10:29+00:00",
"Tags": []
}
}
Q4
Attached policies can have multiple versions. View the default version of this policy. The aws iam command to get a policy version can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/index.html
The above output shows the DefaultVersionId
as v1
. I’ll try to pull this with list-policy-versions
, but it fails:
elf@4b51136ae1b1:~$ aws iam list-policy-versions --policy-arn arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY --max-items 100
An error occurred (AccessDeniedException) when calling the ListPolicyVersions operation: User: arn:aws:iam::602123424321:user/haug is not authorized to perform: iam:ListPolicyVersions on resource: arn:aws:iam:us-east-1:602123424321:* because no identity-based policy allows the iam:ListPolicyVersions action
I already have a version, so get-policy-version
allows me to fetch a policy with a specified version:
elf@7a4e93c00ece:~$ aws iam get-policy-version --policy-arn arn:aws:iam::602123424321:policy/TI
ER1_READONLY_POLICY --version-id v1
{
"PolicyVersion": {
"Document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:ListFunctions",
"lambda:GetFunctionUrlConfig"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:GetUserPolicy",
"iam:ListUserPolicies",
"iam:ListAttachedUserPolicies"
],
"Resource": "arn:aws:iam::602123424321:user/${aws:username}"
},
{
"Effect": "Allow",
"Action": [
"iam:GetPolicy",
"iam:GetPolicyVersion"
],
"Resource": "arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:GetObject",
"lambda:Invoke*"
],
"Resource": "*"
}
]
},
"VersionId": "v1",
"IsDefaultVersion": false,
"CreateDate": "2022-06-21 22:02:30+00:00"
}
}
Q5
Inline policies are policies that are unique to a particular identity or resource. Use the AWS CLI to list the inline policies associated with your user. The aws iam command to list user policies can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/index.html Hint: it is NOT list-attached-user-policies.
elf@3cebf559de62:~$ aws iam list-user-policies --user-name haug
{
"PolicyNames": [
"S3Perms"
],
"IsTruncated": false
}
Q6
Now, use the AWS CLI to get the only inline policy for your user. The aws iam command to get a user policy can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/index.html
elf@3cebf559de62:~$ aws iam get-user-policy --user-name haug --policy-name S3Perms
{
"UserPolicy": {
"UserName": "haug",
"PolicyName": "S3Perms",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListObjects"
],
"Resource": [
"arn:aws:s3:::smogmachines3",
"arn:aws:s3:::smogmachines3/*"
]
}
]
}
},
"IsTruncated": false
}
Q7
The inline user policy named S3Perms disclosed the name of an S3 bucket that you have permissions to list objects. List those objects! The aws s3api command to list objects in an s3 bucket can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/index.html
elf@3cebf559de62:~$ aws s3api list-objects --bucket smogmachines3
{
"IsTruncated": false,
"Marker": "",
"Contents": [
{
"Key": "coal-fired-power-station.jpg",
"LastModified": "2022-09-23 20:40:44+00:00",
"ETag": "\"1c70c98bebaf3cff781a8fd3141c2945\"",
"Size": 59312,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "industry-smog.png",
"LastModified": "2022-09-23 20:40:47+00:00",
"ETag": "\"c0abe5cb56b7a33d39e17f430755e615\"",
"Size": 272528,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "pollution-smoke.jpg",
"LastModified": "2022-09-23 20:40:43+00:00",
"ETag": "\"465b675c70d73027e13ffaec1a38beec\"",
"Size": 33064,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "pollution.jpg",
"LastModified": "2022-09-23 20:40:45+00:00",
"ETag": "\"d40d1db228c9a9b544b4c552df712478\"",
"Size": 81775,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "power-station-smoke.jpg",
"LastModified": "2022-09-23 20:40:48+00:00",
"ETag": "\"2d7a8c8b8f5786103769e98afacf57de\"",
"Size": 45264,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "smog-power-station.jpg",
"LastModified": "2022-09-23 20:40:46+00:00",
"ETag": "\"0e69b8d53d97db0db9f7de8663e9ec09\"",
"Size": 32498,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
},
{
"Key": "smogmachine_lambda_handler_qyJZcqvKOthRMgVrAJqq.py",
"LastModified": "2022-09-26 16:31:33+00:00",
"ETag": "\"fd5d6ab630691dfe56a3fc2fcfb68763\"",
"Size": 5823,
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "grinchum",
"ID": "15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60"
}
}
],
"Name": "smogmachines3",
"Prefix": "",
"MaxKeys": 1000,
"EncodingType": "url"
}
Q8
The attached user policy provided you several Lambda privileges. Use the AWS CLI to list Lambda functions. The aws lambda command to list functions can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/index.html
elf@3cebf559de62:~$ aws lambda list-functions
{
"Functions": [
{
"FunctionName": "smogmachine_lambda",
"FunctionArn": "arn:aws:lambda:us-east-1:602123424321:function:smogmachine_lambda",
"Runtime": "python3.9",
"Role": "arn:aws:iam::602123424321:role/smogmachine_lambda",
"Handler": "handler.lambda_handler",
"CodeSize": 2126,
"Description": "",
"Timeout": 600,
"MemorySize": 256,
"LastModified": "2022-09-07T19:28:23.634+0000",
"CodeSha256": "GFnsIZfgFNA1JZP3TgTI0tIavOpDLiYlg7oziWbtRsa=",
"Version": "$LATEST",
"VpcConfig": {
"SubnetIds": [
"subnet-8c80a9cb8b3fa5505"
],
"SecurityGroupIds": [
"sg-b51a01f5b4711c95c"
],
"VpcId": "vpc-85ea8596648f35e00"
}, "Environment": {
"Variables": {
"LAMBDASECRET": "975ceab170d61c75",
"LOCALMNTPOINT": "/mnt/smogmachine_files" }
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7e198c3c-d4ea-48dd-9370-e5238e9ce06e",
"FileSystemConfigs": [ {
"Arn": "arn:aws:elasticfilesystem:us-east-1:602123424321:access-point/fsap-db3277b03c6e975d2", db "LocalMountPath": "/mnt/smogmachine_files" 3277 } ],
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
}
}
]
}
Q9
Lambda functions can have public URLs from which they are directly accessible. Use the AWS CLI to get the configuration containing the public URL of the Lambda function. The aws lambda command to get the function URL config can be found here: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/index.html
First through was get-function
or get-function-configuration
, but both fail for permissions:
elf@3cebf559de62:~$ aws lambda get-function --function-name smogmachine_lambda
An error occurred (AccessDeniedException) when calling the GetFunction operation: User: arn:aws:iam::602123424321:user/haug is not authorized to perform: lambda:GetFunction on resource: arn:aws:lambda:us-east-1:602123424321:* because no identity-based policy allows the lambda:GetFunction action
elf@3cebf559de62:~$ aws lambda get-function-configuration --function-name smogmachine_lambda
An error occurred (AccessDeniedException) when calling the GetFunctionConfiguration operation: User: arn:aws:iam::602123424321:user/haug is not authorized to perform: lambda:GetFunctionConfiguration on resource: arn:aws:lambda:us-east-1:602123424321:* because no identity-based policy allows the lambda:GetFunctionConfiguration action
A bit more pokling around finds get-function-url-config
:
elf@3cebf559de62:~$ aws lambda get-function-url-config --function-name smogmachine_lambda
{
"FunctionUrl": "https://rxgnav37qmvqxtaksslw5vwwjm0suhwc.lambda-url.us-east-1.on.aws/",
"FunctionArn": "arn:aws:lambda:us-east-1:602123424321:function:smogmachine_lambda",
"AuthType": "AWS_IAM",
"Cors": {
"AllowCredentials": false,
"AllowHeaders": [],
"AllowMethods": [
"GET",
"POST"
],
"AllowOrigins": [
"*"
],
"ExposeHeaders": [],
"MaxAge": 0
},
"CreationTime": "2022-09-07T19:28:23.808713Z",
"LastModifiedTime": "2022-09-07T19:28:23.808713Z"
}
This solves the challenge:
Story
I’ve now recovered the forth ring, the Cloud Ring:
The story is 2/3 complete:
Five Rings for the Christmas king immersed in cold
Each Ring now missing from its zone
The first with bread kindly given, not sold
Another to find ‘ere pipelines get owned
One beneath a fountain where water flowed
Into clouds Grinchum had the fourth thrown
Sulfrod tells me to get out of here:
Ha! Now I have the ring!
This computer stuff sure is easy if you just make someone do it for you.
Wait.. the computer gave you the ring? Gah, whatever.
This never happened, got it? Now beat it, nerd!
Grinchum is hanging out at the bottom as well:
🥺 Four Preciouses - lost!
😫 Noooo… grinchum..grinchum
😐 ….. naggy human doesn’t *only* want coinses and hatses.
…What… 🤨has it got…
😠 in its silly, little, badges!?
😧Stole them… 😠 You STOLE them!
😡 Raaaargh!! We will make sure naggy human never takes our last Precious!