|
.NET ve Uygulama Güvenliği - 2 |
|
Gönderiliyor lütfen bekleyin... |
|
|
Daha önceki makalemizde (.NET ve Uygulama Güvenliği 1)
dağıtım aşamasından sonra uygulamamız üzerinde istemediğimiz şekilde değilik yapılmaya müsait olabildiğini inceledik.
Bu makalemizde iste bu tür sorunlara karşı neler yapılabileceğini Microsoftun araçları ile ya da 3. parti araçlar ile
ne gibi önlemler alabileceğimizi inceleyeceğiz. Kısaca önceki senaryolardan bahsedecek olursak, uygulamanın dağıtımından sonraki
safhada, uygulamamız ya da uygulamanın kullandığı Dlller Reflector ile açılıp
kodlar görüntülendikten sonra (Type bilgileri ve isimleri çözüldükten sonra)
benzer bilgiler ve isimlerden yola çıkılarak orjinallerinden farklı Dll'ler
oluşturulup, orjinalleri ile değiştirilerek farklı ya da zararlı kodların
çalıştırılabilmesi sağlanmaktadır. Diğer bir senaryomuzda ise, yeniden
yazılamayacak kadar karmaşık veya yazılmasına gerek kalmadan sadece belirli bir
metodun veya kod bloğunun değişmesi sonucunda uygulama amacından farklı çalışması sağlanarak istenmeyen durumlara
neden olabildiğini ve bunun da uygulama ya da uygulamanın kullandığı Dlllere kod gömerek (code injection)nasıl
gerçekleştirilebileceğini incelemiştik.
Dijital Imzalama
Daha önce az çok .NET üzerinde uygulama geliştirenler tekil isim (strong name)
kavramı ile karşılaşmışlardır. Geliştirilen assembly'lerin güveliğini sağlamak,
tekilliğini belirtmek, versiyonlamak, Com+ kullanmak veya geliştirilen
assembly'nin GAC'a yüklenmesi gerektiğinde assembly'lerimizin tekil isme sahip
olmaları gerekmektedir. Tekil isim "BTA.CoreDevelopment.Client.dll" şeklinde
assembly adı değil , "assembly adı + versiyon numarası + culture bilgisi ve
public key token" bilgilerinin birleşimi sonucunda oluşur. Aslında çok kısa bir özetle tekil isim "bir assembly'nin
tekilliğini sağlamak" amacı ile kullanılır. Bu tekilliği sağlamak için de tabiki
sadece bize ait olan bazı bilgilerin olması gerekmektedir. Sadece bize özel olan
bu bilgileri kullanarak assembly'yi işaretlenerek tekillik sağlanabilmektedir.
Bunun için de günümüzde yoğun olarak kullanılan dijital imzalama (digital
signature) yöntemini tekilliği sağlamakta kullanabiliriz. Assembly'lerin
tekilliğini nasıl sağlandığını anlayabilmemiz için öncelikle şifreleme tekniğini
biraz inceleyelim.
Temel prensip olarak hashing ve public/private anahtarlar kullanılarak yapılan
imzalar birlikte kullanılarak belirli bir veri için tekillik oluşturulabilir.
Hashing'in mantığında belirli bir veri üzerinden kullanılan algoritmaya göre
özel bir değer üretme söz konusudur.
Yukardaki şekilden de anlaşılacağı üzere hashing işlemine tabi tutulacak veri korumak istediğimiz assembly
olduğunu düşünürsek, assembly üzerinden bir hash algoritması(MD5, SHA1 vsvs) ile bir değer elde edebiliriz.
Eğer assembly içerisinde bir değişiklik olursa üretilecek olan Hash değeri de farklılaşacaktır. Hashing
genelde şifreleme ile karıştırılan bir konudur.Hashing bir sıkıştırma ya da şifreleme yöntemi değildir.
Sıkıştırma ya da şifrelemede orjinal veriyi tekrar elde etmek mümkün iken Hash değerinden yola çıkarak orjinal
veri elde edilemez.
Peki bu durumda hash değeri bizim için ne anlam ifade etmektedir ? Aynı
hashing algoritması kullanılarak farklı iki assembly den elde edilen hashing
değerlerine bakıldığında aynı sonucu elde etmiş isek bu iki assembly aynıdır
diyebiliriz ki bu durumda hash değerleri karşılaştırma amacı ile ile kullanılır
sonucunu çıkarabiliriz. Gerçekte de derleyici kod derlendikten sonra oluşan
assembly'nin içerisine bu elde ettiği hash değerin yazmaktadır.
Yukardaki resimde de görüldüğü gibi CFF Explorer (bir önceki makalede bu
araçtan bahsetmiştik) ile bir assembly incelendiğinde SHA1 algoritmasi
kullanılarak oluşturulan hash değerini görmüş oluyoruz. (MD5 algoritmasi
kullanılarak da hash değerinin yazıldığını görüyoruz ki şu anda detay yapılacak
konu değildir. CLI dökümanları incelendiğinde bu alanın ileriye yönelik kullanım
amaçlı konulduğunu bilgisine erişilebilir) Bu açıklamalardan sonra akla gelecek
ilk soru " bu alanı harici müdahaleler ile değiştiremez miyim ?" olacaktır.
Cevap ise vahim : "evet değiştirilebilir". Bu durumda assembly'nin hash değeri
ile üzerinde değişiklik yapılarak elde edilen sahte assmebly'nin hash
değerleri değiştirilebilir ve bu nedenle de tekillik güvenliğini sağlamamış
oluruz.
Bu soruna çözüm olarak elde ettiğimiz bu hash değerini şifreleyerek assembly
içerisine yazdığımızda dışardan müdahale etmek istendiğinde şifrelenmiş hash
değeri ile karşılaşılacak ve bu hash değerinin gerçek değeri bilinmediğinden
ilerleme kaydedilemeyecektir. Bu durumda assembly içerisine şifrelenerek
yazılmış hash değerinin orjinal değerine erişmek için gerekli anahtarı da
koymamız gerekmektedir. Aksi durumda karşılaştırma yapılmak istenildiğinde gerçek
hash değerine ulaşılamayacağı için biz de sorun yaşıyor olacağız. İşte bu
aşamalarda public/private anahtar yöntemini kullanarak assembly içerisine
hem şifrelenmiş hash değerini hem de şifreyi çözecek public anahtarı koyarak sorunumuzu
halletmiş olacağız.Bu yöntemi kullanmadan önce public/private anahtarlar
kullanılarak nasıl şifreleme yapılabildiğini kısaca inceleyelim.
Yukardaki resimde de açıkça görüldüğü gibi eğer bir veri public anahtar ile
şifrelenmiş ise bunu ancak private anahtar ile, eğer veri private anahtar ile
şifrelenir ise bunu anca public anahtar ile çözebiliriz (Konu hakkında detaylı
bilgiler için Asimetrik Şifreleme konularına bakınız).
Yani şifrelenmiş verinin orjinal halini görebilmek için ya private (public
anahtar ile şifrelenmiş işe) ya da public ( private anahtar ile şifrelenmiş ise)
anahtara sahip olunması gerekmektedir.
Hashleme ve asimetrik şifreleme yöntemleri kullanılarak oluşturulan
assembly'lere tekil isim (strong name) verebilmekteyiz. Aşağıdaki resimde basit
bir şekilde bu işlemin nasıl yapıldığı gösterilmektedir.
Yukardaki şekli kısaca özetlemek gerekirse, derleme sırasında oluşturulan assemblyden öncelikle SHA1 hash algoritmasi ile bir değer üretirlir.
Bu değer assembly içerisine daha önce açıkladığımız gibi değiştirilme ihtimali göz önüne alınırak doğrudan yazılmaz.
Oluşturulan private anahtar ile bu assembly'ye özel hash değeri şifrelenir ve
geri çözülebilmesi için public anahtar ile birlikte assembly içerisine
kaydedilir.
Çalışma zamanında
imzalı assembly'yi yükleyen birim assembly'den bir hash değeri üretir. Daha
sonra assembly içerisindeki şifrelenmiş hash değerini public anahtar kullanarak
gerçek hash değerini elde eder. Bu iki hash değerini karşılaştırdığında eğer
aynı değerleri elde ediyor ise assembly doğrulanmış olur.
Şimdi bir tekil isme sahip assembly oluşturup bu assembly'yi önceki
makalemizdeki gibi değiştirmeye çalışalım. Öncelikle yukardaki anlatılanlar
doğrultusunda public/private anahtar ikilisine ihtiyaç vardır. Bu amaç için
Sn.exe isimli aracı Visual Studio 2008 komut satırından aşağıdaki parametrelerle
kullandığımzda bize public/private anahtar ikilisini üretecektir.
sn -k bta.snk (sn.exe -k dosyaismi)
Eğer üretiken bu anahtar ikilisinden sadece public anahtar dosyasını elde etmek
istenildiğinde sn.exe aşağıdaki gibi kullanılabilir.
sn -p bta.snk btapublic.snk
Bir assembly'nin public anahtarı ile ilgili bilgi almak için ise
sn -Tp bta.dll
şeklinde kullanılması yeterli olacaktır. Oluşturulan snk dosyası projeye eklenir
ve projenin AssemblyInfo.cs dosyası içerisine aşağıdaki kod satırı eklenerek
derleme işlemi sırasında tekil isim oluşturulması sağlanmış olur.
[assembly: AssemblyKeyFile("bta.snk")]
Visual Studio 2008 ile bilirlikte assembly'lerin kolay bir şekilde imzalanmasi
için ek özellik getirildi. Projeye sağ tıkladıkdan sonra açılan menüden
Properties menüsüne tıkladığımızda açılan ekranda Signing sekmesine
tıkladığımızda Signing bölümünü göreceğiz. Eğer daha önceden bir dosya varsa bu
kısımdan seçebilir ya da ilk defa oluşturulmak istenirse yine bu kısımdan yeni
public/private anahtar dosyamızı oluşturabiliriz.
Anahtarı oluşturdukdan sonra Solution Explorer penceresiden uygulama dosyaları
incelendiğinde *.pfx uzantılı bir dosyanın projeye eklendiğini görebiliriz.Artık
bu assembly derlendiğinde şimdiye kadar bahsettiğimiz tekil isme sahip
olacaktır. Oluşan assembly IL Disassembler ile açılıp manifest kısmına
bakıldığında tekil isme sahip olmayan assembly'lerden farklı olarak artık public
anahtarı içerdiğini görebilmekteyiz.
Şimdi önceki makalemizde geçen senaryoları tekil isim verilmiş bir assembly
üzerinde gerçeklemeye çalışalım. Birinci senaryo da geçek assembly'nin Reflector
ile açılarak kod yapısının tespit edilmesi ve benzer bir assembly geliştirilerek
gerçeği ile değiştirilmesi sonucunda uygulamamızın istenemeyen kodları
çalıştırması idi.Önceki makalemizdeki clHavale.dll uygulamasını bu defa
yukardaki açıklamalar doğrultusunda *.pfx dosyasını oluşturup tekil isim
vererek derleyelim.
Bu adımdan sonra waHavale isimli pencere uygulamamıza bu tekil isim verilmiş
dll'i referans ederek derleyelim. Daha sonra sahte dll ile bu tekil isime sahip
dll'i değitirelim. Hatırlayacağınız üzere eğer bizim geliştirdiğimiz dll tekil
isme sahip değildi ve uygulama bu harici müdahale sonucunda sorunsuz olarak
çalışmıştı. Geliştirme aşamasında dll'i tekil isme sahip şekilde
hazırladığımızda ise bu müdahale sonucu başarısız olacaktır.
Görüldüğü gibi tekil isme sahip bir dlli kullanarak derlenen ana uygulamamız çalışma zamanında ilgili
tekil isme sahip dll içerisindeki kodları kullanmak istediğinde ilgili dlli yükleme çalışırken tekil isimli
dll olup olmadığını kontrol etmekte, eğer bu tekil isimli dlli bulamazsa da çalışma zamanında hata üretmektedir.
Bu örnekteki tekil isim "clHavale, Version=1.0.0 ,Culture=neutral,
PublicKeyToken=b0c1e38505bbbe10" dur. Daha öncede bahsettiğimiz gibi tekil
isim için gerekli verilen isim içerisinde yer almıştır. PublicKeyToken
assembly içerisine gömülen public anahtarımızdan üretilen hash değerinden
ibarettir. Önceki makaledeki bilgilerden yola çıkarak , tekil isimli dll
uygulamaya (exe) refere edildiğinde uygulama içerisinde bir yerlerde bu bilgi
kaydedilmekte ve çalışma zamanında bu bilgi kontrol edilmektedir. (Bir önceki
makalemizde açıkladığımız AssemblyRef kısmı olabilir mi ?) . Makalenin ilerleyen
bölümünde bu kısmı detaylandıracağız.
Diğer senaryomuzda ise Reflector ve Reflexil araçları ile bir assembly'nin
kodlarının değiştirilmesi işleminden sonra değişikliğin yine aynı assembly
üzerine yazılması sonucunda uygulama istenemeyen kodlar ya da kodların silinip
kaydedilmesinden sonra çalışması gereken kodların çalışmaması söz konusu
idi.Aynı adımları tekil isme sahip clHavale.dll üzerinde yapmaya çalıştığımızda
Reflexil bize aşağıdaki seçeneklerden hangisini uygulayacağını sormaktadır.
Görüldüğü gibi aslında kodları değiştirilmiş assembly doğrudan kaydedilememekte
alternatif seçenekler sunmaktadır.
Bu seçeneklere kısaca değinirsek;
Register it for verification skipping tercih edildiğinde Reflexil kodları değişmiş assemblyyi
oluşturur ve ana uygulamamızın bu assembly'yi çalışma zamanındaki onaylama
işlemine tabi tutmadan çalıştırabilmesi için kayıt defterine (registry)
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification altında
clHavale,B0C1E38505BBBE10 isimli (dikkat edeceğiniz gibi assembly adı ve
assembly'nin PublicKeyToken bilgisi) bir anahtar oluşturmuştur.
Artık ana uygulama tekil isme sahip bu dll için çalışma zamanında yukarda
bahsedilen hash değerlerinin aynı olması üzerine kurulu kontrol işlemini
yapmayacaktır. Ve bu durumda da uygulama yine sorunsuz bir şekilde çalışmaya devam edecektir.
(Ama kayıt defterine erişim hakkı bulunmayan kullanıcılar için bu seçenek işe
yaramayacağından istenilen sonucu elde edememiş oluyoruz. )
Re-sign with key seçeneğinde ise Reflexil private anahtarı isteyerek
değişmiş olan bu assembly için tekrar bir tekil isim oluşturma işlemi
yapabileceğini söylemektedir. Oysaki geliştirici dışında kimsede private anahtar
olmadığı için bu mümkün olmamaktadır. Eğer private anahtar bizde olsa idi
Reflexil değişen asssembly için hash değeri üretecek bunu da private anahtar ile
şifreledikten sonra assembly içerisine kaydedecek böylelikle ana uygulama
rahatlıkla bu assembly'yi runtime da yükleyerek çalıştıracaktır.Yine eğer
private anahtar bizde olsa idi dll'i yeniden yazdıkdan sonra kendimiz
imzalayabilirdik.Bu nedenle private anahtarın gizliliği oldukça önemlidir.
Remove strong name seçeneği ise oldukça cüretkar imkanlar sunmaktadır.
Kısaca assembly'nin imzasını assembly içinden silecek ve açılan pencerede ise bu
assembly'ye referansda bulunan ana uygulamayı (tercihe göre) isteyerek ana
uygulama içerisindeki tekil isme ait bilgileri yok edecektir. Bu aşamadan sonra
ana uygulama bir önceki makalemizde detaylı bahsettiğimiz assembly bilgilerinden
yola çıkarak değiştirilmiş assembly'yi çalıştırabilecektir. Aslında bu çok da
karmaşık bir işlem değildir. Yani tekil isimli assembly'yi refere eden
uyugulamayı CFF Explorer ile açıp metadata bilgilerinden AssemblyRef
tablosundaki clHavale.dll 'in bilgilerine baktığımızda PublicKeyOrToken
bilgisinin var olduğunu görürüz. İşte bu kısmı düzenleyerek 0 değerini verdikten
sonra ana uygulama kaydedildiğinde sahte dll ile gerçek dll yer değiştirir. Bu
adımdan sonra da ana uygulama tekil isme sahip olup olmadığına bakmadan ilgili
dll'i yükleyebildiğini ve çalıştırdığını göreceğiz.
Bir başka senaryo da şu olabilir. Ana uygulama tekil isme sahip bir dll
beklemektedir. Biz de tekil isme sahip dll'i açıp içerisine kod gömüp ya da
sildikten sonra ana uygulama tarfından çalıştırılmasını istediğimizde ilk adım
olarak yukardaki gibi ana uygulama içerisinde clHavale.dll ine ait
PublicKeyOrToken bilgisini 0 yaparak ana uygulamayı kaydettik. Bu durumda hala
gerçek dll'imiz tekil isme sahip olduğu için ana uygulama da tekil isme sahip
olmayan bir dll aradığı için gerçek clHavale.dll çalıştırılamayacaktır. Bu
durumda gerçek clHavale.dll den imza bilgisini silmemiz gerekmekte ve bunun için
de yine CFF Explorer'dan faydalanarak aşağıdaki görüldüğü gibi PublicKey bilgisi
0 yapılarak tekil ismin yok edilmesi sağlanabilmektedir. Biraz araştırma
yapıldığında tekil ismi silen birkaç uygulama olduğunu görebiliriz.
Cancel with Delay Signed seçeneği ise şu aşamada detaylandırma ihtiyacı
duymadığımız bir konu. Kısaca delay signed geliştirilen dll'in içerisinde sadece
public anahtarın bırakılarak private anahtar olmadan da kodu yazmak ve
assembly'yi geliştirmek için gerekli imkanların sağlanmasına yaramaktadır.
Assembly geliiştirildikten sonra dağıtım aşamasında private anahtar kullanılarak
assembly imzalanır ve dağıtıma hazır duruma getirilir. Delay Singing
hakkında detaylı bilgi için
http://msdn.microsoft.com/en-us/library/t07a3dye.aspx adresinden
faydalanabilirisiniz.
Şu ana kadar bir assembly'nin imzalanması sonucunda eskisi kadar rahat bir
şekilde harici müdahale edilemediğini ama hala bazı müdahaleler sonucunda yine
istenmeyen durumların gerçekleşebiliceğini görmüş olduk. Aslında şu ana kadar
bahsettiğimiz tüm konulardan çıkan tekil isim assembly'nin tekilliğini
sağlayabilmekte ama uygulamanın güvenirliğini görüldüğü üzere
sağlayamamaktadır.
Makalede kullanılan örneği idirmek için tıklayınız.
Oğuz YAĞMUR
MVP
[email protected]
Makale:
.NET ve Uygulama Güvenliği - 2 C#, Visual C# ve .NET Oğuz Yağmur
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|