Meltdown & Spectre Zafiyetlerinin Detaylı Analizi - Part I Meltdown

1.    Giriş

Şekil 1 - Sırasıyla Meltdown ve Spectre zafiyetlerinin logoları.

3 Ocak 2018 günü Google’ın Project Zero ekibinden Jann Horn bilgi güvenliğini derinden etkileyecek bir çalışma yayımladı. [1] Bu çalışmada işletim sisteminin en temel kurallarından olan kullanıcı ile çekirdek alanlarının veya süreç alanlarının birbirinden izolasyonunu, modern işlemci mimarisindeki optimizasyon yöntemlerinden biri olan spekülatif yürütmeyi suistimal ederek aşması yer alıyor ve toplamda 3 atak varyasyonu anlatılıyor. Bunlardan ikisine Spectre ve diğerine de Meltdown ismi verildi.
  • Varyasyon 1: bounds check bypass (CVE-2017-5753) (Spectre)
  • Varyasyon 2: branch target injection (CVE-2017-5715) (Spectre)
  • Varyasyon 3: rogue data cache load (CVE-2017-5754) (Meltdown)
Geçen yıl Haziran ayında bahsi geçen atak yöntemleri hakkında Intel, AMD, ve ARM firmaları bilgilendirildi. Kamu aydınlatılma için geri sayım bitmeden önce ise bir araştırma ekibi de ilgili zafiyetler ile alakalı makaleler yayımladı. [2][3] 

Meltdown ve Spectre zafiyetlerini derinlemesine anlatmaya çalışacağımız yazı dizisinin ilkinde, bizce anlaşılırlığı arttırmak için hakim olunması gereken konseptler ile ilgili bilgi vereceğiz. Bunlar modern işlemci ve spekülatif yürütme, ön bellek teknolojisi ve yan kanal atakları (eğer ilgili konseptlere zaten hakim olduğunuzu düşünüyorsanız, 4. başlıktan devam edebilirsiniz). Ardından Meltdown zafiyetinin modern işlemci mimarisindeki suistimal ettiği durumu ve yetkisiz bir kullanıcı olarak çekirdek alanından nasıl veri okunabileceğini göstereceğiz.

2.    Sözlük

Bu yazıda aşağıda belirtilen İngilizce terimler yerine Türkçe karşılıkları kullanılacaktır:
  • Cache: Ön bellek
  • Cache hit: Ön bellek isabeti
  • Cache miss: Ön bellek kaybı
  • Process: Süreç
  • Speculative execution: Spekülatif yürütme
  • Out of order execution: Sırasız yürütme
  • Side channel attack: Yan kanal atağı
  • Kernel space: Çekirdek alanı
  • User space: Kullanıcı alanı
  • Process space: Süreç alanı
  • Instruction: Komut
  • Instruction queue: Komut sırası
  • Architectural stage: Mimari durum
  • Micro-architectural stage: Mikro mimari durum
  • Scaler processor: Ölçekleyici işlemci
  • Super scaler processor: Süper ölçekleyici işlemci
  • Read only memory: Salt okunur bellek

3.    Gerekli Konseptler

İlgili zafiyetler bilgisayar sisteminin alt seviye teknolojilerini suistimal etmektedir. Mümkün olduğunca siz okuyucuların akıllarında Meltdown ve Spectre ile alakalı soru işareti bırakmamaktır bu yazının esas amacı. Amacımıza ulaşmak için de sırasıyla yan kanal atakları, ön bellek teknolojisi, modern işlemci ve spekülatif yürütmeyi kısaca anlatmaya çalışacağız.

3.1.  Ön Bellek

İşlemci hızı ile bir verinin bellekten okunma hızının yaklaşık bir değer olduğu zamanlar, bir komutu çalıştırabilmek için gerekli verinin bellekten okunması bir performans sorunu değildi. Moore yasasının da öngördüğü gibi [4] yıllar geçtikçe aradaki fark daha da büyüdü. Bundan dolayı, artık bir komut için gerekli verinin bellekten okunması yüzlerce işlemci döngüsüne denk düşer hale geldi. Mümkün olduğunca bellekten okuma işlemlerinin seyrekleştirilmesi için, ön bellek adı verilen işlemciye yakın birimler geliştirilmiştir. Programın sıklıkla bellekte eriştiği, devam eden akışında da erişmesi muhtemel görünen veriler, komple blok halinde bellekten direk bu birime aktarılır. Böylece işlemcinin gerekli verilere çok daha hızlı bir şekilde ulaşması sağlanır.

