欢迎来到 无奈人生 安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

macOS内核提权:利用CVE-2016-1758获取kernel slide(Part1)

来源: 作者: 时间:2019-02-24 20:29 点击: 我要投稿
广告位API接口通信错误,查看德得广告获取帮助

0x001 前言
本文是基于CVE-2016-1758、CVE-2016-1828来讨论一下macOS下的内核提权技术。CVE-2016-1758是一个内核信息泄漏的洞,由于没有严格控制好内核栈数据copy的size,导致可以将额外8个bytes的内核地址泄漏出来,计算得到kernel_slide。CVE-2016-1828则是内核uaf的洞,存在于OSUnserializeBinary函数内,通过一个可控的虚表指针,将执行流劫持到NULL页上作ROP完成提权。
 
0x002 调试环境
虚拟机: OS X Yosemite 10.10.5 14F27主机: macOS Mojave 10.14.2 18C54
这里简单说一下环境搭建,在Parallel Desktop虚拟机安装OS X 10.10.5,主机安装KDK 10.10.5 14F27,安装目录是/Library/Developer/KDKs,提供的内核版本、符号、内核扩展都有release、development、debug三种版本。
启动虚拟机,看一下ip

设置启动参数
sudo nvram boot-args="debug=0x141 kext-dev-mode=1 pmuflags=1 -v"
我们这里直接调试realease版本的内核,所以不需要加kcsuffix=development这条参数。要是需要调试development或debug版本的内核,可以从主机安装的KDK包拷贝对应的内核到虚拟机的/System/Library/Kernels目录,再设置kcsuffix参数。
令内核缓存无效,重启
sudo kextcache -invalidate /
sudo reboot
主机打开lldb,引入调试符号
target create /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel
虚拟机启动起来卡在开机,并等待调试器接入

kdp-remote连上去

 
0x003 内核源码分析
获取xnu内核代码
xnu-2782.40.9
找到/bsd/net/if.c里的if_clone_list方法
/*
 * Provide list of interface cloners to userspace.
 */
