Tinkering with ISP autogenpassword ZyXel EMG2926-Q10A wifi router

Last Modified: Wed, 10 Apr 2019 19:37:57 +0000 ; Created: Mon, 11 Mar 2019 19:11:37 +0000

When I signed up for my new ISP they had a promotion and gave me a free wifi router (not required for their service). I was impressed with the signal quality of this router throughout my home.

One issue, however, is that my ISP set the wireless SSID to an ISP named one with a 10 character password consisting solely of digits [0-9]. I wanted to customize the SSID so I went to login to the web portal of the router.

In short, I was denied using the password printed on the back label of the router. So I did a standard reset, and the router reset. The SSID name and password changed to the "random" one on the back label. However, I could not login to the web portal. The default IP range did not match what was on the label nor did any of the credentials work. Upon further review I discovered that the firmware had been customized for the ISP so that only the ISP could login to the management portal via the WAN interface.

ZyXel does not publish the router firmware update binaries anymore via their website. However, I noticed it was using OpenWRT which uses the GPL license so I submitted a form and a few days later received the firmware source code and build instructions. Further review of the code showed that an autogenerated password must have been in use. (Update): I was successful in obtaining a firmware dump and getting /etc/config/account; however the password that worked for supervisor login was not based on autogenpassword with my serial number or MAC address. Instead it was a 10 character password with a-z0-9 alphabet. Reviewing the source some more I noticed that autogenpassword was only called when the service was "stopped" meaning that only on a clean shutdown command. Since I only ever flipped the power switch to off that code never executed so a different default password was used and never changed.

Since I don't have access to the autogenerated password I decided to review the firmware and nand itself for some clues. I've put my notes at https://github.com/rbeede/ZyXel_EMG2926-Q10A.

Opening the device was not too bad with 2 screws and three plastic clips (which I managed to not break!).

UART (usb to ttl) PIN configuration is same as nbg6716. Don't use the 3.3V pwr pin just power the device with its own AC adapter.

Using a USB to TTL and Linux with Minicom I could follow the boot sequence. 115200 with 8N1 and hardware flow control OFF. You might find it useful to capture all you do with minicom -C capturefile

AAVK-EMG2926Q10A> ATSH
ZLD   Version          : 2.21 (May 25 2015 - 16:22:06)
Bootbase Version       : U-Boot 2009.11 (May 25 2015 - 15:53:53)
Firmware Version       : V1.00(AAVK.7)C0
Product Model          : AAVK-EMG2926Q10A
Serial Number          : S############
First MAC Address      : 5###########
Default Country Code   : FF
Header      Checksum   : 0000d561
RootFS      Checksum   : 00005ae9
Boot Module Debug Flag : 00
Boot Block Write Enable: no
HTP Mode Enable        : no

It has a 16MB flash with uBoot and their custom zLoader followed by crippled uBoot load sequence. They added a password requirement to get to the more featured uBoot. I found threads on the internet about zynpass.c which proved very useful. Also extremely helpful was https://blog.incloudus.com/2017/flasher-routeur-zyxel-emg2926-q10a-de-chez-videotron/ which hints that the EMG2926 is just a nbg6716. Looking at the vendor provided source code seems to confirm this so using openwrt-18.06.2-ar71xx-nand-nbg6716-squashfs-factory.bin looks promising for going to a stock firmware.

AAVK-EMG2926Q10A> ATSE AAVK-EMG2926Q10A

The number result above would change on each boot, but other models apparently just seed from 0's.

zynpass seed from prior

ATEN 1,passcode from zynpass

ATGU

You should now be in a more normal looking uboot. However, it does not have the md or some other useful commands. You can use it as a tftpclient to download images into memory. In theory you could build a MIPS big-endian newer version of uBoot, tftpboot it into memory, and execute it.

printenv - You'll want to run this command and backup all your settings. Helpful for understanding how it boots the OEM firmware and other settings for tftp (like the 2 static IPs expected).

So I needed a way to backup the current nand (128MiB) with its entire JFFS2 filesystem. The only option I had in the OEM uBoot was the "nand read" command. You can only read 2KiB at a time so I wrote a script. This was a Perl script that created a minicom script, but I found that reading via serial was not 100% reliable (first time I got lucky). So I wrote a Python script instead that will do multiple retries to read a page.

dump_nand.py

When you run the script I assumed you already reached the ATGU command prompt of AAVK-EMG2926Q10A#

This should give you a 128MiB raw image of bytes. It will take about 50 hours to complete!

I tried to mount this in my Linux distro, even doing endian conversion with: jffs2dump ./raw-image.bin -r -b -e endian_conversion.jffs2

I was unsuccessful with mtdblock commands. It would mount, but my kernel did not understand the compression used.

Another important thing about JFFS2 is that it can store old versions of files that do not appear in a mount. So I used a different tool to just extract the image I converted to files.

sudo pip install cstruct
# you may need cstruct=1.0

sudo apt-get install python-lzma

git clone https://github.com/sviehb/jefferson
cd jefferson

sudo python setup.py install

jefferson endian_conversion.jffs2

It took quite a while to extract all the image data. I found that my first nand dump gave me many files, but subsequent nand dumps resulted in IOError due to some corruption in the nand. I had to modify the jefferson script (see GitHub for copy) to ignore the errors and keep dumping. So my results are likely incomplete. It took about 2 days for the dump script to complete.

