Jaga reached Meow Olympurr and met some native Meows. While cautious at first, they warmed up and shared that they have recently created a website to promote tourism!
However, the young Meows explained that they are not cy-purr security trained and would like to understand what they might have misconfigured in their environments. The young Meows were trying to get two different environments to work together, but it seems like something is breaking....
Log a cy-purr security case by invoking the mysterious function and retrieve the secret code!
d2p9lw76n0gfo0.cloudfront.net
Finding the Azure Blob Storage
We are provided with a CloudFront page, https://d2p9lw76n0gfo0.cloudfront.net.
I initially tried scanning the page for any hidden files or directories but didn't have any luck with that. But looking at the 404 error page raised some suspicions as an image failed to load.
This is due to mixed content - an HTTP image is being loaded on an HTTPS page, and modern browsers do not allow this.
The image URL is interesting - it uses a CORS-Anywhere proxy running at http://18.141.147.115:8080 to add CORS headers to the resource from https://meowolympurr.z23.web.core.windows.net/images/ohno.jpg.
The resource being fetched is an Azure Blob Storage URL for the meowolympurr account. Let's visit the 404 error page again, this time on the meowolympurr.z23.web.core.windows.net blob storage.
This time, the same error image is fetched with a Shared Access Signature (SAS) token, and an HTML comment hints at using the SAS token to access the website's source code.
<img src="images/ohno.jpg?sv=2017-07-29&ss=b&srt=sco&sp=rl&se=2022-12-12T00:00:00Z&st=2022-09-01T00:00:00Z&spr=https&sig=UE2%2FTMTAzDnyJEABpX4OYFBs1b1uAWjwEEAtjeQtwxg%3D"/>
...
<!--
For access to website source codes:
https://meowolympurr.blob.core.windows.net?sv=2017-07-29&ss=b&srt=sco&sp=rl&se=2022-12-12T00:00:00Z&st=2022-09-01T00:00:00Z&spr=https&sig=UE2%2FTMTAzDnyJEABpX4OYFBs1b1uAWjwEEAtjeQtwxg%3D
-->
The content in the index.html page is similar to what was hosted on the CloudFront page, except for a new paragraph at the end of the page.
<div class="p-5 text-center bg-light">
<p class="lead text-muted">Have an event you are interested to host but it is not listed here? Submit it <a href="https://olympurr-app.azurewebsites.net/api/meowvellous">here</a>!</p>
</div>
The SSRF Rabbit Hole
This new URL appears to be a dynamic site! All it does is fetch the URL provided in the url GET parameter, and return the fetched response.
Found an interesting event you would like to organise in Meow Olympurr?
Pass the URL as a query string. You will see the submitted information if it is successful.
e.g. https://olympurr-app.azurewebsites.net/api/meowvellous?url=<INSERT>
Such an SSRF may prove useful, but we are not yet sure of the environment this code is running in, and most importantly, we don't have the source code. Nonetheless, I spent some time trying to hit potential internal resources with the SSRF but had no luck.
Heading over to the root URL of https://olympurr-app.azurewebsites.net, we see that this is an Azure functions application.
While Azure VMs have access to a metadata server, this is not applicable to a serverless function. It seems that the SSRF itself might not be so useful after all.
A Very Sassy Challenge
We previously found a SAS token and a hint to access the website's source code, so let's see if we could find the source code of this function somewhere.
Let's start with listing the containers available. We can do this using the List Containers operation of the Blob Service REST API. Simply specify comp=list, and append the SAS token:
Once we have the container name, we could use the List Blobs operation to list the blobs under the specified container. For example, the following lists all blobs under the $web container.
Using the same SAS token, we can head over to /dev/readme.md to read it.
# Meow Olympurr One stop service for all fun activites in Meow Olympurr! All resources are hosted on a single tenant: 83e595f4-f086-4f2f-9de8-d698b6012093Meows are not cy-purr security trained, but we are willing to learn! # To do 1. Consolidate the asset list 2. Seek advice from Jaga and team when they arrive! 3. Integrate services 4. Remove credentials used for debugging access to function app# Function app - https://olympurr-app.azurewebsites.net/api/meowvellousSAS token to access the scm-releases container: ?sv=2018-11-09&sr=c&st=2022-09-01T00%3A00%3A00Z&se=2022-12-12T00%3A00%3A00Z&sp=rl&spr=https&sig=jENgCFTrC1mYM1ZNo%2F8pq1Hg9BO1VLbXlk%2FpABrK4Eo%3D## Credentials for debuggingThe following service principal has the same privileges as the function appApplication ID: ee92075f-4ddc-4522-a12c-2bc0ab874c85Client Secret: kmk8Q~mGYD9jNfgm~rcIOMRgiC9ekKtNEw5GPaS7
I Find Your Lack of Sauce... Disturbing
We are finally one step closer to getting the coveted sauce code for the function app!
It looks like we got our hands on some Azure credentials, and yet another SAS token. Using the tenant ID, application ID and client secret, we can login through the Azure CLI.
The Azure CLI provides a very convenient resource API that allows us to list all resources using the az resource list command. Here's the result of running that command.
It looks like this service principal has access to a different storage account, by the name of meowvellousappstorage. After discovering this storage blob name, we can use the provided SAS token to access the scm-releases container:
This contains the source code of the function app.
Goodbye Bill, Hello Jeff
import boto3import requestsimport jsonimport azure.functions as funcfrom azure.identity import ManagedIdentityCredentialfrom azure.keyvault.secrets import SecretClientfunctionName ="event-webservice"keyName ="AKIA5G4XMRW7TLT6XD7R"deflogURL(url): identity =ManagedIdentityCredential() secretClient =SecretClient(vault_url="https://olympurr-aws.vault.azure.net/", credential=identity) secret = secretClient.get_secret(keyName).value session = boto3.Session( aws_access_key_id=keyName, aws_secret_access_key=secret ) lambda_client = session.client("lambda", region_name="ap-southeast-1") details ={"url": url} lambda_client.invoke( FunctionName=functionName, InvocationType="RequestResponse", Payload=bytes(json.dumps(details), "utf-8") )return secretdefmain(req: func.HttpRequest) -> func.HttpResponse: url = req.params.get('url')ifnot url:try: req_body = req.get_json()exceptValueError:passelse: name = req_body.get('url')if url:# Log the URL in AWS secret =logURL(url)try: response = requests.get(url)return func.HttpResponse(response.text)exceptExceptionas e:return func.HttpResponse(str(e))return func.HttpResponse("""Found an interesting event you would like to organise in Meow Olympurr?Pass the URL as a query string. You will see the submitted information if it is successful. e.g. https://olympurr-app.azurewebsites.net/api/meowvellous?url=<INSERT>⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡿⠋⠈⠻⣮⣳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⡿⠋⠀⠀⠀⠀⠙⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠈⠛⠛⠿⠿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣾⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣀⡀⠀⠀⣀⣴⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣄⠀⠀⢀⣤⣾⡿⠟⠛⠛⢿⣿⣶⣾⣿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣀⣀⣤⣶⣿⡿⠿⢿⣿⡀⠀⣿⣿⠏⠀⢰⡆⠀⠀⠉⢿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⢿⡿⠟⠋⠁⠀⠀⢸⣿⠇⠀⣿⡟⠀⣀⠈⣀⡀⠒⠃⠀⠙⣿⡆⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠇⠀⣿⡇⠀⠛⢠⡋⢙⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠀⠀⣿⣧⠀⠀⠀⠓⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠋⠀⠀⢸⣧⣤⣤⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡿⠀⠀⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠻⣷⣶⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⠁⠀⠀⠈⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⣶⣤⣤⣀⡀⠀⠀⠀⢀⣴⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⡿⣄ Send us the details! ⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⠀⢸⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣆⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣶⣾⣿⣿⣿⣿⣤⣄⣀⡀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣻⣷⣶⣾⣿⣿⡿⢯⣛⣛⡋⠁⠀⠀⠉⠙⠛⠛⠿⣿⣿⡷⣶⣿ """, status_code=200 )
Taking a look at the source code, we see that a secret is obtained from an Azure key vault and used as the secret access key for an AWS session.
As stated previously in the readme.md, the service principal account we have access to "has the same privileges as the function app". This means that we should be able to retrieve this secret from the key vault.
Hmm, this function does not return much useful information. Not providing a valid payload also results in an exception, but we can't see much from the response.
{"errorMessage":"'url'","errorType":"KeyError","requestId":"ab4a0452-88fd-4d67-92b2-4861ad4f857e","stackTrace": [" File \"/var/task/main.py\", line 6, in lambda_handler\n print(event[\"url\"])\n"]}
Log Me In
Three of the actions listed in the policy allow us to retrieve logs from the logs API.
Viewing the logs of the function's execution may provide us with more insights.
To begin, we can list the log groups available. The /aws/lambda/event-webservice log group contains the logs of our previously invoked function, but there are several other interesting log groups as well.
This time, we get the message "secrets returned in response". Looks like this function does return something useful after all. Let's try to invoke the corresponding function, internal-secret-of-MeowOlympurr-webservice.
In conclusion, I am too lazy to write one so ChatGPT did it for me.
Verse 1:
In Meow Olympurr, met some native Meows
They were cautious at first, but warmed up soon
They shared with me their new website for tourism
But they were not cy-purr security trained, they needed to learn
Chorus:
Log a cy-purr security case, find the secret code
Invoke the mysterious function, it's time to hit the road
Verse 2:
The young Meows were trying to get two environments to play
But something was breaking, they needed help that day
I found a CloudFront page, https://d2p9lw76n0gfo0.cloudfront.net
Scanned for hidden files, but that didn't get me anywhere yet
Chorus:
Log a cy-purr security case, find the secret code
Invoke the mysterious function, it's time to hit the road
Verse 3:
The 404 error page raised my suspicions, something was amiss
An HTTP image was loaded on an HTTPS page, that's a no-no
The CORS-Anywhere proxy caught my eye, http://18.141.147.115:8080
It added CORS headers to the resource from https://meowolympurr.z23.web.core.windows.net
Chorus:
Log a cy-purr security case, find the secret code
Invoke the mysterious function, it's time to hit the road
Verse 4:
The resource was an Azure Blob Storage URL, time to investigate
I visited the 404 error page again, this time on the blob storage site
The same error image was fetched, but with a SAS token in tow
An HTML comment hinted at using the SAS token to access the website's source code
Chorus:
Log a cy-purr security case, find the secret code
Invoke the mysterious function, it's time to hit the road
Verse 5:
The source code of this function was what I was looking for
I started by listing the containers available, using the Blob Service REST API
I specified comp=list, and appended the SAS token to the URL
This gave me a list of containers, and I found the one I was looking for
Chorus:
Log a cy-purr security case, find the secret code
Invoke the mysterious function, it's time to hit the road
Verse 6:
I accessed the container using the SAS token and the REST API
This gave me access to the source code of the function, time to get busy
I looked through the code and found the secret code, I had won
Invoking the mysterious function with the secret code, mission complete, it's done
Outro:
I found the secret code, invoked the mysterious function
Thanks to the native Meows, I conquered this sassy challenge
Logged a cy-purr security case, and learned a thing or two
In Meow Olympurr, I am a cybersecurity pro.