Şekil 2 - Intel ön bellek hiyerarşisi. L1 ve L2 ön bellekleri işlemci çekirdeklerine özel iken, L3 ön belleği bütün çekirdekler arasında paylaşılır.

Şekil 2’de görüldüğü gibi, modern Intel işlemcileri 3 seviye ön belleğe sahiptir: L1, L2 ve L3. L1 ön belleği en küçük, işlemciye en yakın olmasından dolayı da en hızlı ön bellektir. L2 orta seviye ön bellek olarak adlandırılır ve L1’den kat ve kat daha büyüktür. L3 ise en büyük ve en yavaş ön bellektir ve işlemci çekirdekleri tarafından paylaşılır [5]. İşlemcinin çalıştırdığı komutların veya ihtiyaç duyduğu verinin ön bellekte bulunması ön bellek isabeti olarak adlandırılır. Bulunmama durumuna ise ön bellek kaybı denir ve bellekten gerekli veri çekilir. Bahsedilen iki kavramın arasında ciddi zamansal farklılıklar gözlenmektedir.

3.2.  Yan Kanal Atağı

Algoritmadaki zayıflıklar yerine bilgisayar sisteminin fiziksel durumundaki farklılıklardan çıkarılan bilgilere dayanan bir atak türüdür. Intel’in ocak ayında yayınladığı raporda da belirtildiği gibi; bu atak türü mikro mimari özelliklerini ölçmek gibi, sistemi gözlemleyerek çalışır [6]. Doğrudan programın akışına müdahale etmez. Asıl amacı ise; mikro ölçekte gerçekleşen durum değişikliklerini mimari ölçeğe taşımak ve böylece hedeflenen veriyi ele geçirmektir. Güç tüketimi, ses ve zamana bağlı ölçümler sistemleri istismar etmek için kullanılabilir [7].

Ön bellek yan kanal atakları saldırganın ön bellek kullanımını izleyebilmesine bağlıdır. Flush+Reload isimli teknikte, L3 paylaşımlı ön bellek hedef alınır [8]. Paylaşımlı olduğu için, hedef süreç ile aynı işlemci çekirdeğini kullanmak dahi gerekli değildir. İki sürecin kullanacağı ortak programlar var ise, bu programlar belleğe 2. kez yüklenmez ve aynı adresler ortaklaşa kullanılır. Ortak kodlar salt okunur bellekte bulunduğu için, iki süreç de birbirini etkileyemez. İlgili teknik ile saldırganın amacı ise, hedef sürecin hangi kod bloklarını çalıştırdığını saptamaya çalışmak. Bu atak 3 adımdan oluşur:
  1. Saldırgan L3 ön belleğinde bulunan kontrol kod bloklarını yok eder,
  2. Ardından belli bir süre beklemeye geçer,
  3. Yeterince bekledikten sonra kontrol kod bloklarını çekmeye çalışır ve toplamda geçen süreyi esas alarak hedef sürecin bekleme zamanında bu bloğu kullanıp kullanmadığını tespit etmeye çalışır.
Eğer hedef süreç bekleme zamanında bu kod bloklarını çalıştırdıysa, ön bellek kaybı durumu ortaya çıkar ve bellekten çekmesi gerekir. İlgili kod bloğu paylaşılan ön bellek alanında depolanır. Saldırganın ilgili kod bloğunu çekme süresi kısa sürerse, hedef sürecin bekleme zamanında bu bloğu çalıştırdığı anlaşılır. Eğer uzun sürerse, bu kodların ön bellekte bulunmadığı yani sürecin bunları çalıştırmadığı anlaşılır.

3.3.  Spekülatif Yürütme

