18 Ocak 2015

Web Uygulama Denetimi - Bölüm-9: Veritabanı Tespiti (Database Fingerprinting)

Veritabanlarının metin birleştirme ve aritmetik işlem özelliklerinden faydalanarak karşımızda hangi veritabanı olduğunu anlamamız mümkündür.

Metin birleştirme özelliklerinden faydalanarak veritabanı tespiti için aşağıdaki yöntemleri kullanabiliriz (ilgili veritabanı belirtilen teknikle birleştirilen (concatenate) metni anlayacak ve bu metin bitişik yazıldığında normalde ürettiği sonucu üretecektir). ‘services’ kelimesini aşağıdaki veritabanlarında şu şekillerde birleştirerek oluşturabiliriz:

  • Oracle: ‘serv’||’ices’
  • MS-SQL: ‘serv’+’ices’
  • MySQL: ‘serv’ ‘ices’   (boşluk karakterine dikkat ediniz)

Sayısal bir alanda aritmetik işlemler yaparak veritabanı tespit etmek istediğimizde aşağıdaki yöntemleri kullanabiliriz. Aşağıdaki işlemlerin ilgili veritabanlarında 0 ile sonuçlanması gerekmektedir:

  • Oracle: BITAND(1,1)- BITAND(1,1)
  • MS-SQL: @@PACK_RECEIVED-@@PACK_RECEIVED
  • MySQL: CONNECTIONID()-CONNECTIONID()

Not: MS-SQL ve Sybase’in tarihsel ilişkisi nedeniyle (bu iki veritabanı daha sonra Microsoft ve Sybase’in aynı kod tabanını kullanarak ayrı geliştirme çalışmalarına dönmeleri sonucu üretilmiştir) MS-SQL için çalışan tekniklerin büyük bölümü Sybase veritabanında da çalışmaktadır.

MySQL veritabanı versiyon tahmini için kullanılabilecek çok ince başka bir yöntem daha bulunmaktadır. Bu yöntem veritabanının şu özelliğini kullanır: Eğer bir MySQL inline (satır içinde başlayan ve biten, kendisinden sonra gelen kodlar yorumlanan) bir yorum (comment) “!” karakteriyle başlarsa ve mevcut veritabanı versiyonuna eşit veya daha düşük bir versiyon metni ile devam ederse bu bilgiden sonraki yorum içeriği kod olarak yorumlanır. Bu şekilde bir anlamda C dilindeki pre-processor özelliği kazanılmıştır. Bu özellik kullanılarak bir sorgunun içine bu şekilde bir inline yorum eklenirse ve bu yorum içindeki kod çalışırsa, veritabanı versiyonunun yorum içindeki versiyon numarasına eşit veya daha yüksek olduğunu anlayabiliriz. Bir örnek vermek gerekirse, aşağıdaki metnin eklendiği bir WHERE yan cümlesi olumsuz dönecek ve SELECT cümlesi sonuç üretmeyecektir:

/*!32302 and 1=0*/

İşe Yarar Verinin Elde Edilmesi


Normalde veritabanlarından işimize yarayacak bilgileri elde etmek için yukarıda da belirtildiği gibi tablo ve kolon isimlerini biliyor olmamız gerekir. Ancak kurumsal seviyede fonksiyonaliteye sahip veritabanları çok miktarda veritabanı metadata’sı barındırır. Bu metadata’dan faydalanarak (tabi söz konusu veritabanı modeline uygun olarak) tablo ve kolon isimlerini, hatta veritabanı kullanıcı bilgilerini elde etmek mümkündür.

Aşağıda Oracle ve MS-SQL için bu bilgilerin nasıl kullanılabileceğine dair örnekler bulabilirsiniz:

Oracle

Bilgi: Kullanıcılara ait nesneler
Tablo adı: “user_objects” (tüm kullanıcılara ait nesnelerin tutulduğu tablonun adı all_user_objects’dir)
Önemli kolonlar: “object_name”, “object_type”

Bilgi: Tablo kolon bilgileri
Tablo adı: “user_tab_columns”
Önemli kolonlar: “table_name”, “column_name”

Sorguların yanıtlarının UNION operatörü ile başka bir listeye eklenerek görüntülenebildiği durumlarda örnek bir saldırı olarak “user_objects” tablosundaki “object_name” ve “object_type” bilgileri çekildikten (veya sadece object_type=’table’ kayıtlarının isimleri çekildikten) sonra hedeflenen tabloların kolon isimleri “user_tab_columns” tablosundan elde edilebilir. Daha sonra hedeflenen tablodan bilgiler edinilebilir.

MS-SQL

