0x00 分析

ROP技术:

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。通过上一篇文章栈溢出漏洞原理详解与利用,我们可以发现栈溢出的控制点是ret处,那么ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,从而控制程序的执行流程。

这题有SMC那味了

程序流动调看

0x01 解

进入main函数,发现只有一个vuln函数

跟进去可以发现调用了一个read函数

看不太懂,直接断在call read处后向下走

之后调用retn会发现执行到了这个地方

之后不停执行popretn,目的将栈中的数据放入寄存器中

之后到了这个函数

关于mmap可以看这个:linux库函数mmap()原理及用法详解

之后继续执行会到mprotect函数

关于mprotect可以看这个:Linux中mprotect()函数的用法

这两个连在一起用:通过mmap&mprotect来绕过nx

继续执行会回到一次read函数,之后到这里

关于memfrob

The memfrob() function obfuscates the first n bytes of the memory area s by exclusive-ORing each character with the number 42. The effect can be reversed by using memfrob() on the obfuscated

说白了就是对某个内存块的前几个字节进行解密,使用XOR的方法

因此这里需要找到被解密的内存块

这里直接到retn的位置,然后查看上一个指令就能找到被解密的内存块的位置了

当然也可以去解密部分查看被用作计算的寄存器

执行retn之后过几个ROP部分就会到被解密的这个内存块了

程序流动调可得,这里不多赘述,直接按照动调所得的程序流进行解题

首先获取长度

然后执行比较

1
2
debug003:0000000000500016                 xor     rdi, rax
debug003:0000000000500019 xor rdi, rsi
1
2
rax = 25649D88
rsi = 25649DA8

因此计算使用

1
2
print(0x25649DA8 ^ 0x25649D88)
# 32

flag长度为32

更改长度,重新运行

执行至这里,可以看到输入被拿去做运算了

输入为flag{xxxxxxxxxxxxxxxxxxxxxxxxxx},由此可见程序将输入以八个字符为一组进行分组,共四组

将字符丢入[rax+94h]然后将一串固定字符丢入[rax+74h]

固定字符提取出来是HDIN2024

之后调用上面的函数进行运算

函数执行流程大致是:

将分组后的字符与固定字符进行异或

之后加上固定字符

最后调用上面的比较函数

因此要解密需要将raxrsi中的值分别提取然后作异或,然后减去固定值。最后异或固定值

由于每次都会进行判断,因此取值一次就要解密一次,然后将正确值补全,才能获取下一次的密文和异或值

这里省略了,直接上个全解出来的版本

1
2
3
4
5
6
rax = [0x11DB2A3F,0x30836D0F,0xAD48145,0x1ECB02BB]
rsi = [0x9A7BA6984AB8636B,0x8F739F7345DC15CF,0x399F7938C150EA1A,0x7D454145674F5DD5]
for i in range(len(rsi)):
rsi[i] = ((rax[i] ^ rsi[i])- 0x343230324E494448)^0x343230324E494448
print(bytes.fromhex(hex(rsi[i])[2:]).decode()[::-1],end="")
# DASCTF{R0p_is_so_cr34y_1n_re!!!}