Tinkering with ISP autogenpassword ZyXel EMG2926-Q10A wifi router

Last Modified: Mon, 18 Mar 2019 13:45:06 +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 fiber 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.

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!).

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 to generate a minicom script.

stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb

minicom -8 -b 115200 -D /dev/ttyUSB0 -C capture_minicom.txt -S script-dump-nand.txt

dump-script should start with
timeout 999999

After that add all the output from this script:

for( my $i = 0; $i < 0x8000000; $i = $i + 0x800 ){
	print 'send "nand dump ';

	printf("%lX", $i);
	print '"' . "\n";

	print 'expect {' . "\n";
        print '	"AAVK-EMG2926Q10A#"' . "\n";
	print '}' . "\n";
	print "\n";
}

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

This should result in a text file dump around 437MB in size. You need to parse out only the hex codes of the filesystem with commands like:

grep -Po '^\t([a-z0-9]{2}\s){8}\s.+' firmware.dump > grepped_firmware.dump

sed -i $'s/^\t//g' grepped_firmware.dump

xxd -r -p grepped_firmware.dump raw-image.bin

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 extra the image I converted to binary.

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.

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

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.

The autogenpassword routine

/etc/init.d/password is responsible for changing the password during the device boot. 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.

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.

Aside about the bootloader 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.