Kernel Exploitation Notları - figbux

roro

Üye
[+] Kernel Exploitation Notları

[*] Önsöz
Kod:
Bu yazı linux'da userspace binary exploitation konusuna aşina olup da,
kernel exploitation'a giriş yapan birisi tarafından tutulan notlardan
oluşmaktadır. Hedef okuyucu kitlesi de benzer nitelikte bilgi sahibi
kişilerdir. Bir "not", yani kişisel ihtiyaçlara yönelik yazılmış bir
yazı; aynı zamanda da yayınlanmış bir yazı, yani biraz konu bağlamı
derli toplu tutulmaya çalışıldı. Konuya başlayanlar için birincil kaynak
olarak okunsun diye değil de, konu başlıklarından haberdar etsin diye
yazıldı. İngilizce/Türkçe karışık bir not oldu. Kernel'e yeni girmiş
biri olarak yazarın bilmediği çok şey var, yazının büyük çoğunluğu
kopyalanmış içerik; yazarın tahminsel olarak sunduğu fikirlerin kesin
doğru olmadığı ise uyarılar ile belirtilmeye gayret edildi.

Yanlışım varsa pullrequest lütfen.
[*] İçindekiler
Kod:
[*] Amaç
[*] Kaynakları edinin
[*] Lab ortamı kurulumu
[*] Kernel ne işe yarıyor?
    [-] Kernel map
    [-] Kernel 5.8'in codebase'ini biraz incelersek:
[-] Ring goygoyu
[+] Kernel Exploitation Temelleri
    [-] Basit exploitation stratejisi - Büyük resim:
    [-] Kernel'de nasıl zafiyetler var?
    [-] Bu zafiyetleri nasıl sömürülür?
    [-] Bazı kernel güvenlik önlemleri ve aşma yöntemleri
    [-] Debugging
    [-] Nerelerde zafiyetler var?
        [~] LKM (Loadable Kernel Module)
            [o] LKM'lerin kullanım alanları:
            [o] LKM dosya yapısı
            [o] LKM'lerle çalışmak için komutlar
[+] LDD3 - Chapter 15, Memory Mapping and DMA
    [-] List of address types used in Linux
        [o] User virtual addresses
        [o] Physical addresses
        [o] Bus addresses
        [o] Kernel logical addresses
        [o] Kernel virtual addresses
    [-] High and Low Memory
        [o] Low memory
        [o] High memory
    [-] Page Tables
    [-] Virtual Memory Areas
    [-] The mmap Device Operation
[-] Proc cheatsheet
[-] Önemli kernel yapıları, fonksiyonlar vs. kernel v5.8 için sunulmuştur
    [~] task_struct: "işlem"in tanımı
    [~] cred struct: işlemin yetkilerinin tanımı
    [-] "current" makrosu: o anki koşan işleme pointer
    [~] file, fdtable, files_struct: dosyalar
    [~] Virtual function table (vft) yaklaşımı
    [~] socket, sock, skb: ağ
    [~] netlink_sock: IPC için özel soket
    [~] Hepsi bir arada: current, task_struct, files_struct, fdtable,
    [~] Reference counter kavramı
[-] cheatsheet
[*] Amaç
Kod:
Neden linux kernelini hacklemek isteyelim?

- Telefonumuzun ya da oyun konsolumuzun üzerinde tam kontrolü elde
  edebiliriz.
- Yazılımsal çok fazla güvenlik önlemi varsa (sandbox vs.) onları teker
  teker aşmak yerine tek seferde sistemi ele geçirebiliriz.
- Sisteme rootkit ya da bootkit yerleştirmek istiyor olabiliriz.
- Milyarlarca linux çalıştıran cihazın olduğu bir gezegeni ele
  geçirebiliriz.
- Root parolamızı unutmuşsak kolayca (!) sıfırlayabiliriz.
- Öylesine.

Tabii ki kernel exploit, userspace exploitation'dan sonraki zincir. Yani
kerneli kırmak için en başta sistemde kod çalıştırabilir konumda
olmalısınız. Mesela bir browser exploit'iniz var, bunu kernel exploitle
zincirliyorsunuz vs.
[*] Kaynakları edinin
Kod:
Kernel'in en güncel dökümantasyonu her zaman kodun kendisi. Çok sık
gelişen bir proje olarak linux kernel'inin sabit bir dökümantasyonu
bulunmuyor. Ayrıca userspace'deki gibi manpages da yok; bir fonksiyonun
ya da makronun ne iş yaptığını öğrenmek için genellikle kodunu okumanız
gerekiyor.

Kodu iyi okuyabileceğiniz bir ortam elzem. Aradığımız fonksiyonu,
sembolü kolayca bulabilir olmalıyız. Bir kaç seçenek var:

1) Online: https://elixir.bootlin.com/linux/v5.8-rc4/source
2) Kodu yerele çekip 'grep' veya 'git grep'le arama yapmak
3) Kodu yerele çekip vim'de gtags (GNU Global) eklentisi kullanmak
4) Kodu yerele çekip vim'de ctags eklentisi kullanmak
5) Kodu yerele çekip bir IDE'de (eclipse vs.) açmak
6) DDG, google vs..

Ben gtags'i seçtim, yerelde kaynakların bulunması iyidir. 'gtags.vim'
eklentisinde önerilen kısayolları da aktifleştirince kolayca kod
üzerinde zıplayabilir oluyoruz.
[*] Lab ortamı kurulumu
Kod:
Başlangıç için lab kurmak yerine halka açık ctf ortamlarındaki kernel
sorularına odaklanmak daha mantıklı. Kerneli derlemek birkaç saat
sürebiliyor falan... Yine de Ubuntu VM içinde QEMU ile gitmek istersek
eksik notlar şu şekilde:

1) Install ubuntu on VM (I've installed ubuntu server 20.04 on vbox)
   - Hey, why not docker? anyways..
2) Download a kernel tarball from kernel.org
3) sudo apt install gcc build-essential libncurses5-dev kernel-package
   bison flex libssl-dev libelf-dev dkms libudev-dev libpci-dev 
   libiberty-dev autoconf
4) make menuconfig    # all needed debug options was set surprisingly,
                      # kgdb, frame pointers, debug info etc. no need to
                      # change anything; just play around
5) make

Then install busybux

1) Download a busybox tarball from https://busybox.net/downloads/
2) make menuconfig
3) make

----8<---- 

Devamı için: https://www.cnblogs.com/hac425/p/9416886.html
[*] CTF’ler
Kod:
pwnable.kr tasks (syscall, rootkit, softmmu, towelroot, kcrc, exynos)
[*] Kernel ne işe yarıyor?
Kod:
"Ooo kernel, en derin, ring-0, matrix" falan filan diyoruz ama samimi
olarak ne yaptığını anlamamız lazım.

