Dirtycow gira (da lib-c a root) bene ma si blocca al riavvio

2

Ho il server ubuntu 14.04.3 in esecuzione in un vm con la versione del kernel 3.13.0-83-generic in esecuzione. Ho testato diversi PoC raccolti qui . La maggior parte di essi danneggia il kernel (non tutte le volte ma a volte) e non sono affidabili; Tranne per l'exploit lib-c to root. Questo sfrutta la vulnerabilità con successo e apre una shell di root per alcuni secondi (circa 20-30 secondi). Per renderlo stabile ho fatto echo 0 > /proc/sys/vm/dirty_writeback_centisecs come menzionato qui . Tutto va bene fino al riavvio. Durante il riavvio, il kernel si arresta in modo anomalo: (

Ho 2 domande:

In primo luogo, da cosa dipendono gli arresti anomali? È l'hardware? La versione del kernel? L'exploit?

In secondo luogo, come posso correggere l'arresto del kernel al riavvio mentre sto usando lib-c per l'exploit root?

UPDATE 1

Questo è ciò che ottengo con kdump:

[  388.077362] kernel BUG at /build/linux-03BQvT/linux-3.13.0/fs/ext4/inode.c:2420!
[  388.077497] invalid opcode: 0000 [#1] SMP 
[  388.077601] Modules linked in: crct10dif_pclmul crc32_pclmul vmw_balloon aesni_intel aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd serio_raw vmw_vmci lp parport psmouse ahci e1000 libahci floppy mptspi mptscsih mptbase
[  388.078190] CPU: 1 PID: 453 Comm: kworker/u256:28 Not tainted 3.13.0-83-generic #127-Ubuntu
[  388.078426] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/20/2014
[  388.078627] Workqueue: writeback bdi_writeback_workfn (flush-8:0)
[  388.078755] task: ffff880135e69800 ti: ffff880135e70000 task.ti: ffff880135e70000
[  388.078878] RIP: 0010:[<ffffffff81241298>]  [<ffffffff81241298>] mpage_prepare_extent_to_map+0x2b8/0x2c0
[  388.079027] RSP: 0018:ffff880135e719d8  EFLAGS: 00010246
[  388.079102] RAX: 01ffff000002007d RBX: ffff880135e71a18 RCX: 0000000000000000
[  388.079187] RDX: ffff880135e71a18 RSI: 0000000000000000 RDI: ffff8801377824a0
[  388.079272] RBP: ffff880135e71aa8 R08: 0000000000000000 R09: 0000000000000000
[  388.079357] R10: 0000000000000100 R11: 0000000000000210 R12: 0000000000003400
[  388.079441] R13: 0007ffffffffffff R14: ffffea0002ec8c80 R15: ffff880135e71b50
[  388.079527] FS:  0000000000000000(0000) GS:ffff88013a620000(0000) knlGS:0000000000000000
[  388.079651] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  388.079729] CR2: 0000000000410000 CR3: 00000000377b5000 CR4: 00000000001407e0
[  388.079852] Stack:
[  388.079912]  ffff880135e71a18 0000000000000000 ffff880137782498 ffff880135e71a18
[  388.080089]  0000000000000001 0000000000000001 0000000000000000 ffffea0002ec8c80
[  388.080265]  ffff8800bba09000 ffff880135e71a68 ffffffff81288bc3 ffff880100000050
[  388.080441] Call Trace:
[  388.080506]  [<ffffffff81288bc3>] ? jbd2__journal_start+0xf3/0x1e0
[  388.080587]  [<ffffffff81245276>] ? ext4_writepages+0x3c6/0xd20
[  388.080667]  [<ffffffff8126f7f9>] ? __ext4_journal_start_sb+0x69/0xe0
[  388.080749]  [<ffffffff812452a2>] ext4_writepages+0x3f2/0xd20
[  388.080830]  [<ffffffff8115bc2e>] do_writepages+0x1e/0x40
[  388.080907]  [<ffffffff811e7f10>] __writeback_single_inode+0x40/0x220
[  388.080989]  [<ffffffff811e8cd7>] writeback_sb_inodes+0x247/0x3e0
[  388.081069]  [<ffffffff811e8f0f>] __writeback_inodes_wb+0x9f/0xd0
[  388.081149]  [<ffffffff811e9183>] wb_writeback+0x243/0x2c0
[  388.081228]  [<ffffffff810870c6>] ? set_worker_desc+0x76/0x90
[  388.081307]  [<ffffffff811ea9a8>] bdi_writeback_workfn+0x108/0x430
[  388.081388]  [<ffffffff81083d22>] process_one_work+0x182/0x450
[  388.081468]  [<ffffffff81084b11>] worker_thread+0x121/0x410
[  388.081545]  [<ffffffff810849f0>] ? rescuer_thread+0x430/0x430
[  388.081624]  [<ffffffff8108b8f2>] kthread+0xd2/0xf0
[  388.081706]  [<ffffffff8108b820>] ? kthread_create_on_node+0x1c0/0x1c0
[  388.081787]  [<ffffffff817364e8>] ret_from_fork+0x58/0x90
[  388.081861]  [<ffffffff8108b820>] ? kthread_create_on_node+0x1c0/0x1c0
[  388.081940] Code: 00 00 00 48 8d bd 58 ff ff ff 89 85 48 ff ff ff e8 6e cf f1 ff 8b 85 48 ff ff ff eb ca 48 8d bd 58 ff ff ff e8 5a cf f1 ff eb 80 <0f> 0b 0f 0b 0f 1f 40 00 0f 1f 44 00 00 55 48 89 e5 41 57 41 56 
[  388.083376] RIP  [<ffffffff81241298>] mpage_prepare_extent_to_map+0x2b8/0x2c0
[  388.083472]  RSP <ffff880135e719d8>
    
