SQLi (SQL Injection) Açıkları ve Türleri

SQL Injection (SQLi), kötüniyetli SQL ifadelerini çalıştırmayı mümkün kılan bir enjeksiyon saldırısı türüdür. Bu ifadeler, bir web uygulamasının arkasındaki veritabanı sunucusunu suistimal eder. Saldırganlar, uygulama güvenlik önlemlerini atlatmak için SQLi güvenlik açıklarını kullanabilir. Bir web sayfasının veya web uygulamasının kimlik doğrulamasını ve yetkilendirmesini atlatabilir, veritabanını kopyalayabilir, kayıt ekleyebilir, kayıtları değiştirebilir ve veritabanını bulunduran sunucunun işletim sistemi üzerinde komut çalıştırarak sunucu üzerindeki diğer verilere erişim sağlayabilir ve sunucuyu ele geçirebilir. Ele geçirilen sunucuyu güvenlik duvarının arkasındaki ağa da erişim sağlamak için kullanabilir.

OWASP organizasyonu (Open Web Application Security Project), SQL enjeksiyonlarını OWASP Top 10 belgesinde web uygulaması güvenliğine yönelik bir numaralı tehdit olarak listelemiştir.

Bir SQL Injection saldırısı yapmak için, bir saldırganın önce web sayfası veya web uygulaması içinde savunmasız kullanıcı girdilerini bulması gerekir. Bu girdiler yaygınlıkla GET/POST metoduyla ulaşan veriler olabileceği gibi HTTP header’ı ile taşınan, cookie ve referer gibi diğer veriler de olabilir.

Basit bir SQL Enjeksiyon Örneği

Bu ilk örnek çok basit. Bir saldırganın, uygulama güvenliğini aşmak ve yönetici olarak kimlik doğrulaması yapmak için bir SQL Injection güvenlik açığını nasıl kullanabileceğini gösterelim.

Aşağıdaki komut dosyası, bir web sunucusunda yürütülen sahte (pusedo) koddur. Bir kullanıcı adı ve parola ile kimlik doğrulamanın basit bir örneğidir. Örnek veritabanı, username ve password sütunlarına sahip olan users adlı bir tabloya sahiptir:

Bu giriş alanları SQL Injection’a karşı savunmasızdır. Saldırgan, girdide SQL komutlarını, veritabanı sunucusu tarafından yürütülen SQL ifadesini değiştirecek şekilde kullanabilir. Örneğin, tek tırnak içeren ve bir girdi kullanabilir ve passwd alanını şu şekilde ayarlayabilir:

Sonuç olarak veritabanı sunucusu aşağıdaki SQL sorgusunu çalıştırır.

OR 1=1 ifadesi nedeniyle, WHERE yan tümcesi, kullanıcı adı ve parola ne olursa olsun, users tablosundaki ilk kimliği döndürür. Bir veritabanındaki ilk kullanıcı kimliği genellikle yöneticidir. Bu şekilde, saldırgan yalnızca kimlik doğrulamasını atlamakla kalmaz, aynı zamanda yönetici ayrıcalıkları da kazanır. SQL sorgusunun yürütülmesini daha fazla kontrol etmek için SQL ifadesinin geri kalanını önemsiz hale getiren yorum karakterlerini kullanabilir. Farklı veritabanı türleri için kullanılan comment ifadeleri aşağıdaki gibidir.

Union-Based SQL Injection Örneği

En yaygın SQL Injection türlerinden biri UNION operatörünü kullanır. Saldırganın iki veya daha fazla SELECT ifadesinin sonuçlarını tek bir sonuç halinde birleştirmesini sağlar. Tekniğe birleşim tabanlı SQL Enjeksiyon denir.

Aşağıda bu tekniğe bir örnek verilmiştir. Acunetix tarafından barındırılan kasıtlı olarak savunmasız bir web sitesi olan testphp.vulnweb.com web sayfasını kullanır.

Aşağıdaki HTTP isteği, meşru bir kullanıcının göndereceği normal bir istektir:

