type
status
date
slug
summary
tags
category
icon
password
根据漏洞描述
D-Link DIR-645是一款无线路由器设备。
D-Link DIR-645 "post_login.xml","hedwig.cgi","authentication.cgi"不正确过滤用户提交的参数数据,允许远程攻击者利用漏洞提交特制请求触发缓冲区溢出,可使应用程序停止响应,造成拒绝服务攻击。
 
 

程序分析

找到hedwig.cgi 文件所在位置
notion image
发现 hedwig.cgi 是一个软链接,指向 cgibin 文件
这里不知道为什么我的这个不显示,我之后研究一下 正常应该是这样的
notion image
 
直接cgibin 文件是个二进制程序,ida看一下
notion image
 
这里大概就是根据/ 符号最后的参数,来判断要执行的函数,根据之前提到的hedwig.cgi 在这里也可以找到,
notion image
 
进入hedwigcgi_main 函数中看看
notion image
 
这里程序一开始通过
来判断http的请求方式, REQUEST_METHOD 环境变量,存放这请求方式。 从环境变量里取 CGI 的 REQUEST_METHOD(如 "GET", "POST")。Web 服务器在执行 CGI 程序前会设置这些环境变量。
notion image
之后
这里用来判断是否为POST 请求,如果不是就会给v1赋值,并打印再屏幕上,如果是这可以往下执行,
可以看到我们刚刚访问 http://192.168.2.2:4321/hedwig.cgi 时的报错内容:"unsupported HTTP request"
分析可知,其会读取并判断环境变量 REQUEST_METHOD 是否为 POST,因此只支持 POST 请求方式,刚刚通过浏览器访问是 GET 方式,所以报错
notion image
 
接下来会执行
 
notion image
这里要在CONTENT_TYPECONTENT_LENGTHREQUEST_URI
都放入一定的数据用于满足程序的绕过,
 
 
application/x-www-form-urlencoded
1. 为什么是这个值
  • 浏览器在提交表单时,默认的 enctype 就是 application/x-www-form-urlencoded
  • 这意味着:表单里的 key=value 数据会被 URL 编码(比如空格 → +,中文 → %E4%B8%AD),然后放在 HTTP 请求体里传过去。
  • Web 服务器(例如 Apache、Nginx + CGI/FastCGI)会根据请求头自动设置环境变量 CONTENT_TYPE,告诉后端程序请求体是什么格式。
所以你在 CGI 程序里看到:
就是因为浏览器用的默认表单编码。

