
Reproduction et analyse de CVE-2019-18885, une vulnérabilité de déréférencement de pointeur nul dans le système de fichiers BTRFS du noyau Linux, avec une image modifiée et des journaux de débogage.
Équipe de projet dans BoB (a.k.a Best of the Best), République de Corée. ([email protected])
Recherche de bogues dans les modules du système de fichiers du noyau Linux
Project Member: 김동희(Kieast), 조형진(zkaryaJo), 홍승표(Ph4nt0m), 남지효(NJhyo), 정원영(nonetype)
Project Leader: 조성준(DelspoN)
Project Mentor: 이상섭(k1rh4), 박천성(Ashine)
déréférencement nul dans btrfs_verify_dev_extents (entrée de liste en boucle)
Lors du montage d'une image btrfs conçue, obtention d'un Null-Ptr-Deref.
Testé sur le noyau Linux 5.0.21, système de fichiers BTRFS (source ici)
(Peut nécessiter l'option CONFIG_BTRFS_FS=m.)
Image conçue jointe ici
mkdir ./mnt
mount -t btrfs ./poc_2019_18885.img ./mnt
fs/btrfs/volumes.c:430
static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
u64 devid, const u8 *uuid)
{
struct btrfs_device *dev;
[1] list_for_each_entry(dev, &fs_devices->devices, dev_list) {
if (dev->devid == devid &&
(!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) {
return dev;
}
}
return NULL;
}
dans la boucle list_for_each_entry ([1]), l'entrée de liste &fs_devices->devices peut être NULL
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────── registers ────
$rax : 0x0000000000000000 → 0x0000000000000000
$rbx : 0xdffffc0000000000 → 0xdffffc0000000000
$rcx : 0x0000000000000098 → 0x0000000000000098
$rdx : 0x0000000000000013 → 0x0000000000000013
$rsp : 0xffff888063d4e620 → 0xffff888067d9a200 → 0x1e44a09cf7d0cbce → 0x1e44a09cf7d0cbce
$rbp : 0x0000000000c00000 → 0x0000000000c00000
$rsi : 0x0000000000000001 → 0x0000000000000001
$rdi : 0xffff88806a4eb248 → 0x0000000000000000 → 0x0000000000000000
$rip : 0xffffffff81def0e8 → 0x0890850f001a3c80 → 0x0890850f001a3c80
$r8 : 0x1ffff1100d49d781 → 0x1ffff1100d49d781
$r9 : 0x0000000000000000 → 0x0000000000000000
$r10 : 0x0000000000000001 → 0x0000000000000001
$r11 : 0xffffed100cfb3463 → 0x0000000000000000 → 0x0000000000000000
$r12 : 0x0000000000000000 → 0x0000000000000000
$r13 : 0xffff888069f3b4d0 → 0xffff888069d0a560 → 0x0000000001c09000 → 0x0000000001c09000
$r14 : 0x0000000000000001 → 0x0000000000000001
$r15 : 0xffff8880696ee1a0 → 0xffff8880696ee000 → 0x0000000000000001 → 0x0000000000000001
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflowresume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────── stack ────
0xffff888063d4e620│+0x0000: 0xffff888067d9a200 → 0x1e44a09cf7d0cbce →0x1e44a09cf7d0cbce ← $rsp
0xffff888063d4e628│+0x0008: 0xffff888063d4e6b8 → 0x0000000000000001 →0x0000000000000001
0xffff888063d4e630│+0x0010: 0xffffed100c7a9cdf → 0x00000000f2f8f8f8 →0x00000000f2f8f8f8
0xffff888063d4e638│+0x0018: 0x0000000000800000 → 0x0000000000800000
0xffff888063d4e640│+0x0020: 0xffff888063d4e6c0 → 0x00000000c00000cc →0x00000000c00000cc
0xffff888063d4e648│+0x0028: 0xffff888063d4e6f8 → 0x0000000000000001 →0x0000000000000001
0xffff888063d4e650│+0x0030: 0xffff888067d9a318 → 0x0000000000000000 →0x0000000000000000
0xffff888063d4e658│+0x0038: 0x0000000000800000 → 0x0000000000800000
───────────────────────────────────────────────────────── code:x86:64 ────
0xffffffff81def0da <btrfs_verify_dev_extents+1898> lea rcx, [rax+0x98]
0xffffffff81def0e1 <btrfs_verify_dev_extents+1905> mov rdx, rcx
0xffffffff81def0e4 <btrfs_verify_dev_extents+1908> shr rdx, 0x3
→ 0xffffffff81def0e8 <btrfs_verify_dev_extents+1912> cmp BYTE PTR [rdx+rbx*1], 0x0
0xffffffff81def0ec <btrfs_verify_dev_extents+1916> jne 0xffffffff81def982 <btrfs_verify_dev_extents+4114>
0xffffffff81def0f2 <btrfs_verify_dev_extents+1922> mov rax, QWORD PTR [rax+0x98]
0xffffffff81def0f9 <btrfs_verify_dev_extents+1929> cmp rax, rcx
0xffffffff81def0fc <btrfs_verify_dev_extents+1932> je 0xffffffff81def95c <btrfs_verify_dev_extents+4076>
0xffffffff81def102 <btrfs_verify_dev_extents+1938> lea rdi, [rax+0x88]
─────────────────────────────────────── source:fs/btrfs/volumes.c+430 ────
425 static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
426 u64 devid, const u8 *uuid)
427 {
428 struct btrfs_device *dev;
429
→ 430 list_for_each_entry(dev, &fs_devices->devices, dev_list) {
431 if (dev->devid == devid &&
432 (!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) {
433 return dev;
434 }
435 }
───────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
[#1] Id 2, Name: "", stopped, reason: BREAKPOINT
─────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81def0e8 → find_device(uuid=<optimized out>, devid=<optimized out>, fs_devices=<optimized out>)
[#1] 0xffffffff81def0e8 → verify_one_dev_extent(physical_len=<optimized out>, physical_offset=<optimized out>, devid=<optimized out>, chunk_offset=<optimized out>, fs_info=<optimized out>)
[#2] 0xffffffff81def0e8 → btrfs_verify_dev_extents(fs_info=<optimized out>)
[#3] 0xffffffff81d243f0 → open_ctree(sb=0xffff888069cf3b80, fs_devices=<optimized out>, options=<optimized out>)
[#4] 0xffffffff81c7c5d4 → btrfs_fill_super(data=<optimized out>, fs_devices=<optimized out>, sb=<optimized out>)
[#5] 0xffffffff81c7c5d4 → btrfs_mount_root(fs_type=<optimized out>, flags=<optimized out>, device_name=<optimized out>, data=0x0 <irq_stack_union>)
[#6] 0xffffffff816979d9 → mount_fs(type=0xffff88806a4eb248, flags=0x0, name=<optimized out>, data=<optimized out>)
[#7] 0xffffffff8170e8d9 → vfs_kern_mount(type=<optimized out>, flags=<optimized out>, name=0xffff88806c594160 "/dev/loop0", data=0x0 <irq_stack_union>)
[#8] 0xffffffff8170ec2a → vfs_kern_mount(type=<optimized out>, flags=<optimized out>, name=<optimized out>, data=<optimized out>)
[#9] 0xffffffff81c810d5 → btrfs_mount(fs_type=<optimized out>, flags=0x0,device_name=<optimized out>, data=0x0 <irq_stack_union>)
──────────────────────────────────────────────────────────────────────────
Thread 2 hit Breakpoint 1, 0xffffffff81def0e8 in find_device (uuid=<optimized out>, devid=<optimized out>, fs_devices=<optimized out>) at fs/btrfs/volumes.c:430
430 list_for_each_entry(dev, &fs_devices->devices, dev_list) {
l'instruction cmp BYTE PTR [rdx+rbx*1], 0x0 tente de lire la valeur à l'adresse 0xdffffc0000000000 + 0x13
[ 196.251167] BTRFS: device fsid a62e00e8-e94e-4200-8217-12444de93c2e devid 1 transid 8 /dev/loop0
[ 196.392627] BTRFS info (device loop0): disk space caching is enabled
[ 196.395228] BTRFS info (device loop0): has skinny extents
[ 196.770076] BTRFS critical (device loop0): corrupt leaf: root=1 block=29417472 slot=6 ino=6, name hash mismatch with key, have 0x00000000be52bff8 e
xpect 0x000000008dbfc2d2
[ 196.794355] BTRFS info (device loop0): read error corrected: ino 0 off 29417472 (dev /dev/loop0 sector 73840)
[ 196.879172] kasan: CONFIG_KASAN_INLINE enabled
[ 196.881094] kasan: GPF could be caused by NULL-ptr deref or user memory access
[ 196.883004] general protection fault: 0000 [#1] SMP KASAN NOPTI
[ 196.883474] CPU: 0 PID: 1989 Comm: mount Not tainted 5.0.21 #1
[ 196.883474] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 196.883474] RIP: 0010:btrfs_verify_dev_extents+0x778/0x1140
[ 196.883474] Code: c8 00 00 00 48 89 fa 48 c1 ea 03 80 3c 1a 00 0f 85 31 06 00 00 48 8b 80 c8 00 00 00 48 8d 88 98 00 00 00 48 89 ca 48 c1 ea 03 <8$
> 3c 1a 00 0f 85 90 08 00 00 48 8b 80 98 00 00 00 48 39 c8 0f 84
[ 196.883474] RSP: 0018:ffff888067eee620 EFLAGS: 00000202
[ 196.883474] RAX: 0000000000000000 RBX: dffffc0000000000 RCX: 0000000000000098
[ 196.883474] RDX: 0000000000000013 RSI: 0000000000000001 RDI: ffff888069afa348
[ 196.883474] RBP: 0000000000c00000 R08: 1ffff1100d8c3f81 R09: 0000000000000000
[ 196.883474] R10: 0000000000000001 R11: ffffed100c82f023 R12: 0000000000000000
[ 196.883474] R13: ffff888069e5f210 R14: 0000000000000001 R15: ffff888069f061a0
[ 196.883474] FS: 00007f581c668e40(0000) GS:ffff88806d000000(0000) knlGS:0000000000000000
[ 196.883474] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 196.883474] CR2: 0000564ef42ea000 CR3: 0000000069f98000 CR4: 00000000000006f0[ 196.883474] Call Trace:
[ 196.883474] ? run_one_async_done+0x250/0x250
[ 196.883474] ? btrfs_bg_type_to_factor+0x10/0x10
[ 196.883474] ? __kasan_slab_free+0x143/0x180
[ 196.883474] ? btrfs_read_tree_root+0x166/0x330
[ 196.883474] ? btrfs_read_tree_root+0x166/0x330
[ 196.883474] open_ctree+0x4cd0/0x7ada
[ 196.883474] ? close_ctree+0xa10/0xa10
[ 196.883474] ? __lock_text_start+0x8/0x8
[ 196.883474] ? __save_stack_trace+0x8d/0xf0
[ 196.883474] ? depot_save_stack+0x2d9/0x470
[ 196.883474] ? lockref_put_or_lock+0x1c0/0x360
[ 196.883474] ? lockref_put_return+0x280/0x280
[ 196.883474] ? __debugfs_create_file+0x69/0x380
[ 196.883474] ? super_setup_bdi_name+0x135/0x260
[ 196.883474] ? mount_fs+0xb9/0x370
[ 196.883474] ? vfs_kern_mount.part.28+0xb9/0x400
[ 196.883474] ? mount_fs+0xb9/0x370
[ 196.883474] ? vfs_kern_mount.part.28+0xb9/0x400
[ 196.883474] ? do_mount+0xef4/0x2d40
[ 196.883474] ? ksys_mount+0x7b/0xd0
[ 196.883474] ? do_syscall_64+0x12b/0x440
[ 196.883474] ? __dentry_path+0x31e/0x6e0
[ 196.883474] ? rcu_qs+0x2f0/0x2f0
[ 196.883474] ? __alloc_pages_nodemask+0x467/0xa00
[ 196.883474] ? dput+0x33b/0x540
[ 196.883474] ? rcu_qs+0x2f0/0x2f0
[ 196.883474] ? page_alloc_cpu_dead+0x30/0x30
[ 196.883474] ? free_pcp_prepare+0xb8/0x220
[ 196.883474] ? _raw_spin_lock+0x99/0x130
[ 196.883474] ? _raw_read_lock_irq+0x30/0x30
[ 196.883474] ? __fsnotify_inode_delete+0x10/0x10
[ 196.883474] ? get_nr_inodes+0xf0/0xf0
[ 196.883474] ? __d_instantiate+0x366/0x6e0
[ 196.883474] ? __lookup_slow+0x1f7/0x450
[ 196.883474] ? rcu_qs+0x2f0/0x2f0
[ 196.883474] ? _raw_spin_lock+0x30/0x130
[ 196.883474] ? _raw_read_lock_irq+0x30/0x30
[ 196.883474] ? _raw_read_lock_irq+0x30/0x30
[ 196.883474] ? _raw_spin_lock+0x99/0x130
[ 196.883474] ? _raw_read_lock_irq+0x30/0x30
[ 196.883474] ? _raw_spin_lock_bh+0xa4/0x130
[ 196.883474] ? _raw_write_trylock+0x1f0/0x1f0
[ 196.883474] ? __debugfs_create_file+0x277/0x380
[ 196.883474] ? debugfs_create_dir+0x286/0x360
[ 196.883474] ? address_val+0x90/0x90
[ 196.883474] ? set_wb_congested+0x30/0x30
[ 196.883474] ? rcu_qs+0x2f0/0x2f0
[ 196.883474] ? __kasan_kmalloc.constprop.4+0xa0/0xd0
[ 196.883474] ? super_setup_bdi_name+0x135/0x260
[ 196.883474] ? kill_block_super+0xd0/0xd0
[ 196.883474] ? snprintf+0x8f/0xc0
[ 196.883474] ? btrfs_mount_root+0xe24/0x14c0
[ 196.883474] btrfs_mount_root+0xe24/0x14c0
[ 196.883474] ? btrfs_decode_error+0x50/0x50
[ 196.883474] ? module_enable_ro.part.69+0x160/0x160
[ 196.883474] ? deref_stack_reg+0xab/0x110
[ 196.883474] ? __read_once_size_nocheck.constprop.8+0x10/0x10
[ 196.883474] ? deref_stack_reg+0xab/0x110
[ 196.883474] ? __free_insn_slot+0x450/0x450
[ 196.883474] ? rcu_is_watching+0x7d/0x130
[ 196.883474] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 196.883474] ? rcu_barrier_trace+0x260/0x260
[ 196.883474] ? mount_fs+0xb9/0x370
[ 196.883474] ? btrfs_decode_error+0x50/0x50
[ 196.883474] mount_fs+0xb9/0x370
[ 196.883474] ? pcpu_next_unpop+0xe0/0xe0
[ 196.883474] ? __kernel_text_address+0x9/0x30
[ 196.883474] ? emergency_thaw_all+0x230/0x230
[ 196.883474] ? apic_timer_interrupt+0xa/0x20
[ 196.883474] vfs_kern_mount.part.28+0xb9/0x400
[ 196.883474] ? __cleanup_mnt+0x10/0x10
[ 196.883474] ? __lock_text_start+0x8/0x8
[ 196.883474] ? pcpu_alloc_area+0x170/0x3c0
[ 196.883474] btrfs_mount+0x3c5/0x1fd9
[ 196.883474] ? btrfs_remount+0x1190/0x1190
[ 196.883474] ? ida_free+0x3b0/0x3b0
[ 196.883474] ? kasan_unpoison_shadow+0x30/0x40
[ 196.883474] ? __kasan_kmalloc.constprop.4+0xa0/0xd0
[ 196.883474] ? dl_cpu_busy+0x1f0/0x1f0
[ 196.883474] ? alloc_vfsmnt+0x146/0x950
[ 196.883474] ? memcpy+0x34/0x50
[ 196.883474] ? alloc_vfsmnt+0x617/0x950
[ 196.883474] ? m_stop+0x10/0x10
[ 196.883474] ? memcpy+0x34/0x50
[ 196.883474] ? avc_has_perm_noaudit+0x22a/0x4c0
[ 196.883474] ? avc_has_perm+0x24f/0x620
[ 196.883474] ? avc_has_extended_perms+0x1320/0x1320
[ 196.883474] ? __read_once_size_nocheck.constprop.8+0x10/0x10
[ 196.883474] ? mount_fs+0xb9/0x370
[ 196.883474] mount_fs+0xb9/0x370
[ 196.883474] ? emergency_thaw_all+0x230/0x230
[ 196.883474] ? __module_get+0x1a0/0x1a0
[ 196.883474] ? selinux_sb_eat_lsm_opts+0x520/0x520
[ 196.883474] vfs_kern_mount.part.28+0xb9/0x400
[ 196.883474] ? __cleanup_mnt+0x10/0x10
[ 196.883474] ? getname_flags+0xb5/0x500
[ 196.883474] ? __get_fs_type+0x79/0xb0
[ 196.883474] do_mount+0xef4/0x2d40
[ 196.883474] ? copy_mount_string+0x20/0x20
[ 196.883474] ? save_stack+0x89/0xb0
[ 196.883474] ? __kasan_kmalloc.constprop.4+0xa0/0xd0
[ 196.883474] ? __kmalloc_track_caller+0xc7/0x1d0
[ 196.883474] ? memdup_user+0x23/0x60
[ 196.883474] ? __x64_sys_mount+0xb5/0x150
[ 196.883474] ? do_syscall_64+0x12b/0x440
[ 196.883474] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 196.883474] ? security_file_free+0x3a/0x70
[ 196.883474] ? kmem_cache_free+0x70/0x1a0
[ 196.883474] ? __fput+0x3f1/0x840
[ 196.883474] ? _copy_to_user+0x68/0x90
[ 196.883474] ? _raw_spin_lock_irq+0x9a/0x130
[ 196.883474] ? _raw_write_unlock_irqrestore+0x110/0x110
[ 196.883474] ? __close_fd+0x32a/0x540
[ 196.883474] ? task_work_run+0xd0/0x290
[ 196.883474] ? rcu_qs+0x2f0/0x2f0
[ 196.883474] ? kasan_unpoison_shadow+0x30/0x40
[ 196.883474] ? __kasan_kmalloc.constprop.4+0xa0/0xd0
[ 196.883474] ? strndup_user+0x42/0x90
[ 196.883474] ? __kmalloc_track_caller+0xc7/0x1d0
[ 196.883474] ? _copy_from_user+0x70/0xa0
[ 196.883474] ? memdup_user+0x39/0x60
[ 196.883474] ksys_mount+0x7b/0xd0
[ 196.883474] __x64_sys_mount+0xb5/0x150
[ 196.883474] do_syscall_64+0x12b/0x440
[ 196.883474] ? syscall_return_slowpath+0x2e0/0x2e0
[ 196.883474] ? prepare_exit_to_usermode+0x1be/0x210
[ 196.883474] ? perf_trace_sys_enter+0x1050/0x1050
[ 196.883474] ? __x64_sys_sigaltstack+0x270/0x270
[ 196.883474] ? __put_user_4+0x1c/0x30
[ 196.883474] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 196.883474] RIP: 0033:0x7f581bd2348a
[ 196.883474] Code: 48 8b 0d 11 fa 2a 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 00 0f 05 <48
> 3d 01 f0 ff ff 73 01 c3 48 8b 0d de f9 2a 00 f7 d8 64 89 01 48
[ 196.883474] RSP: 002b:00007fffbf988ac8 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5
[ 196.883474] RAX: ffffffffffffffda RBX: 000055718faa2080 RCX: 00007f581bd2348a
[ 196.883474] RDX: 000055718faa2260 RSI: 000055718faa7ec0 RDI: 000055718faa8db0
[ 196.883474] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000020
[ 196.883474] R10: 00000000c0ed0000 R11: 0000000000000202 R12: 000055718faa8db0
[ 196.883474] R13: 000055718faa2260 R14: 0000000000000000 R15: 00000000ffffffff
[ 196.883474] Modules linked in:
[ 196.934565] ---[ end trace b2518a0a4d2b3ae0 ]---
[ 196.935064] RIP: 0010:btrfs_verify_dev_extents+0x778/0x1140
[ 196.935271] Code: c8 00 00 00 48 89 fa 48 c1 ea 03 80 3c 1a 00 0f 85 31 06 00 00 48 8b 80 c8 00 00 00 48 8d 88 98 00 00 00 48 89 ca 48 c1 ea 03 <80
> 3c 1a 00 0f 85 90 08 00 00 48 8b 80 98 00 00 00 48 39 c8 0f 84
[ 196.939094] RSP: 0018:ffff888067eee620 EFLAGS: 00000202
[ 196.942503] RAX: 0000000000000000 RBX: dffffc0000000000 RCX: 0000000000000098
[ 196.942811] RDX: 0000000000000013 RSI: 0000000000000001 RDI: ffff888069afa348
[ 196.943370] RBP: 0000000000c00000 R08: 1ffff1100d8c3f81 R09: 0000000000000000
[ 196.946786] R10: 0000000000000001 R11: ffffed100c82f023 R12: 0000000000000000
[ 196.947634] R13: ffff888069e5f210 R14: 0000000000000001 R15: ffff888069f061a0
[ 196.950778] FS: 00007f581c668e40(0000) GS:ffff88806d000000(0000) knlGS:0000000000000000
[ 196.952541] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 196.952704] CR2: 0000564ef42ea000 CR3: 0000000069f98000 CR4: 00000000000006f0
Ce projet a utilisé une version portée (vers les noyaux Linux 5.0.21 et 5.3.14) du fuzzer de systèmes de fichiers 'JANUS' développé par GeorgiaTech Systems Software & Security Lab (SSLab)
Merci pour l'excellent fuzzer et l'article ci-dessous.