2020网鼎杯部分题目WriteUp。
orwpwn
程序分析
去掉了符号表,不过问题不大,唯一有点注意的是堆分配函数,最开始以为是malloc
,后面调试是才知道是 calloc
。
1 | __int64 __usercall Delete@<rax>(__int64 a1@<rbp>) |
漏洞点很明显,delete
函数没有将堆指针清空。但是这里只限制删除2次。Create
函数中,对创建堆块的大小没有限制,但只能申请19次。
利用分析
这题,我做的十分复杂。主要是受到Create
函数没有限制堆块大小,所以就一下想到 largebin attack
去了。所以后面就越做越复杂。不过还是先说说自己的方法,然后再说说BlueSheep
学长的方法。
- 修改
delete_num
由于delete_num
为2,只能删除2次。所以想到把这个值改大,并且程序没有开启PIE
。而且对申请堆块没有限制。所以就先用largebin attack
将delete_num
改为一个堆块地址。由于2.31
对 largebin attack
加了保护,导致我们只能修改 largebin->bk_nextsize
这一个地方,所以2.31下的 largebin attack
一次只能修改一个地址。这里就先修改 delete_num
。
- 修改 global_max_fast
现在,我们能够无限次delete
,也能够UAF
。但是这里是用 calloc
分配,所以 tcache
相关的攻击都无效了,需要考虑 fastbin attack
。
这里,我主要参考了这篇文章和BlueSheep学长的这篇文章。
大概思路就是先修改global_max_fast
,来使得我们能够将大块放到fastbin
中,然后劫持 main_arena
。
这里,我们执行第二次 largebin attack
来修改 global_max_fast
的值,将其改为 一个堆块值。
- 劫持 main_arena
这里劫持 main_arena
的方法,是我想重点介绍的。我们通过上一步将 global_max_fast
改为一个堆块值后,我们释放 8个 0x100
的堆块。
此时,会填满 tcache
,然后最后一个放入 fastbin
中。此时 main_arena
开头处 会被填入一个 1。我们通过错位便可构造一个 chunk_size
为 0x100
.
然后,我们使用 fastbin attack
,修改 fd
指向 fake_chunk
。最后分配出来,就可以劫持 main_arena
数组。
- 修改
free_hook
这里劫持了 main_arena
方法后,其实就有很多做法了。比较传统的是修改 top chunk
的地址为 free_hook
之上的地址。然后后续就可以不断分配 top chunk
分配到 free_hook
了。但是这里我不推荐,因为我们前面通过 largebin attack
留下了很多 largebin
。这里分配堆块时,会先去切割 largebin
,这里就会报错。
我采取另一种做法:我们先申请一个 0x70
的堆块,在chunk_size_list
中留下 0x70。然后将 main_arena
中 0x70 fastbin
链表指向 chunk_size_list
中0x70的位置。最后,我们申请 0x70
的堆块,就能够再次劫持 chunk_list
了。
getshell
劫持了chunk_list
,我们后面就可以直接修改 free_hook
为 magic_addr
。这个magic_addr
我在之前 2.31
攻击中讲过,这次不赘述。但是这里还是要说明一下我自己写得那个构造 magic_frame
的函数(因为,我自己都忘记这是啥了:)
1 | def magic_frame(rdx_rdi, secontext_addr, rdi, rsi, rdx, rsp, rip): |
这个函数,可以直接构造 对应2.31
magic_gadget所用的frame
数据。7个参数的说明分别如下:
1 | rdx_rdi: magic_frame地址 |
当然,这里我直接构造ORW的rop链,所以我exp中的rip
就是ret
地址。还有一个要点,我们自己后续构造的rsp
前面要加上p64(0)
,用来让rip
占位。
正常思路:
上面我的思路,我踩了很多坑才达到了劫持chunk_list
的目的。但是,如果我们一开始就想着如何劫持chunk_list
。那么我们可以使用 unlink attack
。
这里 BlueSheep
学长的思路就是通过构造 unlink attack
来劫持 chunk_list
,劫持完后的思路大同小异。exp
我就不放了。
EXP
1 | from pwn import * |
message_system
程序分析
程序有很多函数,但是只要头铁不断逆,就能分清楚整个逻辑和找到漏洞函数。毕竟外表上也是一个常见的菜单题。
1 | unsigned __int64 __fastcall insert(__int64 *list_begin, __int64 list_ptr, __int64 addr) |
漏洞点在 Create
函数,创建新堆块后将堆块插入 chunk_list
时。如果原来的chunk_list
满了,就会拓展chunk_list
,其逻辑如上所示。拓展chunk_list
,他会把原来的chunk_list
的值填入新的chunk_list
,然后删掉原来的堆块和原来的chunk_list
。这里就造成了 UAF
,原来的堆块被释放了,可是新的 chunk_list
中仍然有他们的地址。
利用分析
这道题的难点,还在于 每次创建一个堆块写入数据后,程序都会对我们写入的数据和一个随机数数组异或加密。而这个随机数数组是通过如下逻辑生成:
1 | unsigned __int64 init_0() |
通过时间种子mod 8后,通过rand
生成的伪随机数。那么这里我们就可以将这个随机数数组爆破出来。毕竟只有8种情况。
然后我们再写个解密函数,将我们每次输入的数据使用解密函数解密后再输入。这样后面加密后,就变成了正常的数据。
Tips:这里由于加密函数的逻辑不是太难,所以可以直接逆。如果太难了,xmzyshpnc
告诉我了一种取巧的方法,以后如果太难了可以使用。
EXP
1 | from pwn import * |
heatmap
程序分析
程序有一个system
函数:
1 | sprintf(&s, "%s.png", templatea); |
执行system
的参数 command
是受到 aGnuplotEsetTer
影响。而这个数组在data段中紧邻于 point_list
之下。
1 | __int64 __fastcall get_list(double chunk1, double chunk2) |
而 ponit_list
数组有一个 自加功能。这里就存在一个数组越界。
利用分析
数组越界了,且可以自加。那么我们就将 aGnuplotEsetTer
的前几位字符串改为 /bin/sh\x00
,即可完成 getshell
。
EXP
1 | from pwn import * |
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2020/12/02/2020网鼎杯/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!