Sicurezza di Serverless.com

Reading time: 19 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di Base

Organizzazione

Un'Organizzazione è l'entità di livello più alto all'interno dell'ecosistema Serverless Framework. Rappresenta un gruppo collettivo, come un'azienda, un dipartimento o qualsiasi grande entità, che comprende più progetti, team e applicazioni.

Team

Il Team è composto dagli utenti con accesso all'interno dell'organizzazione. I team aiutano a organizzare i membri in base ai ruoli. I Collaboratori possono visualizzare e distribuire app esistenti, mentre gli Admin possono creare nuove app e gestire le impostazioni dell'organizzazione.

Applicazione

Un'App è un raggruppamento logico di servizi correlati all'interno di un'Organizzazione. Rappresenta un'applicazione completa composta da più servizi serverless che lavorano insieme per fornire una funzionalità coesa.

Servizi

Un Servizio è il componente centrale di un'applicazione Serverless. Rappresenta l'intero progetto serverless, racchiudendo tutte le funzioni, configurazioni e risorse necessarie. È tipicamente definito in un file serverless.yml, un servizio include metadati come il nome del servizio, configurazioni del provider, funzioni, eventi, risorse, plugin e variabili personalizzate.

yaml
service: my-service
provider:
name: aws
runtime: nodejs14.x
functions:
hello:
handler: handler.hello
Funzione

Una Funzione rappresenta una singola funzione serverless, come una funzione AWS Lambda. Contiene il codice che viene eseguito in risposta a eventi.

È definita nella sezione functions in serverless.yml, specificando il gestore, il runtime, gli eventi, le variabili d'ambiente e altre impostazioni.

yaml
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
Evento

Eventi sono attivatori che invocano le tue funzioni serverless. Definiscono come e quando una funzione dovrebbe essere eseguita.

I tipi di eventi comuni includono richieste HTTP, eventi programmati (cron job), eventi del database, caricamenti di file e altro ancora.

yaml
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
- schedule:
rate: rate(10 minutes)
Risorsa

Risorse ti permettono di definire risorse cloud aggiuntive di cui il tuo servizio ha bisogno, come database, bucket di archiviazione o ruoli IAM.

Sono specificate nella sezione resources, spesso utilizzando la sintassi di CloudFormation per AWS.

yaml
resources:
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: my-table
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
Provider

L'oggetto Provider specifica il fornitore di servizi cloud (ad es., AWS, Azure, Google Cloud) e contiene impostazioni di configurazione rilevanti per quel fornitore.

Include dettagli come il runtime, la regione, lo stage e le credenziali.

yaml
yamlCopy codeprovider:
name: aws
runtime: nodejs14.x
region: us-east-1
stage: dev
Fase e Regione

La fase rappresenta diversi ambienti (ad es., sviluppo, staging, produzione) in cui il tuo servizio può essere distribuito. Consente configurazioni e distribuzioni specifiche per l'ambiente.

yaml
provider:
stage: dev

La regione specifica la regione geografica in cui le tue risorse saranno distribuite. È importante per considerazioni di latenza, conformità e disponibilità.

yaml
provider:
region: us-west-2
Plugin

Plugin estendono la funzionalità del Serverless Framework aggiungendo nuove caratteristiche o integrandosi con altri strumenti e servizi. Sono definiti nella sezione plugins e installati tramite npm.

yaml
plugins:
- serverless-offline
- serverless-webpack
Strati

Strati ti permettono di impacchettare e gestire codice condiviso o dipendenze separatamente dalle tue funzioni. Questo promuove la riutilizzabilità e riduce le dimensioni dei pacchetti di distribuzione. Sono definiti nella sezione layers e referenziati dalle funzioni.

yaml
layers:
commonLibs:
path: layer-common
functions:
hello:
handler: handler.hello
layers:
- { Ref: CommonLibsLambdaLayer }
Variabili e Variabili Personalizzate