2. 什么时候不是这个值
  • 如果表单设置了 enctype="multipart/form-data"(常见于文件上传),那么 CONTENT_TYPE 就会变成:
    • 如果你用 AJAX/Fetch 手动发 JSON 数据:
      • 那么 CGI 环境变量 CONTENT_TYPE 就会变成 application/json

    3. 总结
    • application/x-www-form-urlencoded 是 HTML 表单的默认编码方式。
    • 所以你用普通表单 POST,服务器端 CONTENT_TYPE 一直都是这个值。
    • 想改它,就要在 请求头里明确指定 Content-Type,或者在 <form> 里修改 enctype
     
     
    之后重点来看 sess_get_uid(v4); 函数
    notion image
     
    获取环境变量HTTP_COOKIE 里的值
    其判断 uid 的逻辑为:以 '=' 作为分隔,'=' 前面的内容存入 v2'=' 后面的内容存入 v4,假设原字符串为 uid=xxx,如果 v2 == 'uid',则 v4 == 'xxx' 就是 uid 数据
    最后将 v4 中的 uid 数据赋值给变量 string,最后将其写入 a1,也就是该函数的形参
     
    之后回到hedwigcgi_main 函数,就会来到程序的第一个溢出点
    notion image
     
    这里程序把我们输入的数据解析之后
    前面 sess_get_uid() 函数会将 uid 写入形参,因此 v4 的值就是 uid
    也就是说,sprintf(v27, "%s/%s/postxml", "/runtime/session", string) 中的 string 就是 uid而这个 uid 是用户可以控制的
    v27 是一个长度为 1024 的字符数组,明显是可以被人为输入的 uid 溢出的:
     
    后面还有一个,相同的指令,
    notion image
    由于 v4 没有被修改过,因此这里的 v20 同样是 uidv27 同样可以被溢出
    因此我们可以利用这里覆盖上一次 sprintf() 的内容
    但是要想执行两次 sprintf() 需要满足两个条件判断:
    1. 第一个是需要存在 /var/tmp/ 路径,其会创建一个 temp.xml 文件并写入数据
    1. 另一个是要求 haystack 非空
    notion image
     
    这个第一个条件比较好满足,这里我要自己创建一个/var/tmp文件就行,这个在真实的路由上传中,我们这里没有
     
    然后是有关haystack 不能为0,这里我没搞懂,可以看看其他师傅的解释
    关于 haystack,通过交叉引用(IDA 快捷键为 X),发现 haystack 在此之前只有 sub_409A6C() 函数进行过修改,也就是 cgibin_parse_request((int)sub_409A6C, 0, 0x20000u) 的第一个参数:
    notion image
    notion image
    cgibin_parse_request() 函数在这里才调用了 sub_409A6C() 函数(作为形参 a1):
    notion image
    off_42C014 处存放的是 "application/" 数据,这是在处理 HTTP 请求时用到的 MIME 类型字符串的一部分:
    notion image
    因此这里是对 POST 内容的读入
    要想读入 POST,就必须先满足 v9 != -1 的 if 判断,而 v9 初值就是 -1,因此需要走中间的 if 分支使 v9 = 0,同时也必须保证环境变量 REQUEST_URI 不为空:
    notion image
     
    反正最后就是要让REQUEST_URI 不为空。

    QEMU 用户级复现

    QEMU 用户级层面的漏洞复现不需要进行仿真,但相比之下,需要进行仿真的系统级复现更加直观、更符合现实场景,这里主要是介绍 QEMU 用户级层面的漏洞复现方式
    这里我们先进行这种类似于本地测试的,之后在进行系统级远程测试,
     
    我们在宿主机的路由器文件系统根目录
    生成 2000 个字符的 payload 文件,用来测试 uid 溢出到栈上返回地址所需的字节数:
     
    创建以下 run.sh 脚本,通过 QEMU 用户模式启动 /htdocs/cgibin 程序:
    这里通过 -g 1234 在 1234 端口开启了 gdbserver 监听,cat payload 可以将 payload 文件中的内容读到 uid= 之后,echo $INPUT | 可以做到 POST 的效果,-0 就是 argv[0]-E 是设置环境变量
    然后执行 run.sh
    notion image
     
    可以看到本机开启了 1234 端口
    然后使用本机的 gdb-multiarch 连接 gdbserver:
    notion image
     
    这里有个问题就是vmmap 这个不能是用,我们不能直接看到libc地址,我们要在调试的过程中寻找,这个比较简单,我们可以直接利用程序的延迟绑定机制找到
    notion image
     
    通过两次t9寄存器跳转memset函数拿到libc地址
    notion image
    此时的t9为第一次调用是用plt表里的内容,
    notion image
    第二次就是libc中的真实地址。
    在路由器文件系统的 /lib 文件夹内,找到其所使用的 libc 文件:libc.so.0
    利用 objdump 查找 memset() 函数的偏移地址:
    notion image
    可以知道libc的基地址为0x3ff6ca20-00034a20=0x3FF38000
    接下来就是 GDB 执行到 hedwigcgi_main() 函数结束将要返回的地方,观察返回地址来确定溢出的长度:
    notion image
    说明程序最后在溢出之后马上要执行的是0x646b6161 这里,
    这个数据在payload中为1009位置,说明我们要填充的数据长度为这个之后就是函数结束会执行的地址。

    向用户态 QEMU 传递 payload 参数

    由于 QEMU 用户级复现不需要仿真,我们只需要用 qemu-mipsel-static 运行 /htdocs/cgibin 程序,然后将 payload 作为参数传递
    poc 如下:
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    mips基础知识

    加法:add $1,$2,$3 ⇒ $2+$3=$1
    减法:sub $1,$2,$3 ⇒ $2-$3=$1
     
    取数:lw $1,20($2) ⇒ 2号寄存器中的值为基值,偏移20字节,取出数据写入1号寄存器中
    存数:sw $1,20($2) ⇒ 2号寄存器中的值为基值,偏移20字节,将1号寄存器中的值写入其中
     
    加立即数 addi $1,$1,1 ⇒ $1=$1+1(这里1可以为负数,所以没有减立即数指令)
     
    addu 指令用于计算无符号数之间进行的加法操作,addu $t0,$t1,$t2 将 $t1 和 $t2 进行无符号相加,结果存储在 $t0
     
    add 指令和 addu 一样,只不过进行的是有符号数之间的加法。
     
    addiu 指令将上面的 addi 和 addu 结合了一下, addiu $a1, $zero, 2 进行的是将寄存器$zero 加上一个立即无符号数 2 ,并将结果存回寄存器 $a1 中
     
    与·或·或非,同1则1,有1为1,同0则1 and,or,nor
     
    左移:sll $1,$2,4 将$2中的数据左移4位放入$1中
    右移:srl $1,$2,4 将$2中的数据右移4位放入$1中
     
    相等跳转:beq $1,$2,label 如果1,2号寄存器中的值相等则跳转执行label
    不相等跳转:bne $1,$2,label 如果1,2号寄存器中的值不相等则跳转执行label
    (这两个指令的原理都是相减,判断结果是否为0)
     
    跳转:j label 直接跳转执行label jr $1 直接跳转执行1号寄存器里的地址
     
    li (Load Immediate)指令用于将一个立即数存入一个通用寄存器, li $gp, 0x498300 将 $gp 寄存器的值赋值为 0x498300
     
    lui 指令将一个 16 位的立即数左移 16 位后存入目标寄存器中, lui $v0, 0x46 是将 0x46 立即数左移 16 位后存入 $v0 寄存器,即 $v0 寄存器的值为 0x460000
     
    ori 指令是 MIPS 汇编中的一种逻辑运算指令,它可以将一个寄存器的低 16 位与一个 16 位的立即数按位或运算,并将结果存入另一个寄存器中。ori $t6,$t6,0x430a 指令将 t6 寄存器与 0x430a 立即数进行或运算,将结果放回 $t6
     
    la (Load Address) 指令用于将一个地址或标签存入一个寄存器,la $v0, puts 指令将 puts 函数地址存入 $v0 寄存器中
    lw (Load Word) 指令用于从一个指定的地址加载一个 word 类型的值到一个寄存器 lw $v0, 0x14($fp) 将 $fp+0x14 的位置中的数据存入到 v0 中
    sw (Store Word) 将源寄存器中的值存入指定地址,sw $ra, 0x24($sp) 将 $ra 的值写入距离栈顶($sp)偏移 0x24 的内存单元中
     
    jr 是跳转指令,jr $ra 跳转到 $ra 寄存器指向的地址处
     
    jal 指令是跳转指令,jal target 复制当前的 PC 值到 $ra 寄存器,然后跳转到 target 处
     
    bnez 指令用于在寄存器的值不为零时进行分支跳转,bnez $v0, loc_4005E8 表示当 $v0 不为零时跳转到 0x4005E8
    b 是无条件跳转指令,b loc_400604 直接跳转到 0x400604 地址处
     
    notion image
     
     
     
    notion image
     
    notion image
    house of系列CVE-2024-39226复现
    Loading...