ssp leak

简介

这是一种利用canary检测来进行任意地址读的打法,因为只能泄漏内存,所以使用场景不多。

原理

当发生栈溢出时,如果有canary保护,则会产生报错:

*** stack smashing detected ***: ./ez_ssp\xe9\x99\x84\xe4\xbb\xb6/pwn terminated\n

在低版本中后面那一串很明显是运行的程序名,也就是__libc_argv[0],而这个的地址会在栈里面存在,所以只要溢出覆盖到这个地址就可以实现任意地址读。

来展开一个栈看看:

rsp 0x7fffffffe0f8 —▸ 0x400c55 ◂— mov edx, dword ptr [rbp - 0xa0]
01:0008│     0x7fffffffe100 ◂— 0x2f2f2f2f2f2f2f2f ('////////')
02:0010│     0x7fffffffe108 ◂— 0x1
03:0018│     0x7fffffffe110 ◂— 0x0
04:0020│     0x7fffffffe118 ◂— 0x300000000
05:0028│     0x7fffffffe120 ◂— 0x3e /* '>' */
06:0030│     0x7fffffffe128 ◂— 0x0
... ↓        2 skipped
09:0048│     0x7fffffffe140 ◂— 'flag{aaaaaaa}\n'
0a:0050│     0x7fffffffe148 ◂— 0xa7d61616161 /* 'aaaa}\n' */
0b:0058│     0x7fffffffe150 ◂— 0x0
... ↓        5 skipped
11:0088│     0x7fffffffe180 ◂— 0x1
12:0090│     0x7fffffffe188 —▸ 0x400d4d ◂— add rbx, 1
13:0098│     0x7fffffffe190 ◂— 0x0
14:00a0│     0x7fffffffe198 ◂— 0x0
15:00a8│     0x7fffffffe1a0 —▸ 0x400d00 ◂— push r15
16:00b0│     0x7fffffffe1a8 —▸ 0x400940 ◂— xor ebp, ebp
17:00b8│     0x7fffffffe1b0 —▸ 0x7fffffffe2a0 ◂— 0x1
18:00c0│     0x7fffffffe1b8 ◂— 0x470b970accd0dd00
19:00c8│ rbp 0x7fffffffe1c0 —▸ 0x400d00 ◂— push r15
1a:00d0│     0x7fffffffe1c8 —▸ 0x7ffff7820830 (__libc_start_main+240) ◂— mov edi, eax
1b:00d8│     0x7fffffffe1d0 ◂— 0x0
1c:00e0│     0x7fffffffe1d8 —▸ 0x7fffffffe2a8 —▸ 0x7fffffffe55d ◂— 0x692f73726573552f ('/Users/i')
1d:00e8│     0x7fffffffe1e0 ◂— 0x1f7e25ca0
1e:00f0│     0x7fffffffe1e8 —▸ 0x400afa ◂— push rbp

可以看到在24行,里面的字符就有我的目录了,所以很明显这个路径指针存储的位置在0x7fffffffe2a8,其值为指针0x7fffffffe55d。所以如果能在出发栈溢出后覆盖这个指针,就可以实现任意地址读。

需求

一般而言,这种题目需要有fork函数来保证栈溢出在子线程中,这样才能保证程序在发现栈溢出后主线程不会崩溃。

在遇见的题目中,一般flag会藏在栈上,而即使未开地址随机化,栈的地址也是随机的,那应该怎么办呢?

这时候需要知道一个参数environ,该参数在libc中,用于指示当前栈的位置,所以如果能泄漏libc的地址,就可以泄漏栈地址。libc的地址可以通过got表泄漏,而栈地址则可以通过environ泄漏,再次选择即为读取栈上内容。