

很明显的栈溢出漏洞,但是可操作空间太小,只能覆盖到ret,所以要用栈迁移

有system函数,但是需要自己传参,可以把/bin/sh字符段放在栈上
有两次输入,第一次输入可以用来泄露ebp的地址
第二次要根据泄露地址去找加上偏移得到s的头部,在那里构造我们的后门函数,之后通过ret返回s头部执行这个后门函数。
先查看ebp距离s的实际距离(gdb动态调试查看)

相距0xffffcf18 - 0xffffcee0 = 0x38
不能直接通过下面这种方式查看,因为现在的操作系统默认开启了ASLR,所以栈上的地址一直会变化

接着构造我们的后门函数

且因为跳转到s后还要执行,所以程序不能崩溃 ,如果s可以直接跳转到后门函数就可以直接把地址写在ret上
1 | payload=b'aaaa'+p32(sys)+p32(main)+p32(s+16)+b"/bin/sh" |
payload.ljust(0x28, b'\x00') 中必须使用 b'\x00' 因为printf会在接下来的数据中找符合%s的数据,如果没有”/x00”,他可能去读取继续往后读到栈上的数据,%s时字符串,printf读到不可打印的字符会引发错误
leave ret解释
leave
mov esp, ebp:把esp移动到ebp-0x38的位置(ebp-0x38)其实就是aaaa的位置
pop ebp:把aaaa这一块pop出去了
ret
pop eip:,把栈顶的system函数pop出去作为返回地址
脚本
1 | from pwn import * |
收获
栈空间识别

1 | 偏移量 │ 寄存器/标识符 | 地址 | 内容 <-- 解释 |
0xffffced0: ESP 寄存器当前指向的内存地址。
—▸ 0x80486ca: ESP 指向的内存地址里存储的值,它又是一个地址,指向了 0x80486ca。
◂— dec eax /\* 'Hello, %s\n' \*/: 对 0x80486ca 这个地址的反汇编/注释,这里显示它是一条指令的起始,并且其附近有字符串 “Hello, %s\n”。
要找到存放内容是111111的位置,就是11111直接指向的位置0xffffcee0
esp与s的距离
一直疑惑不是相距0x28吗,为什么要看这个0x38,看了很多大佬的wp也没有说的,查了才知道还要考虑堆栈平衡:joy:
🧠 关键点在于:
1 | char s[0x28]; |
这个数组虽然是 0x28 字节,但 它不是从 ebp - 0x28 到 ebp,而是从 ebp - 0x2c 到 ebp - 4。
也就是说,编译器会:
- 为变量
s[0x28]分配 0x28 字节; - 还会额外分配 对齐空间(padding),使整个栈帧是对齐的;
- 所以,真正变量起始地址是
ebp - 0x2c,不是ebp - 0x28。
👉 在 32 位程序中,变量 char s[0x28] 通常从 ebp - 0x2c 开始,而不是 ebp - 0x28。
- 在 32 位程序里,
esp在建立栈帧时,常常是 为了给局部变量和返回地址对齐到 16 字节,因此会多分配几个字节; - 所以
sub esp, 0x2c是非常常见的; - 即使变量只有 0x28 字节,编译器会额外多给 4 字节 padding,形成 0x2c。

在这里也可以看到它开辟了不止0x28个空间,所以才要gdb调试看它实际的
1 | 高地址 (栈底,向高地址增长) |