type
status
date
slug
summary
tags
category
icon
password
上个周打了网鼎杯(传说中的网安奥运会,代理人战争,py咸鱼大会,顿悟高手聚集地)。
就简单的整理了一下各个赛道的pwn的附件放在这里,有需要的自取。
朱雀组的pwn好像没有附件(太强啦)
我用夸克网盘分享了「玄武」
链接:https://pan.quark.cn/s/3532cf7e2de7
我用夸克网盘分享了「青龙」
我用夸克网盘分享了「白虎」
链接:https://pan.quark.cn/s/5b748b90f1d6
也正好就拿这篇文章当这次赛题复现的wp。
白虎的pwn1。
这道题是个堆体,其中还是涉及到几个之前不了解的知识点,可以好好看一看。这道题大致有两种解法
这里先讨论我看来最简洁的一种(不一定简单)。其中涉及到了
mp_.tcache_bins
这个的修改从而做到控制修改申请的chunk地址。就先介绍一下这个东西。mp_结构体
关于这个结构体的具体内容这里不做解释(因为我也不是很明白)。但这个
这个地址记录就是tcache bin的最大索引值,什么是索引值,就是在程序中有一个数组记录了程序中的chunk的所有可能的大小。其中这里的64就是值在这恶鬼数组中的第64位数字就是tcache bin 的最大值,我们知道这个值就是0x410。
于是就有我们就有一种攻击手法,如果我们把这个的这个64修改为一个很大值,会发生什么?程序对chunk的判断就会出现问题
当我们free掉一个chunk时,会先判断是不是符合tcache bin的大小,而这个判断就是看其大小有没有超过这个索引值索引到的最大数据。
如果此时我们把这个值修改的很大那就意味这此时tcache bin的最大值已经被修改了。如果此时我们free一个原本不属于tcache bin的chunk,在程序中由于其最大索引值被我们修改了,那我们刚刚释放的这个大chunk原本来说是应该通过检查发现大于最大的值,从而进入其他的bin中。但是这里因为我们把tcache bin的最大索引值修改了,于是这个chunk检查结果为小于tcache bin的最大值,依然按照tcache bin的分配模式。被放入tcache bin中。
mp_.tcache_bins
这个漏洞的主要作用就是上文的内容,我们修改这个地址的内容为一个比较大的数字从而导致我们能把大小大于tcache bin的chunk都放入tcache bin中。然后就这个漏洞中比较严重的问题,现在我们把一个大小超过tcache bin的chunk放入了其中。我们知道在tcache bin中有一个结构体前半部分用于存放tcache bin的不同大小的chunk的个数,下半部分用于存放不同chunk的起始chunk的地址,具体可以看下面这篇文章,
反正就是
tcache_entry
和 tcache_perthread_struct
这两个,我们知道tcache bin用于存放起始地址的结构体的位置就是程序中第一个chunk,0x290的这个chunk。这个chunk的大小时刚好根据tcache bin的最大大小来设置的,当我们把一个最大的chunk放入tcache bin中时,这个0x290的chunk的最后一个地址就发放入这个chunk的地址,我们知道tcache bin的不同大小的第一个chunk的头地址要放入程序的第一个chunk中,并由对于的顺序,我们在上面说了我可以通过修改
mp_.tcache_bins
来实现把远远超过tcache bin额定大小的chunk放入tcache bin中,此时由于tcache bin的其他机制没有改变,于是这个大chunk的头地址也会放入那个chunk中,但是由于在之前那个0x290的chunk没有其对应位置,这个地址便会往后寻找对用的长度的位置放入其中,而这就会导致去放入了可以被我们控制的chunk中,于是我们可以修改这个头地址,然后在申请相同大小的chunk,从而使得我们达到任意地址写。这个过程用文字描述有点干巴,那就来看下面这道题,
关于如何找到这个东西在程序中的偏移可以使用这个命令
这里注意一下这个命令必须在patchelf了glibc_all_in_one里的libc库才能使用这个命令并把这个打印出来,如果使用了题目给的libc库可能会打印不出来这个。然后我们还可以使用这个命令吧这个mp_的地址打印出来
注意可能在有的题目给的libc文件和我们的这个不同,最好在那个libc中在仔细寻找。
wp
这道题的程序整体比较简单,一个创造chunk,一个释放chunk,一个打印chunk。还有一个edit函数比较重要
这个函数的内容是向buf中写8个字节,然后向buf中的内容当在地址写入0xa2c2a,实现了一个向任意地址写入0xa2c2a的目的。
这道程序是libc2.31的存在tcache bin,于是我们就可以考虑使用我们上文提到的手法,从而去修改tcache bin的范围,使的比较大的chunk进入tcache bin中。其头地址被放入我们可控的chunk中,从而修改其地址,使再次申请相同大小的chunk,于是程序就会申请我们修改过的地址供我们使用。我们就可以设置这个地址为free_hook的地址(这个版本这个还有可以用),然后就把它申请出来改system的地址。
此时
mp_.tcache_bins
里的值就已经被改为0xa2c2a我们现在释放这个大小为0x630的chunk,其头地址会被放入最上面那个0x610的chunk中
此时在0x610的chunk1的第17个地址处有0x630的chunk2的头地址,然后我们就可以通过修改chunk1中的这个地址从而修改起始地址。
这里可以通过讲这个free之后在申请就可以修改内容。
修改后的结果如此我们把原本在这里的chunk2的头地址修改为free_hook的地址。然后申请与之前那个相同大小就可以把地址当做chunk供我们使用。
于是我们可以把__free_hook里的地址修改为system的地址然后在free一个内容第一处为的
/bin/sh
的chunk从而拿到shellexp
这道题还有宁一种解法以后慢慢说,这个这道题好像还有一个问题就是free和show的功能函数可以输入负数,并就是往后寻找地址。然后在show是输入-13就会打印一个地址从而破解pie保护,但好像没什么用。
青龙组pwn 错误附件
这道题是原本青龙组的pwn4,但一开始的时候平台上的附件和远程环境不对,后面才上了正确的附件(强网拟态也是出现过附件和环境对不上大的事,一群草台班子。虽然我也是),
这道题是个2.27的堆题,有uaf,然后难点在于加上了沙箱的保护,就必须使用orw将flag读出来(这里不得不提到远程文件叫flag.txt的那些事)
也算是第一次做堆的orw。当时比赛的时候以为这道题会在后面上,就一直做了,当时也是做到就差执行orw了,但不会这个于是就没做出来后面看了网上的很多资料,很多都提到利用
setcontext+53
这个函数的指令执行orw看了几天,最后还是没看懂,这种做法最后还是放弃了,等什么时候遇到了在好好研究。
后面发现在程序中有个好东西叫
environ
函数,这里面刚好有栈地址,我们可以通过泄露这个地址里的内容从而将栈地址暴露出来,然后我们就可以直接控制向栈上的返回地址申请chunk,并写入orw。我们可以在gdb中使用这个命令将
environ
函数的地址找出来,然后我们就可以知道这个函数的偏移地址是多少
将这里的内容就是栈上的
0x7fffffffdf28
这个地址,将其打印出来,然后在计算其与rbp后一位的差距,就可以知道当前程序的返回地址。在来说这道题,这道题有个uaf,但会清零对应的大小值导致不能用,但还是可以free。
wp
- 利用uaf泄露heap的地址
因为在free堆块时会把chunk的大小清零但指针不会,就可以先free一个chunk再在不同的位置上把这个chunk申请出来,然后free这个chunk最开始的哪个地址,就会使其虽然被free,但我们可以通过控制第二次申请的位置从而做到show和edit。
- 修改tcachebins的fd指针,使我们能申请到存放tcachebins结构体的chunk。修改其中的chunk值为7,
原理
3.此时在tcachebins中0x50的chunk数为7,我们在free这种大小的chunk,就会直接放入unsortedbin中,我们利用相同的思路就可以将libc地址泄露出来。
4.然后就可以在修改tcachebins的fd指针为
environ
地址,把这个地址申请为chunk,将其内容打印出来得到栈上的地址。5.然后就是修改tcachebins的fd指针为栈上的返回地址,并将orw写入到返回地址中。
这里要注意一个点,rbp的上一个位置也要填充数据,负责会出现执行open的函数的rdi指针传入的数据不对。
exp
这道题中对于栈的泄露还是比较奇妙的办法。但这到题的做法还有利用
setcontext+53
这个函数的。这种办法以后遇到再说吧。青龙组pwn4爆破
这道题实在是不想写了,太麻烦了,感觉就是为了出题而出题。
这里放一下爆破脚本,总感觉有点问题,写的这个太麻烦了。
这里程序要我们输入用户名和密码,然后对我们输入的内容有两个检查,
第一次处是用
sub_12EB
函数对我们输入的数据与程序中的数据进行检测,这就是函数内容,这里程序会用
strlen
函数先对输入的数据进行一个长度判断,然后再把输入的数据与正确数据的每一个长度进行检测。这里其实就已经有一个问题了。程序用的是
strlen
函数对输入的数据长度进行记录,然后再从正确的数据中拿出相同长度的数据每一个依次比较。strlen
函数有个问题就是当他再检查长度时如果遇到\x00
就会直接停止记录前面的长度。这里我们就可以只输入很少的数据再加上
\x00
,然后程序就会把对应长度的正确数据拿来比较。如果正确就会继续往下检测,不满就会直接退出重新输入。再用这个函数检测正误后还有一个检查用于判读长度是否正确的。
这里的两个检查在报错后输出的内容是不同的,内容不对输出的为
长度不对为
于是我们就可以直接进行爆破
而爆破的思路就是我们在输入时先输入一个字符,然后加上
\x00
如果这个字符不对就会返回Invalid username!
,由于这里是个无限函数,我们就可以在下次输入时换一个字符(+1),如果正确但长度不对就会返回Invalid username length!
我们就可以记下之前输入的在下次输入时后面加上一个新的字符,开始新的一次加1。知道完全正确。其实我们就是一个一个的字符通过将所有的字符进行循环判断。
由于不同大的返回值从而知道完全正确。密码也可以使用相同的思路。
脚本
总感觉哪有点问题,没打过远程不知道。
- 作者:wgiegie
- 链接:https://tangly1024.com/article/1353ecc9-5160-80ab-aab0-ddb07a1d56f0
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。