-
-
[原创]针对jhttp程序的url接口与处理函数的关系,以及授权配置相关问题的分析
-
发表于: 2025-3-18 01:23 4905
-
在固件分析http中,常常遇到以下问题,比如url接口和对应的处理函数怎么配对的?有时候缺失了符号表看不到ida函数名阻碍了逆向分析。还有页面的接口如何配置权限,怎么挖到未授权漏洞?
本文以dlink某款设备的jhttpd程序为例,分析以上问题
jhttpd放ida里打开后,看到在main函数中,做了一系列初始化init,初始化部分先不深入,其中关注到httpd_poll这个函数,根据linux网络编程的知识,判断这里很可能是创建poll模型服务器,处理客户端连接和通信
进入其中,看到poll函数,accept函数,确认判断没错。这一部分是通过 accept 接受新的客户端连接,并将其加入到连接管理中。
这一部分是根据连接状态调用不同的处理函数,其中重点关注到httpd_do_recv中,可以通过函数名猜测这是处理客户端请求的
跟进后,看到其中对内容做了get和post的匹配
这里跟进进入httpd_dowith_get函数,应该是处理get请求的
最后到了httpd_send_file函数中的这部分(在此之前还有几层调用,贴出来意义不大先忽略了),v17这里是通过httpd_find_ext_file等函数返回出来的,经过动调可以确认是url对应的处理函数的指针,那么猜测这里大概率是通过url接口名从哈希表中获取的函数指针
httpd_find_ext_file函数内容如下,可以确认判断没错,确实有一张哈希表gl_vfile_ext_hash,里面应该有存储函数指针。同时,这张表是在bss段的,说明是在程序初始化后填数据进去的的。这里去找这张哈希表的调用
通过层层回溯,确认了这样一条调用链,main --> mem_init_fun --> httpd_file_ext_init --> httpd_file_ext_add,查看其中内容,可以确认这里是初始化函数名和接口名在哈希表中
其中httpd_file_ext_init函数这里有个名称很醒目的全局变量httpd_cgi_all_fun
查看其内容,如下。看到分别有三字节的内容,如果以小端序来看的话,这好像都是程序中的地址段,那么很有可能就是函数和接口名的位置了。经过比对,此猜测是正确的,那么这里可以看做一张符号表,。
结合该部分的代码,可以确认这里的符号表一个条目有4*5=20字节,比如这里
fun[0]=0,fun[1]=0,fun[2]=0,fun[3]=0x0058b51c(函数位置),fun[4]=0x0046ea04(接口名位置)
到这一步,就能做到确认函数对应的接口,以及在符号表缺失的情况下恢复ida中的函数名了。
这里继续探究该jhttpd是如何配置页面的访问权限的,有些页面可以直接访问,有些页面输入进去就会返回登录界面。弄清这个问题或许可以帮助挖未授权的洞。
回到前文的一个逻辑:通过接口名在哈希表中找函数指针然后进入对应的函数。可以看到在v17(a1)之前是有一个httpd_check_user的,其中的参数a1确认是整个http包,v14[2]经过动调发现是一个数字,而v14又是前面提到的符号表条目,这里猜测v14[2]指代的很可能是接口的权限配置
在httpd_check_user的具体逻辑中,先判断a2(也就是v14[2])是否大于2,再验证cookie,如果a2等于0,则不需要验证cookie。进一步确认了猜想
此时回去看符号表条目
有的fun[2]=0
而有的却是3
将这两者的接口访问,fun[2]为0的可以直接访问,为3的却会跳转到登录界面,也就是说没有通过httpd_check_user的校验。
那么到这里,接就能做到未授权接口的收集了,ida python脚本如下。
通过该方法,最终发现了一个未授权获取敏感信息漏洞
import
idaapi
import
idautils
import
idc
# 符号表的起始和终止位置
symbol_table_start
=
0x5D4E00
symbol_table_end
=
0x5D6DF4
output_file_path
=
"output.txt"
with
open
(output_file_path,
"w"
) as output_file:
# 遍历符号表
for
addr
in
range
(symbol_table_start, symbol_table_end,
20
):
# 每个元素占用20字节(5个4字节)
# 读取每个元素的值
fun0
=
idc.get_wide_dword(addr)
fun1
=
idc.get_wide_dword(addr
+
4
)
fun2
=
idc.get_wide_dword(addr
+
8
)
fun3
=
idc.get_wide_dword(addr
+
12
)
fun4
=
idc.get_wide_dword(addr
+
16
)
# 检查权限是否为 0
if
fun2
=
=
0
:
# 获取接口名和函数位置
interface_name_address
=
fun3
function_address
=
fun4
# 读取接口名字符串
interface_name
=
""
while
True
:
char
=
idc.get_wide_byte(interface_name_address)
if
char
=
=
0
:
break
interface_name
+
=
chr
(char)
interface_name_address
+
=
1
# 获取函数名
function_name
=
idc.get_name(function_address)
# 将结果写入文件
output_file.write(f
"函数名: {function_name}, 函数位置: {hex(function_address)}, 接口名: {interface_name}\n"
)
# 提示信息
print
(f
"结果已保存到 {output_file_path}"
)
import
idaapi
import
idautils
import
idc
# 符号表的起始和终止位置
symbol_table_start
=
0x5D4E00
symbol_table_end
=
0x5D6DF4
output_file_path
=
"output.txt"
with
open
(output_file_path,
"w"
) as output_file:
# 遍历符号表
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课