Bilgi: Veritabanında bulunan tüm sistem nesneleri
Tablo adı: “sysobjects”
Önemli kolonlar: “xtype” (xtype=’U’ kriteri ile sorgulama yapıldığında kullanıcı nesnelerinin isimleri elde edilebilir), “id” (nesnenin numarası, bu bilgi syscolumns tablosu ile sysobjects tablosundaki kayıtları ilişkilendirmek için kullanılır)

Bilgi: Tabloda bulunan kolon isimleri
Tablo adı: “syscolumns”
Önemli kolonlar: “name”

ODBC Hata Mesajlarından Faydalanma (sadece MS-SQL için)


Eğer saldırılan veritabanı MS-SQL ise ve uygulama veritabanı hatalarını görüntülüyorsa istenen bilgileri elde etmenin diğer alternatiflerinden biri de ODBC hata mesajlarını kullanmaktır. Bu şekilde bilgi edinmek için aşağıdaki adımlar izlenebilir:

  • Enjeksiyon yapılabilen kolon adlarından birine ‘ having 1=1--  girilir. MS-SQL bu yan cümlenin olduğu sorgu için ilk kolon isminden başlayarak orjinal SELECT cümlesinin kolon isimlerini göstermeye başlar.
  • Sorgu şu şekilde düzenlenerek tüm kolon isimleri öğrenilebilir, örneğin ilk sorguya verilen hata mesajında ilk kolonun adının “users.ID” olduğunu varsayalım: ‘ group by users.ID having 1=1- -
  • Bu şekilde tüm kolon adları öğrenildikten sonra sum() fonksiyonu kullanılarak kolonların veri tipleri öğrenilmeye çalışılır. Örneğin: ‘ union select sum(username) from users- - cümlesi enjekte edildiğinde “username” kolonu numerik değilse MS-SQL “varchar” tipi bir verinin “aggregate” operasyonlarda kullanılamayacağı hatasını verir. ‘ union select sum(ID) from users - - enjeksiyonu yapıldığında ID kolonunun numerik olması durumunda MS-SQL UNION operatöründen sonra gelen sorgudaki kolon sayısının birinci SELECT cümlesinden farklı olduğundan şikayet etmeye başlayacaktır.

Yukarıdaki bilgiler elbette sadece enjeksiyon yapabildiğimiz sorgunun içinde bulunan tablo ve kolon isimlerini elde etmemize yarayacaktır. Eğer bu bilgiler işimize yarıyorsa söz konusu tablodan farklı sorgular yapılabilir.

Ayrıca tablo ve alan isimlerini bildiğimizde içine enjeksiyon yapabildiğimiz herhangi bir SELECT cümlesini “;” karakteriyle sonlandırıp bir INSERT cümlesi başlatarak adını, kolon sırası ve veri tiplerini bildiğimiz bir tabloya kayıt atabiliriz. Atılan kaydın uygulama için çok kritik sonuçları olabilir. Örneğin kullanıcı tablosuna kayıt atılabilirse yeni bir kullanıcı yaratılabilir.

MS-SQL’den Herhangi Bir Verinin Elde Edilmesi
 

Veri elde etmek için faydalı ODBC hata mesajlarından biri de veritabanı metin tipi bir veriyi numerik veri tipine dönüştürmek isterse (cast etmek isterse) söz konusu verinin değerini de görüntülediği dönüşüm hatasıdır. Eğer hata mesajları doğrudan kullanıcıya görüntüleniyorsa bu imkan saldırganlar için (tablo ve kolon isimlerini yukarıda bahsedilen metadata tablolarından elde edebileceklerini düşünürsek) istedikleri veriye ulaşma imkanı tanır.

Bunu gerçekleştirmenin yollarından biri bir SELECT cümlesinin WHERE yan cümlesine enjeksiyon yapılabiliyorsa burada ikinci bir sorgu yaratarak hata alınmasını sağlamaktır. Örneğin:

‘ or 1 in (select @@version) - -

metni enjekte edildiğinde hata mesajı içinde veritabanı versiyonu görüntülenecektir. Ya da tablo ve kolon isimlerini biliyorsak aşağıdaki gibi bir enjeksiyon metni kullanabiliriz:

‘ or 1 in (select password from users where username = ‘admin’) - -

Veritabanını metin veri tipini numerik veriye dönüştürmeye zorlamanın diğer yolları da şunlardır:

  • 1+@@version : Eğer metin veri tipli bir değişkeni numerik veriyi başa koyarak toplama işlemine dahil edersek veritabanı bu veriyi numerik veriye dönüştürmek isteyecektir.
  • SELECT CAST(@@version AS int) : CAST fonksiyonu kullanılarak metin tipinde bir veriyi “integer” tipine dönüştürmeye zorlarsak veritabanı hata üretecektir.

