ROP Emporium 是一个ROP攻击入门教学网站,提供了一系列的挑战任务,这些挑战对逆向工程或debug的要求不高,因此对初学者十分友好,适合初学者了解ROP攻击。网站中共有8个挑战任务,每个挑战都引入了一个新概念/知识点,其复杂性和难度逐渐增加,循序渐进,而且每个挑战都有32/64位两个版本的程序,适合初学者了解二者之间的差异。
本文提供ROP Emporium所有挑战的说明文件、二进制文件、在线动态靶机环境以及一些相关解题线索。读者可前往动态靶场完成任务后,再返回本文查看相应题目的知识点、解题思路以及解题脚本。
附件下载:rop_emporium_all_challenges.zip
动态靶场:CTFq动态靶机练习平台
靶机环境:Ubuntu 18.04
读者可前往动态靶场完成任务后,再返回本文查看相应题目的知识点、解题思路以及解题脚本。
ret2win means ‘return here to win’ and it’s recommended you start with this challenge.
ret2win就是利用漏洞直接将程序控制流劫持到程序预置的后门函数win
win函数通常是可以打印flag,或者getshell
ret2win
的地址建议完成任务后再查看题解
movaps xmmword ptr [rsp + 0x40], xmm0
出现这个问题的原因是执行某些libc
函数(如printf
, system
)时,栈指针rsp
没有对齐0x10
字节,解决方法如下:
ret2win
时尽量避开栈操作指令:
.text:0000000000400811 ret2win proc near
.text:0000000000400811 ; __unwind {
.text:0000000000400811 push rbp
.text:0000000000400812 mov rbp, rsp
.text:0000000000400815 mov edi, offset aThankYouHereSY ; "Thank you! Here's your flag:"
.text:000000000040081A mov eax, 0
.text:000000000040081F call _printf
.text:0000000000400824 mov edi, offset command ; "/bin/cat flag.txt"
.text:0000000000400829 call _system
.text:000000000040082E nop
.text:000000000040082F pop rbp
.text:0000000000400830 retn
.text:0000000000400830 ; } // starts at 400811
.text:0000000000400830 ret2win endp
0x400811
处的push rbp
将使得rsp=rsp-0x8
,导致rsp
不能对齐0x10
稍作调整,跳过栈操作,改用
0x400815
即可
使用gadget (ret) 调整栈指针
Combine elements from the ret2win challenge that have been split apart to beat this challenge. Learn how to use another tool whilst crafting a short ROP chain.
与ret2win
相比,这题将后门执行的命令由cat flag
改为了/bin/ls
,因此不能直接获取flag,但程序中给出了/bin/cat flag.txt
这个字符串,因此可以通过构造简单的ROP链获取flag
将
/bin/cat flag.txt
作为参数传给system
函数
call system
的地址/bin/cat flag.txt
的地址建议完成任务后再查看题解
Chain calls to multiple imported methods with specific arguments and see how the differences between 64 & 32 bit calling conventions affect your ROP chain.
这题提供了一个自定义的动态链接库,其中包含callme_one
,callme_two
和callme_three
,读者需要通过ROP配置好相应的参数,并依次调用这三个函数来解密并输出flag。
callme
函数的功能callme
函数在PLT表中的地址建议完成任务后再查看题解
Find and manipulate gadgets to construct an arbitrary write primitive and use it to learn where and how to get your data into process memory.
在这道题中,通过gadget可以实现内存任意写,目标是将字符串/bin/sh
或$0
写入内存。
bss
段建议完成任务后再查看题解
$0
的含义是第0个参数,即argv[0]
,默认情况下程序是通过/bin/sh
启动的,因此argv[0]
就是/bin/sh
,在本题32位程序的题解中,使用$0
代替/bin/sh
作为system
执行的命令。
Learn to deal with badchars, characters that will not make it into process memory intact or cause other issues such as premature chain termination.
这道题与③write4相比,新增了对badchar的检测,并且将用于任意写的gadget换成了具有异或(写内存)功能的gadget,因此目的是练习通过异或对badchar进行绕过。
badchar为 b i c / f n s,所以其实也可以通过
$0
绕过
bss
段建议完成任务后再查看题解
懒得写异或的exp了,大概就那个意思…
Sort the useful gadgets from the fluff to construct another write primitive in this challenge. You’ll have to get creative though, the gadgets aren’t straight forward.
这题还是和③write4类似,与之相比,去掉了可以直接写内存的gadget,换成了一些更为复杂的gadget,因此需要利用多个gadget组合、配合实现写内存的功能。
bss
段建议完成任务后再查看题解,出于练习的目的,建议使用预期解再看一遍
32位程序的解法是预期解,64位的我偷了个懒:-p
Stack space is at a premium in this challenge and you’ll have to pivot the stack onto a second ROP chain elsewhere in memory to ensure your success.
这题给了一个自定义的动态库,要求调用其中的ret2win
函数,此外栈溢出的可利用空间不足,需要利用题目中给出的gadget进行栈迁移(交换rax和rsp)
建议完成任务后再查看题解,出于练习的目的,建议使用预期解再看一遍
Learn a ROP technique that lets you populate useful 64 bit calling convention registers like rdi, rsi and rdx even in an environment where gadgets are sparse.
__libc_csu_init
中的gadget__libc_csu_init
中的gadget给rdx赋值fini_array
中的函数,保持rdx不变建议完成任务后再查看题解,出于练习的目的,建议使用预期解再看一遍