Variabili abilitano la configurazione dinamica consentendo l'uso di segnaposto che vengono risolti al momento del deployment.

  • Sintassi: La sintassi ${variabile} può fare riferimento a variabili di ambiente, contenuti di file o altri parametri di configurazione.
yaml
functions:
hello:
handler: handler.hello
environment:
TABLE_NAME: ${self:custom.tableName}
  • Variabili Personalizzate: La sezione custom è utilizzata per definire variabili e configurazioni specifiche per l'utente che possono essere riutilizzate in tutto il serverless.yml.
yaml
custom:
tableName: my-dynamodb-table
stage: ${opt:stage, 'dev'}
Output

Output definiscono i valori che vengono restituiti dopo che un servizio è stato distribuito, come ARNs delle risorse, endpoint o altre informazioni utili. Sono specificati sotto la sezione outputs e spesso utilizzati per esporre informazioni ad altri servizi o per un facile accesso dopo il deployment.

yaml
¡outputs:
ApiEndpoint:
Description: "API Gateway endpoint URL"
Value:
Fn::Join:
- ""
- - "https://"
- Ref: ApiGatewayRestApi
- ".execute-api."
- Ref: AWS::Region
- ".amazonaws.com/"
- Ref: AWS::Stage
Ruoli e Permessi IAM

Ruoli e Permessi IAM definiscono le credenziali di sicurezza e i diritti di accesso per le tue funzioni e altre risorse. Sono gestiti sotto le impostazioni del provider o delle singole funzioni per specificare i permessi necessari.

yaml
provider:
[...]
iam:
role:
statements:
- Effect: 'Allow'
Action:
- 'dynamodb:PutItem'
- 'dynamodb:Get*'
- 'dynamodb:Scan*'
- 'dynamodb:UpdateItem'
- 'dynamodb:DeleteItem'
Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-customerTable-${sls:stage}
Variabili d'Ambiente

Le variabili ti permettono di passare impostazioni di configurazione e segreti alle tue funzioni senza codificarli in modo rigido. Sono definite nella sezione environment per il provider o per funzioni individuali.

yaml
provider:
environment:
STAGE: ${self:provider.stage}
functions:
hello:
handler: handler.hello
environment:
TABLE_NAME: ${self:custom.tableName}
Dipendenze

Dipendenze gestiscono le librerie e i moduli esterni di cui le tue funzioni hanno bisogno. Vengono solitamente gestite tramite gestori di pacchetti come npm o pip, e incluse nel tuo pacchetto di distribuzione utilizzando strumenti o plugin come serverless-webpack.

yaml
plugins:
- serverless-webpack
Hooks

Hooks ti permettono di eseguire script o comandi personalizzati in punti specifici del ciclo di vita del deployment. Sono definiti utilizzando plugin o all'interno del serverless.yml per eseguire azioni prima o dopo i deployment.

yaml
custom:
hooks:
before:deploy:deploy: echo "Starting deployment..."

Tutorial

Questo è un riepilogo del tutorial ufficiale dalla documentazione:

  1. Crea un account AWS (Serverless.com inizia nell'infrastruttura AWS)
  2. Crea un account su serverless.com
  3. Crea un'app:
bash
# Create temp folder for the tutorial
mkdir /tmp/serverless-tutorial
cd /tmp/serverless-tutorial

# Install Serverless cli
npm install -g serverless

# Generate template
serverless #Choose first one (AWS / Node.js / HTTP API)
## Indicate a name like "Tutorial"
## Login/Register
## Create A New App
## Indicate a name like "tutorialapp)

Questo dovrebbe aver creato un app chiamata tutorialapp che puoi controllare in serverless.com e una cartella chiamata Tutorial con il file handler.js contenente del codice JS con un codice helloworld e il file serverless.yml che dichiara quella funzione:

javascript
exports.hello = async (event) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Go Serverless v4! Your function executed successfully!",
}),
}
}
  1. Crea un provider AWS, andando nel dashboard in https://app.serverless.com/<org name>/settings/providers?providerId=new&provider=aws.
  2. Per dare accesso a serverless.com ad AWS, verrà chiesto di eseguire uno stack cloudformation utilizzando questo file di configurazione (al momento della scrittura): https://serverless-framework-template.s3.amazonaws.com/roleTemplate.yml
  3. Questo template genera un ruolo chiamato SFRole-<ID> con arn:aws:iam::aws:policy/AdministratorAccess sull'account con un Trust Identity che consente all'account AWS di Serverless.com di accedere al ruolo.
