CVE-2020-14386: Linux in its core Yetki Yükseltme
Son zamanlarda, Linux çekirdeğindeki paket soketleri kaynak kodlarını denetleme üzerine zaman harcadım. Bu, Linux çekirdeğindeki bir bellek bozulması açığı olan CVE-2020-14386'nın keşfine yol açtı. Bu tür bir açık, bir Linux sistemde ayrıcalıklı olmayan bir kullanıcıdan kök kullanıcıya ayrıcalıklarını yükseltmek için kullanılabilir. Bu blogda, bu açığın teknik bir incelemesini, nasıl sömürülebileceğini ve Palo Alto Networks müşterilerinin nasıl korunduğunu sağlayacağım.
Birkaç yıl önce, paket soketlerinde (CVE-2017-7308 ve CVE-2016-8655) birkaç açık keşfedildi ve Project Zero blogunda ve Openwall'da bu türün ana işlevselliği hakkında birkaç yayın var.
Özellikle, açığın tetiklenebilmesi için çekirdeğin AF_PACKET soketlerinin etkinleştirilmiş olması gerekmektedir (CONFIG_PACKET=y) ve tetikleme işlemi için CAP_NET_RAW ayrıcalığının olması gerekmektedir, bu da kullanıcı ad alanlarının etkinleştirilmiş olması (CONFIG_USER_NS=y) ve ayrıcalıklı olmayan kullanıcılar tarafından erişilebilir olması durumunda bir ayrıcalıklı olmayan kullanıcı ad alanında elde edilebilir. Şaşırtıcı bir şekilde, bu uzun kısıtlama listesi Ubuntu gibi bazı dağıtımlarda varsayılan olarak karşılanır.
Palo Alto Networks Cortex XDR müşterileri, kötü niyetli davranışları bir dizi olay üzerinde izleyen ve saldırı tespit edildiğinde hemen durduran Davranışsal Tehdit Koruması (BTP) özelliği ve Yerel Ayrıcalık Yükseltme Koruması modülü kombinasyonu ile bu hatayı engelleyebilirler.
Teknik Detaylar
(Bu bölümdeki tüm kod figürleri 5.7 çekirdek kaynaklarından alınmıştır.)
AF_PACKET soketlerinin uygulanmasının Project Zero blogunda detaylı olarak ele alındığı gerçeğinden dolayı, bu makalede zaten açıklanan bazı ayrıntıları (örneğin, çerçeveler ile bloklar arasındaki ilişki) atlayacağım ve doğrudan açığı ve kök nedenini açıklayacağım.
Hata, bellek bozulmasına yol açan bir aritmetik sorundan kaynaklanmaktadır. Sorun, (net/packet/af_packet.c) konumunda bulunan tpacket_rcv işlevinde yatmaktadır.
Aritmetik hata, 19 Temmuz 2008'de, 8913336 ("packet: add PACKET_RESERVE sockopt") taahhütünde tanıtıldı. Ancak, sadece Şubat 2016'da, 58d19b19cd99 ("packet: vnet_hdr support for tpacket_rcv") taahhütünde bellek bozulması için tetiklenebilir hale geldi. Bu sorunu düzeltmek için bazı girişimler yapıldı, örneğin Mayıs 2017'de bcc536 ("net/packet: fix overflow in check for tp_reserve") taahhüdü ve Ağustos 2017'de edb58be ("packet: Don't write vnet header beyond end of buffer") taahhüdü. Ancak, bu düzeltmeler bellek bozulmasını önlemek için yeterli değildi.
Öncelikle PACKET_RESERVE seçeneğine bir göz atalım: Açığı tetiklemek için, AF_PACKET etki alanında SOCK_RAW türünde bir ham soket oluşturulmalı ve PACKET_RESERVE seçeneği için belirli bir değer olmalıdır.
Kılavuzda bahsedilen baş boşluk, her paketin halka tamponunda alınan gerçek verilerden önce ayrılan, kullanıcı tarafından belirtilen boyutta bir tampondur. Bu değer, setsockopt sistem çağrısı aracılığıyla kullanıcı alanından ayarlanabilir.
Kod:
case PACKET_RESERVE:
{
unsigned int val;
if (optlen != sizeof(val))
return -EINVAL;
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
if (val > INT_MAX)
return -EINVAL;
lock_sock(sk);
if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
ret = -EBUSY;
} else {
po->tp_reserve = val;
ret = 0;
}
release_sock(sk);
return ret;
}
Şekil 1. setsockopt - PACKET_RESERVE Uygulaması
Şekil 1'de gördüğümüz gibi, başlangıçta, değerin INT_MAX'ten daha küçük olduğu kontrol edilir. Bu kontrol, packet_set_ring'de minimum çerçeve boyutunun hesaplanmasında bir taşmanın önlenmesi için bu yamada eklenmiştir. Daha sonra, alınan/gönderilen halka tamponu için sayfaların ayrılmadığı doğrulanır. Bu, tp_reserve alanı ile halka tamponu arasındaki tutarsızlığın önlenmesi için yapılır.
tp_reserve değerini ayarladıktan sonra, PACKET_RX_RING optname ile setsockopt sistem çağrısı aracılığıyla halka tamponunun kendisi için ayrılması tetiklenebilir.
Şekil 1'de gördüğümüz gibi, başlangıçta, değerin INT_MAX'ten daha küçük olduğu kontrol edilir. Bu kontrol, packet_set_ring'de minimum çerçeve boyutunun hesaplanmasında bir taşmanın önlenmesi için bu yamada eklenmiştir. Daha sonra, alınan/gönderilen halka tamponu için sayfaların ayrılmadığı doğrulanır. Bu, tp_reserve alanı ile halka tamponu arasındaki tutarsızlığın önlenmesi için yapılır.
tp_reserve değerini ayarladıktan sonra, PACKET_RX_RING optname ile setsockopt sistem çağrısı aracılığıyla halka tamponunun kendisi için ayrılması tetiklenebilir.
Kod:
Create a memory-mapped ring buffer for asynchronous packet reception.
Şekil 2. Manuel paket - PACKET_RX_RING seçeneği.
Bu, packet_set_ring işlevinde uygulanmıştır. Başlangıçta, halka tamponu ayrılmadan önce, kullanıcı alanından alınan tpacket_req yapısında birkaç aritmetik kontrol yapılır:
Bu, packet_set_ring işlevinde uygulanmıştır. Başlangıçta, halka tamponu ayrılmadan önce, kullanıcı alanından alınan tpacket_req yapısında birkaç aritmetik kontrol yapılır:
Kod:
min_frame_size = po->tp_hdrlen + po->tp_reserve;
…
...
if (unlikely(req->tp_frame_size < min_frame_size))
goto out;
Şekil 3. packet_set_ring işlevindeki sağlamlık kontrollerinin bir kısmı.
Şekil 3'te gördüğümüz gibi, öncelikle, minimum çerçeve boyutu hesaplanır, ardından bu değer kullanıcı alanından alınan değerle karşılaştırılır. Bu kontrol, her çerçevede tpacket başlık yapısı için (ilgili sürümü için) ve tp_reserve bayt sayısı için alan olup olmadığını sağlar.
Tüm sağlamlık kontrollerini yaptıktan sonra, halka tamponu kendisi, alloc_pg_vec çağrısı ile ayrılır:
Şekil 3'te gördüğümüz gibi, öncelikle, minimum çerçeve boyutu hesaplanır, ardından bu değer kullanıcı alanından alınan değerle karşılaştırılır. Bu kontrol, her çerçevede tpacket başlık yapısı için (ilgili sürümü için) ve tp_reserve bayt sayısı için alan olup olmadığını sağlar.
Tüm sağlamlık kontrollerini yaptıktan sonra, halka tamponu kendisi, alloc_pg_vec çağrısı ile ayrılır:
Kod:
order = get_order(req->tp_block_size);
pg_vec = alloc_pg_vec(req, order);
Şekil 4. Halka tamponu ayırma işlevinin çağrıldığı packet_set_ring işlevi.
Yukarıdaki şekilden gördüğümüz gibi, blok boyutu kullanıcı alanından kontrol edilir. alloc_pg_vec işlevi pg_vec dizisini ayırır ve ardından her bloğu alloc_one_pg_vec_page işlevi aracılığıyla ayırır:
Yukarıdaki şekilden gördüğümüz gibi, blok boyutu kullanıcı alanından kontrol edilir. alloc_pg_vec işlevi pg_vec dizisini ayırır ve ardından her bloğu alloc_one_pg_vec_page işlevi aracılığıyla ayırır:
Kod:
static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order)
{
unsigned int block_nr = req->tp_block_nr;
struct pgv *pg_vec;
int i;
pg_vec = kcalloc(block_nr, sizeof(struct pgv), GFP_KERNEL | __GFP_NOWARN);
if (unlikely(!pg_vec))
goto out;
for (i = 0; i < block_nr; i++) {
pg_vec[i].buffer = alloc_one_pg_vec_page(order);
Şekil 5. alloc_pg_vec uygulaması.
alloc_one_pg_vec_page işlevi, blok sayfalarını ayırmak için __get_free_pages işlevini kullanır:
Kod:
static char *alloc_one_pg_vec_page(unsigned long order)
{
char *buffer;
gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP |
__GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY;
buffer = (char *) __get_free_pages(gfp_flags, order);
if (buffer)
return buffer;
Şekil 6. alloc_one_pg_vec_page uygulaması.
Blokların ayrılmasından sonra, pg_vec dizisi, soketi temsil eden packet_sock yapısına gömülü olan packet_ring_buffer yapısında saklanır.
Bir arayüzde bir paket alındığında, tpacket_rcv işlevine bağlanan soket çağrılır ve paket verileri, TPACKET meta verileri ile birlikte halka tamponuna yazılır. Gerçek bir uygulamada, tcpdump gibi, bu tampon kullanıcı alanına mmap ile eşlenir ve paket verileri buradan okunabilir.
Hata
Şimdi, tpacket_rcv işlevinin uygulanmasına (Şekil 7) derinlemesine inelim. İlk olarak, alınan paketin ağ başlığının ofsetini maclen'e çıkarmak için skb_network_offset çağrılır. Bizim durumumuzda, bu boyut 14 bayttır, yani bir Ethernet başlığının boyutudur. Bundan sonra, netoff (çerçevedeki ağ başlığının ofsetini temsil eder) hesaplanır, sabit bir versiyona göre değişmeyen TPACKET başlığı, maclen ve kullanıcı tarafından kontrol edilen tp_reserve değeri dikkate alınarak hesaplanır.
Ancak, bu hesaplama taşabilir, çünkü tp_reserve'in türü unsigned int ve netoff'un türü unsigned short'tur ve tp_reserve değerinin (daha önce gördüğümüz gibi) tek kısıtı INT_MAX'ten küçük olmasıdır.
Blokların ayrılmasından sonra, pg_vec dizisi, soketi temsil eden packet_sock yapısına gömülü olan packet_ring_buffer yapısında saklanır.
Bir arayüzde bir paket alındığında, tpacket_rcv işlevine bağlanan soket çağrılır ve paket verileri, TPACKET meta verileri ile birlikte halka tamponuna yazılır. Gerçek bir uygulamada, tcpdump gibi, bu tampon kullanıcı alanına mmap ile eşlenir ve paket verileri buradan okunabilir.
Hata
Şimdi, tpacket_rcv işlevinin uygulanmasına (Şekil 7) derinlemesine inelim. İlk olarak, alınan paketin ağ başlığının ofsetini maclen'e çıkarmak için skb_network_offset çağrılır. Bizim durumumuzda, bu boyut 14 bayttır, yani bir Ethernet başlığının boyutudur. Bundan sonra, netoff (çerçevedeki ağ başlığının ofsetini temsil eder) hesaplanır, sabit bir versiyona göre değişmeyen TPACKET başlığı, maclen ve kullanıcı tarafından kontrol edilen tp_reserve değeri dikkate alınarak hesaplanır.
Ancak, bu hesaplama taşabilir, çünkü tp_reserve'in türü unsigned int ve netoff'un türü unsigned short'tur ve tp_reserve değerinin (daha önce gördüğümüz gibi) tek kısıtı INT_MAX'ten küçük olmasıdır.
Kod:
if (sk->sk_type == SOCK_DGRAM) {
...
else {
unsigned int maclen = skb_network_offset(skb);
netoff = TPACKET_ALIGN(po->tp_hdrlen +
(maclen < 16 ? 16 : maclen)) +
po->tp_reserve;
if (po->has_vnet_hdr) {
netoff += sizeof(struct virtio_net_hdr);
do_vnet = true;
}
macoff = netoff - maclen;
}
Şekil 7. tpacket_rcv'deki aritmetik hesaplama
Şekil 7'de ayrıca gösterildiği gibi, sokette PACKET_VNET_HDR seçeneği ayarlanmışsa, virtio_net_hdr yapısını hesaba katmak için sizeof(struct virtio_net_hdr) eklenir, bu da doğrudan Ethernet başlığının hemen ötesinde olmalıdır. Ve nihayetinde, Ethernet başlığının ofseti hesaplanır ve macoff'a kaydedilir.
Daha sonra, bu işlevde, aşağıdaki Şekil 8'de görüldüğü gibi, virtio_net_hdr yapısı virtio_net_hdr_from_skb işlevini kullanarak halka tamponuna yazılır. Şekil 8'de, h.raw, halka tamponunda şu anda boş olan çerçeve içine işaret eder (ki bu, alloc_pg_vec ile ayrılmıştır).
Şekil 7'de ayrıca gösterildiği gibi, sokette PACKET_VNET_HDR seçeneği ayarlanmışsa, virtio_net_hdr yapısını hesaba katmak için sizeof(struct virtio_net_hdr) eklenir, bu da doğrudan Ethernet başlığının hemen ötesinde olmalıdır. Ve nihayetinde, Ethernet başlığının ofseti hesaplanır ve macoff'a kaydedilir.
Daha sonra, bu işlevde, aşağıdaki Şekil 8'de görüldüğü gibi, virtio_net_hdr yapısı virtio_net_hdr_from_skb işlevini kullanarak halka tamponuna yazılır. Şekil 8'de, h.raw, halka tamponunda şu anda boş olan çerçeve içine işaret eder (ki bu, alloc_pg_vec ile ayrılmıştır).
Kod:
if (do_vnet &&
virtio_net_hdr_from_skb(skb, h.raw + macoff -
sizeof(struct virtio_net_hdr),
vio_le(), true, 0))
goto drop_n_account;
Şekil 8. tpacket_rcv işlevinde virtio_net_hdr_from_skb işlevine çağrı
Başlangıçta, netoff'un küçük bir değer olmasını sağlamak için taşmayı kullanabileceğimi düşündüm, böylece macoff, bir bloğun boyutundan daha büyük bir değer alabilir (taşma ile) ve tamponun sınırlarının ötesine yazabilir.
Ancak, bunu aşağıdaki kontrol engeller:
Başlangıçta, netoff'un küçük bir değer olmasını sağlamak için taşmayı kullanabileceğimi düşündüm, böylece macoff, bir bloğun boyutundan daha büyük bir değer alabilir (taşma ile) ve tamponun sınırlarının ötesine yazabilir.
Ancak, bunu aşağıdaki kontrol engeller:
Kod:
if (po->tp_version <= TPACKET_V2) {
if (macoff + snaplen > po->rx_ring.frame_size) {
...
...
snaplen = po->rx_ring.frame_size - macoff;
if ((int)snaplen < 0) {
snaplen = 0;
do_vnet = false;
}
}
Şekil 9. tpacket_rcv işlevinde başka bir aritmetik kontrol.
Bu kontrol, hafıza bozulmasını önlemek için yeterli değildir, çünkü netoff'u taşarak hala küçük bir tamsayı değeri yapabiliriz. Özellikle, macoff'u, 10 bayt olan struct virtio_net_hdr boyutundan daha küçük yapabiliriz ve virtio_net_hdr_from_skb kullanarak tamponun sınırlarının ötesine yazabiliriz.
İlkel
macoff'un değerini kontrol ederek, virtio_net_hdr yapısını kontrol edilebilir bir ofset olan halka tamponunun en fazla 10 bayt gerisine kadar başlatabiliriz. virtio_net_hdr_from_skb işlevi, struct'un tamamını sıfırlayarak başlar ve ardından struct içindeki bazı alanları skb yapısına dayalı olarak başlatır.
Bu kontrol, hafıza bozulmasını önlemek için yeterli değildir, çünkü netoff'u taşarak hala küçük bir tamsayı değeri yapabiliriz. Özellikle, macoff'u, 10 bayt olan struct virtio_net_hdr boyutundan daha küçük yapabiliriz ve virtio_net_hdr_from_skb kullanarak tamponun sınırlarının ötesine yazabiliriz.
İlkel
macoff'un değerini kontrol ederek, virtio_net_hdr yapısını kontrol edilebilir bir ofset olan halka tamponunun en fazla 10 bayt gerisine kadar başlatabiliriz. virtio_net_hdr_from_skb işlevi, struct'un tamamını sıfırlayarak başlar ve ardından struct içindeki bazı alanları skb yapısına dayalı olarak başlatır.
Kod:
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
struct virtio_net_hdr *hdr,
bool little_endian,
bool has_data_valid,
int vlan_hlen)
{
memset(hdr, 0, sizeof(*hdr)); /* no info leak */
if (skb_is_gso(skb)) {
...
if (skb->ip_summed == CHECKSUM_PARTIAL) {
…
Şekil 10. virtio_net_hdr_from_skb işlevinin uygulanması.
Ancak, skb'yi sıfırlanmış bir şekilde ayarlayabiliriz, böylece yapıya yalnızca sıfırlar yazılacaktır. Bu, __get_free_pages ayrımının 1-10 bayt arkasında sıfırlama yeteneğimizle bize kalır. Herhangi bir heap manipülasyon taktiği yapmadan, derhal bir çekirdek çökmesi oluşacaktır.
POC
Aşağıdaki Openwall konusunda, açığı tetiklemek için bir POC kodu bulunabilir.
Yama
Hatayı düzeltmek için aşağıdaki yamayı sundum.
Şekil 11. Hatam için önerdiğim yama.
Fikir, netoff'un türünü unsigned short'tan unsigned int'e değiştirirsek, USHRT_MAX'i aşıp aşmadığını kontrol edebiliriz ve öyleyse paketi bırakabilir ve daha fazla işleme izin vermeyebiliriz.
Sömürü FikriSömürü için fikrimiz, ilkel bir kullanım sonrası bellek erişimi oluşturmaktır. Bunun için, bir nesnenin bir referans sayısını azaltmayı düşündük. Örneğin, bir nesnenin 0x10001 değerinde bir refcount değeri varsa, bozulma aşağıdaki gibi görünecektir:
Ancak, skb'yi sıfırlanmış bir şekilde ayarlayabiliriz, böylece yapıya yalnızca sıfırlar yazılacaktır. Bu, __get_free_pages ayrımının 1-10 bayt arkasında sıfırlama yeteneğimizle bize kalır. Herhangi bir heap manipülasyon taktiği yapmadan, derhal bir çekirdek çökmesi oluşacaktır.
POC
Aşağıdaki Openwall konusunda, açığı tetiklemek için bir POC kodu bulunabilir.
Yama
Hatayı düzeltmek için aşağıdaki yamayı sundum.
Şekil 11. Hatam için önerdiğim yama.
Fikir, netoff'un türünü unsigned short'tan unsigned int'e değiştirirsek, USHRT_MAX'i aşıp aşmadığını kontrol edebiliriz ve öyleyse paketi bırakabilir ve daha fazla işleme izin vermeyebiliriz.
Sömürü FikriSömürü için fikrimiz, ilkel bir kullanım sonrası bellek erişimi oluşturmaktır. Bunun için, bir nesnenin bir referans sayısını azaltmayı düşündük. Örneğin, bir nesnenin 0x10001 değerinde bir refcount değeri varsa, bozulma aşağıdaki gibi görünecektir:
Şekil 12. Bir nesne referans sayısında bir baytın sıfırlanması.
Aşağıdaki Şekil 13'te gördüğümüz gibi, bozulmadan sonra refcount'un değeri 0x1 olacaktır, bu yüzden bir referans serbest bırakıldıktan sonra nesne serbest bırakılacaktır.
Ancak, bunun gerçekleşmesi için aşağıdaki kısıtlamaların karşılanması gerekmektedir:
Referans sayısı nesnenin son 1-10 baytında bulunmalıdır.Nesneyi bir sayfanın sonunda ayırabilmeliyiz.Bu, get_free_pages'ın bir sayfa hizalı adres döndürmesi nedeniyledir.Birkaç grep ifadesi kullanarak ve kodun bazı manuel analizleriyle, aşağıdaki nesneyi bulduk:
Aşağıdaki Şekil 13'te gördüğümüz gibi, bozulmadan sonra refcount'un değeri 0x1 olacaktır, bu yüzden bir referans serbest bırakıldıktan sonra nesne serbest bırakılacaktır.
Ancak, bunun gerçekleşmesi için aşağıdaki kısıtlamaların karşılanması gerekmektedir:
Referans sayısı nesnenin son 1-10 baytında bulunmalıdır.Nesneyi bir sayfanın sonunda ayırabilmeliyiz.Bu, get_free_pages'ın bir sayfa hizalı adres döndürmesi nedeniyledir.Birkaç grep ifadesi kullanarak ve kodun bazı manuel analizleriyle, aşağıdaki nesneyi bulduk:
Kod:
struct sctp_shared_key {
struct list_head key_list;
struct sctp_auth_bytes *key;
refcount_t refcnt;
__u16 key_id;
__u8 deactivated;
};
Şekil 13. sctp_shared_key yapısının tanımı.
Bu nesnenin kısıtlamalarımızı karşıladığını gözlemledik:
Ayrıcalıklı olmayan bir kullanıcı bağlamından bir sctp sunucusu ve istemcisi oluşturabiliriz.
Nesne özellikle sctp_auth_shkey_create işlevinde ayrılır.
Nesneyi bir sayfanın sonunda ayırabiliriz.
Nesnenin boyutu 32 bayt ve kmalloc aracılığıyla ayrılır. Bu, nesnenin kmalloc-32 önbelleğinde ayrıldığı anlamına gelir.
get_free_pages tahsisi arkasında bir kmalloc-32 yığın önbellek sayfası ayırabildiğimizi doğrulayabildik. Bu nedenle, bu yığın önbellek sayfasındaki son nesneyi bozabiliriz.
4096 % 32 = 0 nedeniyle, yığın sayfasının sonunda boş bir alan bulunmamaktadır ve son nesne tahsisimizin hemen arkasında ayrılır. Diğer yığın önbellek boyutları, 96 bayt gibi, bizim için iyi olmayabilir, çünkü 4096 % 96 != 0.
Refcnt alanının en yüksek 2 baytını bozabiliriz.
Derleme sonrası, key_id ve deactivated'in boyutu sırasıyla 4 bayttır.
Hatanın 9-10 baytını bozmak için kullandığımızda, refcnt alanının en anlamlı 1-2 baytını bozmuş oluruz.
Sonuç
Linux çekirdeğinde hala bu kadar basit aritmetik güvenlik sorunlarının varlığına ve daha önce keşfedilmemiş olmasına şaşırdım. Ayrıca, ayrıcalıklı olmayan kullanıcı ad alanları, yerel ayrıcalık yükseltmesi için büyük bir saldırı yüzeyi sunar, bu nedenle dağıtımlar bunları etkinleştirip etmemeleri konusunda düşünmelidir.
Palo Alto Networks Cortex XDR, tehditleri uç noktalarda durdurur ve ağ ve bulut güvenliği ile uygulama uygularak başarılı siber saldırıları önler. Bu hatanın sömürülmesini önlemek için, Cortex XDR'daki Davranışsal Tehdit Koruması (BTP) özelliği ve Yerel Ayrıcalık Yükseltme Koruması modülü, kötü amaçlı davranışları bir dizi olay boyunca izleyerek ve tespit edildiğinde saldırıyı hemen sonlandırarak görev yapar.
Bu nesnenin kısıtlamalarımızı karşıladığını gözlemledik:
Ayrıcalıklı olmayan bir kullanıcı bağlamından bir sctp sunucusu ve istemcisi oluşturabiliriz.
Nesne özellikle sctp_auth_shkey_create işlevinde ayrılır.
Nesneyi bir sayfanın sonunda ayırabiliriz.
Nesnenin boyutu 32 bayt ve kmalloc aracılığıyla ayrılır. Bu, nesnenin kmalloc-32 önbelleğinde ayrıldığı anlamına gelir.
get_free_pages tahsisi arkasında bir kmalloc-32 yığın önbellek sayfası ayırabildiğimizi doğrulayabildik. Bu nedenle, bu yığın önbellek sayfasındaki son nesneyi bozabiliriz.
4096 % 32 = 0 nedeniyle, yığın sayfasının sonunda boş bir alan bulunmamaktadır ve son nesne tahsisimizin hemen arkasında ayrılır. Diğer yığın önbellek boyutları, 96 bayt gibi, bizim için iyi olmayabilir, çünkü 4096 % 96 != 0.
Refcnt alanının en yüksek 2 baytını bozabiliriz.
Derleme sonrası, key_id ve deactivated'in boyutu sırasıyla 4 bayttır.
Hatanın 9-10 baytını bozmak için kullandığımızda, refcnt alanının en anlamlı 1-2 baytını bozmuş oluruz.
Sonuç
Linux çekirdeğinde hala bu kadar basit aritmetik güvenlik sorunlarının varlığına ve daha önce keşfedilmemiş olmasına şaşırdım. Ayrıca, ayrıcalıklı olmayan kullanıcı ad alanları, yerel ayrıcalık yükseltmesi için büyük bir saldırı yüzeyi sunar, bu nedenle dağıtımlar bunları etkinleştirip etmemeleri konusunda düşünmelidir.
Palo Alto Networks Cortex XDR, tehditleri uç noktalarda durdurur ve ağ ve bulut güvenliği ile uygulama uygularak başarılı siber saldırıları önler. Bu hatanın sömürülmesini önlemek için, Cortex XDR'daki Davranışsal Tehdit Koruması (BTP) özelliği ve Yerel Ayrıcalık Yükseltme Koruması modülü, kötü amaçlı davranışları bir dizi olay boyunca izleyerek ve tespit edildiğinde saldırıyı hemen sonlandırarak görev yapar.


