type
status
date
slug
summary
tags
category
icon
password
这个暑期在把ctfwiki上的house of的手法学的差不多了之后总感觉题目刷的不足,于是便决定在去网上寻找一些题来练练手,如何这些题的题解就写在这篇文章里。用于记录做过的题目的过程和思路。
 

XCTF 1000levevls

在攻防世界里直接搜1000levevls就可以找到
 
这是一道栈的题目,不是之前一直学的堆题,不过这个题目还是比较有趣的,正好用来复习一下栈的内容。
 
简单分析一下这个程序的基本逻辑
notion image
main函数,其实也是一个选项函数,(在栈题中有选项还是比较少遇到的)
当然这道题的重点也就是这上面的两个函数一个hint函数go函数
再来看看这两个函数有没有特别的地方。
notion image
这里hint函数是吧system函数的地址放置在程序栈中,这里可以记录下这个点。后期来看是否可以用到这个地方
notion image
这是go函数,这里我们可以注意到如果v2<=0的话,v5是不会被初始化的,而之前的hint函数中出现了system函数的地址,在结合上面的来看,是可以知道system函数的地址被放到了rbp-110h的地方,而go函数中v5变量的位置正好就是rbp-110h这里就出现了一个可利用的地方,如果我们进入go函数之后第一次输入小于等于0,system的地址就被保留在了栈上,而之后程序还会让我们进行一次输入,并把rbp-110h位置处的值变为加上新的输入的值,这个新的输入是没有限制的,意味着我们可以把system的地址改为libc中的任何地址。
 
再看go函数中的sub_E43函数,
notion image
很明显本题的漏洞点就是在这里一个明显的栈溢出,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有三个,地址如下
 
整个过程简单来说就是
  1. 先执行hint函数,使栈中有system函数地址
 
  1. 在进入go函数之后,第一次我们先输入0,第二次输入(one_gadget -system的偏移)
这个one_gadget 的值直接从链接库中拿
 
  1. 这个偏移是大于100的,所以我们要玩99次游戏,在第100次时通过栈溢出修改返回地址获得shell
exp
这道题重点在于vsyscall这个的使用。然后就是对程序的读取过程要清楚。
 

攻防世界 babyheap

还是在攻防世界上的题目,直接去上面那个链接里找就行。
这个题的话,是一个比较基础的堆题,整体的流程相对来说是比较不复杂的,不过其中还是有个比较重要的一个地方,这个算是这道题相对来说困难的一个步骤,这个步骤本身不难,但是由于之前不了解过这个地方,故导致这道题卡卡了一点时间。现在就来分析一下这道题目。
 
这道题的整体逻辑比较简单,4个选项函数,
1为创建chunk,2为free堆块,3为将chunk中的数据打印出来,4不用管。
重点在创建chunk函数这里,这里最多能同时存在7个chunk,同时chunk中的内容由创建时就行其中输入。通过这个函数
notion image
很明显这里有个null off by one漏洞,而整个程序我们能利用的也就这一个漏洞。
那么第一步仍然是想办法泄露一些关键地址。
  1. 创建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中
notion image
 
然后再相程序中申请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给覆盖了
 
notion image
执行后的结果如上,很明显在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
notion image
notion image
这个函数要注意以下几点:
  • 输入索引的范围是0~0x10,也就是说最多可以存储17个chunk
  • malloc的范围为0~0x1000
  • 输出了每个chunk地址的低3个字节
  • 0x6020e0存储chunk的指针,但是存储的是输入的idx和分配到的chunk_ptr的或值
  • 外部输入的idx和实际存放在0x6020e0数组的索引并不一致!!
 
del_note
notion image
这里有两点要注意:
  • 外部输入的idx并不是会对应去删除0x6020e0[idx]处的chunk,而是遍历0x6020e0处的数组,对每一个地址ptr & 0xf取出索引,再和外部输入的idx比较,如果一样,就去删除这个地方的chunk
  • 找到索引后,取出的要删除的chunk的地址是通过ptr & 0xfffffffffffffff0计算得到的
 
edit_note
notion image
这里寻找索引和取出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_ptr0x70 & 0xf0 = 0x70。那么如果调用del_noteedit_note,实际上处理的地址不是0x60,而是为0x70
  • 也就是说,如果首先创建0x10idxchunk,调用edit_note的时候,要输入的索引实际不能是0x10,而是0,并且编辑的地址会往高地址移动0x10个字节。这可以修改下一个chunkpre_sizesize域大小
 
大致思路:
申请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
notion image
这里有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了一段可读可写可执行的区域,还告诉了我们地址
notion image
 
可以直接读取在后面的时候使用
notion image
还可以知道heap的地址,
这个程序的漏洞就是一个off by null
notion image
漏洞点有三个,分别是:
  • 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
 
 
IO_File基础知识周报8.7
Loading...