Basitçe kernel:
1) Donanımları işletir
2) Kullanıcı uygulamalarının çalışacağı bir ortam sağlar

Bunları şunlarla yapar:
- Donanımları yazılımsal olarak sürer (device drivers)
- Birden çok olan işlemlerin zaman planlamasını yapar (task scheduling)
- Diski yönetir ve dosya sistemi sunar
- Belleği yönetir, virtual memory falan sağlar
- Kullanıcılara syscall'lar sağlar
- vs...


[-] Kernel map

    \Function|  human   |        |            |     |         |       
Layer\       |interface | system | processing | mem | storage | network
-------------+---------------------------------------------------------
user space   |
interfaces   |
-------------+
virtual      |
-------------+
bridges      |
-------------+          (https://makelinux.github.io/kernel/map/)
logical      |               (Not: kernel 2.6.36; eski!)
-------------+
device control
-------------+
hw interfaces|
-------------+
electronics  |
-------------+


[-] Kernel 5.8'in codebase'ini biraz incelersek:

    ~/srcs/linux 0 $ ls -laF
     arch/      --> architecture specific codes
     block/     --> ?
     crypto/    --> crypto implementations
     drivers/   --> device drivers (biggest part of the kernel)
     fs/        --> filesystem
     include/   --> header files
     init/      --> ??bootloader stuff??
     ipc/       --> inter process communications
     kernel/    --> ?
     lib/       --> ?
     mm/        --> memory management
     net/       --> networking
     samples/   --> ?
     security/  --> security subsystems
     sound/     --> sound subsystems
     tools/     --> ?
     usr/       --> ?
     virt/      --> ??virtualization subsystems??
[-] Ring goygoyu
Kod:
CPU'lar katmanlı bir güvenlik mimarisi sağlar. 4 katman için yapı:

Ring 3  --> Kullanıcı uygulamaları
Ring 2  --> (Günümüzde pek kullanılmıyor)
Ring 1  --> (Günümüzde pek kullanılmıyor)
Ring 0  --> Kernel

Root olmak ring 0'da olmak anlamına gelmiyor; hala userspacedeyiz!

Syscall'lar kernelin kullanıcılara sunduğu "API"si olduğu için, bir
syscall kullandığımız zaman (mesela "chown") işlemci ring 0'a
geçiyor, syscall'ı çalıştırıyor ve tekrar ring 3'e zıplıyor.
[+] Kernel Exploitation Temelleri
Kod:
[-] Basit exploitation stratejisi - Büyük resim:

    1) Kernel kodunda bir zafiyet bul
    2) Bu zafiyeti sömürerek kod çalıştır
    3) Kendi işleminin yetkilerini root'unkine yükselt 
    4) Kernelden userland'e geri dön
    5) Root yetkileriyle hoş vakit geçir*

    *Kernel'de neden kalmıyoruz: Çünkü kernel uzayında networking,
     dosya okuma/yazma, process oluşturma vs. userspace'e göre çok
     zahmetli. Userspace'e bu yüzden dönmeyi tercih ediyoruz.

[-] Kernel'de nasıl zafiyetler var?

    Userspace'dekilerin aynıları! Yani:
     - Buffer overflows
     - Signedness issues
     - Partial overwrites
     - Use-after-free's
     - TOCTOU
     - Race conditions
     - ...

[-] Bu zafiyetleri nasıl sömürülür?

    Userspace'dekiler sömürdüğümüz gibi! Yani:
     - Shellcoding
     - ROP
     - Pointer overwrites
     - Type confusion
     - Memory leaking (KASLR!)
     - ...

[-] Bazı kernel güvenlik önlemleri ve aşma yöntemleri

    - DEP: ROP
    - KASLR: Memory leak
    - Canaries: Memory leak, ya da heap'e yöneliş
    - SMEP/SMAP: Supervisor Mode Execution/Access Protection
                 SMEP Userspace'deki shellkodu kernelden çalıştırmayı
                 engelliyor, SMAP doğrudan erişimi engelliyor. Belli
                 register'ların belli bitlerini ROP'la değiştirerek
                 kapatılabiliyor.
    - LSM: Linux Security Modules. Apparmor, selinux ve security/
           dizinindeki diğer modüller. Erişim konusunda fazladan katman
           sağlıyorlar. Sandbox'ing falan. Detay bilmiyorum.
    - PIE ve RELRO kernel için söz konusu değil

    Not: Bunlar işlemcinin sunduğu özelliklerle doğrudan alakalı; mesela
         SMEP/SMAP x86'nın özellikleri

    - Diğer: mmap_min_addr değişkeni (null ptr deref için çözüm).
             Eskiden bir program mmap(0,...) şeklinde 0 adresine kodunu
             yazsa ve kernelde bir null ptr dereference tetiklese kernel
             0 adresindeki shellcode'u çalıştırmaya başlarmış...
             mmap_min_addr limiti, mmap ile düşük adreslerin alınmasını
             engelliyor, NULL ptr deref hatalarının sömürülmesini
             oldukça zorlaştırıyor.

[-] Debugging

    - Çoğu zaman crash dump'larına dayanacağız, bunlarda değerli
      bilgiler olabiliyor (stack, register dumpları vs)
    - Systemtap; scriptlerle kernel fonksiyonlarını hooklamayı
      sağlıyor,debug sembolleri gerekiyor
    - Netconsole; dmesg'in network tabanlı olanı, crash dumpları dmesg'e
      basılmadan kernel çökebiliyor. Netconsole daha garanti
    - Qemu ve vmware gdb arayüzü sağlıyormuş
    - Virtualbox'u bilmiyorum
    - Kernel derlenirken menuconfig üzerinden debug symbolleri ve pek
      çok diğer debug seçeneği açılabiliyor
    - Debug sembolleri sonradan paket olarak da indirilebiliyor
    - NOT: Debug sembollü bir kernel'de geliştirilen exploit yüksek
      ihtimalle standart kernelde çalışmaz (adres kaymaları vs.)


[-] Nerelerde zafiyetler var?

    [~] LKM (Loadable Kernel Module)

        En sık LKM'lerde (Loadable Kernel Module) bulunuyormuş.

        LKM'ler, kernel space'de çalışan exe'ler gibi. Modül olduğu için
        kernel çalışırken eklenebiliyor, çıkartılabiliyor vs. Tek başına
        derlendikleri içın kernel'le beraber saatlerce derlenmesini
        beklemenize de gerek yok, bu yüzden driver'ların belki de çoğu
        LKM halinde bulunuyor.

        [o] LKM'lerin kullanım alanları:
            - Device drivers
            - Filesystem drivers
            - Networking drivers
            - Executable interpreters
            - Kernel extensions
            - Rootkits (lol)

        [o] LKM dosya yapısı

            Linux için bildiğimiz ELF! Yani reverse yaparken userspace
            için kullandığımız tool'ları kullanabiliyoruz.

        [o] LKM'lerle çalışmak için komutlar

            $ modprobe   # 'zekice' modülü kernele yükle/kaldır
            $ insmod     # biraz uğraştırarak modülü yükle
            $ rmmod      # modülü kaldır
            $ lsmod      # yüklü modülleri listele
