Kurgusal Yürütme Yan Kanalları için C++ Geliştirici Kılavuzu
Bu makale, geliştiricilerin C++ yazılımındaki kurgusal yürütme tarafı kanal donanım güvenlik açıklarını tanımlama ve azaltma konusunda yardımcı olması için rehberlik içerir. Bu güvenlik açıkları güven sınırları genelinde hassas bilgileri açığa çıkarır ve tahmine dayalı, sıra dışı yönergelerin yürütülmesini destekleyen işlemcilerde çalışan yazılımları etkileyebilir. Bu güvenlik açıkları sınıfı ilk olarak Ocak 2018'de açıklanmıştır ve Ek arka plan ve yönergeler Microsoft'un güvenlik danışmanlığında bulunabilir.
Bu makale tarafından sağlanan kılavuz, aşağıdakiler tarafından temsil edilen güvenlik açığı sınıfları ile ilgilidir:
CVE-2017-5753, Spectre varyant 1 olarak da bilinir. Bu donanım güvenlik açığı sınıfı, koşullu dal yanlış gösterimi sonucunda oluşan tahmini yürütme nedeniyle ortaya çıkabilecek yan kanallarla ilgilidir. Visual Studio 2017'deki Microsoft C++ derleyicisi (sürüm 15.5.5'den başlayarak), CVE-2017-5753 ile ilgili sınırlı sayıda güvenlik açığı olabilecek kodlama deseni için derleme süresi azaltma sağlayan anahtar desteği
/Qspectre
içerir. Bu/Qspectre
anahtar, Visual Studio 2015 Güncelleştirme 3 ile KB 4338871 arasında da kullanılabilir. Bayrağın/Qspectre
belgeleri, etkileri ve kullanımı hakkında daha fazla bilgi sağlar.CVE-2018-3639, Tahmini Mağaza Atlama (SSB) olarak da bilinir. Bu donanım güvenlik açığı sınıfı, bir bellek erişimi yanlış gösterimi sonucunda bağımlı deponun önündeki bir yükün tahmini olarak yürütülmesi nedeniyle ortaya çıkabilecek yan kanallarla ilgilidir.
Kurgusal yürütme tarafı kanal güvenlik açıklarına erişilebilir bir giriş, bu sorunları keşfeden araştırma ekiplerinden biri tarafından The Case of Spectre and Meltdown başlıklı sunuda bulunabilir.
Kurgusal Yürütme Tarafı Kanalı donanım güvenlik açıkları nelerdir?
Modern CPU'lar, yönergelerin kurgusal ve sıra dışı yürütülmesini kullanarak daha yüksek performans dereceleri sağlar. Örneğin, bu genellikle CPU'nun tahmin edilen dal hedefinde yönergeleri tahmine dayalı olarak yürütmeye başlamasını sağlayan dalların hedefini tahmin ederek (koşullu ve dolaylı) gerçekleştirilir ve böylece gerçek dal hedefi çözümlenene kadar duraklamadan kaçınılır. CPU'nun daha sonra bir yanlış tahmin gerçekleştiğini bulması durumunda, tahmine dayalı olarak hesaplanan tüm makine durumu atılır. Bu, yanlış tahmin edilen spekülasyonun mimari olarak görünür bir etkisi olmamasını sağlar.
Kurgusal yürütme mimari olarak görünür durumu etkilemese de, CPU tarafından kullanılan çeşitli önbellekler gibi mimari olmayan durumda artık izlemeler bırakabilir. Yan kanal güvenlik açıklarına neden olabilecek, kurgusal yürütmenin bu artık izleridir. Bunu daha iyi anlamak için CVE-2017-5753 (Sınır Denetimi Atlama) örneğini sağlayan aşağıdaki kod parçasını göz önünde bulundurun:
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
if (untrusted_index < buffer_size) {
unsigned char value = buffer[untrusted_index];
return shared_buffer[value * 4096];
}
}
Bu örnekte, ReadByte
bu arabelleğe bir arabellek, arabellek boyutu ve dizin sağlanır. tarafından untrusted_index
belirtilen dizin parametresi, yönetim dışı bir işlem gibi daha az ayrıcalıklı bir bağlam tarafından sağlanır. değerinden küçükseuntrusted_index
, bu dizindeki karakter konumundan buffer
okunur ve tarafından shared_buffer
başvurulan paylaşılan bellek bölgesine dizin oluşturmak için buffer_size
kullanılır.
Mimari açıdan bakıldığında, her zaman değerinden buffer_size
küçük olacağı garanti edilen untrusted_index
bu kod dizisi son derece güvenlidir. Ancak, kurgusal yürütmenin varlığında, CPU'nun koşullu dalı yanlış tanıtıp , değerinden büyük veya eşit olsa untrusted_index
bile if deyiminin gövdesini yürütmesi buffer_size
mümkündür. Bunun bir sonucu olarak CPU, sınırlarının ötesinden buffer
(gizli dizi olabilir) tahmini olarak bir bayt okuyabilir ve ardından aracılığıyla shared_buffer
sonraki bir yükün adresini hesaplamak için bu bayt değerini kullanabilir.
CPU sonunda bu yanlış gösterimi algılasa da, artık yan etkiler, sınırlardan okunan bayt değeri hakkındaki bilgileri ortaya koyan CPU önbelleğinde buffer
bırakılabilir. Bu yan etkiler, içindeki her önbellek satırına shared_buffer
ne kadar hızlı erişilebileceğini araştırmak suretiyle sistemde çalışan daha az ayrıcalıklı bir bağlam tarafından algılanabilir. Bunu gerçekleştirmek için izleyebileceğiniz adımlar şunlardır:
değerinden küçük olmasıyla
untrusted_index
birden çok kez çağırmaReadByte
.buffer_size
Saldırı bağlamı, dal tahmincisi küçük olduğu gibi alınmayacak şekilde eğitilecek şekildeuntrusted_index
buffer_size
kurban bağlamını çağırmasınaReadByte
(örneğin RPC aracılığıyla) neden olabilir.içindeki
shared_buffer
tüm önbellek satırlarını temizleyin. Saldırıya uğrayan bağlam, tarafındanshared_buffer
başvurulan belleğin paylaşılan bölgesindeki tüm önbellek satırlarını temizlemelidir. Bellek bölgesi paylaşıldığından bu basittir ve gibi_mm_clflush
iç bilgileri kullanarak gerçekleştirilebilir.untrusted_index
değerindenbuffer_size
büyük olarak çağır.ReadByte
Saldırı bağlamı, kurban bağlamın dalın alınmayacak olduğunu yanlış tahmin edecek şekilde çağırmasınaReadByte
neden olur. Bu, işlemcinin , değerinden büyükbuffer_size
olan if bloğunununtrusted_index
gövdesini tahmine dayalı olarak yürütmesine neden olur, böylece sınır dışı okumasınabuffer
neden olur. Sonuç olarak,shared_buffer
sınır dışı olarak okunan olası bir gizli dizi değeri kullanılarak dizine alınır ve bu da ilgili önbellek satırının CPU tarafından yüklenmesine neden olur.Hangisine en hızlı şekilde erişildiğine bakmak için içindeki her önbellek satırını
shared_buffer
okuyun. Saldırı bağlamı içindekishared_buffer
her önbellek satırını okuyabilir ve diğerlerinden önemli ölçüde daha hızlı yüklenen önbellek satırını algılayabilir. Bu, 3. adıma kadar getirilmiş olma olasılığı olan önbellek satırıdır. Bu örnekte bayt değeri ile önbellek satırı arasında 1:1 ilişkisi olduğundan, bu, saldırganın sınırların dışında okunan bayt değerini çıkarmasını sağlar.
Yukarıdaki adımlar, CVE-2017-5753 örneğinden yararlanma ile birlikte FLUSH+RELOAD olarak bilinen bir tekniği kullanma örneği sağlar.
Hangi yazılım senaryoları etkilenebilir?
Güvenlik Geliştirme Yaşam Döngüsü (SDL) gibi bir işlem kullanarak güvenli yazılım geliştirmek için genellikle geliştiricilerin kendi uygulamalarındaki güven sınırlarını belirlemesi gerekir. Bir uygulamanın sistemdeki başka bir işlem veya çekirdek modu cihaz sürücüsü söz konusu olduğunda yönetici olmayan bir kullanıcı modu işlemi gibi daha az güvenilen bir bağlam tarafından sağlanan verilerle etkileşim kurabileceği yerlerde güven sınırı vardır. Kurgusal yürütme tarafı kanallarını içeren yeni güvenlik açıkları sınıfı, bir cihazdaki kodu ve verileri yalıtan mevcut yazılım güvenlik modellerindeki güven sınırlarının çoğuyla ilgilidir.
Aşağıdaki tabloda geliştiricilerin ortaya çıkan bu güvenlik açıklarıyla ilgilenmeleri gerekebilecek yazılım güvenlik modellerinin bir özeti yer alır:
Güven sınırı | Açıklama |
---|---|
Sanal makine sınırı | Başka bir sanal makineden güvenilmeyen veriler alan ayrı sanal makinelerdeki iş yüklerini yalıtan uygulamalar risk altında olabilir. |
Çekirdek sınırı | Yönetici olmayan bir kullanıcı modu işleminden güvenilmeyen veriler alan çekirdek modu cihaz sürücüsü risk altında olabilir. |
İşlem sınırı | Uzak Yordam Çağrısı (RPC), paylaşılan bellek veya diğer İşlemler Arası İletişim (IPC) mekanizmaları gibi yerel sistemde çalışan başka bir işlemden güvenilmeyen veriler alan bir uygulama risk altında olabilir. |
Kapanım sınırı | Güvenli bir kapanım içinde (Intel SGX gibi) yürütülen ve kapanım dışından güvenilmeyen veriler alan bir uygulama risk altında olabilir. |
Dil sınırı | Daha üst düzey bir dilde yazılmış güvenilmeyen kodu yorumlayan veya Tam Zamanında (JIT) derleyen ve yürüten bir uygulama risk altında olabilir. |
Yukarıdaki güven sınırlarından herhangi birine maruz kalan saldırı yüzeyi olan uygulamalar, kurgusal yürütme tarafı kanal güvenlik açıklarının olası örneklerini belirlemek ve azaltmak için saldırı yüzeyindeki kodu gözden geçirmelidir. Uzak ağ protokolleri gibi uzak saldırı yüzeylerine maruz kalan güven sınırlarının kurgusal yürütme tarafı kanal güvenlik açıklarına karşı risk altında olduğu gösterilmediği belirtilmelidir.
Güvenlik açığı olabilecek kodlama desenleri
Birden çok kodlama deseninin sonucu olarak kurgusal yürütme tarafı kanal güvenlik açıkları ortaya çıkabilir. Bu bölümde, güvenlik açığı olabilecek kodlama desenleri açıklanır ve her biri için örnekler sağlanır, ancak bu temalardaki varyasyonların mevcut olabileceği kabul edilmelidir. Bu nedenle, geliştiricilerin bu desenleri örnek olarak alması ve güvenlik açığı olabilecek tüm kodlama desenlerinin kapsamlı bir listesi olarak almaları tavsiye edilir. Günümüzde yazılımlarda mevcut olabilecek bellek güvenliği güvenlik açıklarının aynı sınıfları, arabellek taşmaları, sınır dışı dizi erişimleri, başlatılmamış bellek kullanımı, tür karışıklığı vb. dahil ancak bunlarla sınırlı olmamak üzere kurgusal ve sıra dışı yürütme yollarında da bulunabilir. Saldırganların mimari yollarda bellek güvenliği güvenlik açıklarından yararlanmak için kullanabileceği temel öğeler, kurgusal yollar için de geçerli olabilir.
Genel olarak, koşullu dal yanlış kullanımla ilgili kurgusal yürütme tarafı kanalları, bir koşullu ifade daha az güvenilir bir bağlam tarafından denetlenebilen veya etkilenebilen veriler üzerinde çalıştığında ortaya çıkabilir. Örneğin, bu, , for
, while
, switch
veya üçüncül deyimlerde if
kullanılan koşullu ifadeleri içerebilir. Bu deyimlerin her biri için derleyici, CPU'nun çalışma zamanında dal hedefini tahmin edebileceği bir koşullu dal oluşturabilir.
Her örnek için, bir geliştiricinin bir engeli azaltma olarak tanıtabileceği "SPEKÜLASYON ENGELİ" ifadesini içeren bir açıklama eklenir. Bu, risk azaltmalar bölümünde daha ayrıntılı olarak ele alınıyor.
Tahmini sınır dışı yük
Bu kodlama desenleri kategorisi, tahmini sınır dışı bellek erişimine yol açan bir koşullu dal yanlış kullanım içerir.
Bir yükü besleyen sınır dışı dizi yükü
Bu kodlama düzeni başlangıçta CVE-2017-5753 (Sınır Denetimi Atlama) için güvenlik açığı bulunan kodlama desenidir. Bu makalenin arka plan bölümünde bu desen ayrıntılı olarak açıklanmaktadır.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
if (untrusted_index < buffer_size) {
// SPECULATION BARRIER
unsigned char value = buffer[untrusted_index];
return shared_buffer[value * 4096];
}
}
Benzer şekilde, bir dizi sınır dışı yükü, bir yanlış kullanım nedeniyle sonlandırıcı koşulunu aşan bir döngüyle birlikte oluşabilir. Bu örnekte, ifadeyle ilişkili koşullu dal, değerinden x < buffer_size
büyük veya buna eşit buffer_size
olduğunda x
döngünün for
gövdesini yanlış tanıtabilir ve tahmine dayalı olarak yürütebilir ve bu da tahmini sınır dışı yüke neden olur.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadBytes(unsigned char *buffer, unsigned int buffer_size) {
for (unsigned int x = 0; x < buffer_size; x++) {
// SPECULATION BARRIER
unsigned char value = buffer[x];
return shared_buffer[value * 4096];
}
}
Dizi sınır dışı yük dolaylı dalı besliyor
Bu kodlama düzeni, bir koşullu dalın yanlış anlaşılmasının bir işlev işaretçisi dizisine sınır dışı erişime yol açması ve ardından sınır dışı okunan hedef adrese dolaylı bir dala yol açması durumunu içerir. Aşağıdaki kod parçacığı bunu gösteren bir örnek sağlar.
Bu örnekte DispatchMessage'a parametresi aracılığıyla güvenilmeyen bir ileti tanımlayıcısı untrusted_message_id
sağlanır. değerinden MAX_MESSAGE_ID
küçükseuntrusted_message_id
, işlev işaretçileri dizisine dizin oluşturmak ve ilgili dal hedefine dal eklemek için kullanılır. Bu kod mimari açıdan güvenlidir, ancak CPU koşullu dalı yanlış ifade ederse değeri değerinden büyük veya buna eşit MAX_MESSAGE_ID
olduğunda dizine eklenerek untrusted_message_id
sınır dışı erişime neden olabilirDispatchTable
. Bu, dizi sınırlarının ötesinde türetilen bir dal hedef adresinden tahmini yürütmeye neden olabilir ve bu da tahmini olarak yürütülen koda bağlı olarak bilgilerin açığa çıkmasına neden olabilir.
#define MAX_MESSAGE_ID 16
typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);
const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];
void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
if (untrusted_message_id < MAX_MESSAGE_ID) {
// SPECULATION BARRIER
DispatchTable[untrusted_message_id](buffer, buffer_size);
}
}
Başka bir yükü besleyen sınır dışı dizi yükünde olduğu gibi, bu durum da yanlış bir ifade nedeniyle sonlandırıcı koşulunu aşan bir döngüyle birlikte ortaya çıkabilir.
Dizi sınır dışı depo dolaylı dalı besliyor
Önceki örnekte, tahmini sınır dışı yükün dolaylı dal hedefini nasıl etkilediği gösterilmiş olsa da, sınır dışı deponun işlev işaretçisi veya dönüş adresi gibi dolaylı dal hedefini değiştirmesi de mümkündür. Bu, saldırgan tarafından belirtilen bir adresten tahmini yürütmeye yol açabilir.
Bu örnekte, parametresi aracılığıyla untrusted_index
güvenilmeyen bir dizin geçirilir. Dizinin öğe değerinden küçükse untrusted_index
(256 öğe), içinde ptr
sağlanan işaretçi değeri diziye pointers
pointers
yazılır. Bu kod mimari açıdan güvenlidir, ancak CPU koşullu dalı yanlış yorumlarsa yığın tarafından ayrılan pointers
dizinin sınırlarının ötesinde tahmine dayalı olarak yazılmasıyla sonuçlanabilirptr
. Bu, için dönüş adresinin WriteSlot
tahmini bozulmasına neden olabilir. Bir saldırgan değerini ptr
denetleyebilirse, tahmini yol boyunca döndürdüğünde WriteSlot
rastgele bir adresten tahmini yürütmeye neden olabilir.
unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
void *pointers[256];
if (untrusted_index < 256) {
// SPECULATION BARRIER
pointers[untrusted_index] = ptr;
}
}
Benzer şekilde, adlı func
bir işlev işaretçisi yerel değişkeni yığında ayrılmışsa, koşullu dal yanlış kullanım gerçekleştiğinde başvuruda bulunan adresi func
tahmine dayalı olarak değiştirmek mümkün olabilir. Bu, işlev işaretçisi aracılığıyla çağrıldığında rastgele bir adresten tahmini yürütmeye neden olabilir.
unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
void *pointers[256];
void (*func)() = &callback;
if (untrusted_index < 256) {
// SPECULATION BARRIER
pointers[untrusted_index] = ptr;
}
func();
}
Bu örneklerin her ikisinin de yığına ayrılmış dolaylı dal işaretçilerinin tahmini olarak değiştirilmesini içerdiği belirtilmelidir. Genel değişkenler, yığınla ayrılan bellek ve hatta bazı CPU'larda salt okunur bellek için kurgusal değişiklikler de gerçekleşebilir. Yığına ayrılmış bellek için, Microsoft C++ derleyicisi, derleyici güvenlik özelliğinin bir parçası /GS
olarak arabelleklerin bir güvenlik tanımlama bilgisinin bitişiğine yerleştirilmesi gibi yerel değişkenleri yeniden sıralayarak yığın ayrılmış dolaylı dal hedeflerini tahmine dayalı olarak değiştirmeyi zorlaştıracak adımları zaten gerçekleştirmektedir.
Kurgusal tür karışıklığı
Bu kategori, kurgusal tür karışıklığına neden olabilecek kodlama desenleriyle ilgilidir. Bu, kurgusal yürütme sırasında mimari olmayan bir yol boyunca yanlış bir tür kullanılarak belleğe erişildiğinde oluşur. Hem koşullu dal yanlış kullanım hem de tahmini depo atlamaları tahmini tür karışıklığına yol açabilir.
Tahmini depo atlama için bu durum, derleyicinin birden çok türdeki değişkenler için bir yığın konumunu yeniden kullanacağı senaryolarda ortaya çıkabilir. Bunun nedeni, türündeki A
bir değişkenin mimari deposunun atlanması ve dolayısıyla türün A
yükünün değişken atanmadan önce tahmini olarak yürütülmesine olanak sağlamasıdır. Önceden depolanan değişken farklı bir türdeyse, bu tahmine dayalı tür karışıklığı koşullarını oluşturabilir.
Koşullu dal yanlış kullanımı için, spekülatif tür karışıklığının neden olabileceği farklı koşulları açıklamak için aşağıdaki kod parçacığı kullanılır.
enum TypeName {
Type1,
Type2
};
class CBaseType {
public:
CBaseType(TypeName type) : type(type) {}
TypeName type;
};
class CType1 : public CBaseType {
public:
CType1() : CBaseType(Type1) {}
char field1[256];
unsigned char field2;
};
class CType2 : public CBaseType {
public:
CType2() : CBaseType(Type2) {}
void (*dispatch_routine)();
unsigned char field2;
};
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ProcessType(CBaseType *obj)
{
if (obj->type == Type1) {
// SPECULATION BARRIER
CType1 *obj1 = static_cast<CType1 *>(obj);
unsigned char value = obj1->field2;
return shared_buffer[value * 4096];
}
else if (obj->type == Type2) {
// SPECULATION BARRIER
CType2 *obj2 = static_cast<CType2 *>(obj);
obj2->dispatch_routine();
return obj2->field2;
}
}
Sınır dışı yüke yol açan tahmini tür karışıklığı
Bu kodlama düzeni, tahmini tür karışıklığının, yüklenen değerin sonraki bir yük adresini beslediği sınırlar dışında veya tür karışıklığı alan erişimine neden olabileceği durumu içerir. Bu, sınır dışı dizi kodlama düzenine benzer, ancak yukarıda gösterildiği gibi alternatif bir kodlama dizisiyle gösterilir. Bu örnekte, saldırı bağlamı, kurban bağlamının türünde CType1
bir nesneyle birden çok kez yürütülmesine ProcessType
neden olabilir (type
alan eşittir Type1
). Bu, ilk if
deyimin alınmadığını tahmin etmek için koşullu dalı eğitme etkisine sahip olur. Daha sonra saldırı bağlamı, kurban bağlamının türünde CType2
bir nesneyle yürütülmesine ProcessType
neden olabilir. bu, ilk if
deyimin koşullu dalının deyiminin gövdesini yanlış tanıtması ve yürütmesi if
ve bu nedenle türünde bir nesnesini 'a CType1
ataması durumunda tahmini tür CType2
karışıklığına neden olabilir. değerinden küçük CType1
olduğundanCType2
, 'a CType1::field2
bellek erişimi gizli olabilecek tahmini sınır dışı veri yüküne neden olur. Bu değer daha sonra daha önce açıklanan sınır dışı dizi örneğinde olduğu gibi gözlemlenebilir yan etkiler oluşturabilen bir yükte shared_buffer
kullanılır.
Dolaylı dala yol açan kurgusal tür karışıklığı
Bu kodlama düzeni, kurgusal tür karışıklığının kurgusal yürütme sırasında güvenli olmayan bir dolaylı dala neden olabileceği durumu içerir. Bu örnekte, saldırı bağlamı, kurban bağlamının türünde CType2
bir nesneyle birden çok kez yürütülmesine ProcessType
neden olabilir (type
alan eşittir Type2
). Bu, ilk if
deyimin alınması ve else if
deyiminin alınmaması için koşullu dalı eğitme etkisine sahiptir. Daha sonra saldırı bağlamı, kurban bağlamının türünde CType1
bir nesneyle yürütülmesine ProcessType
neden olabilir. bu, ilk if
deyimin koşullu dalının alındığını ve deyiminin else if
alınmadığını tahmin edip gövdesini else if
yürütüp türündeki CType1
bir nesneyi 'ye CType2
ataması durumunda tahmini tür karışıklığına neden olabilir. CType2::dispatch_routine
Alan dizisiyle CType1::field1
çakıştığı için char
bu, istenmeyen dal hedefine kurgusal bir dolaylı dalla sonuçlanabilir. Saldırı bağlamı dizideki CType1::field1
bayt değerlerini denetleyebilirse, dal hedef adresini denetleyebilir.
Tahmini başlatılmamış kullanım
Bu kodlama desenleri kategorisi, kurgusal yürütmenin başlatılmamış belleğe erişebileceği ve bunu sonraki bir yükü veya dolaylı dalı beslemek için kullanabileceği senaryoları içerir. Bu kodlama düzenlerinin kötüye kullanılabilmesi için, saldırganın kullanıldığı bağlam tarafından başlatılmadan kullanılan belleğin içeriğini denetleyebilmesi veya anlamlı bir şekilde etkileyebilmesi gerekir.
Sınır dışı yüke yol açan tahmini başlatılmamış kullanım
Tahmini bir başlatılmamış kullanım, saldırgan tarafından denetlenen bir değer kullanılarak sınır dışı yüke yol açabilir. Aşağıdaki örnekte değeri index
tüm mimari yollara atanır trusted_index
ve trusted_index
değerinden küçük veya eşit buffer_size
olduğu varsayılır. Ancak, derleyici tarafından üretilen koda bağlı olarak, ve bağımlı ifadelerin atamadan buffer[index]
index
önce yürütülmesine izin veren tahmini bir depo atlaması oluşabilir. Bu durumda, için başlatılmamış bir değerindex
, bir saldırganın hassas bilgileri sınırların dışında okumasına ve bağımlı yükü shared_buffer
aracılığıyla bir yan kanal üzerinden iletmesine olanak tanıyan uzaklık buffer
olarak kullanılır.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
void InitializeIndex(unsigned int trusted_index, unsigned int *index) {
*index = trusted_index;
}
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int trusted_index) {
unsigned int index;
InitializeIndex(trusted_index, &index); // not inlined
// SPECULATION BARRIER
unsigned char value = buffer[index];
return shared_buffer[value * 4096];
}
Dolaylı dala yol açan kurgusal başlatılmamış kullanım
Kurgusal bir başlatılmamış kullanım, dal hedefinin bir saldırgan tarafından denetlendiği dolaylı bir dala yol açabilir. Aşağıdaki örnekte, routine
değerine veya değerine bağlı mode
olarak atanır DefaultMessageRoutine1
DefaultMessageRoutine
. Mimari yolda, bu durum her zaman dolaylı daldan önce başlatılmasına neden routine
olur. Ancak, derleyici tarafından üretilen koda bağlı olarak, dolaylı dalın routine
atamadan routine
önce tahmini olarak yürütülmesini sağlayan tahmini bir depo atlama işlemi oluşabilir. Bu durumda, saldırganın başlatılmamış değerini routine
etkileyebileceği veya denetleyebileceği varsayılarak, saldırgan rastgele bir adresten tahmine dayalı olarak yürütebilir.
#define MAX_MESSAGE_ID 16
typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);
const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];
extern unsigned int mode;
void InitializeRoutine(MESSAGE_ROUTINE *routine) {
if (mode == 1) {
*routine = &DefaultMessageRoutine1;
}
else {
*routine = &DefaultMessageRoutine;
}
}
void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
MESSAGE_ROUTINE routine;
InitializeRoutine(&routine); // not inlined
// SPECULATION BARRIER
routine(buffer, buffer_size);
}
Azaltma seçenekleri
Kaynak kodda değişiklik yapılarak kurgusal yürütme tarafı kanal güvenlik açıkları azaltılabilir. Bu değişiklikler, spekülasyon engeli eklemek veya hassas bilgilerin tahmini yürütmeye erişilemez hale getirilmesi için uygulamanın tasarımında değişiklikler yapmak gibi bir güvenlik açığının belirli örneklerini azaltmayı içerebilir.
El ile izleme yoluyla spekülasyon engeli
Kurgusal yürütmenin mimari olmayan bir yol boyunca ilerlemesini önlemek için bir geliştirici tarafından el ile bir spekülasyon engeli eklenebilir. Örneğin, bir geliştirici, koşullu bloğun gövdesinde tehlikeli bir kodlama düzeninden önce, bloğun başına (koşullu daldan sonra) veya endişe duyulan ilk yüklemeden önce bir spekülasyon engeli ekleyebilir. Bu, koşullu dal yanlış beyanlarının yürütmeyi seri hale getirerek mimari olmayan bir yolda tehlikeli kodu yürütmesini engeller. Spekülasyon engeli dizisi, aşağıdaki tabloda açıklandığı gibi donanım mimarisine göre farklılık gösterir:
Mimari | CVE-2017-5753 için spekülasyon engeli | CVE-2018-3639 için spekülasyon engeli |
---|---|---|
x86/x64 | _mm_lfence() | _mm_lfence() |
ARM | şu anda kullanılamıyor | __dsb(0) |
ARM64 | şu anda kullanılamıyor | __dsb(0) |
Örneğin, aşağıdaki kod deseni aşağıda gösterildiği gibi iç kullanılarak azaltılabilir _mm_lfence
.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
if (untrusted_index < buffer_size) {
_mm_lfence();
unsigned char value = buffer[untrusted_index];
return shared_buffer[value * 4096];
}
}
Derleyici zaman izlemesi aracılığıyla spekülasyon engeli
Visual Studio 2017'deki Microsoft C++ derleyicisi (sürüm 15.5.5'den başlayarak) CVE-2017-5753 ile ilgili sınırlı sayıda güvenlik açığı olabilecek kodlama deseni için otomatik olarak bir spekülasyon engeli ekleyen anahtar için destek /Qspectre
içerir. Bayrağın /Qspectre
belgeleri, etkileri ve kullanımı hakkında daha fazla bilgi sağlar. Bu bayrağın tüm güvenlik açığı olabilecek kodlama desenlerini kapsamadığını ve bu tür geliştiricilerin bu güvenlik açıkları sınıfı için kapsamlı bir azaltma olarak buna güvenmemesi gerektiğini unutmayın.
Dizi dizinlerini maskeleme
Tahmini sınır dışı yükün oluşabileceği durumlarda dizi dizini, dizi dizinini açıkça bağlamaya yönelik mantık ekleyerek hem mimari hem de mimari olmayan yolda kesin olarak sınırlanabilir. Örneğin, bir dizi iki üssüne hizalanmış bir boyuta ayrılabiliyorsa basit bir maske eklenebilir. Bu, iki güçle hizalandığı buffer_size
varsayıldığı aşağıdaki örnekte gösterilmiştir. Bu, bir koşullu dal yanlış işlemi gerçekleşse ve değerinden untrusted_index
büyük veya buna eşit bir değerle geçirilse bile her zaman değerinden küçük buffer_size
olmasını buffer_size
sağlar.untrusted_index
Burada gerçekleştirilen dizin maskelemenin, derleyici tarafından oluşturulan koda bağlı olarak tahmini depo atlamasına tabi olabileceği belirtilmelidir.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
if (untrusted_index < buffer_size) {
untrusted_index &= (buffer_size - 1);
unsigned char value = buffer[untrusted_index];
return shared_buffer[value * 4096];
}
}
Bellekten hassas bilgileri kaldırma
Kurgusal yürütme tarafı kanal güvenlik açıklarını azaltmak için kullanılabilecek bir diğer teknik de hassas bilgileri bellekten kaldırmaktır. Yazılım geliştiricileri, kurgusal yürütme sırasında hassas bilgilere erişilmeyecek şekilde uygulamalarını yeniden düzenleme fırsatı arayabilir. Bu, hassas bilgileri ayrı işlemler halinde yalıtmak için bir uygulamanın tasarımını yeniden düzenleyerek gerçekleştirilebilir. Örneğin, bir web tarayıcısı uygulaması her web kaynağıyla ilişkili verileri ayrı işlemlere ayırmayı deneyerek bir işlemin tahmini yürütme yoluyla çıkış noktaları arası verilere erişmesini engelleyebilir.
Ayrıca bkz.
Kurgusal yürütme yan kanal güvenlik açıklarını azaltma kılavuzu
Kurgusal yürütme tarafı kanal donanım güvenlik açıklarını azaltma