首页
社区
课程
招聘
[翻译]task_t指针重大风险预报
发表于: 2017-2-20 10:54 3319

[翻译]task_t指针重大风险预报

2017-2-20 10:54
3319

  引言:大家都知道知名意大利天才少年Luca放出来的针对<=10.2版本的yalu越狱使用的是对kernel portbuffer overflow拿到了kernel_task_port,本文对类似的task_t指针做出了针对性的分析,从mach端口背景知识,到IOkit的相关处理,最终如何利用在堆栈上写出Exploit,最终甚至给苹果团队给出了修复漏洞的建议,由浅入深,偏辟入里,值得推荐。

本文分三篇推出,分别是分析篇,Exploit篇,和修复建议篇。


译者注:


·一些诸如bugexploit之类的行话选择性的翻译,这通常取决于句子的流畅性。

·不确定的地方在括号中附注了原句

·超链接附在括号内,方便查看

 

by ruanbonan

 

分析篇

task_t 指针存在漏洞   lan Beer,Project Zero 发布

 

本文讨论了一个存在于驱动 iOS MacOS XNU 内核核心部分的设计问题。苹果已经发布了两轮缓解策略,紧随其后地,昨天又发布了MacOS 10.12.1/iOS 10.1 中的重大重构(Apple have shipped two iterations of mitigations followed yesterday by a large refactor in MacOS 10.12.1/iOS 10.1)。我们将关注以下内容:这些bug怎样被利用来进行沙箱逃逸并提升权限;我们如何绕过每一个缓解策略。每一步都配有一个可用的exploit

 

一些关于mach端口的背景知识

mach端口是由内核维护的多发送者-单接收者的消息队列。某些特别的mach端口。

提供与用户空间相同的消息传送API,但是发送给它们的消息会被内核消息处理程序同步处理。从这个意义上来说,发给这些端口的消息与系统调用非常像。

任务端口就是这样的一个例子。它们处理那些允许发送者操作一个任务的虚拟内存,并能够访问它的线程的消息。每一个任务有它自己的任务端口。内核占用的消息端口使用MIG这个工具来生成序列化代码。

 

从底层看IOKit

当在用户空间创建一个新的IOKit用户客户端时,你通常会调用IOKitLib库的以下方法:

kern_return_t

IOServiceOpen(

io_service_t service,

task_port_t owningTask,

uint32_t   type,                   

io_connect_t *connect );

 

IOServiceOpen调用MIGio_service_open_extended进程间通信方法生成序列化代码,并把序列化消息发送给已定的IOService端口。mach陷阱mach_msg注意到这个端口被内核占用,并为该消息调用正确的内核MIG处理程序,而不是把它排在端口消息队列的队尾。

这里传来的任务端口是owningTask;这个名字在用户空间和内核代码中相同。它是引起我注意的第一处地方。OwningTask暗示着一个所属关系,这可能导致内核扩展开发者相信IOKit实际在这背后维护了一个所属关系,而这个关系确保userclient的生命周期总由owningTask的生命周期决定。这是一个危险的假设,本篇博客文章是质疑这个假设的结果。让我们来跟随代码流进入内核。下面是一段来自内核里面的MIGio_service_open_extended生成的反序列化代码片段:

mig_internal   novalue _Xio_service_open_extended(

mach_msg_header_t   *InHeadP,

mach_msg_header_t   *OutHeadP)

{

...

owningTask =   convert_port_to_task(In0P->owningTask.n11ame);

 

RetCode   = is_io_service_open_extended(

        service,

             owningTask,

             In0P->connect_type,

             In0P->ndr,

             (io_buf_ptr_t)(In0P->properties.address),

             In0P->propertiesCnt,   &OutP->result, &connection);

 

 task_deallocate(owningTask);

...

}

 

内核已经把所有包含在消息中的权限复制进来,所以In0P->owningTask.name  实际上是指向一个struct ipc_port的指针,而不是用户态看到的mach端口名。

下面是convert_port_to_task:

task_t

convert_port_to_task(

 ipc_port_t   port)

{

 task_t   task = TASK_NULL;

 

 if   (IP_VALID(port)) {

   ip_lock(port);

   if   (ip_active(port) &&

       ip_kotype(port)   == IKOT_TASK)

   {

     task   = (task_t)port->ip_kobject;

     assert(task   != TASK_NULL);

     task_reference_internal(task);

   }

   ip_unlock(port);

 }

 

 return   (task);

}

 

它检查了port参数,确保是一个任务端口对象,然后通过调用task_reference来对任务提供引用,返回task_t指针。task_t是对struct task指针的别名,从代码中可以看出,它是一个引用计数对象。

 

这里is_is_service_open_extended仅仅是将owningTask传给::newUserClient

 

 res   = service->newUserClient(

   owningTask,

   (void   *) owningTask,

   connect_type,

   propertiesDict,

   &client );

 

newUserClient是一个IOService方法,如果它们想提供多userclient类型,它可以被一个IOService覆盖。否则默认执行在IOKit记录中查询IOServiceIOUserClient子类类名,通过IOKit的反射API(链接:https://bugs.chromium.org/p/project-zero/issues/detail?id=221)分配它,并调用它的::initWithTask方法。::initWithTask的默认执行流也不对owningTask做任何处理。

查看代码到此,似乎默认情况下owningTask并不会保有对userclient的引用(这会避免userclient对任务进行引用,形成循环引用,事实却完全相反;如果userclient想要保持对owningTask的引用,它必须对owningTask进行引用——不存在隐式的所属关系。


译者:ruanbonan
原文链接:https://googleprojectzero.blogspot.kr/2016/10/taskt-considered-harmful.html

  原文作者:lan Beer,Project Zero

微信公众号:看雪iOS安全小组 我们的微博:weibo.com/pediyiosteam

我们的知乎:zhihu.com/people/pediyiosteam

加入我们:看雪iOS安全小组成员募集中:http://bbs.pediy.com/showthread.php?t=212949
[
看雪iOS安全小组]置顶向导集合贴:
http://bbs.pediy.com/showthread.php?t=212685

·   



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//