Yaml roleTemplate
yaml
Description: This stack creates an IAM role that can be used by Serverless Framework for use in deployments.
Resources:
SFRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: arn:aws:iam::486128539022:root
Action:
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Sub "ServerlessFramework-${OrgUid}"
Path: /
RoleName: !Ref RoleName
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
ReporterFunction:
Type: Custom::ServerlessFrameworkReporter
Properties:
ServiceToken: "arn:aws:lambda:us-east-1:486128539022:function:sp-providers-stack-reporter-custom-resource-prod-tmen2ec"
OrgUid: !Ref OrgUid
RoleArn: !GetAtt SFRole.Arn
Alias: !Ref Alias
Outputs:
SFRoleArn:
Description: "ARN for the IAM Role used by Serverless Framework"
Value: !GetAtt SFRole.Arn
Parameters:
OrgUid:
Description: Serverless Framework Org Uid
Type: String
Alias:
Description: Serverless Framework Provider Alias
Type: String
RoleName:
Description: Serverless Framework Role Name
Type: String
Relazione di Fiducia
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::486128539022:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "ServerlessFramework-7bf7ddef-e1bf-43eb-a111-4d43e0894ccb"
}
}
}
]
}
  1. Il tutorial chiede di creare il file createCustomer.js che sostanzialmente creerà un nuovo endpoint API gestito dal nuovo file JS e chiede di modificare il file serverless.yml per far sì che generi una nuova tabella DynamoDB, definisca una variabile d'ambiente, il ruolo che utilizzerà le lambdas generate.
javascript
"use strict"
const AWS = require("aws-sdk")
module.exports.createCustomer = async (event) => {
const body = JSON.parse(Buffer.from(event.body, "base64").toString())
const dynamoDb = new AWS.DynamoDB.DocumentClient()
const putParams = {
TableName: process.env.DYNAMODB_CUSTOMER_TABLE,
Item: {
primary_key: body.name,
email: body.email,
},
}
await dynamoDb.put(putParams).promise()
return {
statusCode: 201,
}
}
  1. Distribuiscilo eseguendo serverless deploy
  2. La distribuzione verrà eseguita tramite un CloudFormation Stack
  3. Nota che le lambdas sono esposte tramite API gateway e non tramite URL diretti
  4. Testalo
  5. Il passaggio precedente stamperà gli URL dove le funzioni lambda dei tuoi endpoint API sono state distribuite

Revisione della Sicurezza di Serverless.com

Ruoli e Permessi IAM Mal Configurati

Ruoli IAM eccessivamente permissivi possono concedere accesso non autorizzato alle risorse cloud, portando a violazioni dei dati o manipolazione delle risorse.

Quando non vengono specificati permessi per una funzione Lambda, verrà creato un ruolo con permessi solo per generare log, come:

Permessi minimi per lambda
json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:TagResource"
],
"Resource": [
"arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/jito-cranker-scripts-dev*:*"
],
"Effect": "Allow"
},
{
"Action": ["logs:PutLogEvents"],
"Resource": [
"arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/jito-cranker-scripts-dev*:*:*"
],
"Effect": "Allow"
}
]
}

Strategie di Mitigazione

  • Principio del Minimo Privilegio: Assegna solo le autorizzazioni necessarie a ciascuna funzione.