artist parametresi SQL Injection’a karşı savunmasızdır. Aşağıdaki payload, var olmayan bir kaydı aramak için sorguyu değiştirir. URL sorgu dizesindeki değeri -1 olarak ayarlar. Tabii ki, veritabanında bulunmayan başka herhangi bir değer olabilir. Ancak, bir veritabanındaki tanımlayıcı nadiren negatif bir sayı olduğundan, negatif bir değer kullanmak iyi bir fikirdir.

SQL Injection’da, UNION operatörü, web uygulaması tarafından çalıştırılması amaçlanan orijinal sorguya kötü amaçlı bir SQL sorgusu eklemek için yaygın olarak kullanılır. Enjekte edilen sorgunun sonucu, orijinal sorgunun sonucu ile birleştirilir. Bu, saldırganın diğer tablolardan sütun değerleri almasına izin verir.

Aşağıdaki örnek, kasıtlı olarak savunmasız olan bu siteden daha anlamlı veriler elde etmek için bir SQL Injection payload’unun nasıl kullanılabileceğini gösterir:

Blind SQL Injection Örneği

SQL Injection güvenlik açığına sahip bir web sayfası veya web uygulaması, bu tür kullanıcı girişlerini doğrudan bir SQL sorgusunda kullanır. Saldırgan zararlı bir girdi içeriği oluşturabilir. Bu tür içeriklere genellikle kötü niyetli yük (payload) denir ve saldırının önemli bir parçasıdır. Saldırgan bu içeriği gönderdikten sonra, veritabanında kötü niyetli SQL komutları yürütülür ve bir çıktı döner. Zafiyetin türüne göre herhangi bir çıktı da dönmeyebilir (blind SQL injection). Bu tür durumlarda bir SQL işevi olan sleep() ve if (şartlı kontrol) ifadeleri kullanılarak testi yapılan uygulamanın tepki süresine bağlı çıkarımsal denemeler yapılabilir. Örneğin enjekte edilen bir SQL ile tablo adının ilk harfinin ‘a’ olup olmaması sleep() ile bekletme yapılarak tespit edilebilir. Eğer tablonun adının ilk adı ‘a’ ise 5sn bekle değilse hiç bekleme şeklinde. Tüm olası harfler bir blind SQL açığında bu şekilde denenerek bulunabilir. Aynı yöntem tablo içeriklerini yada diğer veritabanı içeriklerini elde etmek için de kullanılır. İşlemi hızlandırmak için toplam işin paralel çalışan birçok iş parçacığına bölünerek (threads) daha kısa sürede tamamlanması sağlanır. Süreye bağlı çıkarımların yapıldığıbu enjeksiyon türü blind sql injection‘ın özel bir türü olan time-based blind SQL injection olarak adlandırılır.

Yukarıdaki örneklerde uygulanan enjeksiyonun sonucu sayfada çıktı olarak yer alıyordu. Blind yani kör SQL enjeksiyonu adını web uygulamasının uygulanan enjeksiyona karşılık herhangi işe yarar bir çıktı döndürmemesinden alıyor. Bu enjeksiyonun iki alt türü var.

  • Time-Based SQLi: Zaman tabanlı SQL enjeksiyonu
  • Error-Based SQLi : Hata tabanlı SQL enjeksiyonu

Zaman tabanlı SQL enjeksiyonu (time based blind SQLi) örneği

İlk olarak time-based SQLi’dan bahsedelim. Bu SQL enjeksiyonu, veritabanının belirli bir süre duraklatılmasına ve ardından başarılı SQL sorgusu yürütüldüğünü gösteren sonuçları döndürmesine dayanır. Bu yöntemi kullanan bir saldırgan veriyi karakter karakter çekerek oluşturur. Zaman alan bir SQL enjeksiyon tekniğidir ancak sqlmap gibi araçların desteklediği paralel çalışma özellikleriyle (threads) makul şekilde hızlandırılabilir. Bu tekniğin nasıl uygulandığını MySQL veritabanı üzerinden örneklendirmeden önce kullanılacak bazı MySQL işlevlerine göz atalım. Bu işlevler MySQL veritabanı sunucusu üzerinde çalışan ve SQL cümlesi içerisinde kullanılabilen işlevlerdir.

version() : MySQL’in sürüm numarasını döndürür. select version(); cümlesini çalıştırarak veritabanı sürümünü görebilirsiniz. Örn. 5.5.4 gibi bir değer döndürür.

