Günümüzde, şifreleme neredeyse her yerde kullanılırken, yakın zamana kadar bazı ülkelerde silah olarak kabul edilirdi. Tarihsel olarak, anahtar uzunluğu ve şifreleme algoritmaları konusunda bazı kısıtlamalar vardı, ancak şu anda Kerckhoffs’un prensibi sayesinde neredeyse istediğiniz türde şifreleme yöntemini kullanabilir ve sadece anahtarı gizli tutabilirsiniz. Ancak, bu anahtarlar iyi korunmalıdır ve ne yazık ki çoğu modern yazılımda bu durum geçerli değildir. Bu makale, bu anahtarları çok fazla çaba harcamadan nasıl elde edebileceğinizi gösterecektir.
Öyleyse, şifreleme işlemleriyle biraz ısınalım. Temel olarak, simetrik bir şifreleme yöntemi (AES, RC4, vb.), veriyi şifrelemek ve şifreyi çözmek için sadece bir gizli anahtar gerektirirken, asimetrik bir şifreleme yöntemi (RSA, ECC, vb.) her iki işlem için de iki ayrı anahtar (genel ve özel) gerektirir.
Her ne kadar kriptoanaliz için hala bazı olasılıklar olsa da, modern şifreler birçok bilinen saldırıya karşı oldukça dirençlidir ve onları kırmak kolay bir görev değildir, birçok çalışma bunu göstermektedir.
Bu nedenle, her şifreleme veya şifre çözme işlemi gerçekleştirildiğinde bir anahtar kullanılır ve böylece açığa çıkarılır. Anahtarlar, metin dosyalarında sabit diske kaydedilmediyse bile, her zaman RAM’de (geçici bellek) saklanır. Tabii ki, bilgisayarınız kapatıldığında bu anahtarlar kaybolur ve kolayca geri getirilemez, ancak belirli koşullar sağlanırsa böyle bir saldırı pratik olarak gerçekleştirilebilir.
Ancak, bir bilgisayara fiziksel erişiminiz varsa, bunları farklı ve daha kolay bir şekilde çıkarabilirsiniz. Gerçek konuya geçmeden önce, yazılımın nasıl çalıştığı konusunda hafızamızı tazeleyelim. Başlatılan her programın kendi sanal bellek alanı olacaktır ve bu alanlar bölümlere ayrılacaktır. Bunların en önemlileri, kendisi için derlenen kod (assembly kodu), yürütme için gereken sistem kütüphaneleri, nesnelerin ve yapıların kullanıldığı heap (yığın) ve değişkenlerin ve işaretçilerin saklandığı stack (yığın) alanlarıdır. Şifreleme anahtarları genellikle dinamik olarak ayrılan yapılar (heap) ve/veya sadece değişkenlerde saklanan değerler (stack) şeklinde olabilir. Dolayısıyla, bir işlemin hafızasını çalışma sırasında döküp, kriptografik bir anahtara benzeyen bir şey bulmaya çalışabiliriz.
AES şifreleme algoritması, kullanıcının sağladığı anahtarı doğrudan kullanarak veriyi şifrelemek yerine, anahtar planlaması (key scheduling) adı verilen bir işlem yapar ve bu sayede daha rastgele ve güvenli görünmesini sağlar. Verinin “rastgelelik” düzeyi matematiksel olarak entropy (rastgelelik) kullanılarak ölçülebilir. Örneğin, “mykey” gibi bir anahtar AES için kullanıldığında, gerçek planlanmış anahtar “9adbe0b3033881f88ebd825bcf763b43” olacaktır. “mykey” anahtarının entropisi, her bayt için 2/8 (2 bit) veya daha azdır, oysa gerçek anahtarın entropisi 4/8 (4 bit) olacaktır. Bu farkı göstermek için standart sapmayı bir çemberin merkezine orantılı olarak kullanarak grafik bir temsili yapabiliriz.
Tamam, yeterli teoriden sonra, OpenSSL şifreleme sürecini hata ayıklamaya başlayarak pratik yapalım ve anahtarın ne şekilde işlendiğini ve nasıl çıkarılabileceğini görelim. İlk olarak, AES-128-ECB modunda “mykey” anahtarını kullanarak “mytext” verisini şifrelemek için OpenSSL şifreleme işlemini “testAES.enc” adında bir dosyaya başlatalım:
Anahtarımızı iki kez girdikten sonra veriyi gireceğiz, ancak işlemi tamamlamadan önce GDB’yi pwngdb uzantısıyla kullanarak süreci hata ayıklamaya başlayacağız.
Son şifreleme işleminin sonunda bir kesme noktası koymak için ve girdimizi onaylayarak yürümeye devam etmek için aşağıdaki komutları kullanalım:
Bu noktada, RBX kaydı (ve mevcut yığıt konumu) yapının bir işaretçisini içerir ve bu işaretçi, 120 bayt ofsetleme ile zamanlanan anahtarı içermektedir.
Bu bize Little-Endian'daki gerçek şifreleme anahtarını verecektir (01020304 yerine 04030201).Şifrelemeden hemen önce biraz ilerleyelim:
Burada, AES’in ilk turunu hesaplamadan önce anahtar XMM0’da ve şifrelenecek olan metin XMM2’de depolanmıştır.
Sisteminizin 64 bit olduğunu düşündüğünüzü belirtmek istemiş olabilirsiniz, ancak XMM kayıtları 128 bit olduğundan, aslında sisteminizin 64 bit olduğu bir yanlış anlamadır. Modern CPU’lar 128, 256 ve hatta 512 bit kayıtlar içermektedir. Bunlar başlangıçta grafik nesneleri ve veri depolamak için kullanılmıştı, ancak daha sonra şifreleme için de kullanıldı ve AESNI montaj talimatları işlemciye sabitlenerek AES’i daha hızlı hale getirildi. Elbette, bu sonuçları çeşitli çevrim içi araçlar kullanarak doğrulayabilirsiniz.
Tamam, peki hafıza konusunda ne dersiniz? OpenSSL’in durumunda, anahtarlar doğrudan yığına ve heap’e aktarılmaktadır. Bazı yapılar için hafıza eşleştirmesi anonim olabilir ve hafıza adresleri değişebilir, ancak yine de bazı ofsetleri tahmin edebiliriz.
Görebildiğimiz gibi, anahtar her zaman heap bölgesinde 0x23d30 bayt sonrasında (benim OpenSSL sürümüm için) saklanıyor. Eğer anahtar bir dosyada saklanıyorsa, bu ofset biraz farklı olabilir (0x23b40). Ayrıca, anahtarı saklamadan önce sıfırlar ve 0x111 değeri bulunacaktır.
Stack ofseti ve depolama ön-ve-sonekleri değişebilir, ancak her zaman bulunabilir.
Her ne kadar anahtarlar ve veriler bellekten kullanıldıktan sonra güvenli bir şekilde silinse de, bazı projelerde (örneğin OpenVPN) anahtarlar sadece heap’te saklanır ve aynı anahtarın birden fazla konumda depolanabilir (0x23ba0 ve 0x24170 gibi). OpenSSH ise 2014 yılından beri kendi şifreleme uygulamasını kullanmasına rağmen, anahtarları yine stack’te saklamaz, ancak heap için değişmeyen ofsetler kullanır (0x217a0 ve 0x24260 gibi, ancak bunların mesafeleri ve gerçek değerleri değişebilir).
Ancak, yazılım tabanlı ofsetlere ve/veya ön/son eklerine dayanmak pek iyi değildir. Anahtarları bulmanın daha iyi bir yolu istatistikleri doğrudan kullanmaktır. 10 yıldan daha uzun bir süre önce, böyle çalışmalar zaten vardı ve etkili yöntemler bulunup uygulanmıştı. Temel fikir oldukça karmaşık değildir, anahtar uzunluğunu arayın, entropisini doğrulayın, yakında planlama için potansiyel adayları arayın, eğer bulunursa, büyük olasılıkla gerçek bir şifreleme anahtarıdır.
Bu, iyi çalışan harika bir projedir, ancak işlevselliğini artırmak için canlı süreçlerden ve isteğe bağlı ikili dosyalardan kriptografik anahtarları çıkarma olanağı ekledim, bu yüzden adı CryKeX oldu. Belirtilen bir sürecin belleğini döküyorum ve ardından anahtarları arıyorum, ayrıca bir ikili dosyayı başlatabilir ve aracım belleği otomatik olarak dökecektir. Şimdi, bunu uygulamada görelim.
Bu tür bir yöntem neredeyse her şeyde uygulanabilir, tarayıcılar, konteynerler vb. gibi şifreleme kullanan hemen hemen her şeye. Elbette, bu aynı zamanda adli bellek dökümleri için de geçerlidir. Gerçek hayatta, bu oldukça saldırgan bir araçtır; örneğin, fidye yazılımlarından kriptografik anahtarları çıkarabilir. Asimetrik anahtarları (örneğin RSA) aramak daha zor olsa da bazı parametreleri dikkate almak gerekmektedir (üstel, modül vb.), ancak yine de mümkündür ve aynı ekip tarafından yapılmıştır. Bununla birlikte, mükemmel bir şey yoktur; örneğin, Firefox gibi bazı tarayıcılar, OpenSSLv3’ü biraz farklı anahtar depolama mekanizmalarıyla kullanır, PGP/GPG ve LUKS, bellekte anahtar depolamayı daha zor hale getirmek için obfiske edebilir. O zaman anahtarı nerede saklamalıyız? CPU kayıtlarında, güvenli bir yonga setinde veya hatta ayrı bir donanımda gibi çözümler bulunmaktadır. Bunların hiçbiri mükemmel değildir, tabii ki, ancak ideal olarak, donanımın fiziksel erişimi sınırlı olmalıdır, ancak eğer bilgisayarınızın fiziksel güvenliğini sağlayabiliyorsanız, neden zaten şifreleme kullanmanız gereksin ki? Yani, fiziksel güvenliği garanti etmek imkansızdır, çünkü fiziksel katmanda kendimiz de nesneleriz ve onun kurallarına tabi olarak kontrol edemeyiz, ancak yine de doğru yapıldığında %99.9(9)’luk bir güvenlik seviyesi elde edebiliriz. Sorun şu ki, her zaman doğru yapılmaz, ancak umarım bu makaleden bir şeyler öğrenmişsinizdir ve şu anda olduğundan daha iyi, belki mükemmel olmasa da en azından daha iyi yöntemler deneyimlersiniz. Unutmayın, şifreleme güçlüdür, ancak zayıf bir şekilde kullanılabilir.