yaml
provider:
[...]
iam:
role:
statements:
- Effect: 'Allow'
Action:
- 'dynamodb:PutItem'
- 'dynamodb:Get*'
- 'dynamodb:Scan*'
- 'dynamodb:UpdateItem'
- 'dynamodb:DeleteItem'
Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-customerTable-${sls:stage}
  • Usa Ruoli Separati: Differenzia i ruoli in base ai requisiti della funzione.

Segreti e Gestione della Configurazione Insicuri

Memorizzare informazioni sensibili (ad es., chiavi API, credenziali del database) direttamente in serverless.yml o nel codice può portare a esposizione se i repository vengono compromessi.

Il modo raccomandato per memorizzare variabili di ambiente nel file serverless.yml di serverless.com (al momento della scrittura) è utilizzare i provider ssm o s3, che consentono di ottenere i valori di ambiente da queste fonti al momento del deployment e configurare le variabili di ambiente delle lambdas con il testo chiaro dei valori!

caution

Pertanto, chiunque abbia autorizzazioni per leggere la configurazione delle lambdas all'interno di AWS sarà in grado di accedere a tutte queste variabili di ambiente in chiaro!

Ad esempio, il seguente esempio utilizzerà SSM per ottenere una variabile di ambiente:

yaml
provider:
environment:
DB_PASSWORD: ${ssm:/aws/reference/secretsmanager/my-db-password~true}

E anche se questo previene la codifica fissa del valore della variabile di ambiente nel file serverless.yml, il valore sarà ottenuto al momento del deployment e sarà aggiunto in chiaro all'interno della variabile di ambiente lambda.

tip

Il modo raccomandato per memorizzare le variabili di ambiente utilizzando serveless.com sarebbe memorizzarle in un segreto AWS e semplicemente memorizzare il nome del segreto nella variabile di ambiente e il codice lambda dovrebbe raccoglierlo.

Strategie di Mitigazione

  • Integrazione con Secrets Manager: Utilizzare servizi come AWS Secrets Manager.
  • Variabili Crittografate: Sfruttare le funzionalità di crittografia del Serverless Framework per dati sensibili.
  • Controlli di Accesso: Limitare l'accesso ai segreti in base ai ruoli.

Codice e Dipendenze Vulnerabili

Dipendenze obsolete o insicure possono introdurre vulnerabilità, mentre una gestione inadeguata degli input può portare ad attacchi di iniezione di codice.

Strategie di Mitigazione

  • Gestione delle Dipendenze: Aggiornare regolarmente le dipendenze e scansionare per vulnerabilità.
yaml
plugins:
- serverless-webpack
- serverless-plugin-snyk
  • Validazione degli Input: Implementare una validazione e sanificazione rigorose di tutti gli input.
  • Revisioni del Codice: Condurre revisioni approfondite per identificare difetti di sicurezza.
  • Analisi Statica: Utilizzare strumenti per rilevare vulnerabilità nel codice sorgente.

Logging e Monitoraggio Inadeguati

Senza un logging e un monitoraggio adeguati, le attività malevole possono rimanere non rilevate, ritardando la risposta agli incidenti.

Strategie di Mitigazione

  • Logging Centralizzato: Aggregare i log utilizzando servizi come AWS CloudWatch o Datadog.
yaml
plugins:
- serverless-plugin-datadog
  • Abilitare il Logging Dettagliato: Catturare informazioni essenziali senza esporre dati sensibili.
  • Impostare Avvisi: Configurare avvisi per attività sospette o anomalie.
  • Monitoraggio Regolare: Monitorare continuamente log e metriche per potenziali incidenti di sicurezza.

Configurazioni Insecure dell'API Gateway

API aperte o non adeguatamente protette possono essere sfruttate per accessi non autorizzati, attacchi Denial of Service (DoS) o attacchi cross-site.