[-] Proc cheatsheet
Kod:
$ grep "smep" /proc/cpuinfo
$ grep "smap" /proc/cpuinfo
$ cat /proc/iomem
$ cat /proc/slabinfo
$ cat /proc/<pid/maps> # vmmap per pid
$ cat /proc/self/maps  # caller proceses's vmmap
$ cat /proc/cmdline # check if kaslr, kaiser or pti is enabled
[-] Önemli kernel yapıları, fonksiyonlar vs. kernel v5.8 için sunulmuştur
(K: blog.lexfo.fr). UYARİ: Kod parçalarında kırpmalar yapılmıştır.
Kod:
[~] task_struct: "işlem"in tanımı

    Kendi exploit process'imizin yetkilerini root yapma isteyeceğimizden
    bir process nasıl tanımlanıyor bilmeliyiz.

    --------------------------------------------------------------------
    // include/linux/sched.h

    struct task_struct {
        void                                    *stack;
        refcount_t                              usage;
        int                                     prio;

        struct mm_struct                        *mm;
        pid_t                                   pid;
        unsigned long                           stack_canary;

        /* Real parent process: */
        struct task_struct __rcu                *real_parent;

        /* Recipient of SIGCHLD, wait4() reports: */
        struct task_struct __rcu                *parent;

        /*
         * 'ptraced' is the list of tasks this task is using ptrace() on.
         *
         * This includes both natural children and PTRACE_ATTACH targets.
         * 'ptrace_entry' is this task's link on the p->parent->ptraced list.
         */
        struct list_head                        ptraced;
        struct list_head                        ptrace_entry;

        /* Objective and real subjective task credentials (COW): */
        const struct cred __rcu                 *real_cred;

        /* Effective (overridable) subjective task credentials (COW): */
        const struct cred __rcu                 *cred;

        /* Open file information: */
        struct files_struct                     *files;

        struct seccomp                          seccomp;

        struct vm_struct                        *stack_vm_area;

        /* A live task holds one reference: */
        refcount_t                              stack_refcount;

        /* Used by LSM modules for access restriction: */
        void                                    *security;

        /* CPU-specific state of this task: */
        struct thread_struct                    thread;

        // ...
    };
    --------------------------------------------------------------------

    En çok "cred" alanı ilgimizi çekiyor zira işlemin yetkileri burada
    tanımlı.


[~] cred struct: işlemin yetkilerinin tanımı

    --------------------------------------------------------------------
    // include/linux/cred.h

    struct cred {
        kuid_t          uid;            /* real UID of the task */
        kgid_t          gid;            /* real GID of the task */
        kuid_t          suid;           /* saved UID of the task */
        kgid_t          sgid;           /* saved GID of the task */
        kuid_t          euid;           /* effective UID of the task */
        kgid_t          egid;           /* effective GID of the task */
        kuid_t          fsuid;          /* UID for VFS ops */
        kgid_t          fsgid;          /* GID for VFS ops */
        unsigned        securebits;     /* SUID-less security management */
        kernel_cap_t    cap_inheritable;/* caps our children can inherit */
        kernel_cap_t    cap_permitted;  /* caps we're permitted */
        kernel_cap_t    cap_effective;  /* caps we can actually use */
        kernel_cap_t    cap_bset;       /* capability bounding set */
        kernel_cap_t    cap_ambient;    /* Ambient capability set */


    #ifdef CONFIG_SECURITY
        void            *security;      /* subjective LSM security */
    #endif
        struct user_struct *user;       /* real user ID subscription */
        struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
        struct group_info *group_info;  /* supplementary groups for euid/fsgid */

        // ...
    }
    --------------------------------------------------------------------

    Kernel exploitlerin aşağı yukarı ortak amacı istenilen process'in
    cred struct'unu bulup bu "kgid_t" tipindeki değişkenleri 0 (root)
    yapmak oluyor.

    Bunun dışında bu cred struct'ında 'capability'ler, 'namespace'ler
    ve LSM'ye (Selinux, apparmor vs) ait tanımlamalar görüyoruz.


[-] "current" makrosu: o anki koşan işleme pointer

    current makrosu farklı mimariler için farklı şekilde çalışan, ama
    asıl amacı çekirdekte o anki çalışan işleme işaret eden pointer
    döndürmek olan sık rastlanan makro.

    x86 için:

    --------------------------------------------------------------------
    // arch/x86/include/asm/current.h

    DECLARE_PER_CPU(struct task_struct *, current_task);

    static __always_inline struct task_struct *get_current(void)
    {
        return this_cpu_read_stable(current_task);
    }

    #define current get_current()
    --------------------------------------------------------------------

    this_cpu_read_stable inline assembly ile yazılmış, current_task
    değişkenine o anki çalışan işlemin task_struct'unun pointer'ını
    döndüren bir makro.


[~] file, fdtable, files_struct: dosyalar

    "Linux'ta her şey dosyadır" sözünün manasını tam kavrayalım.
    Temel olarak 7 dosya türü var:
        1) Normal dosya (.txt falan)
        2) Dizin
        3) Link ("kısayol")
        4) Character device (seri port, modem, ses, video, klavye..)
        5) Block device (hdd vs.)
        6) FIFO
        7) Socket

    File descriptor'lar özünde bir integer'dan ibaret; ve sadece ait
    olduğu process için anlam taşıyor. Her fd'nin bağlantılı olduğu bir
    "struct file" var.

    --------------------------------------------------------------------
    // include/linux/fs.h

    struct file {
        struct path                     f_path;
        struct inode                    *f_inode; /* cached value */
        const struct file_operations    *f_op; // Virtual function
                                               // table pointer

        atomic_long_t                   f_count; // obj reference count
        loff_t                          f_pos;   // cursor position
        struct fown_struct              f_owner;
        const struct cred               *f_cred;

    #ifdef CONFIG_SECURITY
        void                            *f_security;
    #endif
        /* needed for tty driver, and maybe others */
        void                            *private_data;
        
        // ...
    }
    --------------------------------------------------------------------

    File descriptor bir integer demiştik (0=stdin, 1=stdout, 2=stderr,
    3=dosya.txt, 4=...). Process için bu integerların karşılıklarındaki
    "file" yapılarını eşleştiren tablo "fdtable".

    --------------------------------------------------------------------
    // include/linux/fdtable.h
    
    struct fdtable {
        unsigned int max_fds;
        struct file __rcu **fd;      /* current fd array */

        // ...
    };
    --------------------------------------------------------------------

    Process'in kendisine ait fdtable "files_struct" türündeki struct'ta
    tutuluyor (bkz: task_struct). Neden doğrudan "fdtable" olarak
    tutmuyor derseniz; çünkü files_struct'ta process için gerekli
    fazladan bilgiler de var ve derli toplu durmaları için böyle bir
    wrapper yapmışlar sanıyorum.

    --------------------------------------------------------------------
    // include/linux/fdtable.h

    struct files_struct {
        atomic_t count;                         // obj reference ctr
        struct fdtable __rcu *fdt;              // ptr to fdtable
    
        // ...
    }
    --------------------------------------------------------------------


