starctf2019-hackme
1.首先解包,看init,空的。
2.然后看启动脚本:
1 | #注释头 |
比较正常,开了smep和smap,并且4内核,2线程,这里就可能会是条件竞争的考点。
3.然后拖入IDA分析:程序比较检查,类似菜单题,但是IDA中F5看伪代码中多了很多的奇怪的参数,最好以add->delete->edit->read的顺序来看比较舒服,一般的堆体分析结构也差不多怎么看。
首先判断下结构体:
1 | #注释头 |
其中pool中存储多个chunkNote结构体,不是指针,而是结构体。
(1)add:没什么特别的,创建堆块,拷贝数据,存储chunk到pool中
(2)delete:也没啥问题,释放内存,指针置空。
(3)edit:有点问题,这里判断了chunk指针是否存在,并且offset+size要小于等于之前保存的size。
本来没啥,但是这里offset和size是int类型,可以为负数,那么就可以依据chunk_addr来向上修改数据了,相当于越界写。
(4)read:同样的,也相当于越界读:
▲虽说是越界写和越界读,但是由于copy系列函数,如果我们想往高地址读写的话,(内核系列函数都在高地址)offset一定需要很大,然后又要满足小于size,那么userDataSize势必要设置为负数,负数转换成unsigned long就会变得很大,基本就会导致一半的空间被覆盖,那么程序指定崩盘,所以这里还是申请pool全局变量处来进行任意读写比较好。
另外也由于slub的特性,简单修改fd的话,虽然能够任意申请,但是如果fd的内存处保存的内容是个非法的,再次申请该大小的chunk程序就会崩,所以有全局变量pool就用pool。
4.思考一下漏洞怎么利用。
(1)首先需要地址
①堆地址:释放chunkA,从chunkB向上偏移读取chunkA的fd,即可得到指向的chunkC的地址,即chunkA.fd = chunkA_addr+size*2。
②内核基地址:依据任意读,从第一个chunk往上的地方读取chunk中的内容,从中筛选出vmlinux中的地址,读取之后减去偏移,即可得到内核基地址。
例如这里就可以读取图中的0xffffffff81849ae0,然后减去0x849ae0即可得到基地址。当然这没有用kalsr的效果。开了之后如下,偏移也是一样的。
得到这次打开的基地址:0xffffffff9de00000,用命令检查一下:
可以看到最前面的就是内核基地址。
③函数基地址:
cat /proc/kallsyms|grep commit_creds
从kallsyms中可以查看到所有函数,减去原始基地址0xFFFFFFFF81000000再加上随机化的内核基地址就是随机化之后的函数地址了。比如上例为:
0xffffffff8104d220-0xFFFFFFFF81000000+0xffffffff9de00000=0xffffffff9de4d220
得到运行了kaslr的函数地址。
④模块基地址:由于需要pool变量地址,所以模块的基地址肯定也需要,这里利用mod_tree来获取。mod_tree包含了所有装载的Mod的基地址。
可以看到hackme模块的基地址也包含在里面,开了kaslr之后也是一样的。所以我们利用内核基地址得到mod_tree的地址,申请下来就可以从里面读出hackme模块的基地址。
(2)然后方法就很多了,虽然开了smep,smap,kaslr:
①修改modprobe_path:
可以看到modprobe_path指向一个二进制文件
而当某个特定错误发生时,就会运行该二进制文件,且是以root权限运行的,(我尝试过,运行/bin/sh不能提权)所以如果我们把路径改成我们的二进制文件,将flag复制一下,改下权限就能打开了。(直接cat flag也不行,很奇怪)
我们的二进制文件下只能类似是如下命令才行的:
1 | #注释头 |
注意要使用绝对路径。
▲然后触发这个特定类型的错误需要一个错误格式的二进制文件:
1 | #注释头 |
这个elf的错误格式就是\xff\xff\xff\xff,或者其他错误格式也行。
1 | #注释头 |