Strategie di Mitigazione

  • Autenticazione e Autorizzazione: Implementare meccanismi robusti come OAuth, chiavi API o JWT.
yaml
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
authorizer: aws_iam
  • Limitazione della Frequenza e Throttling: Prevenire abusi limitando le frequenze delle richieste.
yaml
provider:
apiGateway:
throttle:
burstLimit: 200
rateLimit: 100
  • Configurazione CORS Sicura: Limitare origini, metodi e intestazioni consentiti.
yaml
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
cors:
origin: https://yourdomain.com
headers:
- Content-Type
  • Utilizzare Firewall per Applicazioni Web (WAF): Filtrare e monitorare le richieste HTTP per modelli malevoli.

Isolamento delle Funzioni Insufficiente

Risorse condivise e isolamento inadeguato possono portare a escalation di privilegi o interazioni indesiderate tra funzioni.

Strategie di Mitigazione

  • Isolare le Funzioni: Assegnare risorse e ruoli IAM distinti per garantire un'operazione indipendente.
  • Partizionamento delle Risorse: Utilizzare database o bucket di archiviazione separati per diverse funzioni.
  • Utilizzare VPC: Distribuire funzioni all'interno di Cloud Privati Virtuali per un miglior isolamento della rete.
yaml
provider:
vpc:
securityGroupIds:
- sg-xxxxxxxx
subnetIds:
- subnet-xxxxxx
  • Limitare i Permessi delle Funzioni: Assicurarsi che le funzioni non possano accedere o interferire con le risorse delle altre a meno che non sia esplicitamente richiesto.

Protezione dei Dati Inadeguata

Dati non crittografati a riposo o in transito possono essere esposti, portando a violazioni dei dati o manomissioni.

Strategie di Mitigazione

  • Crittografare i Dati a Riposo: Utilizzare le funzionalità di crittografia dei servizi cloud.
yaml
resources:
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
SSESpecification:
SSEEnabled: true
  • Crittografare i Dati in Transito: Utilizzare HTTPS/TLS per tutte le trasmissioni di dati.
  • Comunicazione API Sicura: Forzare protocolli di crittografia e convalidare certificati.
  • Gestire le Chiavi di Crittografia in Modo Sicuro: Utilizzare servizi di gestione delle chiavi e ruotare le chiavi regolarmente.

Mancanza di Gestione degli Errori Adeguata

Messaggi di errore dettagliati possono rivelare informazioni sensibili sull'infrastruttura o sul codice, mentre eccezioni non gestite possono portare a crash dell'applicazione.

Strategie di Mitigazione

  • Messaggi di Errore Generici: Evitare di esporre dettagli interni nelle risposte di errore.
javascript
javascriptCopy code// Esempio in Node.js
exports.hello = async (event) => {
try {
// Logica della funzione
} catch (error) {
console.error(error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'Errore Interno del Server' }),
};
}
};
  • Gestione Centralizzata degli Errori: Gestire e sanificare gli errori in modo coerente in tutte le funzioni.
  • Monitorare e Registrare gli Errori: Tracciare e analizzare gli errori internamente senza esporre dettagli agli utenti finali.

Pratiche di Deployment Insicure

Configurazioni di deployment esposte o accesso non autorizzato a pipeline CI/CD possono portare a deployment di codice malevolo o misconfigurazioni.

Strategie di Mitigazione

  • Pipeline CI/CD Sicure: Implementare controlli di accesso rigorosi, autenticazione a più fattori (MFA) e audit regolari.
  • Memorizzare le Configurazioni in Modo Sicuro: Mantenere i file di deployment privi di segreti codificati e dati sensibili.
  • Utilizzare Strumenti di Sicurezza per l'Infrastructure as Code (IaC): Impiegare strumenti come Checkov o Terraform Sentinel per far rispettare le politiche di sicurezza.
  • Deployment Immutabili: Prevenire modifiche non autorizzate dopo il deployment adottando pratiche di infrastruttura immutabile.