database() : Etkin veritabanının adını göndürür. select database(); cümlesini çalıştırarak veritabanı adını görebilirsiniz.

ascii() : Argüman olarak verilen karakterin ASCII numarasını döndürür. Örneğin select ascii(‘A’); sorgusunu çalıştırırsanız 65 değeri döner. Çünkü büyük a harfinin ASCII kodu 65’dir. ASCII tabloda toplam 256 karakter bulunur.

substring() : Bir string’den (karakter katarı) istenilen bir pozisyondan itibaren istenilen uzunlukta bir alt string’i almak için kullanılır.

Yukarıdaki işlevlerin iç içe kullanıldığı aşağıdaki örnekleri inceleyelim.

Örneğin çalıştığınız MySQL sunucusunun sürüm numarası ‘5.5.4’ün ilk karakteri 5’in ASCII kodunu olan 53 sayısını döndürür.

Yukarıdaki SQL’i çalıştırdığınız veritabanının adının ‘siparisler’ olduğunu varsayarsak SQL cümlesi bize veritabanı adının ilk harfi olan s’nin ASCII kodunu yani 73 sayısını döndürür.

Temel olarak bilgilenmemiz gereken MySQL işlevleri bukadar. Şimdi MySQL’in çalışma sistemi hakkında bilgilenelim. MySQL sunucusu bulundurduğu veritabanı, tablo, kullanıcılar vb. herşeyi yine MySQL formatından bir veritabanında saklar. Bu veritabanının adı INFORMATION_SCHEMA’dır. Sunucuda saklanan veritabanları,tablolar,sütunlar,veritabanı kullanıcıları gibi herşey bu veritabanında saklanır. Aşağıdaki SQL cümlesi bu veritabanını sorgulayarak 2. sıradaki (limit 1,1) veritabanının adını getirir. Limit başlangıç değeri ile oynayarak sırasıyla tüm veritabanlarının adlarını listeletebilirsiniz. Benzer şekilde tabloları, kullanıcıları vb.

Bu SQL cümlesi INFORMATION_SCHEMA veritabanındaki tables adlı tablonun table_schema sütunun getirip, tekrarlı değerleri elemiştir (distinct). Son olarak gelen listenin 2. sırasındaki satırı seçmiştir (limit 1,1). Dönen sonuç aşağıdaki gibidir. Yani sunucuda kullanicilar adında bir veritabanı mevcuttur.

Örneği biraz daha geliştirelim ve yukarıda yer verdiğimiz substring işlevini dahil edelim.

Bu kadar temel bilgi yeterli. Yavaş yavaş bir time based blind SQL injection açığının nasıl suistimal edilebileceğine bakalım. Yapmaya çalışacağımız şey sunucudaki veritabanlarının bir listesini almak. Önce ‘kullanıcılar’ veritabanının ismini tespit etmeye çalışacağız. Bunun için önce k harfini ardından u harfini bulacağız ve böyle devam edecek (bunu sunucudaki herhangi bir veritabanı veya tabloya için düşünebilirsiniz, tüm meta bilgileri INFORMATION_SCHEMA veritabanında tutuluyor). Blind türdeki enjeksiyonların herhangi bir şekilde kullanıcı tarafına çıktı vermediğini söylemiştim. Yapacağımız şey şu; alfabedeki tüm harfleri denemek üzere öncelikle veritabanının ilk harfinin ‘a’ olup olmadığını kontrol edeceğiz. Eğer ‘a’ ise SQL’in çalışma süresini geciktiren sleep() işlevini kullanarak belli bir süre sayfanın dönüş süresini geciktireceğiz. Örneğin 10sn. Tabiki bu değer sayfanın ortalama çıktılanma süresine göre optimize edilebilir. Eğer sayfa 10sn sonra cevap verirse bileceğiz ki veritabanı adının ilk harfi ‘a’. Değilse hiç beklemeden ikinci harf olan ‘b’ yi kontrol edeceğiz. Kullanacağımız SQL genel olarak şöyle.