Her bir işlemci döngüsünde 1 komut çalıştıran işlemci türüne ölçekleyici işlemci denir [9]. Bu işlemci türü modern işlemcilerin en basit halidir. Süper ölçekleyici isimli tür ise her bir işlemci döngüsünde birden fazla komut çalıştırabilme yeteneğine sahiptir. Sıralı süper ölçekleyici işlemciler, gelen komutların gerekliliklerine bağlı olarak analiz edip bunları eş zamanlı yürütmeye çalışır. Böylece işlemci saat hızı artışı belli bir oranda devam etse dahi, genel performans anlamında büyük bir artış sağlanmış olur. Sırasız işlemciler gelen komutları gerekliliklerine bağlı olarak sırasız olarak çalıştırabilir. Yani sıradaki komut için gerekli olan hesaplamalar veya bellekten gelmesi gereken veriler beklenirken, sonradan çalışması gereken komutları çalıştırabilir ve böylece maksimum paralelleşme sağlanır. Bu tip işlemcilerde bir komut sırası yapısı vardır. Gelen komutlar çözümlenmelerinin ardından komut sırasına girerler ve gereklilikleri sağlanana kadar beklerler. Sağlandıktan sonra da ilgili işlem birimlerine geçerler. Peki sıradaki komut hali hazırda belli olmayan bir değere bağlı olarak programın akışını dallandıracaksa (‘if’ sorguları, döngüler), işlemci nasıl bir yol izlemeli? Spekülatif yürütme optimizasyonu böyle durumların önüne geçmek ve gene performans artışı sağlamak amacıyla geliştirilmiştir. Bu optimizasyonda işlemci sofistike hesaplamalar yaparak programın muhtemelen devam edeceği dallanmayı tahmin etmeye çalışır ve buna bağlı olarak ihtiyaç duyulacağı kesinleşmemiş komutları çalıştırmaya başlar. Eğer yaptığı tahmin doğru çıkarsa hesapladığı sonuçları alarak programın akışına devam eder. Ama dallanmayı yanlış tahmin etmişse hesaplanan verileri yok sayar akışa doğru yoldan devam eder. İhtiyaç duyulmayan komutların çalıştırılması elbette gereksiz bir enerji tüketimi manasına geliyor. Fakat geneline vurduğumuzda kazanılan performans çok fazla olduğu için, bu kriter göz ardı edilir.

Bir komutun emekli olması için öncelikle gerekli tüm hesaplamalarının yapılması ve sonuçlarının elde edilmesi gerekir. Ardından ilgili komut ile alakalı bir hata oluşup oluşmadığı kontrol edilir. Hatanın olmadığı durumlarda komut emekli olur ve sonuçları da işlemcinin mimari durumunu değiştirir [10]. Bir hata ile karşılaşılırsa eğer, hesaplanan veriler ve o esnada komut sırasında bekleyen diğer komutlar yok sayılır ve hata ile ilgili programın kendisinden veya yoksa işletim sisteminden o hatayla alakalı bölüm çalıştırılır [11]. Böylece o komut emekli olamaz ve sonuçları işlemcinin mimari durumunu etkilemez. Fakat mikro ölçekte bu sonuçlar farklılıklar oluşturabilir. Bellekten çekilen verilerin ön bellekte depolanması buna bir örnektir. Yetkisiz bir sürecin çekirdek alanından veri çekmesi program akışında hatanın oluşmasına neden olur. İlk önce istenilen veri çekilir. İşlemcinin mimari düzeyini değiştirmeden yani komut emekli olmadan hata olup olmadığı kontrol edilir. Ve bu aşamada, çekirdek alanından yetkisiz bir şekilde veri okunmaya çalışıldığı anlaşılır ve hata oluşur.

Hata kontrolünden önce çekirdek alanından okunan veri, çalışabilmek için bu veriye ihtiyaç duyan komutlara sağlanır. Bu komutların çalışmaya başlaması ile, yetkisiz çekirdek alanından veri çekme kaynaklı program akışından hata oluşması arasındaki sürede, çekilen veri kullanılarak mikro düzeyde durum değişikliklerine neden olunabilir. Böylece yan kanal ataklarına ortam hazırlanmış olur.

4.    Meltdown

Meltdown, işlemci mimarisindeki performans optimizasyon teknolojilerini suistimal ederek haritalandırılmış çekirdek alanından veri okumaya imkan veren donanımsal bir zafiyettir. Yazılımdan bağımsız bir zafiyet olduğu için hem Windows hem de Linux işletim sistemlerinde çalışır, kişisel bilgisayarlar ve bulut sistemlerini de etkiler.