When the extract finished I discovered I had multiple versions of files like ./fs_134/etc/config/account

Some versions had

config account 'supervisor'
	option privilege '1'
	option default '1'
	option username 'U2FsdGVkX19SPvOrleGJDk+T7eQ0tyfwIQVvKiBILX4='
	option password 'U2FsdGVkX19cfSe/dUaL5O9UW9rCEudvLGt32qOjKYI='

config account 'admin'
	option privilege '2'
	option default '1'
	option username 'U2FsdGVkX1+neyorvt4ISdpJy93riIC3'
	option password 'U2FsdGVkX1/1p1KdOxpu2yx0aEUc5+yuIuT2q+p2O1s='

while others had

#privilege 1 > 2
config general general
	option defaultName root
	option maxNumber 5
	option encrypt 0

config account supervisor
	option privilege 1
	option default 1
	option username supervisor
	option password supervisor

config account admin
	option privilege 2
	option default 1
	option username admin
	option password 1234

Yet another reset, reboot, reboot, and dump gave me:

supervisor password encrypted

U2FsdGVkX19cfSe/dUaL5O9UW9rCEudvLGt32qOjKYI=

I tried to decrypt this, but openssl bf just returned errors.
Possibly the password was changed for deployment versus what is in source.

This discovery led to me noticing that their factory run of the ISP firmware included old revisions from previous dev effort. I found decryption passwords in the source code but it did not match the encrypted strings I found in the old revisions of account. It seems to indicate a dev iteration of testing.

Update:

I performed a device reset and power cycled it a few times (after a complete boot). I performed another dump and was able to get the actual supervisor password from /etc/config/account in clear-text.

The autogenpassword routine

/etc/init.d/password is responsible for changing the password during the device ordered shutdown. Each time it sets the password as per the source.

So this appears to be based on the /etc/config/account contents (users supervisor and admin).

The password is assigned based on the device serialnumber (get_fwpwd) and the last 5 upper-case characters of the primary ethernet (WAN) MAC address.

/usr/sbin/autogenpassword is called to generate a password based on the device serial number from the flash (uboot, printenv) stored configuration data.

I have yet to work out how to use autogenpassword since it is big-endian for a MIPS architecture. I did look at dumping the assembly, but it is quite long and difficult to understand.

One idea I had was to reboot the router multiple times so /etc/config/account is updated by the init scripts and then try another nand dump. My first dump test was right after a clean reset (hardware button) before a boot.

I could also just get rid of the OEM firmware and install a standard build of OpenWRT instead. I'm not sure if I would need to update the 16MiB flash area with a new uBoot or zloader first though.

Successful Entry

I was successful in getting a telnet shell on the box. I reset the device and powered it on with no WAN connection. A few reboots and 4 days (dump flash retries) later I was able to get /etc/config/account and login.

I made a backup of all the mtd partitions and ran fw_printenv to get a different view for backup. Some interesting notes:

  • tr069 (see GitHub) had URLs used for the ISP to manage settings remotely on the router
  • Remote telnet over the WAN was enabled (likely for ISP to manage settings)
  • autogenpassword is not used unless you happen to do a clean poweroff/reboot
  • /proc/mtd had multiple backup partitions
  • I also captured a tar-ball of the / while the system was running

Aside about the bootloader (ubtton, zloader) password

Reviewing known CVEs for ZyXel I ran across CVE-2018-9149. It claims (3/11/2019) a CVSS score of 9.8, but it talks about the UART (ttl serial) port not being protected. I disagree with this CVSS score as it claims network exploitable, but clearly requires physical access which pulls the score to 6.8. I sent a note to NVD (3/14/2019), and they have updated the CVE score to reflect the AV:P.

I feel that "researcher" published and chosen CVE scores like this are misleading and harmful for both users and vendors. It generates unfair noise for the vendor that takes away from attention or consideration of other legitimate vulnerabilities.

I don't know why the vendor added a special uBoot bootloader and passcode. I think it just adds to the complexity of this consumer router. I'd recommend dropping it for a more plain uBoot with the latest features. Kudos to the vendor for also providing the UART 4 pins instead of requiring soldering. It makes tinkering by those who like to do so (and not expect a warranty) much more fun.

Flashing Standard OpenWRT

Follow instructions for nbg6716. I recommend starting with OpenWrt Chaos Calmer 15.05.1 when you mtd write /dev/mtd7 then upgrade if you need. Login first via web interface to enable ssh.

Concluding Thoughts

I'm most excited about flashing a standard generic OpenWRT to this device for real use. It is nice to see OpenWRT being used more as it makes it possible for consumers who want to use advanced features available. The portion of where it was locked down to only the ISP doesn't seem to be a security strength. I guess an ISP could push security patches, but it also opens another security hole. Basic changes like network name and password should be made available to the consumer instead of having to pay extra for ISP support of simple things.

I thought about generating a rainbow table for autogenpassword as well, but the serial number combinations would make it over 1TB in size.

crunch 13 13 1234567890 -t S@@@A@@@@@@@@ -o /tmp/zyxel_wordlist
Crunch will now generate the following amount of data: 1400000000000 bytes
1335144 MB
1303 GB
1 TB
0 PB
Crunch will now generate the following number of lines: 100000000000