Merhaba
Hem geliştiriciler, hemde penetration tester’lar için PHP Object Injection zafiyetleri gözden kaçan kritik güvenlik açıklarından bir tanesidir. Zafiyet oluşması ve oluşan zafiyetin exploit edilmesi birden fazla aşamadan oluşmaktadır. Bunun için öncelikle PHP Object yapısı hakkında ve Objeleri ifade edebildiğimiz serialize() methodu hakkında bilgi sahibi olunmalıdır.
Serialize ve Unserialize Fonksiyonları
Serialize ve Unserialize metodları, verileri saklamak için kullanılır. Gerçek hayat örneklerinden edindiğim tecrübelere göre, serialize ve unserialize metodunun kullanıldığı alanlar aşağıda listelenmiştir.
- Oturum gerçekleştiren kullanıcıya ait verilerin bulunduğu array serialize edilerek kullanıcının session’ınında saklanabilir.
- Yapı gereği cache’lenmesi gereken bir sınıf serialize edilerek bir dosyaya yazılabilir.
- Yazılımın teması veya bellirli değişkenleri serialize edilerek, tekrar kullanılmak üzere kullanıcının Cookie’sinde saklanabilir.
Serialize ve Unserialize fonksiyonlarının teknik olarak çalışma mantıkları ele alınmıştır.
<?php /** * Mehmet INCE */ $arr = array('ali', 'ismail', 'korkmaz'); $serialize = serialize($arr); echo $serialize;
PHP kodu çalıştırıldığında çıktısı aşağıdaki gibi olacaktır.
a:3:{i:0;s:3:"ali";i:1;s:6:"ismail";i:2;s:7:"korkmaz";}
Serialize fonksiyonu ile array tipindeki bir değişken, string olarak ifade edilmiştir. Çıktı dikkatlice analiz edildiğinde array elemanlarının değerleri string olarak ifade edilmektedir. Aynı şekilde string ile ifade edilen bir array ise unserialize() fonksiyonu ile tekrardan array’e dönüştürülebilmektedir.
<?php /** * Mehmet INCE */ $str = 'a:3:{i:0;s:3:"ali";i:1;s:6:"ismail";i:2;s:7:"korkmaz";}'; $unserialize = unserialize($str); var_dump($unserialize);
Unserialize ile tekrardan oluşturulmuş array kodunun çıktısı ise aşağıdaki gibi olacaktır.
array(3) { [0]=> string(3) "ali" [1]=> string(6) "ismail" [2]=> string(7) "korkmaz" }
Class’ların Serialize ve Unserialize İşlemi
Serialize ve unserialize işlemi sadece array’ler üzerinde değil class’lar üzerinde de gerçekleştirilebilir. Burada ki en önemli nokta ise class’ların __destruct metodlarıdır. Bir class unserialize edilirken __destruct metodu çağırılır. Eğer class’ın __destruct metodu sınıf değişkenlerini bir dosyaya yazıyor ise ciddi bir güvenlik açığı oluşur. Çünkü array örneğinde gösterildiği gibi serialize edimiş class’ın değişkenlerinin değerleri değiştirilebilmektedir.
Zafiyetin oluşması ve başarılı bir şekilde exploit edilmesi için aşağıda senaryo gerçekleşmelidir.
- Kullanıcıdan alınan input’un unserialize metoduna gönderilmesi.
- Yazılım genelinde herhangi bir sınıfın __destruct metodunun bulunması.
- __destruct metodunun, ait olduğu class’ın sınıf değişkenlerini herhangi bir nedenle yerel diske’e kayıt ediyor olması.
- Bu kayıt edilenen dosyanın bulunduğu dizinin web üzerinden erişilebilir olması.
Örnek
Aşağıda basit bir php class’ı tanımlandığı user.class.php dosyası bulunmaktadır.
<?php /** * Mehmet INCE */ class User { private $username = "Mehmet"; function __destruct(){ echo $this->username."\n"; } };
Bu class’i yapısına dahil edip kullanan user.php kodu ise aşağıdaki gibidir.
<?php /** * Mehmet INCE */ require_once("user.class.php"); $unserialize = $_GET["unserialize"]; $serialize = unserialize($unserialize); var_dump($serialize);
Kullanıcı girdisini GET talebi üzerinden unserialize değişkeniyle alan user.php dosyasında Object Injection zafiyeti olduğu tespit edilir. Bu zafiyetin exploit edilmesi için yazılım genelinde __destruct methodu olan sınıfların listelenmesi gerekmektedir. Her PHP dosyasının tek tek elle kontrol edilmesi imkansızdır. Bu nedenle aşağıdaki bash script çalıştırılabilir.
find /yazilim -type f|grep '.php$'| xargs grep 'function __destruct('
Bu komutun çıktısı, function __destruct satırlarının geçtiği dosyaları listeleyecektir. Bu örnekte çıktıda user.class.php dosyası bulunmaktadır. __destruct metodu analiz edildiğinde, sınıf değişkeni olan username‘i encode etmeden ekrana yazdırmaktadır. Potansiyel bir XSS zafiyeti olduğu görülmektedir. Bu XSS zafiyetinin exploit edilebilmesi için User sınıfının değişkeni olan username’e zararlı javascript/html kodları yerleştirilmelidir.
Saldırgan User sınıfını serialize eder ve User sınıfını ifade eden string’i elde eder. Bu stringi oluşturmak için aşağıdaki php kodunu içeren saldirgan.php ‘yi yazacaktır.
<?php /** * Mehmet INCE */ class User { private $username = "<script>alert(1)</script>"; } echo serialize(new User);
Komutun çıktısı serialize edilmiş user class’ını ifade etmektedir.
O:4:"User":1:{s:8:"username";s:25:"<script>alert(1)</script>";}
Saldırgan user.php dosyasını internet tarayıcısından çağırdığında Reflected XSS zafiyetini exploit etmiş olacaktır. Çağırlan URL aşağıdadır.
localhost/user.php?parametre=O:4:"User":1:{s:8:"username";s:25:"<script>alert(1)</script>";}
Sonuç:
ÖZET:
Örnek olarak yapılan saldırının adım adım analizi aşağıdadır.
- Saldırgan kaynak kod analizinde user.php dosyasında kullanıcıdan alınan girdinin kontrol edilmeden unserialize metoduna aktarıldığını fark etmiştir.
- Saldırgan unserialize fonksiyonu ile oluşturulan PHP sınıfılarının __destruct ve __wakeup fonksiyonlarının çağırıldığını bilmektedir.
- Yazılım genelinde tarama yaparak __destruct metodu bulunan sınıfları bash script yazarak tarar.
- user.class.php sınıfının __destruct metodu sınıf değişkeni olan $username i ekrana encode etmeden yazdırmaktadır.
- User classının username değişkenine XSS payloadı olan <script>alert()</script> kodu unserialize metodu ile yerleştirilecektir. Unserialize ile çağırılan sınıfın __destruct metodu çağırıldığı için ekrana javascript kodu bulunan değişken yazdırılacaktır ve Reflected XSS zafiyeti exploit edilecektir.
- Bunun için içeriğine payload yazılmış ve User classı saldirgan.php olarak kayıt edilir ve serialize edilmiş string alınır.
- Bu string user.php sıfına GET talebi üzerinden gönderilir.
- Bu talepte ki parametreyi alıp unserialize metoduna gönderen user.php Javascript kodlarını print edecek malformed Class’ı oluşturur.
- Classın __destruct metodu kullanıcı tarayıcısına XSS kodunu yazar.
- The end.
Güvenlik Önlemi
- Input Validation
- serialize yerine json_encode kullanılması.