Döngüsel Yöntem (Recursion)


Yukarıda açıklanan yöntemi kullanarak web uygulama kullanıcısının veritabanında ulaşabildiği herhangi bir tablonun herhangi bir metin tipi kolonundan tüm bilgileri döngüsel bir şekilde çekmek mümkündür. Örneğin aşağıdaki sorgu sonunda ‘ali’ değerine hata mesajının içeriğinden ulaşabildiğimizi düşünelim:

‘ or 1 in (select min(username) from users where username > ‘a’) - -

Bir sonraki sorguda ‘ali’ değerinden büyük bir değer sorgulandığında bir sonraki değere hata mesajının içeriğinden ulaşabiliriz:

‘ or 1 in (select min(username) from users where username > ‘ali’) - -

Bu şekilde tüm verileri elde ettikten sonra bunları kullanıcı hakkında diğer bilgileri (örneğin password’ünü) sorgulamak için kullanabiliriz.

Girdi Filtrelerinin Atlatılması


Bazı uygulamalar SQL enjeksiyon açıklığı taşısa bile girdilerde filtreleme kontrolleri ile saldırıyı engellemeye çalışabilir. Örneğin bazı karakterleri encode edebilir veya temizleyebilir, bazı anahtar SQL kelimelerini silebilir. Bu tür kontrollerin varlığı anlaşıldığında da yapılabilecek atlatma yöntemleri mevcuttur.

Bloklanan Karakterlerden Kaçınma


Tek tırnak, yorum (- -) ve noktalı virgül karakterlerinin bloklandığı durumlarda aşağıdaki kaçınma yöntemleri denenebilir:

  • Öncelikle bloklama önlemi numerik veriler için yetersizdir çünkü numerik veri girildikten sonra gelen kelime ve ifadeler öncesinde sentaksın doğru olması için tek tırnak girmeye gerek yoktur. Bu nedenle metin tipi veriler yerine numerik verilere enjeksiyon yapılabilir.
  • Yorum karakterlerinin bloklandığı durumlarda bu karakterleri kullanmadan da SQL cümlesinin sentaks doğruluğu sağlanabilir. Örneğin aşağıdaki metni enjekte etmek yerine:
    • ‘ or 1=1- -
şu metin enjekte edilebilir:
    • ‘ or ‘a’=’a
Bu şekilde uygulama veriyi kendi tek tırnağıyla kapattığında SQL sentaksı doğru olacaktır.
  • MS-SQL veritabanına yönelik toplu iş (batch) gönderilmek istediğinizde SQL cümlelerinin sentaksı doğru olduğu takdirde cümleleri ayırmak için noktalı virgül (;) kullanmaya gerek yoktur. Cümle ayrıştırıcı farklı cümleleri noktalı virgül karakteri olmaksızın doğru yorumlayabilecektir.

Basit Geçerlik Denetimlerini Atlatmak
 

Bazı geçerlik denetim rutinleri basit bir kara listeye göre gelen girdilerde bloklama veya temizleme yapabilir. Örneğin SELECT anahtar kelimesinin bloklandığı veya temizlendiği durumlarda aşağıdaki yöntemlerle kontrol atlatılmaya çalışılabilir:

  • SeLeCt
  • SELSELECTECT
  • %53%45%4c%45%43%54
  • %2553%2545%254c%2545%2543%2554

SQL Yorumlarının Kullanılması


C++’a benzer şekilde SQL cümlelerinin içine /* ve */ sembolleri arasında inline yorumlar eklenebilir. Eğer uygulama boşluk karakterlerini blokluyor veya siliyorsa bunların yerine inline yorum ekleyebilirek boşluk karakterine benzetim (simulate) yapabiliriz. Örneğin:

SELECT/*aaa*/username,password/*bbb*/FROM/*ccc*/users

MySQL veritabanı inline yorumların SQL anahtar kelimeleri içine yapılmasına dahi imkan vermektedir. Bu şekilde SQL anahtar kelimelerinin bloklandığı durumlardan kaçınmak mümkün olabilir:

SEL/*aaa*/ECT username,password FR/*bb*/OM users

Bloklanan Metinlerin Dinamik Olarak Oluşturulmaları


Eğer bazı metinler web uygulaması tarafından bloklanıyorsa bu metinler veritabanına özel metin manipülasyon yöntemleri ile dinamik olarak oluşturulabilir. Örneğin “admin” kelimesi farklı veritabanları tarafından aşağıdaki şekillerde tekrar oluşturulabilir:

  • Oracle: ‘adm’ || ‘in’
  • MS-SQL: ‘adm’ + ‘in’
  • MYSQL: concat(‘adm’,’in’)
  •  
