大土豆安全笔记 | 在线求团购 OS Internals III

接下来到年前应该都是在学习漏洞CVE-2019-8605

  • https://bugs.chromium.org/p/project-zero/issues/detail?id=1806

两篇相关的文章,不得不说,PJ0的人写的文章就是棒!

  • https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html
  • https://jsherman212.github.io/used_sock/

这个漏洞源于调用函数disconnectx()后,在对inp->in6p_outputopts进行释放操作时,没有将其置为空,导致的一个释放后重用漏洞

ip6_freepcbopts(inp->in6p_outputopts);

这里的调用路径上有一个CHECK需要绕过

#define	SS_CANTRCVMORE		0x0020	/* can't receive more data from peer */
#define	SS_CANTSENDMORE		0x0010	/* can't send more data to peer */
#define	SOF_NPX_SETOPTSHUT	0x00002000 /* Non POSIX extension to allow

int
sosetoptlock(struct socket *so, struct sockopt *sopt, int dolock)
{
	...

	if ((so->so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE)) ==
	    (SS_CANTRCVMORE | SS_CANTSENDMORE) &&
	    (so->so_flags & SOF_NPX_SETOPTSHUT) == 0) {
		/* the socket has been shutdown, no more sockopt's */
		error = EINVAL;
		goto out;
	}
	...

理解一下这个检查,左边so->so_state只能是SS_CANTRCVMORESS_CANTSENDMORE之间任意一种且右边so->so_flags不能是SOF_NPX_SETOPTSHUT,就会跳到goto out

(so->so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE)) == (SS_CANTRCVMORE | SS_CANTSENDMORE) 
&& (so->so_flags & SOF_NPX_SETOPTSHUT) == 0

但是天无绝人之路,看下面这个宏,允许在关闭Socket之后使用函数setsockopt

#define	SONPX_SETOPTSHUT	0x000000001	/* flag for allowing setsockopt after shutdown */

找到这个宏的使用场景,发现是在levelSOL_SOCKET的分支里,当满足sonpx.npx_masksonpx.npx_flags都为SONPX_SETOPTSHUT时,就会给so->so_flags添加SOF_NPX_SETOPTSHUT标志位

int
sosetoptlock(struct socket *so, struct sockopt *sopt, int dolock)
{
	...
	if (sopt->sopt_level != SOL_SOCKET) {
		...
	} else {
		...
		switch (sopt->sopt_name) {
		...
		case SO_NP_EXTENSIONS: {
			struct so_np_extensions sonpx;

			error = sooptcopyin(sopt, &sonpx, sizeof (sonpx),
			    sizeof (sonpx));
			if (error != 0)
				goto out;
			if (sonpx.npx_mask & ~SONPX_MASK_VALID) {
				error = EINVAL;
				goto out;
			}
			/*
			 * Only one bit defined for now
			 */
			if ((sonpx.npx_mask & SONPX_SETOPTSHUT)) {
				if ((sonpx.npx_flags & SONPX_SETOPTSHUT))
					so->so_flags |= SOF_NPX_SETOPTSHUT;    // 添加标志位
				else
					so->so_flags &= ~SOF_NPX_SETOPTSHUT;
			}
			break;
		}

so->so_flags拥有SOF_NPX_SETOPTSHUT标志位,那么右边的检查就不能成立,成功绕过

之后Socket就可以顺利的在关闭后调用函数setsockopt()

我们知道UAF漏洞的一个技巧是利用内存分配来占用被释放的空间,那填充这个空间的技术也是比较讲究的

函数ipc_kmsg_copyin_ool_ports_descriptor()专门用于处理MACH_MSG_OOL_PORTS_DESCRIPTOR,使用MACH_MSG_OOL_PORTS_DESCRIPTOR可以在消息中传输大量的Port

在层层往下调用后,会调用函数ipc_entry_lookup(),该函数会在is_table数组内搜索对应nameipc_entry,我们可以从这个ipc_entry获取传入用户态Port的真实内核态对象

ipc_entry_t
ipc_entry_lookup(
	ipc_space_t		space,
	mach_port_name_t	name)
{
	mach_port_index_t index;
	ipc_entry_t entry;
	index = MACH_PORT_INDEX(name);
	if (index <  space->is_table_size) {
                entry = &space->is_table[index];
		...
	}

	return entry;
}

层层往回走,函数ipc_object_copyin()的参数objectp会被存储到Caller函数ipc_kmsg_copyin_ool_ports_descriptor()objects[]数组里

for ( i = 0; i < count; i++) {
    mach_port_name_t name = names[i];
    ipc_object_t object;
    if (!MACH_PORT_VALID(name)) {
        objects[i] = (ipc_object_t)CAST_MACH_NAME_TO_PORT(name);
        continue;
    }
    kern_return_t kr = ipc_object_copyin(space, name, user_disp, &object);
    objects[i] = object;
}

数组objects[]在函数ipc_kmsg_copyin_ool_ports_descriptor进行内存空间分配,所以我们只要让ports_length等于inp->in6p_outputopts的大小,就可以让它分配到我们释放掉的空间里

data = kalloc(ports_length);
objects = (ipc_object_t *) data;

还有填充之后,如何构造所需原语,这些都需要我再仔细学习研究

有很多的细节真的还是要看《Mac OS X技术内幕》这本书,其它地方完全搜不到

上次说的卷三可能要等之后团购了,大佬们要是有渠道一定喊我一下,最好有优惠的价格那种,我比较穷

要是有大佬不玩了,可以二手卖我

《Exploiting CVE-2020-15994 Chrome WebAssembly Engine UAF Vulnerability》,浏览器要跟起来了,毕竟是要浏览器打到内核

  • https://blog.br0vvnn.io/pages/blogpost.aspx?id=5

Flanker的分享《Fuzzing战争系列之二:不畏浮云遮望眼》,我底子差,认真看了个乐,师傅们看懂了教我

  • https://speakerdeck.com/flankerhqd/blowing-the-cover-of-android-binary-fuzzing

我计划在农历年前或者年后组织一波技术分享,线上视频会议模式

人数不会很多,大概三到五个人,所有参与者都需要准备分享议题,分享前一周提供完整版文章与Slides,所有参与者都要提前阅读其他分享者的文章并准备要提问的问题

吃瓜时间,这个我不知道真假,只是道听途说的,本分厂的瓜就吃到这

IMAGE