官方的漏洞报告:e18K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8Y4k6V1i4K6u0W2L8%4u0Y4i4K6u0W2j5$3&6Q4x3V1k6X3L8r3q4%4i4K6u0r3M7$3S2G2N6#2)9J5c8V1y4z5g2V1c8Q4x3X3b7J5x3o6p5K6i4K6u0V1x3e0p5$3x3U0f1`. 官方的漏洞报告中只提及了DIR-645型号的hedwig.cgi中会存在缓冲区溢出的漏洞,其实D-Link的DIR-815/300/600/645等型号都存在这个漏洞,在本篇漏洞复现报告中,笔者仅以DIR-815为例进行分析。笔者用于复现的物理机为Ubuntu-20.04,请先安装qemu / binwalk / sasquatch / gdb-multiarch等工具,并对MIPS架构下的汇编语法进行一定的了解。固件包下载地址(挂个梯子):DIR-815A1_FW101SSB03.bin将固件包通过binwalk解压后,先找到官方漏洞报告中所说的hedwig.cgi文件,其路径为/htdocs/web/hedwig.cgi,通过ls -l命令看一下: 可以看见,/htdocs/web/hedwig.cgi是/htdocs/cgibin的软链接,因此,我们需要逆向分析的二进制文件是/htdocs/cgibin。先进入到main函数中: 再走到hedwigcgi_main函数: 首先,会先取环境变量REQUEST_METHOD,按照这里的判断,必须得是 POST请求 才行。接着,会走到cgibin_parse_request函数: 这个函数已经不陌生了,会对url先进行解析,然后将POST的内容读入进来,再通过sub_409A6C函数进行解析。在cgibin_parse_request函数内: 这里会获取圈出的几个环境变量,不过和后面的栈溢出漏洞关系都不大,但是不能没有,需要随便输入一点东西,具体原因之后会分析。接着,就会走到sess_get_uid函数: 里面有对环境变量HTTP_COOKIE的获取: 对HTTP_COOKIE中=的前后进行了分离: 可以看见,=前面的内容被存入了v2,后面的内容被存入了v4,最后会对v2中的内容进行一个判断: 也就是判断等号前的内容是否为uid,判断通过了以后,就会将等号后面的字符串拼接入a1,也就是主函数传进来的参数v4。再然后,就到了一个可能发生栈溢出的漏洞点: 这里的string就是v4中的字符串,也就是cookie中uid=之后的内容,是可以由用户自由控制的,然而v27数组的大小仅有1024,因此,很容易造成缓冲区溢出。我们发现,在之后还有一个类似的sprintf: 这里的string仍然是v4,进一步观察,发现v4在两个sprintf之间未被改变过,也就是说,这里的string仍然是cookie中uid=后面的字符串,如果能走到这第二个sprintf的话,那么这里才是真正的溢出漏洞点,因为仍然是v27数组的溢出,两次拼接的字符串又一样,所以这里能覆盖上一次sprintf的内容。容易看出,如果能走到第二个sprintf的话,就需要过这两个判断: 这第一个判断需要有/var/tmp这个目录,这个在真机上是有的,因此为了更真实地模拟环境,我们需要在解压后得到的文件系统内创建一个/var/tmp文件夹,这样cgibin才能在此路径下创建temp.xml文件用于数据的写入。第二个判断haystack的值在这之前只有cgibin_parse_request的第一个参数sub_409A6C中可对其操作: 这个sub_409A6C函数需要POST传入内容的时候才能走到,那么要使得POST传入内容,也就要走到cgibin_parse_request中的这里: 这里跳转到的sub_403B10函数后,才有对POST内容的读入,并调用到sub_409A6C: 上图是进入sub_403B10函数后,最终走到的sub_402B40函数,其中这里的v9函数指针就是指向的sub_409A6C函数。再回到cgibin_parse_request函数中,要走到读入POST内容的那里,就必须要使得v9!=-1才行,再往cgibin_parse_request函数上面看看: 因此,环境变量REQUEST_URI中也必须有内容才行,这里环境变量CONTENT_TYPE仍然是老规矩application/x-www-form-urlencoded,不再多分析了。满足了以上条件,就能顺利地走到第二个sprintf了,也就是真机环境中真正的栈溢出漏洞点。MIPS架构下的栈溢出肯定也是需要通过构造ROP链来getshell的,不过由于MIPS有个特性,即无法开NX保护,这样就有了两种构造ROP链的方式:第一种就是纯ROP链,通过调用system函数来getshell;第二种就是通过构造ROP链,跳转至读入到栈/bss段等处的shellcode执行。在实际应用中,最常用的还是通过ROP + shellcode的方式来getshell。寻找IDA的gadget最好还是用IDA的插件mipsrop,里面的stackfinder()/tail()/system()等选项很便于寻找一些gadget,也可以使用如mipsrop.find("li .*, 1")的形式,通过.*进行模糊匹配: 附上IDA-7.5以上版本的mipsrop插件下载地址:mipsrop.zip,解压到IDA的plugins目录下即可。关于MIPS架构的寄存器及指令集请自行查阅资料,这里就不多作介绍了。下面主要来介绍一些常见的MIPS架构的特性(32位mipsel)。叶子函数指的是没有调用任何子函数的函数,其返回地址会存放在$ra寄存器中,在该函数结束时,直接就通过$ra寄存器跳转返回。非叶子函数自然就是指其中调用了其他子函数的函数,其返回地址$ra会在程序开始(prologue)通过sw指令存放在栈上,因为其中调用了其他子函数,肯定会需要改变$ra寄存器的值,来作为其他子函数的返回地址,所以最先的数据需要保存下来,然后在该函数结束(epilogue)时,再对应地通过lw指令取出,并跳转返回。同样的道理,如果在某个函数中使用到了 $s0 ~ $s7中的某些保存寄存器(包括$fp) ,则也会在prologue处保存下来,并在epilogue处取出。需要注意的是,$s0 ~ $s7, $fp, $sp在栈中存放的地址依次递增,因此,很容易想到,我们可以在栈溢出的同时,顺带着控制到$s0 ~ $s7的值。MIPS的这个特性是一个在栈溢出中很好利用的点,若是二进制文件中没有或没有完整的prologue/epilogue段,在libc的scandir/scandir64中可以找到完整的汇编段,来控制这所有的寄存器: 最后,都会通过addiu $sp, ...的命令,将栈抬高到$ra后面的位置。MIPS架构存在“流水线效应”,简单来说,就是本应该顺序执行的几条命令却同时执行了,其还存在缓存不一致性(cache incoherency)的问题
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-272318.htm
[原创] 从零开始复现 DIR-815 栈溢出漏洞
361 浏览
13 回复
强!
大佬
谢谢师傅分享,学到了很多
问下为什么从`&off_42C014`那里可以可以看到其跳转到了`sub_403B10`函数?
为啥我调试libc_base时,memset那里的地址不是7f开头的呢?
这里已经是第二次调用memset了,为啥地址是这个呢?libc的地址不是7f开头的码?
加我qq,大神
请教下楼主是怎么走到溢出点2的?我审了一遍发现控制haystack是调用sub_409A6C生成的,但是sub_403B10 -> sub_402B40这个链上会要求sub_402B40的第一个参数非空才能成功调用v9,但是在每一处调用sub_402B40之前,第一个参数似乎都设置成了0
请问这个系统模式下init.sh这个脚本文件里面的这些815漏洞需要用到的库是怎么确定的呢
XiaozaYa
这里已经是第二次调用memset了,为啥地址是这个呢?libc的地址不是7f开头的码?
要不你再接着执行si进入libc,这个时候memset的地址应该是7f开头了吧
这里已经是第二次调用memset了,为啥地址是这个呢?libc的地址不是7f开头的码?
要不你再接着执行si进入libc,这个时候memset的地址应该是7f开头了吧
好棒
真神,得细品
写的真的好,困扰我的好几个细节都写出来了,mips栈漏洞分析看了这么多,这是最精品的一篇