接下来到年前应该都是在学习漏洞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_CANTRCVMORE
与SS_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 */
找到这个宏的使用场景,发现是在level
为SOL_SOCKET
的分支里,当满足sonpx.npx_mask
和sonpx.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
数组内搜索对应name
的ipc_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,所有参与者都要提前阅读其他分享者的文章并准备要提问的问题
吃瓜时间,这个我不知道真假,只是道听途说的,本分厂的瓜就吃到这