[~] Virtual function table (vft) yaklaşımı

    C ile yazılmış olsa da linux kerneli object-oriented bir kernel.
    VFT'ler, kernele OOP'lik katan yaklaşımlardan. Kısaca, fonksiyon
    pointer'ları tutan struct'lar.

    En meşhur VFT'lerden biri "file_operations" struct'ı:

    --------------------------------------------------------------------
    // include/linux/fs.h

    struct file_operations {
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        
        // ...
    }
    --------------------------------------------------------------------

    Her şeyin dosya olduğu ama dosyaların farklı farklı biçimlerde
    olduğu (soket, dosya, dizin vb...) linux'da vft yaklaşımıyla mesela
    dosya okuma işlemleri "generic"lik kazanarak şu hale geliyor:

    --------------------------------------------------------------------
        // soket için de, dizin için de, dosya için de...
        if (file->f_op->read)
            ret = file->f_op->read(file, buf, count, pos);
    --------------------------------------------------------------------
    
    Çünkü her dosyanın "f_op"sundaki fonksiyon pointer'ları kendi
    türünün özel fonksiyonlarına işaret ediyor.

    Kernel exploitlerinde fonksiyon pointer'ları ile oyunlar yapmak çok
    sık başvurulan bir yöntem olduğu için (mesela UAF) bu yapıyı da
    bilelim.


[~] socket, sock, skb: ağ

    socket(2) syscall'ı ile yeni bir soket oluşturunca, kernel bizim
    için bir "struct file" oluşturur ve "f_op" alanını "socket_file_ops"
    olarak ayarlar. Özünde dosya oldukları için de klasik open(2),
    write(2) gibi syscall'larla çalışırlar.

    --------------------------------------------------------------------
    // net/socket.h

    struct socket {
            socket_state                state;

            short                       type;

            unsigned long               flags;

            struct file                 *file;
            struct sock                  *sk;
            const struct proto_ops      *ops;

            struct socket_wq            wq;
    };

    static const struct file_operations socket_file_ops = {
        .llseek =       no_llseek,
        .read_iter =    sock_read_iter,
        .write_iter =   sock_write_iter,

        // ...
    }

    struct proto_ops {
            int                family;
            int                (*release)   (struct socket *sock);
            int                (*bind)             (struct socket *sock,
                                          struct sockaddr *myaddr,
                                          int sockaddr_len);
            int                (*connect)   (struct socket *sock,
                                          struct sockaddr *vaddr,
                                          int sockaddr_len, int flags);
            int                (*sendmsg)   (struct socket *sock, struct msghdr *m,
                                          size_t total_len);
            int                (*recvmsg)   (struct socket *sock, struct msghdr *m,
                                          size_t total_len, int flags);

        // ...
    }
    --------------------------------------------------------------------

    Burada ne görüyoruz: socket struct'ı, file stuct'ı için bir
    "wrapper" olarak yazılmış. socket->file->f_op şeklinde open, close
    işlemlerine erişildiği gibi ayrıca özünde BSD socket API'sinin
    gerçeklemesi olduğundan, proto_ops ile socket'in bind, release gibi
    BSD çıkışlı fonksiyonları da gerçeklenmiş.

    struct socket, yine de bir "dosya" türü olarak tasarlandığından
    "high-level" bir yapı olarak sayılabilir. Fakat sıradan dosyalardan
    farklı olarak soketler network kartına gidip okuma/yazma işlemleri
    yaparlar; bu gibi low-level işlemler için struct socket'in içinde
    bir de "struct sock" diye bir veri yapısına pointer var. sock'da
    daha detaylı işler dönüyor:

    --------------------------------------------------------------------
    // include/net/sock.h

    struct sock {
            struct sk_buff_head        sk_error_queue;
            struct sk_buff_head        sk_receive_queue;
            int                        sk_rcvbuf;
            int                        sk_sndbuf;
            struct sk_buff_head        sk_write_queue;

            struct socket              *sk_socket;
    #ifdef CONFIG_SECURITY
            void                       *sk_security;
    #endif

            // ...
    };
    --------------------------------------------------------------------

    Mesela ağ üzerinden bir paket gelince, ağ kartının sürücüsü bunu
    sk_receive_queue'ya alır, bir program recvmsg(2) syscall'ını
    çağırana kadar o queue'da kalır o paket.

    Bu paketler ise, sk_buff'lardır (kısaca skb):

    --------------------------------------------------------------------
    // include/linux/skbuff.h

    struct sk_buff {
            union {
                    struct {
                            /* These two members must be first. */
                            struct sk_buff                *next;
                            struct sk_buff                *prev;

                            union {
                                    struct net_device     *dev;
                                    // ...
                            };
                    };
            };

            union {
                    struct sock                *sk;
                    int                        ip_defrag_offset;
            };

            unsigned int                len,
                                        data_len;
            __u16                       mac_len,
                                        hdr_len;

            __be16                      protocol;
            __u16                       transport_header;
            __u16                       network_header;
            __u16                       mac_header;

            unsigned char               *head,
                                        *data;

            refcount_t                  users;

            // ...
    };
    --------------------------------------------------------------------

    struct sock'un içindeki queue'larda tutulan sk_buff'un içinde temel
    network terimlerini görmeye başladık. Ayrıca *data pointerını
    görüyoruz. Sonunda veriye ulaştık yani!

    Yukarıdaki veri yapılarının her birinde bir üst ve bir alt katmana
    işaret eden pointerlar mevcut. Bu katmanlar OSI katmanları DEĞİL!
    Kernel'in katmanları. Özet diyagramı:

              +-----------------------------------+
              |   struct file {                   | 
          high|           void *private_data;     |
         level|   }                               |
              |               |   ^               |
              | - - - - - - - | - | - - - - - - - |
              |               V   |               |
              |   struct socket {                 |
              |           struct file *file;      |
              |           struct sock *sk;        |
              |   }                               |
              |               |   ^               |
              | - - - - - - - | - | - - - - - - - |
              |               V   |               |
              |                                   |              
              |   struct sock {                   |
              |           struct socket *sk_sock; |
              |           struct sk_buff_head sk_XXX_queue;
              |   }                               |
              |               |   ^               |
              | - - - - - - - | - | - - - - - - - |
              |               V   |               |
              |                                   |
          low |   struct sk_buff {                |
         lovel|           struct sock *sk;        |
              |   }                               |
              +-----------------------------------+
                   Şekil: Kernel network stack   

    Dikkat: "struct socket"ler genelde "sock" olarak,
            "struct sock"lar da genelde "sk" olarak
            isimlendirilirler.


