Linux Yazılım Güvenliği

Önleme Mekanizmaları

Tampon taşmaları kullanılarak yapılan saldırılardan korunmak için çeşitli yöntemler bulunmaktadır, biz burada üç tanesinden kısaca bahsedeceğiz.

Adres Alanı Randomizasyonu (Address Space Layout Randomization)

İşletim sistemi çekirdeği, güvenlik amaçlı olarak, proseslerin yığın alanlarını farklı adreslerden başlatmaktadır. Bu özelliği test amaçlı olarak aşağıdaki gibi kapattığımızı hatırlayınız.

echo 0 > /proc/sys/kernel/randomize_va_space

Bu sayede komut satırı argümanlarının yerini bulabilmiş ve başka bir program üzerinden kabuk uygulamasını çalıştırabilmiştik. Bu özelliğin açık olması durumunda yığındaki bir alanın adresinin bulunması güçleşecektir.

Yığının Çalıştırılabilir Olmaması (Non-executable Stack)

Tampona kod enjekte edilmesi durumunda yığındaki bir kodun çalıştırıldığını hatırlayınız. Yığının çalıştırılamaz olması bu saldırıyı önleyecektir.

Yığının çalıştırılabilir olup olmadığı ELF dosya formatında PT_GNU_STACK isimli bir alanda tutulmaktadır. Bu alanın değeri değiştirilerek yığının çalıştırılabilir olma durumuna sonradan müdahale edilebilir. Bu yüzden bu koruma yöntemi kolaylıkla aşılabilir.

execstack uygulaması ile çalışacak olan prosese ait yığın alanı çalıştırılabilir olarak işaretlenebilir.

execstack --set-execstack <program ismi>

Derleyicinin Yığın Kontrolünü Etkinleştirmek (Stack Smashing Protection)

Bu özelliğin açık olması durumunda, derleyici tarafından fazladan kod yazılarak yığının bütünlüğü kontrol edilmektedir. Derleyici fonksiyonun başında tamponun altına bir değer eklemekte ve fonksiyondan çıkarken bu değerin değişip değişmediğini kontrol etmektedir. Koruma değişkeni (guard variable) olarak isimlendirilen bu alandaki değerin değişmesi durumunda __stack_chk_fail isimli fonksiyon çağrılır. __stack_chk_fail yığının durumu hakkında bilgi verdikten sonra uygulamayı sonlandırır.

Bir örnek üzerinden bu durumu inceleyelim.

#include <stdio.h>
#include <string.h>

void foo() {
    char buf[4] = {0};
    strcpy(buf, "sincap");
}

int main() {
    foo();
    return 0;
}

Örnek kodu guard.c ismiyle saklayıp aşağıdaki gibi derleyebilirsiniz.

gcc -m32 -oguard guard.c -fstack-protector --save-temps

foo için derleyicinin ürettiği sembolik makina kodları ve yığının temsili aşağıdaki gibidir.

foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    %gs:20, %eax
        movl    %eax, -12(%ebp)
        xorl    %eax, %eax
        movl    $0, -16(%ebp)
        leal    -16(%ebp), %eax
        movl    $1668180339, (%eax)
        movw    $28769, 4(%eax)
        movb    $0, 6(%eax)
        movl    -12(%ebp), %eax
        xorl    %gs:20, %eax
        je      .L2
        call    __stack_chk_fail
.L2:
        leave
        ret

Uygulamayı çalıştırdığımızda, yığına fazladan veri yazarak koruma değişkenini bozduğumuz için, aşağıdaki gibi sonlandığını görmekteyiz.

./guard
*** stack smashing detected ***: ./guard terminated
Aborted (core dumped)