posta arashkgpt 06.11.2016 - 15:07
fonte

2 risposte

2

TL; DR si è verificata una condizione di competizione che ha portato il bit di writeback a essere impostato su una pagina immediatamente dopo aver atteso che il bit venisse disattivato, inciampando in un controllo di integrità che ha bloccato il kernel.

Sfortunatamente, non posso rispondere alle tue due domande, dal momento che richiedono molto più debugging, ma posso fare chiarezza sul perché questo si sta verificando, il che può essere d'aiuto. Sembra probabile che questo venga attivato quando il kernel sincronizza i filesystem giusto prima del riavvio. Puoi testare questo eseguendo sync te stesso (o facendo una sincronizzazione di emergenza, cioè sysrq-s) per vedere se questo attiva questo comportamento, o riavviando senza prima la sincronizzazione, cioè con reboot -n e vedendo se non si verifica alcun arresto anomalo.

Il problema stesso

Sembra che provenga da un BUG() in fs/ext4/inode.c:mpage_prepare_extent_to_map . La macro BUG() è usata per oops intenzionalmente il kernel generando un'eccezione di istruzione illegale. È inteso per essere utilizzato come controllo di integrità runtime. La macro BUG() solleva incondizionatamente un'eccezione, mentre BUG_ON() prende un singolo argomento e solleva l'eccezione se l'argomento valuta true.

Nel log che hai pubblicato, viene attivato BUG_ON(PageWriteback(page)) , il cui argomento è valutato su true. Ciò significa che, a questo punto del codice, PageWriteback(page) dovrebbe sempre restituire false. Questo è usato per prevenire un writeout simultaneo della stessa pagina. Le righe pertinenti sono:

wait_on_page_writeback(page);
BUG_ON(PageWriteback(page));

La prima riga è una funzione definita in include/linux/pagemap.h :

/*
 * Wait for a page to complete writeback
 */
static inline void wait_on_page_writeback(struct page *page)
{
    if (PageWriteback(page))
        wait_on_page_bit(page, PG_writeback);
}

La seconda riga proviene da include/linux/page-flags.h :