[~] netlink_sock: IPC için özel soket

    socket'ler sadece network işleri için kullanılmıyorlar.
    Özelleştirilmiş bir socket olan netlink_sock, inter process
    communication'da kullanılıyor. Ayrıca kernel ve userspace arası
    haberleşme için de kullanılıyor.
    
    --------------------------------------------------------------------
    // net/netlink/netlink_sock.h

    struct netlink_sock {
            /* struct sock has to be the first member of netlink_sock */
            struct sock                sk;
            u32                        portid;
            u32                        dst_portid;
            u32                        dst_group;

            // ...
    };
    --------------------------------------------------------------------

    Yorum satırına dikkat! İlk elemanın "struct sock" olması ne işe
    yarar dersiniz? C dilinde type polymorphism bu şekilde yapılıyormuş.
    &netlink_sock == &netlink_sock.sk oluyor ve bunlardan birini "free"
    etmek, bir diğerinin free olması anlamına geliyor.
    
    Tabii netlink için socket'in özelleştirilmiş proto_ops'a gereği var:

    --------------------------------------------------------------------
    // net/netlink/af_netlink.c

    static const struct proto_ops netlink_ops = {
            .family =   PF_NETLINK,
            .owner =    THIS_MODULE,
            .release =  netlink_release,
            .bind =     netlink_bind,
            .connect =  netlink_connect,

            // ...
    }
    --------------------------------------------------------------------


[~] Hepsi bir arada: current, task_struct, files_struct, fdtable,
                     file. socket, sock, sk_buff, socket_file_ops, 
                     netlink_sock, netlink_ops


        current
           |
           V
      task_struct
           |
         *files
           |
           V
     files_struct
           |
          *fdt
           |
           V
        fdtable
           |
          **fd                    sk_buff
           |                        ^|
           V                        |v    
  +----------------------+     netlink_sock
  | 0xffffff12345 // [0] |        (sock)
  +----------------------+          ^|
  | 0xffffff12567 // [1] |          |v
  +----------------------+        socket --*proto_ops--> netlink_ops
  |                      |          ^|
  +----------------------+          |v
  | 0xffffff12987 // [9] |------> file --*f_op--> socket_file_ops
  .                      .
  .                      .

                    Şekil: Hepsi bir arada (oklar pointer)
                           Pointer isimlerinin hepsi yazilmadı


