type
status
date
slug
summary
tags
category
icon
password
上个周末打了一下这个字节跳动的比赛,质量和难度都挺高的。pwn的方向有两道题,一道内核的不会,还有一道堆的,重点在堆的这道题,不算太难,不过还是暴露出一些问题,重点讲一下这这道题。
 
这道题是直到比赛结束后看了W&M Team的的wp才明白的,他们的有关于这道题的解法,是我看过的wp中最好理解的,感谢大佬的分享,这里放一下原链接
 
在讲这道题之前先说一下有关这道题用到的知识点。、
  1. house of orange
  1. tcache_entry 和 tcache_perthread_struct
  1. House Of Force
 
在这之前还有讲个东西,如果在题目的附件中附带的libc文件比较多,那可以直接连上那个libc文件,不用在去连自己的libc库,这样可以保证运行文件的libc库与远程一样。
比如像这道题的附件
notion image
除了正常的这个pwn的题目,还有宁外两个文件,像这种情况,虽然可以连自己的libc库,但有的时候可能我们自己的库与远程的库在小的版本上有区别,因此这里我们可以直接连这两个,用以保证库的一样。
使用的命令如下
 
notion image

house of orange

 
这个手法之前有写到过,可以看看这里
 
这里具体讲一下在做题时出现的问题。
这个手法在之前说只能在libc2.27以下使用,这个是不太对的,至少在这道题的版本中,这个手法是适用的。虽然我在做的时候想到了这个但是当时由于对这个手法不太熟练,导致有个地方的输入不对,最后没有做出来。
这个手法的目的在于没有free函数时也能泄露libc的地址,
但是出问题的点在与,修改top chunk的这个。这里我们知道我们修改的top chunk的大小要与其起始地址相加能对齐内存页,既相加之后最后的3位结果为000.但是当时做的时候就忘记之一点,导致我直接使用的是一起用的大小,并没有更具本题来满足要求,这是一个很大的问题,说明在学这个手法时,掌握的就不是很彻底,这个在以后的学习中要注意。
notion image
 
像此时,我们top chunk的起始地址就是0x5ec7269f9360,我们要保证修改的libc地址对齐内存页,就要让top chunk的大小与这个地址相加后的最后3位为000.
notion image
这样,我们就可以修改top chunk的值为0xca1(最后的1,表示前一个不是free chunk)
notion image
修改的像这样就行。
然后就是这类手法的基本操作,先申请一个大于这个top chunk的chunk,使程序产生一个unsortedbin(这里申请的为0x1000)
notion image
然后在申请一个比较小的,这样unsortedbin的fd和bk的值就会被放入其中,同时还可以知道堆的地址(这里申请0x100的)
notion image
只要将这个chunk的内容打印出来就可以知道libc的地址和堆的地址。
 
这个手法到这就基本结束了,这里在讲个东西,之前一直不知道直到做这道题的时候才明白的有个东西。这里到最后的时候我们可以看见用heap 是找不到top chunk的存在了,但是在程序中其实是还在的,只是在太后面所以不好被看见,但是依然存在。
 
如何找到top chunk的位置,这里我们也有办法,现在我们有一个大小为0xb70的unsortedbin,只要我们申请的chunk大小小于这个,那程序都会直接从这里面切割一个给我们使用,因此这里我们之间把这个chunk全部申请掉。直接申请一个大小为0xb60的chunk,结果如下
notion image
此时我们可以看见我们并没有free chunk给我使用,但同时我们还是看不到top chunk的存在,那此时我们如果再向程序申请一个chunk,那程序该从哪给我们,这个我们就在申请一个0x100的chunk看看,
notion image
和之前没变化,那程序是没有给我们分配吗?再来看那一个地方,用来放我们申请了的chunk的地址的地址
notion image
之前我们申请了4个chunk,加上刚刚的这个0x100,刚好5个,说明程序还是给我们了,但我们仔细看会发现有个问题,新的这个的地址为0x00005ec726a1b020 ,而之前的为0x00005ec7269f9480 中间差距离的好像有点大,我们在来看看这个新的chunk
notion image
这里我们可以看见在这个chunk的下一位就是个0x20ee1如果你在程序申请相相同大小的chunk你会发现,这个chunk的位置就在这个0x20ee1的这里,其实这里就说明,这个0x20ee1的位置就是当前程序的top chunk,不过由于之前我们使用的手法,从而到导致这个top chunk与我们使用heap看的chunk的距离很远,从而看不见,但程序中依然存在,并从这里分配使用。
 
通过上面的讲述,我们可以知道在使用完这个house of orange之后如何找到程序中的top chunk的位置。这个在后面要用到这个地方。
 

tcache_entry 和 tcache_perthread_struct

这个算是在libc2.27引入tacche后存在的两个东西,这两个都是结构体,分别用于掌管tcache的不同大小的个数和起始地址。
 