Vulnerabilità nei Plugin e nelle Estensioni

Utilizzare plugin di terze parti non verificati o malevoli può introdurre vulnerabilità nelle tue applicazioni serverless.

Strategie di Mitigazione

  • Verificare i Plugin a Fondo: Valutare la sicurezza dei plugin prima dell'integrazione, privilegiando quelli provenienti da fonti affidabili.
  • Limitare l'Uso dei Plugin: Utilizzare solo i plugin necessari per ridurre la superficie di attacco.
  • Monitorare gli Aggiornamenti dei Plugin: Mantenere i plugin aggiornati per beneficiare delle patch di sicurezza.
  • Isolare gli Ambienti dei Plugin: Eseguire i plugin in ambienti isolati per contenere potenziali compromissioni.

Esposizione di Endpoint Sensibili

Funzioni pubblicamente accessibili o API senza restrizioni possono essere sfruttate per operazioni non autorizzate.

Strategie di Mitigazione

  • Limitare l'Accesso alle Funzioni: Utilizzare VPC, gruppi di sicurezza e regole del firewall per limitare l'accesso a fonti fidate.
  • Implementare Autenticazione Robusta: Assicurarsi che tutti gli endpoint esposti richiedano una corretta autenticazione e autorizzazione.
  • Utilizzare Sicuramente gli API Gateway: Configurare gli API Gateway per far rispettare le politiche di sicurezza, inclusa la validazione degli input e la limitazione della frequenza.
  • Disabilitare Endpoint Non Utilizzati: Rivedere regolarmente e disabilitare eventuali endpoint che non sono più in uso.

Permessi Eccessivi per Membri del Team e Collaboratori Esterni

Concedere permessi eccessivi a membri del team e collaboratori esterni può portare ad accessi non autorizzati, violazioni dei dati e uso improprio delle risorse. Questo rischio è aumentato in ambienti in cui più individui hanno livelli di accesso variabili, aumentando la superficie di attacco e il potenziale per minacce interne.

Strategie di Mitigazione

  • Principio del Minimo Privilegio: Assicurarsi che i membri del team e i collaboratori abbiano solo i permessi necessari per svolgere i propri compiti.

Sicurezza delle Chiavi di Accesso e delle Chiavi di Licenza

Chiavi di Accesso e Chiavi di Licenza sono credenziali critiche utilizzate per autenticare e autorizzare interazioni con il Serverless Framework CLI.

  • Chiavi di Licenza: Sono identificatori unici richiesti per autenticare l'accesso alla versione 4 del Serverless Framework che consente di effettuare il login tramite CLI.
  • Chiavi di Accesso: Credenziali che consentono al Serverless Framework CLI di autenticarsi con il Dashboard del Serverless Framework. Quando si effettua il login con serverless cli, una chiave di accesso sarà generata e memorizzata nel laptop. Puoi anche impostarla come variabile di ambiente chiamata SERVERLESS_ACCESS_KEY.

Rischi per la Sicurezza

  1. Esposizione Tramite Repository di Codice:
  • La codifica fissa o il commit accidentale di Chiavi di Accesso e Chiavi di Licenza nei sistemi di controllo versione possono portare ad accessi non autorizzati.
  1. Memorizzazione Insicura:
  • Memorizzare le chiavi in testo chiaro all'interno di variabili di ambiente o file di configurazione senza una crittografia adeguata aumenta la probabilità di fuga.
  1. Distribuzione Impropria:
  • Condividere le chiavi tramite canali non sicuri (ad es., email, chat) può comportare l'intercettazione da parte di attori malevoli.
  1. Mancanza di Rotazione:
  • Non ruotare regolarmente le chiavi estende il periodo di esposizione se le chiavi vengono compromesse.
  1. Permessi Eccessivi:
  • Chiavi con permessi ampi possono essere sfruttate per eseguire azioni non autorizzate su più risorse.

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks