JOP 利用思想和 JOP 链构造分析全过程,以一个题目为例
selph 二进制安全 169浏览 · 2025-02-28 02:24

前言

题目来自 Hack the Box 困难难度的 pwn 题目:no return,是栈溢出题目,但是真的没有可用的ret指令,gadget反而以jmp结尾居多,很容易想到曾经见过的一个名词:JOP

本文以该题目为例,分享JOP的思想和构造JOP链的过程,本题构造方法不唯一,以其中一种方式为例

参考资料[4]为非预期解,通过构造SROP配合jmp完成RCE,并非本文主题,有兴趣可以去看看

题目情况

A hop, skip, and a jump to the flag.

逆向分析

start:

给了个地址泄露,位于rsp的值

还有一些其他的代码:

利用分析

利用思路分析

程序情况分析:

程序是栈溢出,但是没有ret退出,而是jmp的方式跳转到栈上的返回地址

给了栈地址,意味着缓冲区地址可知

没有PIE,意味着可以任意跳转到其他的片段,可以通过控制寄存器的值来控制跳转

那么思路就是,想办法凑出参数执行execve的syscall

目标就是:

rax = 0x3b

rdi = 指向/bin/sh的指针

rsi = 0

rdx = 0

最后调用syscall

而程序中提供的gadget,全都是以jmp结尾的跳转而非ret,这里是JOP的技法!

在各种资料里介绍ROP的时候,都会顺带一提,还有JOP,通过jmp指令完成跳转

ROP 和 JOP

image.png


ROPJOP二者结构如上图(参考资料[3])

ROP依赖返回指令(ret) 链式调用gadget。攻击者通过覆盖栈上的返回地址,使程序依次执行多个以ret结尾的代码片段。通过栈溢出控制连续的返回地址,形成“链式”执行流程。例如,通过pop指令设置寄存器参数,再调用系统函数,构造的栈本身就是个夹杂数据的跳转表

JOP利用间接跳转指令(jmp) 链接gadget。攻击者篡改寄存器或内存中的跳转目标地址,构造非连续的代码执行链。依赖间接跳转指令的灵活性,可能通过“调度器gadget”动态选择后续执行的代码片段,攻击链更复杂且非线性。

构造JOP链路需要找到一组分发器-分发表组合,通过分发器选择分发表,跳转到指定的以jmp返回的gadget中,然后jmp回分发器,再次进入分发表跳转到下一个gadget。

找到 JOP 分发器 gadget

一个比较理想的情况是,有一组寄存器在后续gadget中不会被影响,分发器选择分发表中的地址时,通过同一组gadget进行

程序中给出的gadget大多以jmp [rcx]jmp [rdx]结尾,这两个寄存器的变化应该会很频繁,不适合用作分发器gadget

排除掉这俩以后,还剩如下gadget:

只剩下2个,初始状态,rbp,rbx,rdx的值都是可控的,因为rdx的值会经常变化,所以只剩下1个满足要求的gadget

rbp-0x39指向内存中可控区域(分发表),让rbx=8,即可每次跳转到该gadget的时候,执行分发表中的下一个gadget

构造 JOP 链 - 初始形态

此时 payload 的初始形态已经构成:

可控区域总共0xc0大小,其中0xb0位置是栈溢出控制的返回地址所在,设置为初始化寄存器的跳转gadget那里:

其中0xb8是新的rsp,然后依次设置了其他寄存器,通过jmp qword ptr [rdi+1]跳转到分发器,分发器跳转到分发表(0x80处开始)

构造 JOP 链 - 分析寄存器赋值

目标要处理的寄存器是:

rax = 0x3b

rdi = 指向/bin/sh的指针

rsi = 0

rdx = 0

可控rdi的gadget只有一个:通过rcx来赋值,然后跳转到rdx,需要rcx和rdx可控,rcx执行字符串地址,rdx指向分发器

rcx的值需要是指针,这里还有一个gadget能完成:

可控rax的gadget也只有一个:通过rdx赋值,跳转到rcx,需要rdx和rcx可控,rdx=0x3b,rcx指向分发器(因为初始rax=0,意味着后续在执行到这之前,rax始终是0,所以同时这里还能满足最后要rdx=0的条件

对于rsi,可以在初始化阶段完成赋值,且后续可能不会用到

对于rdx,除了rax那个之外,也只有一个gadget,通过栈将值pop给rdx,然后跳转到rcx

构造 JOP 链

rdx和rcx都是初始可控的,可以手动指向分发器

根据刚刚找出的gadget分析,最难搞的是让rcx指向字符串地址,但是也有gadget可以完成赋值,所以先凑rax还是rdi都行,这里以先rdi为例进行构造

所以第一步,先完成rdi指向字符串的目标

这里设置rcx指向字符串地址,rdx指向分发表,完成第一次跳转:

结束之后,rcx是不可用的数据,准备进入下一次跳转

第二步,设置rdx,这里的gadget可以修复变得不可用的rcx的值:

通过栈提供rdx需要的值(0x3b),后续赋值rax使用

此时的payload:

此时rsi=0,rdx=0x3b,rdi=sh字符串地址,rax=0

去完成rax的设置:

最后rcx依然指向分发器:

此时参数已经凑齐了,下一次跳转依然从分发表中取值,只需要指向syscall的地址即可

完整exp

总结

JOP链构造的思路:

1找分发器和分发表

2找到可能要用的gadget(例如我需要对某个寄存器赋值,找到相关gadget)

3分析可能要用到的gadget,在分发器结构不变的情况下,串联gadget

参考资料

[1] Hack-The-Box-pwn-challenge[no-return] | 0xfd's blog

[2] Will's Root: Jump Oriented Programming and Call Oriented Programming (JOP and PCOP)

[3] asiaccs11.pdf

[4] No Return | 7Rocky

0 条评论
某人
表情
可输入 255