我们知道在tcachebin中,其对free chunk的存放规矩为0x20~0x410的大小的chunk,然后每个大小的chunk的最多能放7个,多的就要放在其他的bin中,同时在tcachebin中有一个地方专门用来存放tcachebin各个大小的chunk的第一个chunk的头地址。
这样我就知道程序中要存放的有两个数据,一个是不同大小的free chunk在tcachebin中的个数,用于判断是否达到7个;宁一个是不同大小的tcachebin的chunk的第一个chunk的数据段的地址。
而我们上文中提到的的tcache_entry 和 tcache_perthread_struct 这两个结构体就是用来分别存放chunk的个数和头地址的。tcache_entry 用来记录相同大小的chunk的个数;tcache_perthread_struct 用来记录相同大小的chunk连成的结构体中的第一个chunk的大小。
 
这两个不同的结构体的在程序中是有专门的位置的,这个位置就是在使用malloc函数申请chunk后,我们申请的chunk的上面一个大小为0x250的chunk这里面。
notion image
这里我们只向程序申请了一个大小为0x110的chunk,但是我们可以看见,在这个0x110的上面,程序自行申请了一个大小为0x250的chunk,这个chunk就是用来放置上文中提到的两个结构的位置。
在没有tcachebin的时候,这个chunk中没有内容,当出现tcachebin后再这个chunk就会存放我们刚刚说的两个数据。
 
这个chunk有0x250的大小,除了最开始的0x10是控制字段,剩下的0x240前0x40是tcache_entry 的位置,后面的0x200的位置就是对应的tcache_perthread_struct 的位置。
notion image
 
然后是有关于这两结构体的存放数据的方式,
tcache_entry 中有0x40的位置,我们知道tcachebin的范围是0x20~0410,这里面用一共有64种大小不同的chunk(chunk的大小最后一位一定是0)。于是在这个0x40的空间中,刚好满足的是每两个16位数字的地址表示对应的大小的chunk的个数。
比如在0x40的开头的前两个位置的数字,就表示在tcachebin中,大小为0x20的chunk的个数有多少个,而最后的两个就表示大小为0x410的chunk的大小为多少。
notion image
像这样,就表示在tcachebin中,大小0x20的chunk有3个,0x30的有5个,0x40的有6个,0x50的有8个,而其他的就没有。
notion image
 
与上面这个个数形成呼应的就是剩下的这0x200的tcache_perthread_struct 的位置,我们说过在这个结构体用来存的是tcachebin中相同大小的chunk形成的chunk链的头地址,而在tcachebin中有64种不同的chunk的大小,每中不同大小的chunk的头地址占一个字段,一共64个字段,刚好就是0x200的大小。
所以这里0x200的位置存放的就是各个大小的chunk的头地址,
notion image
如上,因此如果我们想要修改tcachebin中的某个大小的chunk的个数和头地址,可以在程序的堆地址的前0x250的地址里修改,其中前0x40存放的是对应顺序大小的chunk的个数,后面的0x200,存放的是对应大小顺序的chunk的头地址。
 
像上面那张,我们把0x20到0x50的tcachechunk的头地址都修改为0x7dca0c9ebc30,那我们如果此时向程序申请0x20到0x50的大小的chunk,那程序就会直接从0x7dca0c9ebc30这个地址分配给我们使用(注意这个是指向的数据段,因此申请的chunk也是会直接吧这里当数据段的起始地址开始输入数据。)
这里我们可以把某个要修改的地址输入进去,然后把那个地址当成chunk申请出来,供我们使用,从而达到我们修改么个地址的目的。
 

House Of Force

这道题用到的这个手法,不算复杂,只是简单的用一下这个手法。去修改top chunk的位置,使程序分配一个正常情况下不会给我们的地址。、
关于这个手法的具体可以看看我之前写的这里。
 
在这里这道题用到的地方,简单来说,就是先找到top chunk的位置,然后将topchunk的大小修改为0xffffffffffffffff,然后malloc一个负的chunk,使得这个top chunk被抬升到heap的头地址,使得整个heap的地址都变成top chunk,然后在申请一个chunk,使得程序将原本用于放tcachechunk的tcache_entry 和 tcache_perthread_struct 这两个结构体的大小为0x250的chunk,分配给我们使用,从而做到修改tcachechunk的内容。
 
这里具体讲一下关于申请负数的大小chunk,然后使top chunk抬升到heap的头地址这个负数的大小怎么确定。
首先要知道的是有关top chunk的地址多少,这个应该是没什么问题的,虽然chunk的地址不是固定的,但top chunk和heap的头地址的差是固定的。
notion image
0x5afef90fc120为top chunk的头地址,然后在寻找到heap的头地址
notion image
0x5afef90da000这个就是heap的头地址。
这两个地址的距离为0x22120
现在我们的目的是通过申请一个负数使得top chunk的头地址修改到heap的头地址,那这个负数的大小就要为-(0x22120+0x10)这里的增加的0x10是为了绕过程序一个检测,这个具体可以看我写的那篇文章。(这里建议以后为了方便记忆,这手法要加的统一为0x10,不然其他的数字其实也是可以的)。
 
 