#define TESTPAGEFLAG(uname, lname, policy)                             \
    static __always_inline int Page##uname(struct page *page)          \
    { return test_bit(PG_##lname, &policy(page, 0)->flags); }

La radice del problema

Sfortunatamente, la ragione esatta per cui ciò si sta verificando non mi è chiara. A giudicare da come funziona l'exploit di DirtyCow, ha senso, poiché sembra ovvio che questo sia il risultato di una condizione di competizione (altrimenti la pagina non dovrebbe riguadagnare improvvisamente il bit di writeback dopo aver atteso che venisse rimossa se non esplicitamente detto a). Vorresti eseguire un debug più approfondito per comprendere completamente il problema. Poiché è probabile che sia una condizione di gara, non avrai molta fortuna con questo solo registro di oops. Dovrai esaminare il codice (non-faulting, così silenzioso) che viene eseguito su un'altra CPU che lo sta attivando.

Posso dire che ha provocato uno dei tre possibili eventi che hanno attivato questo risultato:

  • La pagina di destinazione non ha impostato il bit di writeback. Di conseguenza, wait_on_page_writeback() ha funzionato come no-op. In qualche modo, il bit è stato impostato poco dopo la funzione restituita.
  • La pagina di destinazione ha impostato il bit di riscrittura. Il wait_on_page_writeback() ha aspettato fino a quando il bit è stato rimosso prima di tornare. In qualche modo, dopo che il bit è stato rimosso, è stato nuovamente impostato.
  • La pagina di destinazione ha impostato il bit di riscrittura e si suppone che wait_on_page_writeback() venga restituito solo quando il bit non è stato impostato. In qualche modo, è tornato anche se il bit era ancora impostato.

Indipendentemente da quale di questi è il caso, è stata restituita la funzione wait_on_page_writeback() , ma la pagina ha ancora o rapidamente recuperato il bit di writeback. Questo è stato rilevato da un controllo di integrità di BUG_ON() perché non dovrebbe mai accadere.

Come il registro di oops rivela questo

Invece di dire semplicemente "questo è quello che è", ti spiegherò come l'ho raccolto dal log che hai postato.

[  388.077362] kernel BUG at /build/linux-03BQvT/linux-3.13.0/fs/ext4/inode.c:2420!
[  388.077497] invalid opcode: 0000 [#1] SMP 

Questo mi dice che il problema era un controllo di integrità relativo a BUG() . La prima riga lo chiama esplicitamente, e indica anche la riga esatta e il file sorgente di interesse. Anche la seconda riga lo rafforza implicitamente, poiché BUG() e le funzioni correlate eseguono l'istruzione ud2 , che genera un errore opcode (istruzioni) non valido.

[  388.078878] RIP: 0010:[<ffffffff81241298>]  [<ffffffff81241298>] mpage_prepare_extent_to_map+0x2b8/0x2c0

Il RIP è il puntatore dell'istruzione (chiamato EIP e IP su processori a 32 e 16 bit, rispettivamente). Punterà alla posizione dell'istruzione attualmente in esecuzione, che sarà all'interno della funzione corrente. Poiché ho una versione del kernel diversa e non volevo cercare la tua versione esatta, sono stato in grado di passare alla funzione mpage_prepare_extent_to_map() in fs/ext4/inode.c e cercare qualcosa come BUG() o BUG_ON() , così come qualsiasi linea appena sopra di essa. Cercando i file in cui sono state definite le due funzioni wait_on_page_writeback() e PageWriteback() hanno rivelato il loro scopo. Nello specifico, queste garanzie fornite dalle due righe del codice sorgente si sono rivelate errate:

  • Dopo il ritorno di wait_on_page_writeback(page) , page non avrà il bit di writeback impostato.
  • PageWriteback(page) restituirà false, poiché page non avrà il bit di writeback impostato.
  • Pertanto, BUG_ON(PageWriteback(page)) non verrà attivato.

Poiché il BUG_ON() è stato attivato, il problema diventa ovvio.

    
risposta data 03.12.2017 - 10:31
fonte
0

what do the crashes depend on? Is it the hardware? The kernel version? The exploit?

Per ottenere informazioni sull'arresto del kernel dovresti usare kdump-tools , un tuto completo può essere trovato qui

How can I fix the kernel crash on reboot while I'm using the lib-c to root exploit?

Dovresti avviare da un live cd / usb di linux, creare l'ambiente chroot e quindi reinstallare l'immagine di linux .

    
risposta data 06.11.2016 - 17:51
fonte

Leggi altre domande sui tag