Meltdown toplamda 3 adımdan oluşur:
  1. Saldırganın yetkisi dahilinde olmayan bir alandan veri çekilir,
  2. Bu veri kullanılarak işlemcinin mikro mimari durumunda değişiklik meydana getirilir,
  3. Yan kanal atağı yöntemi kullanılarak mikro mimari durumdaki değişiklik mimari duruma taşınır, böylece gizli bilgi erişilebilir hale gelir.
Saldırganın saldırıyı gerçekleştirebilmesi için hedef sistemde yetkisiz kod çalıştırabiliyor olması gerekmektedir. Sisteme fiziksel olarak erişimi ise gerekli değildir.

Şekil 3 - Meltdown zafiyetinin detaylarını incelemek amacıyla örnek olarak kullandığımız assembly komutları. Meltdown makalesinden [2] alınarak düzenlenmiştir.

Şekil 3’te mov komutu ile ilgili adreslerden verilerin çekilmesi, shl komutu ile de ilgili değerde sola kaydırma amaçlanmıştır. Yetkisiz bir kullanıcı tarafından çalıştırıldığında ilk olarak 4. satırdaki mov komutu işlemci tarafından çözümlenir ve komut sırasına alınır. Gereklilikleri sağlandığında, ilgili adresten veri istenir. Ön bellek isabeti veya kaybı durumlarına göre, istenilen veri ön bellekten veya bellekten temin edilir. Veri işlemciye ulaştığında, herhangi bir hata oluşup oluşmadığının kontrolünden önce (ki yetkisiz bir şekilde çekirdek alanından veri okunduğu için hata oluşacaktır) bu veriye ihtiyaç duyan ve spekülatif olarak çalışmayı bekleyen komutlara ulaştırılır. Hata kontrolünün yapılmasından önce gerekli işlemler yapılırsa, çekirdek alanından çekilen veri işlemci mikro mimari durumunu etkileyebilir ve yan kanal atağına uygun bir ortam oluşturur. O aşamada Flush+Reload yöntemi kullanılarak, ön bellekte hangi verinin depolandığını hassas ölçümler yaparak tespit edip, veriler ele geçirilmiş olur.

Flush+Reload atağının kullanılabilmesi için, öncelikle kullanıcı alanından okunacak verinin ön bellekte bulunmaması gerekir. Çalıştırılan komutlar sonrasında ön bellekte depolanan verinin izinden giderek çekirdek alanındaki gizli veriyi ele geçirebiliriz. Gizli veri kullanılarak kullanıcı alanından veri çekilmesi sırasında (9. satır) ön bellek kaybı oluşacaktır ve veri bellekten istenecektir. Sonradan ihtiyaç duyulması halinde daha hızlı erişebilmek adına işlemci bu veri bloğunu ön belleğe depolayacaktır. Buradaki önemli nokta; gizli veri ile kullanıcı alanından adres hesabı yaparken oluşacak adreslerin aynı sayfa içerisinde yer almalarını önlemek. Çünkü eğer aynı sayfa içerisinde yer alırlarsa, Flush+Reload kullanarak ön bellekte hangi verinin depolandığını tespit etmeye çalıştığımızda, veri blok halinde depolandığı için, başarısız oluruz. Onun için 8. satırdaki komutu çalıştırıyoruz. Eğer işlemcinin 4. satırdaki komut ile alakalı hata kontrolü yapmasından önce bu işlemleri yapabilirsek, işlemciyi mikro düzeyde etkilemiş oluruz (ön bellekte veri depolanması). Hata kontrolünün ardından bütün komut sırası ve elde edilen veriler yok sayılıp hata ile ilgili kod bloğu çalıştırılmaya başlanacaktır. Yani 8. ve 9. satırdaki komutların çalışmasına ihtiyaç duyulmayacaktı. İşlemcide spekülatif yürütme optimizasyonu olmasaydı, ihtiyaç duyulup duyulmayacağı belli olmayan komutlar çalıştırılmayacaktı, dolayısıyla atağımız başarısız olacaktı.