ezheap

其实讲完上面的3个手法,这道题的做题逻辑基本就出来了。
  • 先用house of orange将这道题的libc泄露出来
  • 然后再使用house of force将程序中的top chunk的头地址修改到heap段的头地址,
  • 然后在将最开始的那个0x250的chunk分配给我们,
  • 然后修改其中一个tcachebin的头地址为malloc_hook,
  • 之后申请我们修改的那个地址对应大小的chunk使得malloc_hook被当做chunk给我们使用,
  • 向其中注入one_gadget,然后使用malloc函数触发one_gadget拿到shell
这就是这道题的基本过程,看起来不是很难,但还是比较考验人的积累的,当时做的时候就出现了积累不足导致最后没能做出来的问题。
 
还是来简单分析一下这道题的具体过程。
notion image
除去开始的那个悲伤的故事,之后是4个选项,
1为add chunk的函数,对chunk的大小没有限制,同时将申请的chunk的数据段的头地址放入bss段chunklist中。
notion image
2显示的是free函数,但进去会发现,没有任何作用,既没有参数输出也没有输入。
notion image
3就是个打印内容的函数,将chunk中的内容打印出来
notion image
4,用于向chunk中输入数据,最先选择要注入数据的chunk,然后在输入要注入的数据长度,之后输入数据。
notion image
这里有个漏洞,这里输入的数据大小是由我们输入确定的,程序没有检测,因此这里存在一个堆溢出,我们可以输入任意大小的长度,然后输入内容形成堆溢出。
 
这道题的程序的·逻辑不算复杂,是个基本的堆题,其中chunk的大小由我们决定,最多可以申请15个。然后存在堆溢出漏洞,但是程序中没有free函数的存在。
这是我们目前知道的信息,首先我们没有free函数,有堆溢出,chunk的大小由我们决定,这里就可以知道可以使用house of orange的手法将libc甚至堆的地址泄露出来。
 
这个过程在上面就基本写出来了。
  1. 先申请一个0x100的chunk,通过这个chunk修改top chunk的大小和地址对齐内存页(相加的最后3位为000)
notion image
  1. 再申请一个大于top chunk的chunk,使得程序中产生unsortedbin,然后在申请一个小的chunk,使得原本unsortedbin中的bk和fd指针进入这个小的chunk中 。
这里还可以通过修改新的chunk中的前两个字节为垃圾数据,从而将后面的堆的地址泄露出来。
notion image
 
现在我们就掌握了libc的地址,后面我们就要想如何才能修改malloc_hook的地址内容。我们这道给的libc版本为libc2.27,在这个版本中有tcachebin的存在,因此我们可以考虑使用这个的特点。
 
之后的套路就在上面了(懒得写了)。
 
找到top chunk的地址,并修改为0xffffffffffffffff(-1)。把整个unsortedbin申请出来,然后在申请一个小的chunk,从而找到top chunk的位置,并修改为(-1)
notion image
notion image
notion image
 
然后更具top chunk的头地址0x5c1b7257f120和heap段的头地址0x5c1b7255d000的距离差加上0x10,申请这个数的负数,使得top chunk到heap的头地址
notion image
 
之后便是将heap段的前0x250的chunk申请出来,修改为对应的数字加malloc_hook的地址。
notion image
 
可以看见现在在tcachebin中大小为0xa0的chunk的头地址为我们修改的malloc_hook地址,因此我们只要在申请一个大小为0xa0的chunk就会把malloc_hook当做chunk分配给我们,我们就可以向其中输入one_gadget
notion image
然后在使用malloc函数从而触发malloc_hook执行one_gadget就可以达到shell。
 

exp

 
 
这道题还有其他的做法,但这个做法是我看见,最为简洁和最后理解的,我还看见有使用攻击io等的手法,但这个我还没学,等后面学了再说吧。
这道题做的时候想到了house of orange的手法,但因为没有理解对齐的含义导致但是没有泄露libc出来,问了学长,看见学长在遇到报错时直接去看libc源代码,这是处理报错的比较好的办法,但目前我一直没有研究过libc的源代码,看来后面要好好看看,问问学长是怎么学这个源代码的。
这道题整体来说不难甚至是个被打烂的题目,但比较靠积累,目前我的积累还是不足,以后要多积累一点手法和见识。
 
再次感谢我的学长和W&M Team的wp,助我理解这道题。
一道有趣的栈题简单的SROP利用
Loading...