Veritabanlarının bu yöntemlerin dışında da kompleks metin işleme fonksiyonları bulunmaktadır. Örneğin Oracle CHR, REVERSE, TRANSLATE, REPLACE ve SUBSTR fonksiyonlarına sahiptir. CHR fonksiyonu tek tırnağın bloklandığı durumlarda bir kelime oluşturmak için kullanılabilir. Örneğin aşağıdaki sorgu “admin” kelimesini dinamik olarak oluşturabilir:

select password from users where username = chr(97) || chr(100) || chr(109) || chr(105) || chr(110)

Dinamik Çalıştırmanın Kullanılması


Bazı veritabanları dinamik olarak SQL cümlelerini çalıştıracak fonksiyonlara sahiptir. Eğer bu fonksiyonların kullanıldığını tahmin edebiliyorsak, bu fonksiyonların parametre olarak aldığı SQL cümleleri de yukarıda örnekleri verilen metin manipülasyon yöntemleri ile filtrelerden kaçınılacak biçimde oluşturulabilir. Örneğin:

  • MS-SQL:
    • exec(‘sel’ + ‘ect * from ‘ + ‘users)
    • declare @q varchar(8000)
select @q = 0x73656c656374202a2066726f6d207573657273
exec (@q)

  • Oracle:
    • declare
l_cnt varchar2(20);
begin
            execute immediate ‘sel’ || ‘ect * fr’ || ‘om users’ into l_cnt;
            dbms_output.put_line(l_cnt);
end;

Yetersiz Filtrelerin Aşılması


Bilindiği üzere SQL enjeksiyonuna karşı en sık uygulanan kontrollerden biri tek tırnağın SQL içindeki anlamını gerçekten tek tırnağa dönüştürmek için bir tek tırnak daha eklenmesi suretiyle “escape” edilmesidir (yani ‘’ SQL parçalayıcısı tarafından gerçek ‘ karakteri olarak algılanır, metin tipi bir veriyi bitirme işareti olarak değil). Bu kontrol örneğin admin’- - girdisini aşağıdaki SQL cümlesine dönüştürür ve veritabanı SQL cümlesini yorumlarken hata verir:
select * from users where username = ‘admin’’- -‘ and password =’’

Ancak eğer tırnaklar çift tırnağa çevrildikten sonra girdi 20 karakter içerecek biçimde budanıyorsa bu durumda 19 adet “a” karakteri ve sonundaki tek tırnağa bir tırnak daha eklenip budama yapıldıktan sonra girilen metin orjinal haline döner. SQL cümlesinin yapısını kestirebiliyorsak bu tür önlem uygulayan bir uygulamaya yaptığı kontrolü aleyhine kullanacak biçimde metin girdisiyle saldırmak da mümkündür. Örneğin cümlemizin şöyle olduğunu düşünün:

“select * from users where username = ‘” + param1 + “‘and password = ‘” + param2 + “’”

Böyle bir cümleye param1 için (aaa’) param 2 için ( or 1=1- -) girdilerinin girildiğini düşünün. Bu durumda tek tırnak escape edildikten sonra oluşan sorgu şu şekilde olacaktır:

select * from users where username = ‘aaa’’ and password = ‘ or 1=1- -‘

Bu durumda gri ile boyanmış bölüm tek bir parametre olarak algılanacak ve cümle amacından saptırılarak password kontrolünden kaçınılabilecektir.


İkincil SQL Enjeksiyon Saldırısı


Daha önce de bahsettiğimiz gibi uygulamalar tek tırnağı gerçek anlamına çevirmek için ikinci bir tek tırnaklar escape edebilir. Ancak bu şekilde bir INSERT cümlesine enjekte edilen ve tek tırnak karakteriyle veritabanına kaydedilen bir metin daha sonra bir SELECT cümlesiyle sorgulama yapılması için parametre olarak kullanıldığında SQL cümlesinin yapısını değiştirebilir. Örneğin aşağıdaki metnin INSERT cümlesine kullanıcı adı yerine enjekte edildiğini düşünelim:

‘ or 1 in (select password from users where username=’admin’)- -

Daha sonra kullanıcı password değişiklik fonksiyonu içinde mevcut password’ü kontrol etmek için bu kullanıcı adı bilgisi kullanılarak SELECT cümlesi oluşturulduktan sonra çalıştırıldığında eğer ODBC hataları görüntüleniyorsa bir MS-SQL veritabanından admin kullanıcısının password’ünü hata mesajının içinde elde edebiliriz.


<<Önceki Bölüm                                                                                                      Sonraki Bölüm>>