首页
社区
课程
招聘
[原创]针对jhttp程序的url接口与处理函数的关系,以及授权配置相关问题的分析
发表于: 2025-3-18 01:23 4905

[原创]针对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直播授课

最后于 2025-3-29 17:57 被CLan_nad编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回