GCP Cloud Function Abuse

渗透技巧 1个月前 admin
122 0 0
GCP Cloud Function Abuse
To start, how do we know we are interacting with a Google Cloud Function (GCF)? If the developer opted to not use a custom domain, then Cloud Functions URL typically looks something like https://function-vulnapp2-bvvciwqnka-ue.a.run.app/. If they are using a custom domain name, then try to look for the following headers as a tip-off:

server: Google Frontend
x-cloud-trace-context: <md5-hash>
GCF Fingerprint Headers

By default, all GCFs are configured to require authorization to trigger. This is done by supplying the usual Authorization: Bearer <your-jwt-here> header along with your request. If you see this pop up in your proxy history, it is worth taking a look at the bearer token to see exactly what account is being used to authorize the request. We'll dig into that more another time. For the purpose of this post, I've included some resources in the GitHub repository so you can stand up your own vulnerable GCF and follow along. The GCF will not require authorization though, so no need to worry about it. You will need to alter the variables.tffile to include a proper GPC Project ID so that the terraform script knows where to put everything.

Local File Inclusion (LFI)

So, you've found a vulnerable application and it seems to be running as a GCF. What can we do with that though?

There aren't many interesting files to look at unfortunately. You may be lucky though and find some hidden treasure inside the application's source files. In our example function, we have gone with Python as our language of choice.

GCP Cloud Function Abuse
Example Source Directory Listing

We have the ability in our example to list directory contents, however you may not be so lucky in the real world. You will find the rest of the source artifacts inside /workspace. The default naming for a Python GCF script is main.py so that's what we'll try to view.

GCP Cloud Function Abuse
Example Script Source Code

Server-Side Request Forgery (SSRF)

With SSRF vulnerabilities in GCF, you can obviously try to also pull down information just as you would with an LFI vulnerability utilizing the file://protocol. But are there things specific to GCP that we can access?

There is a metadata endpoint available that will return valid authorization tokens for the Service Account used for the GCF. However, GCP requires that you supply a custom HTTP header to interact with it. This mean that the majority of SSRF vulnerabilities won't get you anything really interesting. For this article though, that isn't much fun.

If you are enjoying this article your support would be greatly appreciated!

So, our vulnerable application, for no real reason at all, supports the gopher://protocol. Using the Gopher protocol, it is possible to build full request messages with custom headers. In this rare situation, we can pull down authorization information for the GCF and begin enumerating the access it has been given.

Double URL Encoded gopher:// SSRF

In the above URL, we are constructing a valid HTTP request within the Gopher URL. Because we're dealing with Flask, we need to double URL encode the payload for it to execute properly. In addition to the authorization token, we can also view what scopes the service account has assigned to it.

GCP Cloud Function Abuse
Service Account Access Token
GCP Cloud Function Abuse
Service Account Default Scopes

Command Injection

Considering the source code could look like basically anything, having any directory structure that works, it may be pretty hard to pull out the source code that you want. In this case, hopefully you've found some way to exploit a command injection or remote code execution vulnerability. If you have, then there is a packaged version of the source code located at /workspace/.googlebuild/source-code.tar.gz. Attempting to pull this file with the example LFI will throw a 500 error, however with command injection we should be able to pull it out.

Since we don't have things like netcat available in a GCF we have to get a little creative for exfiltration. sftp is available, as is curl. For this post, we're going to utilize curl's ability to post multipart form-data and send it to a VM setup with a python script to receive files. Credit goes to this StackOverflow user for providing a simple Python script for exfiltration. The script used is copied below:

#!/usr/env python3
import http.server
import socketserver
import io
import cgi

# Change this to serve on a different port
PORT = 80

class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):

    def do_POST(self):        
        r, info = self.deal_post_data()
        print(r, info, "by: ", self.client_address)
        f = io.BytesIO()
        if r:
        length = f.tell()
        self.send_header("Content-type", "text/plain")
        self.send_header("Content-Length", str(length))
        if f:
            self.copyfile(f, self.wfile)

    def deal_post_data(self):
        ctype, pdict = cgi.parse_header(self.headers['Content-Type'])
        pdict['boundary'] = bytes(pdict['boundary'], "utf-8")
        pdict['CONTENT-LENGTH'] = int(self.headers['Content-Length'])
        if ctype == 'multipart/form-data':
            form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], })
            print (type(form))
                if isinstance(form["file"], list):
                    for record in form["file"]:
                        open("./%s"%record.filename, "wb").write(record.file.read())
                    open("./%s"%form["file"].filename, "wb").write(form["file"].file.read())
            except IOError:
                    return (False, "Can't create file to write, do you have permission to write?")
        return (True, "Files uploaded")

Handler = CustomHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)

With that script running on port 80, all we need to do is inject the following command to receive our file:

Command Injection Payload
GCP Cloud Function Abuse
Receiving the File
GCP Cloud Function Abuse
Source Code Retrieved

Secret Manager

If the application requires the use of sensitive information, then a good developer is likely to use the GCP Secret Manager to avoid leaving it in the source itself. There are two ways in which you can attach a secret to the GCF. The first option available is to reference it as an environment variable. In this case, any vulnerability that would allow you to read those variables would expose the secrets attached. You are allowed to supply the name and value of the environment variable, so it could be anything really.

GCP Cloud Function Abuse
Secret Exposed via Environment Variables

The second option available is to mount the secret somewhere on the file system. When attaching the secret to the GCF, you supply the mount location you want it to be located at. The name of the secret will be the actual name of the secret in Secret Manager.

GCP Cloud Function Abuse
Secret Exposed via Mounted File


While this is in no way an exhaustive set of examples of how you might utilize a vulnerability in a GCF to gain further access, I hope it sheds some light on what is possible. It is very important to keep your Service Account permissions as restricted as possible to avoid an attacker pivoting from a GCF to other cloud infrastructure.

If you felt I was missing something that should have been included, or would like something explained in more detail, please feel free to reach out to me over Twitter @codymartin.


原文始发于Cody Martin:GCP Cloud Function Abuse

版权声明:admin 发表于 2023年4月25日 上午10:24。
转载请注明:GCP Cloud Function Abuse | CTF导航