Yukarıdaki SQL’in çalışması 10sn kadar sürmüştür. Eğer veritabanı adının ilk harfi ‘k’ olmasaydı hiç beklemeden tamamlanacaktı. Bu SQL’de ek olarak if komutunu kullandık. if komutunun kullanımı oldukça basittir, ilk parametre kontrol edilecek ifadeyi, ikinci parametre kontrol sonucunun doğru olması durumunda yapılacak işi ve son parametre ise kontrol sonucunun yanlış olması durumunda yapılacak işi tanımlar. Şart doğru olduğunda 10sn’lik bir bekletme yaratmak için sleep(10) komutunu kullandık, aksi takdirde (yani şart yanlış olduğunda) geriye 0 değeri döndürmesini söyledik. MySQL’de sleep komutu yerine gecikme yaratmak için kullanılabilecek birçok işlev vardır. Eğer sleep işlevi devre dışı ise diğer işlevler denenebilir. Bu şekilde veritabanında tutulan tüm tabloların adlarını, sütun adlarını öğrenebiliriz. Ardından bir tabloyu hedef alarak istediğimiz kaydı okuyabiliriz.

Son yazıdığımız SQL cümlesi tam olarak istediğimiz şeyi yaptı fakat yeterli değil. Yukarıdaki örnekte if’in şartı olarak ‘k’ karakterini kontrol etmiştik. Bir karakter karşılaştırması yapabilmek için ifadeyi tırnak işaretleri içinde kullanmak zorundayız lakin bu WAF (Web Application Firewall)’ların sevdiği birşey olmadığı gibi web uygulaması tarafından tırnaklar temizleniyor olabilir. Bu durumda SQL enjeksiyonumuz çalışmayacaktır. ‘k’ ifadesinin yerine k’nın ASCII kodu olan 107’yi kullanabiliriz. Bunun için k harfini döndüren kısmı ascii() işlevi içine yerleştireceğiz ve if şartı olarak 107 ile karşılaştıracağız. Son SQL ifademiz şu şekilde:

Evet bu işlemler gerçekten karışık ve belli bir veriyi elde etmek uzun sürebilir. Burada sadece time-based blind SQL’in tam olarak nasıl çalıştığını anlatmaya çalıştım. Tabiki burada yer verdiğim sqlmap aracını kullanmak çok daha iyi bir fikirdir. sqlmap blind-sqli açıklarını başarıyla ve hızlı test edebilen bir araç.

Son olarak bu SQL’i nasıl enjekte edebileceğimize bakalım. Bunun da birçok farklı yolu var fakat anlaşılır olması açısından konunun başında verdiğim örnek üzerinden gidecek olursak payload’umuz tam olarak şunun gibi birşey olacak:

Denemeler Yapmak Zafiyetli Web Uygulamaları

Damn Vulnerable Web Application (DVWA)
https://dvwa.co.uk/

SQLInjection deneme tahtası uygulaması
http://testphp.vulnweb.com/

Referanslar:

PHP’de SQL Enjeksiyonlarını Önleme
https://www.tankado.com/phpde-sql-enjeksiyonlari-nasil-onlenir-pdo/

Web Uygulamalarının Derinlemesini Savunulması
https://www.acunetix.com/websitesecurity/defence-in-depth-and-how-it-applies-to-web-applications/

PHP Top 5 Zafiyetleri
https://wiki.owasp.org/index.php/PHP_Top_5
OWASP SQL enjeksiyon zafiyetleri
https://owasp.org/www-community/attacks/SQL_Injection

PHP dilinde SQLi zafiyetleri nasıl önlenir:
https://www.acunetix.com/blog/articles/prevent-sql-injection-vulnerabilities-in-php-applications/

Hangi programlama dilinde/framework’de netür tekniklerle SQLi’a karşı önlem alınır.
https://bobby-tables.com/

Acunetix SQL enjeksiyon zafiyetleri
https://www.acunetix.com/websitesecurity/sql-injection/

WebSec SQL Injection
https://www.websec.ca/kb/sql_injection

PortSwigger SQL Injection Dökümanları:
https://portswigger.net/web-security/sql-injection/blind

Enjeksiyona maruz kaldıktan sonra yapılacaklar:
https://www.securitynik.com/2020/07/continuing-sql-injection-with-sqlmap_7.htm

Yazar: Özgür Koca

Yazar - Tankado.com

“SQLi (SQL Injection) Açıkları ve Türleri” için bir yorum

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.