static int
if_clone_list(int count, int *ret_total, user_addr_t dst)
{
  char outbuf[IFNAMSIZ];
  struct if_clone *ifc;
  int error = 0;
  *ret_total = if_cloners_count;
  if (dst == USER_ADDR_NULL) {
    /* Just asking how many there are. */
    return (0);
  }
  if (count 0)
    return (EINVAL);
  count = (if_cloners_count for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
       ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
    strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
    error = copyout(outbuf, dst, IFNAMSIZ);
    if (error)
      break;
  }
  return (error);
}
IFNAMSIZ长度为16,由于ifc是定义在内核栈上的局部数据,当ifc_name小于outbuf的长度,所以会将未初始化的内核地址拷贝到用户空间,计算得到kernel slide。
ifc_name存放着6个bytes的数据bridge,剩余9个bytes为初始化的数据存在outbuf上。
下面是if_clone_list方法的调用链
soo_ioctl -> soioctl -> ifioctllocked -> ifioctl -> ifioctl_ifclone -> if_clone_list
soo_ioctl方法在socketops结构体中被引用
const struct fileops socketops = {
  DTYPE_SOCKET,
  soo_read,
  soo_write,
  soo_ioctl,
  soo_select,
  soo_close,
  soo_kqfilter,
  soo_drain
};
要使得ifioctl调用ifioctl_ifclone,要传进cmd参数SIOCIFGCLONERS,类似这样ioctl(sockfd,SIOCIFGCLONERS,&ifcr)
int
ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
{
  char ifname[IFNAMSIZ + 1];
  struct ifnet *ifp = NULL;
  struct ifstat *ifs = NULL;
  int error = 0;
  bzero(ifname, sizeof (ifname));
  /*
   * ioctls which don't require ifp, or ifreq ioctls
   */
  switch (cmd) {
  case OSIOCGIFCONF32:      /* struct ifconf32 */
  case SIOCGIFCONF32:     /* struct ifconf32 */
  case SIOCGIFCONF64:     /* struct ifconf64 */
  case OSIOCGIFCONF64:      /* struct ifconf64 */
    error = ifioctl_ifconf(cmd, data);
    goto done;
  case SIOCIFGCLONERS32:      /* struct if_clonereq32 */
  case SIOCIFGCLONERS64:      /* struct if_clonereq64 */
    error = ifioctl_ifclone(cmd, data);
    goto done;
  case SIOCGIFAGENTDATA32:    /* struct netagent_req32 */
  case SIOCGIFAGENTDATA64:    /* struct netagent_req64 */
    error = netagent_ioctl(cmd, data);
    goto done;
查看ifioctl_ifclone方法,要使用if_clonereq结构作为if_clone_list的调用参数

[1] [2] [3] [4] [5]  下一页

0x001 前言
本文是基于CVE-2016-1758、CVE-2016-1828来讨论一下macOS下的内核提权技术。CVE-2016-1758是一个内核信息泄漏的洞,由于没有严格控制好内核栈数据copy的size,导致可以将额外8个bytes的内核地址泄漏出来,计算得到kernel_slide。CVE-2016-1828则是内核uaf的洞,存在于OSUnserializeBinary函数内,通过一个可控的虚表指针,将执行流劫持到NULL页上作ROP完成提权。
 
0x002 调试环境
虚拟机: OS X Yosemite 10.10.5 14F27主机: macOS Mojave 10.14.2 18C54
这里简单说一下环境搭建,在Parallel Desktop虚拟机安装OS X 10.10.5,主机安装KDK 10.10.5 14F27,安装目录是/Library/Developer/KDKs,提供的内核版本、符号、内核扩展都有release、development、debug三种版本。
启动虚拟机,看一下ip

设置启动参数
sudo nvram boot-args="debug=0x141 kext-dev-mode=1 pmuflags=1 -v"

无奈人生安全网


我们这里直接调试realease版本的内核,所以不需要加kcsuffix=development这条参数。要是需要调试development或debug版本的内核,可以从主机安装的KDK包拷贝对应的内核到虚拟机的/System/Library/Kernels目录,再设置kcsuffix参数。
令内核缓存无效,重启
sudo kextcache -invalidate /
sudo reboot
主机打开lldb,引入调试符号
target create /Library/Developer/KDKs/KDK_10.10.5_14F27.kdk/System/Library/Kernels/kernel
虚拟机启动起来卡在开机,并等待调试器接入

kdp-remote连上去

 
0x003 内核源码分析
获取xnu内核代码
xnu-2782.40.9
找到/bsd/net/if.c里的if_clone_list方法 www.wnhack.com
/*
 * Provide list of interface cloners to userspace.
 */
static int
if_clone_list(int count, int *ret_total, user_addr_t dst)
{
  char outbuf[IFNAMSIZ];
  struct if_clone *ifc;
  int error = 0;
  *ret_total = if_cloners_count;
  if (dst == USER_ADDR_NULL) {
    /* Just asking how many there are. */
    return (0);
  }
  if (count 0)
    return (EINVAL);
  count = (if_cloners_count for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
       ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
    strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
    error = copyout(outbuf, dst, IFNAMSIZ);
    if (error)
      break;
  }
  return (error);
}
IFNAMSIZ长度为16,由于ifc是定义在内核栈上的局部数据,当ifc_name小于outbuf的长度,所以会将未初始化的内核地址拷贝到用户空间,计算得到kernel slide。 无奈人生安全网
ifc_name存放着6个bytes的数据bridge,剩余9个bytes为初始化的数据存在outbuf上。
下面是if_clone_list方法的调用链
soo_ioctl -> soioctl -> ifioctllocked -> ifioctl -> ifioctl_ifclone -> if_clone_list
soo_ioctl方法在socketops结构体中被引用
const struct fileops socketops = {
  DTYPE_SOCKET,
  soo_read,
  soo_write,
  soo_ioctl,
  soo_select,
  soo_close,
  soo_kqfilter,
  soo_drain
};
要使得ifioctl调用ifioctl_ifclone,要传进cmd参数SIOCIFGCLONERS,类似这样ioctl(sockfd,SIOCIFGCLONERS,&ifcr)
int
ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
{
  char ifname[IFNAMSIZ + 1];
  struct ifnet *ifp = NULL;
  struct ifstat *ifs = NULL;
  int error = 0;
  bzero(ifname, sizeof (ifname));
  /*
   * ioctls which don't require ifp, or ifreq ioctls
   */
  switch (cmd) {
  case OSIOCGIFCONF32:      /* struct ifconf32 */ copyright 无奈人生
  case SIOCGIFCONF32:     /* struct ifconf32 */
  case SIOCGIFCONF64:     /* struct ifconf64 */
  case OSIOCGIFCONF64:      /* struct ifconf64 */
    error = ifioctl_ifconf(cmd, data);
    goto done;
  case SIOCIFGCLONERS32:      /* struct if_clonereq32 */
  case SIOCIFGCLONERS64:      /* struct if_clonereq64 */
    error = ifioctl_ifclone(cmd, data);
    goto done;
  case SIOCGIFAGENTDATA32:    /* struct netagent_req32 */
  case SIOCGIFAGENTDATA64:    /* struct netagent_req64 */
    error = netagent_ioctl(cmd, data);
    goto done;
查看ifioctl_ifclone方法,要使用if_clonereq结构作为if_clone_list的调用参数
本文来自无奈人生安全网

[1] [2] [3] [4] [5]  下一页

本文来自无奈人生安全网

。 (责任编辑:admin)
【声明】:无奈人生安全网(http://www.wnhack.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱472701013@qq.com,我们会在最短的时间内进行处理。