type
status
date
slug
summary
tags
category
icon
password
这个暑期在把ctfwiki上的house of的手法学的差不多了之后总感觉题目刷的不足,于是便决定在去网上寻找一些题来练练手,如何这些题的题解就写在这篇文章里。用于记录做过的题目的过程和思路。
XCTF 1000levevls
在攻防世界里直接搜1000levevls就可以找到
这是一道栈的题目,不是之前一直学的堆题,不过这个题目还是比较有趣的,正好用来复习一下栈的内容。
简单分析一下这个程序的基本逻辑
main函数,其实也是一个选项函数,(在栈题中有选项还是比较少遇到的)
当然这道题的重点也就是这上面的两个函数一个
hint函数
和go函数
再来看看这两个函数有没有特别的地方。
这里hint函数是吧system函数的地址放置在程序栈中,这里可以记录下这个点。后期来看是否可以用到这个地方
这是go函数,这里我们可以注意到如果v2<=0的话,v5是不会被初始化的,而之前的hint函数中出现了system函数的地址,在结合上面的来看,是可以知道system函数的地址被放到了rbp-110h的地方,而go函数中v5变量的位置正好就是rbp-110h这里就出现了一个可利用的地方,如果我们进入go函数之后第一次输入小于等于0,system的地址就被保留在了栈上,而之后程序还会让我们进行一次输入,并把rbp-110h位置处的值变为加上新的输入的值,这个新的输入是没有限制的,意味着我们可以把system的地址改为libc中的任何地址。
再看go函数中的sub_E43函数,
很明显本题的漏洞点就是在这里一个明显的栈溢出,buf的大小仅为0x8,却可以读入最多0x400的数据,这意味着我们可以改变函数的返回地址,但是开启了PIE保护,我们不知道很多地址,
到这里利用的思路基本就出来了,
在调用
hint()
函数时,将 system()
函数的地址保存在了栈上。而在调用 go()
函数时,system()
函数的地址仍然在栈中,因此可通过控制函数流程 main()->hint()->main()->go()->level()
,结合 vsyscall
中的 gadgets
,实现将控制流劫持到栈上保存的 system()
函数地址处。
这里由于程序开启了pie的保护,这里我们要用到用vsyscall和之前保留在栈上的system函数了vsyscall是什么?
vsyscall是第一种也是最古老的一种用于加快系统调用的机制,工作原理十分简单,许多硬件上的操作都会被包装成内核函数,然后提供一个接口,供用户层代码调用,这个接口就是我们常用的int 0x80和syscall+调用号。当通过这个接口来调用时,由于需要进入到内核去处理,因此为了保证数据的完整性,需要在进入内核之前把寄存器的状态保存好,然后进入到内核状态运行内核函数,当内核函数执行完的时候会将返回结果放到相应的寄存器和内存中,然后再对寄存器进行恢复,转换到用户层模式。这一过程需要消耗一定的性能,对于某些经常被调用的系统函数来说,肯定会造成很大的内存浪费,因此,系统把几个常用的内核调用从内核中映射到用户层空间中,从而引入了vsyscall
值得注意的是,vsyscall的地址是固定不变的,即使开启了PIE也不会改变!
可以利用的vsyscall有三个,地址如下
整个过程简单来说就是
- 先执行hint函数,使栈中有system函数地址
- 在进入go函数之后,第一次我们先输入0,第二次输入(
one_gadget
-system的偏移)
这个
one_gadget
的值直接从链接库中拿- 这个偏移是大于100的,所以我们要玩99次游戏,在第100次时通过栈溢出修改返回地址获得shell
exp
这道题重点在于vsyscall这个的使用。然后就是对程序的读取过程要清楚。
攻防世界 babyheap
还是在攻防世界上的题目,直接去上面那个链接里找就行。
这个题的话,是一个比较基础的堆题,整体的流程相对来说是比较不复杂的,不过其中还是有个比较重要的一个地方,这个算是这道题相对来说困难的一个步骤,这个步骤本身不难,但是由于之前不了解过这个地方,故导致这道题卡卡了一点时间。现在就来分析一下这道题目。
这道题的整体逻辑比较简单,4个选项函数,
1为创建chunk,2为free堆块,3为将chunk中的数据打印出来,4不用管。
重点在创建chunk函数这里,这里最多能同时存在7个chunk,同时chunk中的内容由创建时就行其中输入。通过这个函数
很明显这里有个null off by one漏洞,而整个程序我们能利用的也就这一个漏洞。
那么第一步仍然是想办法泄露一些关键地址。
- 创建5个chunk,第5个chunk的最后两个字长要修改为0x100,0x11
其中,chunk0、chunk1、chunk4大小在unsorted bin范围,当他们free后就进入了unsorted bin,并且相邻的chunk会进行unlink合并。既然能够溢出一字节0数据,那么假如我们从chunk3中溢出,覆盖chunk4的size域的低一字节为0,那么就标志了chunk4的前一chunk处于空闲状态,那么,当我们free chunk4的时候,chunk4就会与它的前一chunk合并
假如,我们在chunk3中,把chunk4的prev_size设置为(0x110+0x110+0x70+0x70 = 0x300),然后覆盖chunk4的size低1字节为0,那么,当我们delete(4)时,就会合并chunk4和chunk4-0x300处的chunk,也就是会合并chunk0、chunk1、chunk2、chunk3、chunk4
由于没有编辑功能,我们只能delete(3)后再重新create分配到那个位置,同时构造payload溢出到chunk4
由于合并时,unlink会报错,因此,我们在事先,应该delete(0),让chunk0加入unsorted bin中。delete(2)用于将chunk2放入fastbin,供后续fastbin attack使用,注意,必需在合并chunk0、chunk1、chunk2、chunk3、chunk4之前让chunk2加入fastbin,合并后再delete(2)不能让chunk2加入到fastbin中
然后再相程序中申请0x100的chunk,会把chunk0申请,main_area+88指针放到了chunk1的fd和bk处
之后就可以通过show函数将chunk中的内容打印出来,从而得到libc的基地址
接下来是想办法把gadget的地址写入到malloc_hook里,这样当程序再次malloc时,便会触发one_gadget,从而get shell
fastbin只检查chunk的size域是否符合要求,因此,我们的chunk2的size要与它大小相当,这样,我们想办法把chunk2的fd指向malloc_hook-0x23这个假chunk处,这样,chunk2和malloc_hook-0x23处构成了单向链表,当我们第二次申请0x68大小的堆时,就会申请到malloc_hook-0x23处
这里就是用到一个堆重叠
这里我们创建一个0x118的chunk,由于之前已经add_1(0x100)过一次,那么这次chunk分配的范围就是chunk0 + 0x110 ~ chunk0+0x110+0x128也就是chunk1~chunk2+0x18处,正好可以把chunk2的fd给覆盖了
执行后的结果如上,很明显在fastbins中0x7955369c3aed(_IO_wide_data_0+301)这个地址出现了,那么我们接下来就只需要malloc chunk一直到这个位置,然后修改malloc_hook中的值为one_gadget就行
这里就出现了本题的难点所在,当我们安装刚刚我说的直接修改malloc_hook中的值为one_gadget后执行malloc函数,会发现程序无论执行那一个one_gadget都无法拿到shell,
关于这种情况解决办法如下
有些情况下one_gadget因为栈环境原因全部都不可用,这时可以通过realloc_hook来调整堆栈环境使one_gadget可用。realloc函数在函数起始会检查realloc_hook的值是否为0,不为0则跳转至realloc_hook指向地址。realloc_hook同malloc_hook相邻,故可通过fastbin attack一同修改两个值。
如何利用realloc_hook来调整栈环境呢?
观察realloc函数的流程push寄存器,最后全部pop出来跳转至realloc_hook的值。
因此可以将realloc_hook设置为选择好的one_gadget,将malloc_hook设置为realloc函数开头某一push寄存器处。push和pop的次数是一致的,若push次数减少则会压低堆栈,改变栈环境。这时one_gadget就会可以使用。具体要压低栈多少要根据环境决定,这里我们可以进行小于48字节内或72字节的堆栈调整。
经过测试,我们只需在realloc函数地址向下偏移2就可以使栈环境正常
于是,我们的代码应该为
exp
整体来说,这不是一个很难的题目,但这题还是困扰了我一段时间,这个问题还是出在,通过realloc_hook来调整堆栈环境使one_gadget可用,这种办法并不了解,这个点知道后,这题并不算一个难题。也算是新学到一个知识点了。
ciscn_2019_final_5
这是一个比较很经典的菜单题。一共三个选择,接下来依次来看
new_note
这个函数要注意以下几点:
- 输入索引的范围是0~0x10,也就是说最多可以存储17个
chunk
malloc
的范围为0~0x1000
- 输出了每个
chunk
地址的低3个字节
0x6020e0
存储chunk
的指针,但是存储的是输入的idx
和分配到的chunk_ptr
的或值
- 外部输入的
idx
和实际存放在0x6020e0
数组的索引并不一致!!
del_note
这里有两点要注意:
- 外部输入的
idx
并不是会对应去删除0x6020e0[idx]
处的chunk
,而是遍历0x6020e0
处的数组,对每一个地址ptr & 0xf
取出索引,再和外部输入的idx
比较,如果一样,就去删除这个地方的chunk
- 找到索引后,取出的要删除的
chunk
的地址是通过ptr & 0xfffffffffffffff0
计算得到的
edit_note
这里寻找索引和取出
chunk
的方式和del_note
是一样的。
简单讲一下这里的漏洞
漏洞点就在于很奇怪的计算索引和计算
chunk
地址的方式,分析这两种计算方式,可以发现:- 由于
chunk
的地址一定是页对齐的,所以分配堆的地址的最后一位肯定是0x?0。这个地址和[0, 0xf]
之间的索引取或值,对地址前面的值是不影响的,如0x20 | 0xf = 0x2f
。因此,这个时候使用ptr & 0xf
取索引没问题,使用ptr & 0xf0
取原来的chunk
指针,也没问题。
- 但是,如果给的索引是
0x10
,那么就有问题了。举例说明:假设分配到的chunk_ptr
地址的最后一位为0x60
,那么按照new_note
的存储方式,数组中最后存的地址为0x60 | 0x10 = 0x70
。要取出索引,得输入0x70 & 0xf = 0x0
,取出的chunk_ptr
为0x70 & 0xf0 = 0x70
。那么如果调用del_note
或edit_note
,实际上处理的地址不是0x60
,而是为0x70
。
- 也就是说,如果首先创建
0x10
为idx
的chunk
,调用edit_note
的时候,要输入的索引实际不能是0x10
,而是0
,并且编辑的地址会往高地址移动0x10
个字节。这可以修改下一个chunk
的pre_size
和size
域大小。
大致思路:
申请index为16的堆,布置好伪造的size,利用上面的特殊性质
可以释放到bin链中我们控制的size大小,造成堆块复用,我们改fd为heap_list的地址,add后,再add就申请到heap_list处了,我们劫持free_got
泄露libc,再劫持atoi为system,输入/bin/sh
exp
ciscn_2019_s_6
一个简单的uaf漏洞的题目,并且还是在libc2.27的版本下进行的比较简单。
可以直接doubel free
这里有uaf漏洞
1.保护全开,没有后门函数,就想到改malloc_hook或者free_hook为system函数或者onegadget,这道题的远程没有double free检查。
2.我们要先泄露libc基址,需要用unsorted_bin头为main_arena+88计算。
3.然后就有很多方法getshell了,如1所述。用double free来add堆块到free_hook,在free_hook内填入system地址,然后free的时候会调用system函数。
sctf_2019_easy_heap
在一开始给我们mmap了一段可读可写可执行的区域,还告诉了我们地址
可以直接读取在后面的时候使用
还可以知道heap的地址,
这个程序的漏洞就是一个off by null
漏洞点有三个,分别是:
Fill
函数中,调用的是read_off_by_null
,会溢出一个字节。注意到,题目使用的libc
版本为2.27
,因此,引入了tcache
机制。只有当chunk
的大小小于0x410
的时候,才会把空闲的chunk
放到tcache bin
里面去,否则会先放到unsorted bin
mmap
申请了一块具有读写可执行的内存,并打印出了这块内存的地址
- 在
Allocate
函数中申请chunk
的时候,会把bss
段的地址打印出来,等于泄露出程序的基地址
这个题的利用过程不算复杂,但是其中要用到shellcode,这个是第一次在堆题中使用这个,因为这道题中有mmap的地方,便可以吧shellcode写入其中,然后执行便可以。
1.利用漏洞off-by-null造成unlink合并,再申请回来,函数指针里就有两个指针指向同一块地方,我们delete两次就造成了doublefree
2.我们用doublefree往mmap的地方写shellcode
3.再用off-by-null造成unlink合并,再申请回来,使unsortbin里的malloc_hook+96的地址挂在已经free状态的fd里,我们改成malloc_hook,然后申请到malloc_hook里填入mmap的地址
4.最后add触发
exp
- 作者:wgiegie
- 链接:https://tangly1024.com/article/999554fd-7caa-4584-9cd6-e16d4c292da3
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。