Bu aşamada, ön bellekte gizli veriye bağlı bir adresin verisi depolanmıştır. Ön bellekte depolanan veriyi tespit ettiğimizde, gizli veriye bağlı olduğu için otomatik olarak gizli veriyi de tespit etmiş oluyoruz. Böylece sistemin çekirdek alanından yetkisiz bir şekilde bilgi okumuş oluyoruz. Bu işlem farklı adresler ile tekrarlandığında (4. satırdaki adres), haritalandırılmış bütün çekirdek alanını yani dolayısıyla bütün parola bilgilerini, kredi kartı bilgilerini, şifreleme anahtarlarını ve diğer hassas verileri ele geçirmiş oluyoruz.

Araştırma grubunun test maksatlı yaptığı saldırıda maksimum 503 KB/s okuma hızına ulaştığı belirtilmiş. Ayrıca yapılan testlerde, konteyner teknolojilerinde (Docker, LXC, OpenVZ vs.) başarılı sonuçlar elde edilmiştir. Ortak çekirdeği paylaşan konteynerlerin birinden yapılan saldırıda, sadece çekirdek alanı değil, diğer konteynerlerin de verilerine ulaşılabilmiş.

5.    Meltdown’a Karşı Önlemler

İşletim sistemi çekirdeğini hedef alan ve yan kanal ataklarına karşı geliştirilmiş olan KAISER sistemi, çekirdek ile kullanıcı alanlarının izolasyonunu kuvvetlendirir ve bu tip ataklara karşı koruma sağlar. İşlemci kullanıcı modunda iken, donanımın çekirdek adresleri ile ilgili herhangi bir bilgi tutmamasını sağlar [12]. Başka bir deyişle, kullanıcı alanına x86 mimarisinin birkaç ihtiyacı harici, çekirdek alanının haritalandırılmasını engeller. Araştırma ekibinin yayınladığı makalede, KAISER sisteminin kazara Meltdown ataklarına karşı da güvenlik sağladığı belirtiliyor. Bu güvenlik sisteminin, Linux’e “Kernel page-table isolation” adı ile geleceğini ve Windows ve Mac OSX için de benzer güncellemelerin geleceğini kaydediyor.

KAISER koruma sağlasa da x86 mimarisinin dizaynından dolayı, birçok çekirdek alanının kullanıcı alanından erişilmesi gerekmektedir. Yani bu durum Meltdown’a atak yüzeyi kazandırmış oluyor.



Intel yayınlamış olduğu dokümanda, bu zafiyet için yazılımsal modifikasyonlar yapılmasını öneriyor. Spekülatif yürütmenin durdurulması gereken yerlerde LFENCE komutunun kullanılması gerektiğini belirtiyor. Linux çekirdeği üzerinde statik analizler yaptıklarını ve spekülatif yürütme bariyerinin ihtiyaç duyulduğu birçok bölüm bulduklarını da ayrıca ekliyorlar.


6.    Referanslar

[1].               https://googleprojectzero.blogspot.com.tr/2018/01/reading-privileged-memory-with-side.html?m=1
[2].               https://meltdownattack.com/meltdown.pdf
[3].               https://spectreattack.com/spectre.pdf
[4].               https://en.wikipedia.org/wiki/Moore%27s_law
[5].               https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cache-allocation-technology-white-paper.pdf, Improving Real-Time Performance by Utilizing Cache Allocation Technology
[6].               https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/Intel-Analysis-of-Speculative-Execution-Side-Channels.pdf, Intel Analysis of Speculative Execution Side Channels
[7].               https://en.wikipedia.org/wiki/Side-channel_attack
[8].               https://eprint.iacr.org/2013/448.pdf, FLUSH+RELOAD: a High Resolution, Low Noise, L3 Cache Side-Channel Attack
[9].               https://www.raspberrypi.org/blog/why-raspberry-pi-isnt-vulnerable-to-spectre-or-meltdown/
[10].            https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
[11].            https://cyber.wtf/2017/07/28/negative-result-reading-kernel-memory-from-user-mode/, Negative Result: Reading Kernel Memory From User Mode
[12].            https://gruss.cc/files/kaiser.pdf, KASLR is Dead: Long Live KASLR





Mert Değirmenci
Cyber Security Expert
CRYPTTECH - Cyber Security Intelligence

Yorumlar

Bu blogdaki popüler yayınlar

1. Geleneksel Stajyer CTF Soru ve Cevapları

B*-Tree (BTree, BPlusTree) Veri Yapısı ile Veri İndeksleme

2. Geleneksel Stajyer CTF Soru ve Cevapları - 2017