[~] Reference counter kavramı

    C ile yazıldığı için kernelde use-after-free gibi hatalara yer
    vermemek ve bellek sızdırmamak geliştiricilerin sorumluluğunda.
    Bunun için refcounting yaklaşımı geliştirilmiş. Kısaca, bellekteki
    bir objeye eriştiğiniz zaman (mesela socket olsun), refcount'u 1
    arttırılıyor. Kullanım bitince de 1 azaltılıyor. Refcount 0 olduğu
    an obje otomatik siliniyor (free).

    Ha tabii, c++'daki gibi smart pointer kavramı olmadığı için tüm bu
    refcount arttırma ve düşürme işlemleri, o objeye erişen kodu 
    geliştiren kişi tarafından elle yapılmalı. Bir socket objesi
    kullanacaksanız, o socket'in refcount'unu (helper fonksiyonlar ile)
    arrtırmalısınız. Mesela:

    --------------------------------------------------------------------
    static __always_inline void __sock_put(struct sock *sk)
    {
            refcount_dec(&sk->sk_refcnt);
    }
    --------------------------------------------------------------------

    Bu arttırma ve azaltma işlemlerini "generic"leştirmek için
    girişimlerde bulunulmuş olsa da (kref, kobject gibi) genelde her
    yapı kendi refcount sistemini kurmuş durumda. Referans almak (taking
    a reference == ref++) _get()'imsi helper'larla, referans bırakmak
    ise (dropping a reference == ref--) _put()'umsu helper
    fonksiyonlarla sağlanıyor.

    Yukarıdaki veri yapılarında da görülebileceği gibi, refcount, count,
    usage gibi farklı farklı isimlendirmeler var. Helper fonksiyonlar
    için de farklı isimler var:

        struct sock         : sock_hold(), sock_put()
        struct file         : fget(), fput()
        struct files_struct : get_files_struct(), put_files_struct()

    gibi.

    Ama mesela skbuff'ların skb_put() fonksiyonunun refcountingle
    alakası yok. Kernel'da isimlendirmelere bakıp da içerik hakkında
    tahminde bulunma; git ne iş yapıyor bak!

    Hülasa; elle yapılan bir refcount arttırma azaltma söz konusu
    olunca, karışık "path"lerde programcı hatalarından kaynaklı UAF'ler,
    double free'ler, integer overflow'lar (ve buna bağlı UAF'ler)
    kernelin her yerinde karşımıza çıkabilir.

[~] Task state
[~] Run queues
[~] Wait queues
[~] Waking up tasks
[~] Memory, SLAB https://www.kernel.org/doc/gorman/html/understand/understand011.html
[~] Cache and slab
[~] Per-cpu array cache
[~] General purpose and dedicated caches
[~] container_of() macro
[~] Doubly-linked lists: list_for_each, list_for_each_safe,
    list_for_each_entry, list_for_each_entry_safe
[~] UAF and Type confusion: cache vs., UAF tricks, reallocation gadgets
[~] Detecting object sizes: 5 methods
[~] Mimicking kernel data structures in userspace
[~] struct thread_info: addr_limit, set_fs()
[~] seccomp and escaping seccomp
[~] __user macro
[~] execve() --> start_stread()
[~] virtual memory map
[~] kernel thread stacks
[~] SMEP: ret2usr sonu
[~] ret2dir
[~] Understanding page fault trace

    Rootkitimizi insmod yaptık ve oops!
   
    --------------------------------------------------------------------
    / # insmod wootkit
    [   39.847344] BUG: unable to handle kernel paging request at c15fa0b8
    [   39.848063] IP: [<c47fa006>] initmodule+0x6/0x1000 [wootkit]
    [   39.848063] *pdpt = 00000000019aa001 *pde = 0000000002b07063 *pte = 00000000015fa161
    [   39.848063] Oops: 0003 [#1] SMP
    [   39.848063] Modules linked in: wootkit(POF+)
    [   39.848063] Pid: 77, comm: insmod Tainted: PF          O 3.7.1 #1 QEMU Standard PC (i440FX + PIIX, 1996)
    [   39.848063] EIP: 0060:[<c47fa006>] EFLAGS: 00000246 CPU: 0
    [   39.848063] EIP is at initmodule+0x6/0x1000 [wootkit]
    [   39.848063] EAX: c15fa0b8 EBX: 00001449 ECX: 00000002 EDX: 00000001
    [   39.848063] ESI: c47fa000 EDI: 00000000 EBP: c26dbee4 ESP: c26dbeb4
    [   39.848063]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
    [   39.848063] CR0: 8005003b CR2: c15fa0b8 CR3: 02d81000 CR4: 000006b0
    [   39.848063] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
    [   39.848063] DR6: ffff0ff0 DR7: 00000400
    [   39.848063] Process insmod (pid: 77, ti=c26da000 task=c2ede580 task.ti=c26da000)
    [   39.848063] Stack:
    [   39.848063]  c26dbee4 c1003152 00000000 00000000 00000000 00000000 c47fb000 c26dbee4
    [   39.848063]  c10a6532 00001449 c47f7000 c2eeab40 c26dbfac c10a7db3 00001449 c187d8a8
    [   39.848063]  00000000 c17ea42c c47f700c 00150000 00000014 00001000 00001000 c3b5f000
    [   39.848063] Call Trace:
    [   39.848063]  [<c1003152>] ? do_one_initcall+0x112/0x160
    [   39.848063]  [<c10a6532>] ? set_section_ro_nx+0x62/0x80
    [   39.848063]  [<c10a7db3>] sys_init_module+0xf3/0x1d00
    [   39.848063]  [<c15f5ccd>] sysenter_do_call+0x12/0x28
    [   39.848063] Code: <c7> 00 d8 a4 ff 32 80 c4 20 a0 5f c1 90 2c 02 10 55 90 bc a4 5f c1
    [   39.848063] EIP: [<c47fa006>] initmodule+0x6/0x1000 [wootkit] SS:ESP 0068:c26dbeb4
    [   39.848063] CR2: 00000000c15fa0b8
    [   39.848063] ---[ end trace af0d9cc236996cb9 ]---
    Killed
    --------------------------------------------------------------------


    "Oops" kodlarımız:

    --------------------------------------------------------------------
    // arch/x86/include/asm/traps.h

    /*
     * Page fault error code bits:
     *
     *   bit 0 ==    0: no page found       1: protection fault
     *   bit 1 ==    0: read access         1: write access
     *   bit 2 ==    0: kernel-mode access  1: user-mode access
     *   bit 3 ==                           1: use of reserved bit detected
     *   bit 4 ==                           1: fault was an instruction fetch
     *   bit 5 ==                           1: protection keys block access
     */
    enum x86_pf_error_code {
            X86_PF_PROT     =               1 << 0,
            X86_PF_WRITE    =               1 << 1,
            X86_PF_USER     =               1 << 2,
            X86_PF_RSVD     =               1 << 3,
            X86_PF_INSTR    =               1 << 4,
            X86_PF_PK       =               1 << 5,
    };
    --------------------------------------------------------------------

    Örnekteki 0003 == X86_PF_PROT | X86_PF_WRITE;


[~] ROP in kernelspace
[~] Stack pivoting
[~] Debugging kernel w/ gdb
[~] getting root
[+] Kernel’in bellek yönetimi
Kod:
Kernel'in en kritik görevlerinden biri belleği hızlı, güvenli, stabil ve
"fragmentation" en az olacak şekilde yönetmesidir.

Burada mmap başlığı dahil "LDD3 - Chapter 15, Memory Mapping and DMA"den
alıntılar var. (http://static.lwn.net/images/pdf/LDD3/ch15.pdf)

[-] List of address types used in Linux

    [o] User virtual addresses

        These are the regular addresses seen by user-space programs.
        User addresses are either 32 or 64 bits in length, depending on
        the underlying hardware architecture, and each process has its
        own virtual address space.

    [o] Physical addresses

        The addresses used between the processor and the system's
        memory. Physical addresses are 32- or 64-bit quantities; even
        32-bit systems can use larger physical addresses in some
        situations.

    [o] Bus addresses

        The addresses used between peripheral buses and memory. Often,
        they are the same as the physical addresses used by the
        processor, but that is not necessarily the case. Some
        architectures can provide an I/O memory management unit (IOMMU)
        that remaps addresses between a bus and main memory. An IOMMU
        can make life easier in a number of ways (making a buffer
        scattered in memory appear contiguous to the device for
        example), but programming the IOMMU is an extra step that must
        be performed when setting up DMA operations. Bus addresses are
        highly architecture dependent, of course.

    [o] Kernel logical addresses

        These make up the normal address space of the kernel. These
        addresses map some portion (perhaps all) of main memory and are
        often treated as if they were physical addresses. On most
        architectures, logical addresses and their associated physical
        addresses differ only by a constant offset. Logical addresses
        use the hardware's native pointer size and, therefore, may be
        unable to address all of physical memory on heavily equipped
        32-bit systems. Logical addresses are usually stored in
        variables of type unsigned long or void *. Memory returned from
        kmalloc has a kernel logical address.

    [o] Kernel virtual addresses

        Kernel virtual addresses are similar to logical addresses in
        that they are a mapping from a kernel-space address to a
        physical address. Kernel virtual addresses do not necessarily
        have the linear, one-to-one mapping to physical addresses that
        characterize the logical address space, however. All logical
        addresses are kernel virtual addresses, but many kernel virtual
        addresses are not logical addresses. For example, memory
        allocated by vmalloc has a virtual address (but no direct
        physical mapping). The kmap function (described later in this
        chapter) also returns virtual addresses. Virtual addresses are
        usually stored in pointer variables.


[-] High and Low Memory

    [o] Low memory

        Memory for which logical addresses exist in kernel space. On
        almost every system you will likely encounter, all memory is
        low memory.

    [o] High memory

        Memory for which logical addresses do not exist, because it is
        beyond the address range set aside for kernel virtual addresses.


[-] Page Tables

    On any modern system, the processor must have a mechanism for
    translating virtual addresses into its corresponding physical
    addresses. This mechanism is called a page table; it is essentially
    a multilevel tree-structured array containing virtual-to-physical
    mappings and a few associated flags. The Linux kernel maintains a
    set of page tables even on architectures that do not use such tables
    directly.


[-] Virtual Memory Areas

    The virtual memory area (VMA) is the kernel data structure used to
    manage distinct regions of a process's address space. A VMA
    represents a homogeneous region in the virtual memory of a process:
    a contiguous range of virtual addresses that have thesame permission
    flags and are backed up by the same object (a file, say, or
    swapspace). It corresponds loosely to the concept of a "segment",
    although it is better described as "a memory object with its own
    properties." The memory map of a process is made up of (at least)
    the following areas:

        • An area for the program's executable code (often called text)
        • Multiple areas for data, including initialized data (that 
          which has an explicitly assigned value at the beginning of
          execution), uninitialized data (BSS), and the program stack
        • One area for each active memory mapping

    $ cat /proc/<pid/maps> # vmmap per pid
    $ cat /proc/self/maps  # caller proceses's vmmap


        00400000-00405000   r-xp   00000000 03:01 1596291     /bin/cat
           ^         ^       ^        ^      ^  ^   ^            ^
        va start  va end   perm     offset  major  inode       image
                          p=priv    (from    minor
                          s=shared  image
                                    start)


[-] The mmap Device Operation

    Memory mapping is one of the most interesting features of modern
    Unix systems. Asfar as drivers are concerned, memory mapping can be
    implemented to provide user programs with direct access to device
    memory.

    The kernel can manage virtual addresses only at the level of page 
    tables the mapped area must be amultiple of PAGE_SIZE and must live
    in physical memory starting at an address that is a multiple of
    PAGE_SIZE (usually 4096).

    Device driver developer must implement mmap interface himself for 
    supporting mmap operations.

    $ cat /proc/<pid/maps> | grep /dev/mem
    $ cat /proc/iomem


[-] Allocators

    Kernel'de belli başlı allocatorlar ile yapılıyor bellek işlemleri.
    Biz malloc()'la falan bir bellek istediğimizde kernel gidiyor en
    uygun, en kopukluk olmayacak fiziksel bölgeyi sanal bellek alanına
    "map"liyor. Bu bölgelere ait bilgiler "struct page"lerde saklanıyor
    (free mi değil mi vs.).

    Bu yönetimi yapan farklı farklı allocator'lar geliştirilmiş; ve
    exploit edilecek sistemde hangisi varsa onun detayını bilmek
    elzemmiş (tıpkı userspace heap implementasyonları gibi).

    Zoned page frame allocator (buddy allocator)
    --------------------------------------------
        alloc_pages() ve free_pages() çağrılarına bakan, 'buddy system
        algorithm' kullanan, genel bir allocator.

    Slab allocators
    ---------------
        Buddy çok genel kalıyormuş, daha özelleştirilmiş algoritmalara
        ihtiyaç duyulmuş zamanla. Daha ince ayarlı allocator sistemi
        olarak slab'ler geliştirilmiş. Mesela 128B bellek istiyoruz,
        buddy'de mecbur PAGE_SIZE'lık alan ayrılırmış ama Slab'ler daha
        ufak ayırmalar yapabiliyor. Kısaca çoğu zaman Slab'le haşır
        neşir oluyoruz. Kernelde 3 farklı Slab gerçeklemesi var 
        (sadece 1'i aktif):
        
        1- SLAB: Geleneksel ve eski, cache optimizasyonlarına odaklı,
                 Debian hala bunu kullanıyormuş. UAF exploitleri SLUB'a
                 göre daha kolay. İyi dökümante edilmiş.
        2- SLUB: 2007'den beri yeni standart bu, Ubuntu, CentOS, Android
                 Bunu exploit etmenin ise 'slab aliasing' diye bir
                 kolaylığı varmış: SLUB general kmemcache'lerde daha
                 fazla obje saklıyor. İyi dökümante edilmemiş ???? TODO
                 fakat anlaması SLAB'e göre daha kolaymış (SLAB'deki
                 gibi "cache coloring", "full slab" takibi,
                 internal/external slab yönetimi falan yokmuş).
        3- SLOB: Çok ufak hafızalı gömülü sistemler için tasarlanmış

        (Bunların her birine genel olarak "Slab" deniliyor)
        
        Sistemde hangisi aktif bakmak için:
        ----------------------------------------------------------------
        $ grep "CONFIG_SL.B=" /boot/config-$(uname -r)

        # ya da

        $ cat /proc/slabinfo  # eğer "size-XXX li cache'ler varsa SLAB
                              # "kmalloc-XXX li cache'ler varsa SLUB
        ----------------------------------------------------------------

        Ayrıca cache'ler var ve bunlara (küçük harfle) slab deniliyor.
        Kısaca bir slab bir veya birden çok sürekli page frame'i demek.


    [o] İsimlendirme
       
        SLAB: algoritma
        Slab: yöntemlerden biri (SLAB/SLUB/SLOB)
        slab: sürekli bellek alanı, PAGE_SIZE veya katları boyutunda
    

    [~] Cache

        Kernel, tekrar tekrar aynı büyüklükte obje oluşturmaya meyilli
        olduğundan; Slab'lerin yönettiği cache mekanizmaları
        oluşturulmuş. Kısaca çeşitli boyutlarda pek çok cache var ve
        yeni oluşturulan obje hangisine en iyi oturuyorsa ona konuluyor.

        NOT: Bu cache'ler CPU cache'leri değil. Bunlar yine bellekte.

        Cache descriptor olan "struct kmem_cache":
        ----------------------------------------------------------------
        // include/linux/slab_def.h

        /*
         * Definitions unique to the original Linux SLAB allocator.
         */

        struct kmem_cache {
                struct array_cache __percpu *cpu_cache;
                unsigned int num;		/* # of objs per slab */

                /* order of pgs per slab (2^n) */
                unsigned int gfporder;

                struct kmem_cache *freelist_cache;
                unsigned int freelist_size;

                /* constructor func */
                void (*ctor)(void *obj);

                const char *name;
                int refcount;

        #ifdef CONFIG_KASAN
                struct kasan_cache kasan_info;
        #endif

                // GALİBA: holds list of empty/partial/full slabs
                struct kmem_cache_node *node[MAX_NUMNODES];

                // ...
        };
        ----------------------------------------------------------------


    [~] slab

        Küçük harfle "slab", sürekli bellek alanı, PAGE_SIZE veya
        katları boyutunda demiştik.

        TODO Hey 5.8'de yok

        ----------------------------------------------------------------

        ----------------------------------------------------------------
[-] Random notes to put somewhere in the note
Kod:
KASLR -> KAISER -> PTI
----------------------
KASLR (Kernel Address Space Layout Randomization) is for the most part
deprecated and replaced by KAISER (Kernel Address Isolation to have
Side-channels Efficiently Removed) which in turn is deprecated and
replaced by KPTI (Kernel Page-Table Isolation). These three "K-steps"
are all part of "Kernel Hardening".


initramfs playbook
-----------------
cat initramfs | cpio -id    # extract it
lsinitrd initramfs          # list contents w/o extracting
[-] Notes from pwn.college kernel module
Kod:
'hlt', 'in' and 'out' insturictions require kernel privileges.

cr3 register controls the page table used to translate virtual addresses
to physical addresses. Accessed using 'mov' instruction.

MSR_LSTAR register (model-specific register, long syscall target address
register); defines where the 'syscall' instruction jumps to (in kernel).
Accessed using 'wrmsr' and 'rdmsr' privileged instructions.


[+] Rings

    VM guest shouldn't  be able to have unlimited access to the host's
    physical hardware.

    Early 2000's solution: force the VM kernel into ring 1.
    Drawback             : Cost of emulating ring-0 tasks of the vm

    Modern solution      : Ring -1; hypervisor mode. Able to intercept
                           sensitive ring 0 actions done by guests and
                           handle them in the host OS.


[+] Different types of OS models

    1. Monolithic kernel: A single, unified binary handling all tasks.
                          Linux, FreeBSD.

    2. Microkernel: A tiny core binary provides IPC and barebone
                    interactions with the hardware. Only that core
                    binary runs at ring 0; all other drivers are
                    normal-ish userspace programs with slightly special
                    privileges. Slow but usable for security critical
                    situations.
                    Minux, seL4

    3. Hybrid kernel: Micrekernel features combined with a monolithic
                      component.
                      Windows(NT), MacOS


[+] How kernel handles syscalls

    1. At bootup, in ring 0, the kernel sets 'MSR_LSTAR' register to
       point to the syscall handler routine (handle_syscall ?)

    2. When a userspace (ring 3) process wants to intract w/ the kernel,
       it calls 'syscall' instruction. In 'syscall':
       2.1 Privilege level switches to ring-0
       2.2 Control flow jupts to value of 'MSR_LSTAR'
       2.3 Return address saved to 'rcx', not stack for security reasons
    
    3. Whet kernel is ready to return to userspace, it calls 'sysret'
       instruction.
       2.1 Privilege level swithes back to ring 3
       2.2 control flow jumps to the value of 'rcx'


[+] Kernel memory vs userspace memory

    Kernel has it's own virtual memory space, but in high addresses.

    Syscalls don't switch the virtual memory mapping, but kernel memory
    is only accessible from ring 0 (what dat means???)

    $ cat /proc/self/maps
    .
    .
    .
    7fd97c9d2000-7fd97c9d3000 rw-p 0002d000 08:05 1181832   /usr/lib/...
    7fd97c9d3000-7fd97c9d4000 rw-p 00000000 00:00 0 
    7ffd1f0f1000-7ffd1f112000 rw-p 00000000 00:00 0         [stack]
    7ffd1f18c000-7ffd1f18f000 r--p 00000000 00:00 0         [vvar]
    7ffd1f18f000-7ffd1f190000 r-xp 00000000 00:00 0         [vdso]
    ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]

    vsyscall is the only mapped kernel memory in userspace; which used
    to be there to make syscalls faster but not used often nowadays. BTW
    linux-hardened doesn't show (or doesn't have) that mapping. This is
    from ubuntu.


[+] Possible kernel attack vectors
    
    1. Remote (network etc.). Was popular around '95s. Rare nowadays.
    2. From userspace. Sandbox escaping etc.
    3. Via devices. USB device pwning USB driver etc.


[+] Kernel modules

    Are libraries being loaded into the kernel.

    [*] Ways to interact w/ kernel modules

        1. System calls
        2. Interrupts
        3. Files (/dev/sda etc.)
           3.1 Via read() and write()
           3.2 Via ioctl()

    [*] Syscall hooking

        Historically, kernel modules could add system call entries via
        modifying the system call table. This is now explicitly
        unsupported in modern kernels.

    [*] Interrupt hooking

        A module could register an interrupt handler by using "LIDT" and
        "LGDT" instructions and be triggered by, say, an 'int 42'
        instruction.

        Useful one-byte interrupt instructions to hook:

            int3 (0xcc): normally causes a SIGTRAP; used by gdb for
                         breakpointing
            int1 (0xf1): normally used for hardware debugging

        A module can also hook the Invalid Opcode Exception interrupt to
        implement custom instructions in software.

    [*] Interation w/ modules via files

        - /dev; mostly traditional devices have files here
        - /proc; driver files about running processes (?)
        - /sys; non-process information interface w/ the kernel

        A module can register a file in one of the above locations.
        Userspace can open() that file to interact w/ the module.


        [~] read() and write()

            Useful for streaming data.

            Kernel side provides:
                
                static ssize_t device_read(struct file *filp, char *buf,
                                               size_t len, loff_t *off);
                static ssize_t device_write(struct file *filp,
                                    char *buf, size_t len, loff_t *off);

            Userspace side uses:
            
                fd = open("/dev/mydevice", 0);
                read(fd, buffer, 128);



        [~] ioctl()
            
            Input/Output control provides a much more flexible
            interface. Useful for setting and querying not-stream data
            (i.e., webcam resolution settings).

            Kernel side provides:

                static long device_ioctl(struct file *filp,
                     unsigned int ioctl_num, unsigned long ioctl_param);

            Userspace side uses:

                int fd = open("/dev/mydevice", 0);
                ioctl(fd, COMMAND_CODE, &custom_data_structure);


            That &custom_data_structure thing causes lots of
            vulnerabilities!


        [~] Kernel Race Conditions

            They are always prone to multi-threading!
             - What happens if two devices (/dev/mydev) open 
               simultaneously?

            They could disappear or swap resources mid-execution!
             - What happens if module.ko is removed while /proc/mydev
               is open?

            Race conditions are huge problems plaguing kernels!


        [~] On credentials

            The credentials are supposed to be immutable (i.e., they can
            be cached elsewhere, and shouldn't be updated in place).
            Instead, they can be replaced with fresh ones (via 
            commit_creds()).
[-] Misc
Kod:
[*] UML (user mode linux)

    "uml": We could configure the kernel to run as User Mode Linux.  Running
    a UML kernel requires no privileges. The kernel just runs a user space
    process. UML is pretty cool, but sadly, it doesn't support KASAN,
    therefore the chances of finding a memory corruption bug are reduced.
    Finally, UML is a pretty magic special environment - bugs found in UML
    may not be relevant on real environments.  Interestingly, UML is used by
    Android network_tests framework.

Linux insides kalinan yer:
https://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-3.html
[-] Fuzzing
Kod:
https://blog.cloudflare.com/a-gentle-introduction-to-linux-kernel-fuzzing/
vim:set ts=8 sw=4 sts=4 et cc=80 tw=80:
 
Son düzenleme:

tonysa

Üye
İsmail abi bir de rip’ledikten sonra biraz görünüşe baksan çok iyi olur abi bayağı kaymış abi
 
  • Like
Tepkiler: sef
Üst