Category Archives: Embedded

CVE-2017-9457: CompuLab Intense PC lacks firmware signature validation

Summary
CompuLab have not enabled signature checking of firmware updates for the Intense PC product line. This allows anyone in possession of the Phoenix UEFI update program to write a modified UEFI firmware to system flash. DOS/Windows versions of the Phoenix utility are easily obtained online, allowing a local or remote attacker to install a persistent firmware level rootkit to the computer, or to corrupt the system firmware, causing a denial of service.

Installation of a modified firmware can occur entirely in the background, without any user interaction, and once performed is virtually impossible to difficult to detect using operating system utilities. Physical access is not required.

Product description
The CompuLab Intense PC is fanless mini-PC. A model pre-installed with Linux Mint is also marketed under the name MintBox 2. The system firmware is the same for the Intense PC and MintBox 2. CompuLab also sell the Intense PC with an extended temperature range for industrial applications.

The product was introduced in mid-2013 and is still being sold through Amazon US, Amazon Canada, Amazon Germany, Amazon Spain, and directly from CompuLab.

Affected products

  • Intense PC (Intense PC Value, Intense PC Business, Intense PC Pro)
  • MintBox 2

Impact
Any software running with local administrator privileges has unrestricted access to read and write the system’s firmware.

An attacker can modify the contents of the system firmware to install a persistent rootkit/bootkit, or to corrupt the firmware causing the computer to cease functioning.

The attack only requires local administrator privileges, and can be executed either by using an existing OS-level exploit to gain local administrator, or via tricking the user into running an executable (e.g. via an attachment in a phishing email).

Proof of Concept
The proof of concept provided for CVE-2017-8083 can be leveraged for this vulnerability as well. The proof of concept uses the Phoenix UEFI Winflash utility to write a modified firmware to flash. Please refer to the article about CVE-2017-8083 for a detailed description of the proof of concept.

The latest CompuLab firmware for the Intense PC (20170521) modified with the upstream EDKII shell can be downloaded here.

Mitigation
At this time there is no means for the end user to enable Capsule Signature verification or to prevent the Phoenix update utility from updating the system firmware.

Therefore Intense PC owners should consider the following options:

  • Ensure your operating system is up to date with the latest security patches. Do not run software from untrusted sources.
  • Do not connect your Intense PC to any networks with internet access (i.e. air-gap the computer).
  • Discontinue your use of the Intense PC and consider replacing the computer with one from a different manufacturer who implements signature validation for firmware updates.

Should CompuLab decide to improve the security of the Intense PC firmware by enabling Capsule Signature validation, then the above recommendations would no longer apply. However, in my communication with CompuLab regarding this issue no indication was given that they have any plans to enable Capsule Signature verification in a future update. Therefore, it seems very unlikely to me CompuLab will issue an update which enables Capsule Signature verification.

Disclosure timeline:
6 June 2017: Issue reported to CompuLab
6 June 2017: CompuLab confirms that “Default settings of this source tree [Phoenix SecureCore Tiano Enhanced Intel Ivy Bridge CPU Panther Point M] has disabled Capsule Signature option.”
6 June 2017: Issue is reported to MITRE
6 June 2017: Vulnerability is assigned CVE-2017-9457
7 June 2017: CompuLab are informed that the vulnerability has been assigned CVE-2017-9457 and details of the vulnerability will be published after 45 days

CVE-2017-8083: Intense PC lacks BIOS Write Protection

Summary
CompuLab Intense PC and MintBox 2 fail to properly write protect flash regions, allowing an attacker with local administrator privileges to write arbitrary code to the platform firmware. This could allow a remote attacker to install a persistent firmware level rootkit to the computer, or to erase the system firmware, causing a denial of service.

Installation of a modified firmware can occur entirely in the background, without any user interaction, and once performed is virtually impossible to difficult to detect using operating system utilities. Physical access is not required.

Product description
The CompuLab Intense PC is fanless mini-PC. A model pre-installed with Linux Mint is also marketed under the name MintBox 2. The system firmware is the same for the Intense PC and MintBox 2. CompuLab also sell the Intense PC with an extended temperature range for industrial applications.

The product was introduced in mid-2013 and is still being sold through Amazon US, Amazon Canada, Amazon Germany, Amazon Spain, and directly from CompuLab.

Affected products

  • Intense PC (Intense PC Value, Intense PC Business, Intense PC Pro)
  • MintBox 2

At the time of discovery in March 2017, the latest firmware for CompuLab was dated 21 June 2016, and did not enable write protection on any flash regions.

Impact
Any software running with local administrator privileges has unrestricted access to read and write the system’s firmware.

An attacker can modify the contents of the system firmware to install a persistent rootkit/bootkit, or to corrupt the firmware causing the computer to cease functioning.

The attack only requires local administrator privileges, and can be executed either by using an existing OS-level exploit to gain local administrator, or via tricking the user into running an executable (e.g. via an attachment in a phishing email).

Proof of Concept
The firmware update from CompuLab was downloaded, decompressed, and loaded into UEFITool.

The default UEFI shell provided in Phoenix SecureCore was replaced with a newer version of the UEFI shell from EDK2:

The Phoenix SecureCore UEFI Shell was replaced with the EDK2 UEFI Shell.

The modified update was then written to the system firmware using the Phoenix UEFI Winflash utility:

Phoenix UEFI Winflash

It was later realized that the Phoenix UEFI Winflash utility includes a flag enabling a silent firmware update from the command line:

Phoenix UEFI Winflash supports silently updating the firmware from the command line

Using the /remote2 option removes all visual notifications that a firmware update is in progress. Additionally, when used with /console or /remote2 options, the Winflash utility does not reboot the platform when finished. The system continues to function normally, and there is no indication to the user that a firmware update has taken place at all.

Additional information
Output of the chipsec utility:

python chipsec_main.py -m common.bios_wp
################################################################
## ##
## CHIPSEC: Platform Hardware Security Assessment Framework ##
## ##
################################################################
[CHIPSEC] Version 1.3.0
[CHIPSEC] Arguments: -m common.bios_wp

WARNING: *******************************************************************
WARNING: Chipsec should only be used on test systems!
WARNING: It should not be installed/deployed on production end-user systems.
WARNING: See WARNING.txt
WARNING: *******************************************************************

[CHIPSEC] API mode: using CHIPSEC kernel module API
[CHIPSEC] OS : Windows 8.1 6.3.9600 AMD64
[CHIPSEC] Platform: Mobile 3rd Generation Core Processor (Ivy Bridge CPU / Panth
er Point PCH)
[CHIPSEC] VID: 8086
[CHIPSEC] DID: 0154

[+] loaded chipsec.modules.common.bios_wp
[*] running loaded modules ..

[*] running module: chipsec.modules.common.bios_wp
[x][ =======================================================================
[x][ Module: BIOS Region Write Protection
[x][ =======================================================================
[*] BC = 0x08 << BIOS Control (b:d.f 00:31.0 + 0xDC)
[00] BIOSWE = 0 << BIOS Write Enable
[01] BLE = 0 << BIOS Lock Enable
[02] SRC = 2 << SPI Read Configuration
[04] TSS = 0 << Top Swap Status
[05] SMM_BWP = 0 << SMM BIOS Write Protection
[-] BIOS region write protection is disabled!

[*] BIOS Region: Base = 0x00D00000, Limit = 0x00FFFFFF
SPI Protected Ranges
————————————————————
PRx (offset) | Value | Base | Limit | WP? | RP?
————————————————————
PR0 (74) | 00000000 | 00000000 | 00000000 | 0 | 0
PR1 (78) | 00000000 | 00000000 | 00000000 | 0 | 0
PR2 (7C) | 00000000 | 00000000 | 00000000 | 0 | 0
PR3 (80) | 00000000 | 00000000 | 00000000 | 0 | 0
PR4 (84) | 00000000 | 00000000 | 00000000 | 0 | 0

[!] None of the SPI protected ranges write-protect BIOS region

[!] BIOS should enable all available SMM based write protection mechanisms or co
nfigure SPI protected ranges to protect the entire BIOS region
[-] FAILED: BIOS is NOT protected completely

Output of the Intel Flash Programming Tool (FPT):

Intel’s fpt utility showing full write access to flash regions on the Intense PC

Through my discussion with CompuLab support, it has emerged that the issue is due to CompuLab not running CloseMnf prior to shipping. CloseMnf stands for “Close of Manufacturing” and hardens the system by setting write-protect flags for the various flash regions in the Master Access Section of the Descriptor Region.

Intel documentation regarding CloseMnf:

Disclosure timeline:
1 March 2017: Vulnerability is reported to CompuLab via their support email address
2 March 2017: CompuLab replies they will create a beta BIOS to address the vulnerability
6 March 2017: I request a timeline to fix the issue
7 March 2017: CompuLab replies they will create a beta BIOS for testing and they “will provide an official public release in the future”
8 March 2017: CompuLab replies with instructions to run closemnf via the Intel FPT tool
8 March 2017: I inform CompuLab I am waiting for the official BIOS update to resolve the issue
8 March 2017: CompuLab replies with copy of Intel FPT tool and requests “not to publish or disclose this information”
8 March 2017: CompuLab is informed that details of the vulnerability will be published on 4 June 2017
23 April 2017: Issue is reported to MITRE
24 April 2017: Vulnerability is assigned CVE-2017-8083
3 May 2017: CompuLab communicates that they will delay fixing this vulnerability until Intel provides an updated ME firmware to address CVE-2017-5689
4 May 2017: I inform CompuLab that details of this vulnerability will be published on 4 June 2017 as previously discussed
11 May 2017: CompuLab sends a proposed fix for testing, the update script fails due to invalid command syntax for flashrom
14 May 2017: I inform CompuLab of the invalid syntax and provide the correct usage, and confirm that the fix enables write-protection on the ME/BIOS/GbE regions of flash
15 May 2017: CompuLab replies with a revised update script
15 May 2017: I inform CompuLab that the syntax of the revised script is correct, however my unit has already been updated so I cannot re-test
4 June 2017: Details of the vulnerability are published.

CompuLab have provided an update to address the issue.

I can confirm that the Phoenix update utility still functions so it is still possible to update the BIOS even after the FDR has been locked.

Disabling Secure Boot on Intel Quark “secure SKU” silicon

Secure Boot is a bit like SELinux: people who use it really like it, and tell all their friends to use it. For everyone else, apart from those who don’t know about or even notice Secure Boot, it’s an annoyance that they almost immediately disable.

We’ve looked at the Intel DK200 from a hardware perspective before. Now it’s time to look at it from a software perspective. “Internet of Things Gateway” is pretty generic, so what can it actually do?

Following the instructions, I tried to register the system on Intel’s website so I could download the Wind River Intelligent Device Platform XT 2.0 SDK. I didn’t get very far:

No WindRiver SDK for you

Stormtrooper #1: This is not the product you’re looking for

Yeah… I guess this is what Mouser meant when they said the DK200 was End of Life.

Since this ships with the Linux Kernel, which is GPLv2 licensed, I believe Intel may be violating the GPL. Specifically:

Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange

But I am not a lawyer, and I am not really that interested in starting a legal battle over the source code for an ancient version of Wind River Linux I am not interested in using anyway.

So let’s go try to build Yocto. The Intel rep did say there was a Yocto BSP coming “soon” but “soon” in Intel time seems kind of variable.

After some hiccups (Yocto needs python2 and GCC <6) I had built a Yocto image and put it on an SD card. Does it boot?

...no

…no

So we can’t boot Yocto because this is a “secure SKU” which means Secure Boot is enabled. Is there some way we can disable Secure Boot? What about updating the BSP to a newer version with Secure Boot disabled?

Back to hardware
If I’ve learned anything from messing around with electronics, you want to make a backup before you start modifying things. This is doubly so if the data in question is related to the booting process. It sucks to end up with a brick, so make a backup!

Taking a backup of flash

Taking a backup of flash

The Intel Quark guide mentions using a Dediprog SF100 to flash EDKII. I don’t have a Dediprog, but I do have an SPI programmer. Unfortunately, none of the Intel documentation I could find mentions the Dediprog header on the DK200, so I had to go hunting.

I traced the pins from the Winbond flash to header J23. J23 is only 8 pins, so trial and error with a multimeter to find the pin mapping wasn’t terrible:

J23 pinout

J23 pinout

Here’s the pinout of J23 in text form:

J23 pin 25Q64 pin Pin description
1 8 VCC
2 4 GND
3 1 /CS
4 6 CLK
5 2 DO
6 5 DI
7 Not connected
8 Not connected

/WP and HOLD pins on the 25Q64FV are not routed to J23, but they aren’t required for flashing.

With the pinout known, I could attach the SPI programmer to the header instead of using the chip clip:

J23 to ch341a SPI programmer

J23 to ch341a SPI programmer

I took a dump of the Winbond 25Q64FV and then for good measure desoldered the chip and read it again to confirm the images were exactly the same. It was strange because the image from the chip clip wasn’t identical. But, the image from the desoldered chip was identical to the image taken from J23, so we’re done here. I wrote the image to a new 25Q64FV and soldered that back onto the board.

Firmware disassembly
Disassembling the firmware which shipped on my DK200, we see that a Secure Boot certificate was created by WindRiver.

I assume that had I been able to download the WindRiver SDK, I would have been able to build and sign Secure Boot with my own certificates. Given that industrial customers spend a lot of time and money worrying about security, I was surprised to see that the Secure Boot certificate in the firmware was created by WindRiver China.

I did try to load up the image in IDA, but not being a power user of IDA, I couldn’t figure out how to get it to analyze the SPI dump, and gave up to try and compile the firmware from source.

Building the BSP

Being Intel, there are hundreds of pages you can read about developing for EDK2 and other really fun things, probably. I didn’t read them.

A document which I did end up reading religiously was the Intel ® QuarkTM SoC X1000 Board Support Package (BSP) Build and Software User Guide [PDF] which describes how to build all the firmware components needed to bring up the X1000 SoC. I found out there is actually a newer version of this document (1.2.1 instead of 1.1) and there are some important differences between the documents I want to get to later.

By building the firmware, we’re hoping for one of two outcomes:

  1. A firmware with our own Secure Boot certificates, or
  2. A firmware which has Secure Boot disabled

Version 1.1 of the BSP Build and Software User Guide includes a section on pages 29 and 30 on how to bundle your own db, kek, and pk certificates:

Page 29 and 30 condensed

Unfortunately if you follow the instructions and try to use a layout.conf which specifies these files, you’ll get an error because there’s no address specified for this data in the image:

I do have a reference file from WindRiver with Secure Boot certificates, so if I was really interested in making Secure Boot work as intended, I could have reverse engineered the address to store the certificates.

The certificates section of layout.conf was removed from the 1.2.1 revision of the BSP Build and Software User Guide. I guess since it no longer works, Intel decided to remove it from the documentation.

So, we can’t install our own Secure Boot certificates in the firmware. What happens if we just leave out the certificates section entirely and build it?

Error 37: Quark signature file not found

Right, so even though there’s now no certificate in the firmware bundle, we still can’t boot.

Interestingly, if you don’t partition the uSD or USB stick correctly, you end up with this pretty screen:

I never saw that in the stock firmware.

Hacking GRUB
So it seems that we can’t include our own Secure Boot certificate in the firmware, due to the sample layout.conf file missing the certificates section, and not knowing the appropriate address to store the certificates.

What if we dig into Error 37: Quark signature file not found a bit more?

If you look in the grub source code included in the BSP, you can see a giant ~1000KB patch that Intel has made to the original upstream code to support the Quark platform.

If you grep for “Quark signature file not found” you’ll find it was added in stage2/common.c:
diff --git a/stage2/common.c b/stage2/common.c
index e96bec2..e122745 100644
--- a/stage2/common.c
+++ b/stage2/common.c
@@ -88,6 +88,8 @@ char *err_list[] =
[ERR_UNRECOGNIZED] = "Unrecognized command",
[ERR_WONT_FIT] = "Selected item cannot fit into memory",
[ERR_WRITE] = "Disk write error",
+ [ERR_QUARK_VERIFICATION] = "Quark signature verification failed",
+ [ERR_SGN_FILE_NOT_FOUND] = "Quark signature file not found",
};

If you grep for ERR_SGN_FILE_NOT_FOUND you’ll find it’s in the following files:
./work/efi/ia32/loader/linux.c:410: errnum = ERR_SGN_FILE_NOT_FOUND;
./work/efi/ia32/loader/linux.c:732: errnum = ERR_SGN_FILE_NOT_FOUND;
./work/efi/quark/boot_settings.c:190: errnum = ERR_SGN_FILE_NOT_FOUND;

Going back to Intel’s modifications to grub, we can see what they added:

It takes a bit of searching, but if you strip out all of the grub_quark_secure logic from linux.c and boot_settings.c, you end up with…

Ta-da! I can boot Yocto Linux

No more Secure Boot!

At the end of the day, the Quark X1000 is an x86: “secure SKU” is nothing but a fuse setting.

The comment should read:

Determine whether or not grub should enforce Secure Boot.

In our case, this is not a mandatory option 😉

Special offer for DK200 owners
As shown above, it is possible to modify the Intel sources to disable Secure Boot. If there are other people have a DK200 from Intel and are interested in running a firmware without Secure Boot, leave a comment with your contact details. Upon request, I can provide a firmware image* with generic Ethernet MAC addresses for you to flash. Note that this firmware is specific to the DK200 (Clanton Hill) hardware.

* No warranty, express or implied, provided for said firmware image. You flash at your own risk!

Reassembling a firmware from pieces

Working on firmware is always interesting. Modern x86 computers are incredibly complicated, due to the evolution of the architecture over the last 40 years, and it’s difficult to debug issues past “Well it doesn’t POST, better try something else.”

Unlike most ARM/MIPS systems, where you have a UART console or something to see output from u-boot, if you mess up the firmware on an x86, you’ll have a non-communicative brick on your hands.

Of course you can also have firmware issues on ARM/MIPS, if you manage to corrupt u-boot on SPI flash, but since u-boot is open and not proprietary it’s easy to rebuild it from source and flash it again to recover.

Not so in the PC world. UEFI is horrendously complex compared to u-boot, and Intel’s reference implementation known as TianoCore is usually “improved” by several middlemen before going into the final product.

Trammel Hudson’s excellent talk from 33c3 (slides) on Bootstrapping a slightly more secure laptop highlights the situation with UEFI quite well:

Farm to table firmware

In this case, we’ve got TianoCore from Intel, insyde adds some magic fairy dust, this goes to Compal (the ODM of this laptop), and finally Dell (who would probably also claim to add magic fairy dust). In the end there’s a lot of proprietary fairy dust floating around, and we probably wouldn’t be able to boot the laptop if we just build TianoCore from source.

TianoCore itself is open source and BSD licensed, which is why all the vendors use it. Intel manages porting TianoCore to their new platforms, and since it’s BSD licensed, it means that someone like insyde can take the working base from Intel and add their proprietary fairy dust without having to release the modified source code.

SPI flash
The start of flash contains a region called the Flash Descriptor (PDF; page 3) which is programmed at system manufacture and tells the system where different firmware components are present on flash. Think of it as a partition table for the system flash. Under normal circumstances, the flash descriptor prevents the user from reading and/or writing portions of ROM. If you try to use tools to read or write the ME regions of flash, you’ll get a error similar to this:

Error 26: The host CPU does not have read access to the target flash area. To en
able read access for this operation you must modify the descriptor settings to g
ive host access to this region.

And this makes sense. If the system allowed unrestricted write access it would be trivial for some malware to write itself into the system firmware, and then you’d have a persistent rootkit. In my opinion, blocking the ability to read portions of the firmware serves no purpose except to discourage reverse engineering attempts.

Thankfully, there is a method known as Flash Descriptor Security Override Strap which can be used to disable the flash descriptor protection.

The first step is to locate the ME_FWP pin in the circuit diagram:
Override Strap

Now that we’ve located the pin on the logical diagram, we need to find the HDA chip itself so we can see which pins we need to bridge to disable the flash descriptor lock on the ME region:
la-7731p_sda

In this case, it’s most convenient for us to short pins 5 and 9:
la-7731p_hda

You could also short pins 1 and 5, but this requires a very steady hand and small instrument. However pins 5 and 9 are connected to surface mount components located away from the chip. These components (a capacitor and a pad) are much easier to access with a wire:
la_7731p_pin5_pin9

Amazingly, this area can be accessed without completely disassembling the laptop. Just taking off the palm rest and keyboard, which is about 8 screws, is enough to access the pins. They’re right under the LVDS cable to the display. Thanks, Compal!

At this point, I have two options:

  1. Find or buy an SPI dump online and flash that
  2. Find a way to dump the firmware from the working laptop without soldering

As you read in the previous post, I only managed to find an E6320 SPI dump online, and ended up with a laptop that worked-ish. I tried for many hours, but I wasn’t able to find any free SPI dumps for the E6230 online. After seeing many forums promising to sell you the firmware for outrageous prices, I finally found one that wanted only 8 złoty ($2) to download the SPI dump for the E6230. Principles be damned, I’ve wasted enough time trying to get this to work. I paid up and downloaded the files for the 4MB and 8MB chips.

Did I really get a valid firmware for $2? Yes!

But it’s BIOS A12, the current version is A16, but none of the Dell update utilities work! I installed Windows to try the update tool from Dell, but the laptop just rebooted without updating the firmware. I tried from FreeDOS and again, the laptop would just reboot when it got to flashing. Hrmm…

So, at this point I’m going to cheat a little: I have a working E6230, but I decided when I started this that I would not touch it with a soldering iron or heat gun. If it ain’t broke, don’t fix it!

Can I get a full firmware dump from the working E6230?

Intel FPT

Intel FPT is a proprietary command-line utility created by Intel for flashing firmware files through the computer’s internal SPI flashing interface.

Alright, let’s go dump the firmware! First you need to remember to apply the Flash Descriptor Security Override Strap, or the CPU will block your read attempt to the ME region.

However, once we’ve done that, FPT will allow us to dump regions of flash to a file.

At first I tried to dump just the ME and BIOS, using the A12 firmware from above, I reflashed these regions using the FPT utility from FreeDOS. The verification of the BIOS region failed, and when I rebooted I had a brick again.

But, the FPT tool lets you dump all the regions at once! I dumped the entire flash, but now we’ve got a 12MB file and I don’t know where the split is between the 8MB and 4MB flash chips.

After more searching, I found the Intel ME System Tools, including a utility called Flash Image Tool, which allows you to import the firmware image file created by FPT.

Well, the flash component density matches what’s in the laptop, so I guess this is correct.

Flash Image Tool also lets you build a new firmware image:

Writing ROM image file “C:\Users\hmartin\Documents\MESYS\Flash Image Tool\v8.1.10.1286\Build\outimage.bin”.
Writing file “C:\Users\hmartin\Documents\MESYS\Flash Image Tool\v8.1.10.1286\Build\outimage(1).bin” (size = 8388608)
Writing file “C:\Users\hmartin\Documents\MESYS\Flash Image Tool\v8.1.10.1286\Build\outimage(2).bin” (size = 4194304)
Writing MAP file “C:\Users\hmartin\Documents\MESYS\Flash Image Tool\v8.1.10.1286\Build\outimage.map”.

Image size = 0xC00000 bytes

Interesting… outimage(1).bin is exactly 8192KB, and outimage(2).bin is exactly 4096KB. I wrote these two images to the respective chips and put them into the laptop. The moment of truth had arrived.

It boots!

When you’re trying to work through firmware issues, it’s really helpful to have the flash chip in a socket. Soldering and desoldering gets really old when you have to do it more than a couple of times.

Thoughts on the sale of firmware images

I’m against the sale of these firmware images. I realize that it takes a non-zero amount of time to get the image, but the whole experience of BIOS forums just leaves you feeling dirty. You have no way to verify before payment that the files they’re providing even work. Combine this with the fact that most websites want $10-$20 for the SPI dump, the experience leaves a bad taste in your mouth.

Is it worth it to pay? It depends entirely on your willingness to pay. Is this your only computer? Do you need it working now? How much of your own time are you willing to invest to learn about UEFI firmware? I’ve probably put 20 hours into this project, and I still don’t understand the internals of UEFI.

The wrong way to clear a BIOS password

I was browsing on eBay and ran into a listing for a Dell E6230. By now you might guess where this is leading:

Dell Latitude E6230 12.5″ i3-2350M 2.3GHz 4GB RAM Admin Pass Set AS IS READ!

s-l1600

The whole laptop, everything working perfectly, except… it has a firmware password set, and the seller doesn’t know the password. Well no bother, it’s cheap (83 EUR), I know how to flash SPI chips. Should be a piece of cake to clear this password! I also had an E6230 motherboard with a Core i5-3320M which I had found on eBay for 40 EUR, so my plan was to swap the boards, and then work on clearing the password.

These boards contain two flash chips: Winbond 64Mbit (8MB, U52) and Winbond 32Mbit (4MB, U53) located to the left of the SIM card slot.

f_0020914

The first step was to dump the SPI flash from U52 and U53 on both boards as a backup before flashing anything. This seemingly went well, but when I flashed the i5-3320M firmware dumps to the board with the i3-2350M, I had a brick.

It turns out that several of the pins to each chip are connected to the same pins in the PCH. Important pins like Data Out, Clock, and Data In are shared:

PCH SPI wiring

U52 and U53 share data out, data in, and clock lines to the PCH

I’m not sure exactly what happened, but my guess is that by leaving the flash soldered to the board, I somehow managed to get data from both chips. Alarms should have been going off in my head when my SPI reader dumped 8MB from a 4MB chip.

Not wanting to risk wrecking the i5-3320M board by desoldering the flash, I turned to the internet and discovered the terrible world of BIOS image dumps. It’s nearly impossible to find any website offering SPI dumps for free. Of course you can download the firmware update tool from the manufacturer, but those are meant to be run only on the intended hardware, and they only contain regions of the SPI flash like the UEFI firmware and Intel Management Engine.

I was able to find an SPI dump of the E6320, which is a 13.3″ laptop one generation previous to the E6230 (12.5″). As I had nothing to lose, I desoldered U52 and U53 and flashed the E6320 images to each chip. To my great surprise, the board passed POST, albeit with a warning about unsupported hardware. I was even able to enter the UEFI setup utility.

But now I am running a firmware intended for a completely different laptop. The E6320 has a QM67 (6 series) chipset, and the E6320 has a QM77 (7 series) chipset. The USB 3 ports on the side don’t work, the internal SATA port doesn’t work, and PXE booting doesn’t work. The only port that seems to work, at all, is a Mini PCIe USB port:

mini_pcie-usb30-1_zps098e038b

My next thought was to try the Dell E6230 BIOS update tool, which can run from DOS. I put FreeDOS on a USB stick in the above adapter and installed that in the WWAN Mini PCIe slot. Unfortunately for me, Dell has put checks into the update utility to check that it’s running on the correct hardware. This makes total sense, if a user downloads the wrong update for their laptop, they shouldn’t end up with a brick.

However, it didn’t suit my purpose. I wanted Dell’s BIOS update utility to ignore the fact that it was running on an “E6320” and instead flash the firmware for the E6230, the actual hardware.

Having been foiled by Dell’s checks, I decided to load up the utility in IDA Pro to see if I could bypass the check. A bit of string searching and I found the target, a jnz:
ida_jnz

Changing this check to a jz and I tried again. This time the utility didn’t complain about the machine being an E6320, but as soon as the flashing process started, the laptop shut off. So what happened? My best guess is that Management Engine shut down the platform.

Management Engine
The Intel ME has existed since the mid-2000’s, and is now deeply integrated into all of Intel’s modern x86 SKUs. The ME can provide additional functionality like a TPM (implemented in firmware), cryptographic acceleration, DRM, as well as other patented and super duper proprietary stuff. There’s a fairly comprehensive feature list available on Wikipedia.

Since Intel doesn’t actually release documents on the ME, it’s hard to come by actual information on the inner workings. It’s also why some libre people are concerned about buying newer laptops: the ME is integrated into the PCH, cannot be disabled, runs an OS with direct memory access to system RAM and has never been audited.

Trammell Hudson is currently experimenting with coreboot on the Lenovo X230, and it seems like there’s a non-zero chance that he’ll succeed in disabling the ME.

Anyway, I was on vacation earlier this year and had lots of time to kill in airports/planes/trains so I read a book about the Intel ME called “Platform Embedded Security Technology Revealed” You can download the ebook for free as a PDF or EPUB from the publisher.

Using knowledge from the above book, we can conclude that the security number of the Series 8 ME firmware must be equal to the security number of Series 7 firmware, or the ME would not allow the platform to power on.

Unfortunately, the updater managed to overwrite something important in flash before the ME cut power, because now I’m stuck with a brick again.

Stay tuned for part 2!

Allwinner H2+/H3 Ethernet with Linux 4.9-rc8

The Orange Pi PC is not a new single board computer. It’s been released for over a year now, but has mostly been stuck on a heavily patched 3.4 release kernel.

There have been ongoing efforts since the release to have support for the Allwinner H3 in the mainline kernel. In the past weeks there have been new patches released which enable support for the Ethernet MAC on the H3 (and H2+).

Unfortunately this support is not in mainline yet, and won’t make it in the upcoming 4.9 release. However, that doesn’t stop you from taking the patches and applying them against 4.9 yourself.

I wrote a script to compile the kernel from source, applying the necessary patches to the kernel and using a minimal .config file which compiles the sun8i_emac support as a module. You can download the build script from GitHub.

It does try to be somewhat smart: verifying the integrity of the downloaded files, and will bail out if there are errors in patching the source code. But, it doesn’t do toolchain dependency checking because that’s just too complicated. Since the emac support will end up in mainline soon, I doubt it’s worth the time to improve the build script. However if anyone is interested in improving it, the script is released as GPLv2.

The result? If you are patient enough to wait for the kernel to compile, you get a uImage and modules for Linux 4.9-rc8 with Ethernet support:

Allwinner H3 emac performance:

[email protected]:~$ iperf -n 1024M -c 192.168.1.150
————————————————————
Client connecting to 192.168.1.150, TCP port 5001
TCP window size: 43.8 KByte (default)
————————————————————
[ 3] local 192.168.1.206 port 42572 connected with 192.168.1.150 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-100.6 sec 1.00 GBytes 85.4 Mbits/sec

Orange Pi Zero (Allwinner H2+):

I’ve also just received my Orange Pi Zero and confirmed that the same kernel works on the Orange Pi Zero, so you can run Linux 4.9 on the Orange Pi PC (Allwinner H3) or Orange Pi Zero (Allwinner H2+) with Ethernet support.

Allwinner H2+ emac performance:

[email protected]:~$ iperf -n 1024M -c 192.168.1.150
————————————————————
Client connecting to 192.168.1.150, TCP port 5001
TCP window size: 43.8 KByte (default)
————————————————————
[ 3] local 172.16.4.206 port 54762 connected with 192.168.1.150 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-95.9 sec 1.00 GBytes 89.5 Mbits/sec

Download:
If you want to skip compiling the kernel yourself, I’m providing the kernel uImage and modules here.

I am using u-boot v2016.11 from denx.

Note: HDMI isn’t working on my Orange Pi PC, and since I run it headless I’m not interested in investigating why. If you’re using your Orange Pi PC with HDMI output, you may need to modify the kernel .config file to fix HDMI.

Intel DK200 IoT Gateway

Earlier this year I was at a conference and heard from other attendees that the Intel booth was giving away IoT gateways. Never one to turn down free conference swag, I hurried over to the Intel booth and was told to pick up a gateway out of a pallet of boxes just delivered (and rapidly disappearing).

The Intel IoT gateway series is codenamed Moon Island, but the design targeting the transportation market is codenamed Clanton Hill. Clanton Hill known to us mortals as the “Intel DK200 Series Gateway Solution for the Internet of Things (IoT)” quite the mouthful.

Let’s get down to it.

Availability
Unless you happen to be at a conference where Intel reps are handing these out like candy, I don’t think it’s practical to try and buy one yourself:

dk200_mouser

Some interesting details to note about this product:

  1. Although released in 2014, the DK200 still costs more than the new MacBook (3,712.50 EUR versus 3,199 EUR)
  2. It’s End Of Life

When a low volume product goes EOL and you still have stock, I guess giving it away at conferences is the next logical step.

Hardware Specifications
The DK200 (datasheet) is targeted toward the transportation industry, and it really shows in the appearance of the device:

Only available in 'Cosmic Black'

Only available in ‘Cosmic Black’

I don’t work in the transportation industry, and have never seen connectors that look like this before. They’re very well made, and I suspect probably do a good job of keeping dust, dirt, and debris out of the ports. Since I don’t wish to make a mess by throwing dirt and debris at it, I’m going to have to trust the engineers who designed it.

The build quality is quite good, as one might expect from a device selling for 3,700 EUR. Nearly every screw is secured with loctite to prevent vibration from loosening them:

DK200 screw with loctite

No pentalobe nonsense here

However, I was surprised to find that despite all the physical hardening applied to the enclosure, I couldn’t find any information on an IP rating. In fact the top and bottom of the case don’t appear to offer any additional dust or water seal. There’s clearly been a lot of thought put into the design of this enclosure to withstand vibration and dirt, so it’s strange that there doesn’t seem to be water protection of any kind.

Processor
The Intel Quark series SoC was introduced in late 2013. The X1020D in the DK200 is a single core SoC based around a 80486 core running at 400MHz, with modern I/O and memory.

dk200_x1020d

In 2014 a leaked product roadmap suggested a successor to the X1000 series named “Dublin Bay” to be released in 2015. Then news emerged that “Dublin Bay” had been cancelled, to be replaced by “Liffy Island” and “Seal Beach” which would be released in 2015. As of late 2016 Intel has not released a direct successor to the X1000 series, and there is no new news of “Liffy Island” or “Seal Beach” being cancelled (or released). So it’s anyone’s guess whether Intel is still even interested in the IoT gateway market.

Storage
The DK200 doesn’t include any of the typical storage buses like SATA, NVMe, or NAND (EMMC). This is not overly surprising given the embedded nature of the hardware (requiring lower power) and the simplicity of the Quark processor.

The only storage option is a micro SDHC card, and the DK200 includes an 8GB class 4 micro SD card:
dk200_sdhc

Given that it’s a class 4 card, the performance is quite poor. Use of an SD card for storage isn’t a bad decision per se, but the DK200 uses ext3 for the root partition. Ext3 is not a flash aware filesystem. SD cards have only basic wear leveling, and ext3 has no wear leveling. So it hardly seems like the appropriate combination of storage and filesystem for a headless embedded device with an expected lifetime of 5-10 years.

Input and output

  • Dual 100Mbit Ethernet controllers
  • 3 x USB 2.0 host and 1x device
  • Audio in/out
  • CAN bus
  • RS-232
  • GPIO
  • 1x half-height mini PCI-e slot (populated with Intel 7260)
  • 1x full-height mini PCI-e slot (unpopulated; for 3G modem/GPS)

The Intel documentation also mentions ZigBee, however this is an external device, presumably attached via the USB bus.

Power consumption
Development platforms aren’t known for being highly optimised devices. They often include extra I/O which would not necesssarily be included in the final product, and as such do not have the same energy efficiency as a finished product.

This being said, I was quite surprised that a device intended for 24/7 operation in an embedded environment, and especially serving the “Internet of Things” market, could be so energy inefficient. Issuing a poweroff command in Linux results in the platform going into an S5 (shutdown) state. I was surprised to discover that the energy consumption in the S5 state is 2W. This seems quite high for a device which includes an ignition input for automatic power-on and shutdown.

When booting, the device peaks at 7.9W consumption, while the idle power consumption is 7.5W. This is almost certainly due to the added peripherals as the TDP of the Quark processor is only 2W.

It’s difficult to see how Intel expects the Quark platform to compete with ARM. My PandaBoard ES, an ARM-based development board from 2011, peaks at 4W, idles at 2W, and draws nothing when off. Now some might argue that comparing an ARM board from 2011 with an Intel IoT gateway from 2014 isn’t valid, but they do have a lot of similar features. Now, I will grant that the PandaBoard is not in a rugged enclosure with fancy connectors, but since it cost 95% less than the DK200 does, there’s some room in the budget for an enclosure and funky connectors. And, since Texas Instruments has stopped making OMAP chips, the PandaBoard gets about the same amount of vendor support as the DK200!

Software

I will be exploring the software of the DK200 in a follow up post. Stay tuned!

Linux 4.5 on a Bay Trail tablet

This post is a short update to my original article on booting Arch Linux on a Bay Trail tablet.

I originally wrote this for 4.4.5, but I wasn’t fast enough, and 4.5 was released before the post was completed, so might as well continue with a 4.5 kernel.

To simplify the build process I took the PKGBUILD for linux-mainline in AUR and modified it to build a mainline kernel with patches for SDIO WiFi on BayTrail.

If you’d like to build the kernel yourself (and you happen to run Arch Linux) you can download the PKGBUILD.

The firmware for the rtl8723bs card is in its own package, in keeping with the Arch Linux best practices for separating firmware from the kernel package. Download the firmware PKGBUILD.

Or, if you’d rather just have a newer kernel on your tablet which is already running Arch Linux, you can download the pre-built kernel package, and the firmware package.

I will be submitting both of these packages to AUR shortly.

Turns out you can actually get GRUB working with a menu if you build a standalone version of grub. However, the issue is that even though the grub menu works, there’s some issue with modesetting and you’ll never see any console after grub hands off to the kernel. You can download the standalone version of grub if you want to try, I wasn’t able to get any usable installer environment out of it. You can download standalone grub for ia32 (i686), you will also need grub.cfg.

$ tar -Jxf bootia32.tar.xz
$ cp bootia32.efi /mnt/archiso/EFI/boot/bootia32.efi
$ cp grub.cfg /mnt/archiso/EFI/boot/grub.cfg

Since grub video handoff isn’t working well, the only way I was able to successfully boot was to drop to command line by pressing c at the menu, and typing the following:

set root=hd0,msdos1
linux /arch/boot/x86_64/vmlinuz archisobasedir=arch archisolabel=ARCH_201603 video=VGA-1:[email protected]
initrd /arch/boot/x86_64/archiso.img
boot

There is a small issue with kernel oops, which has been present since at least 4.4.5:

[  164.281827] NMI watchdog: Watchdog detected hard LOCKUP on cpu 2
[  164.281913] Modules linked in:
[  164.281962]  intel_rapl intel_soc_dts_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel aes_x86_64 lrw iTCO_wdt snd_soc_sst_bytcr_rt5640 iTCO_vendor_support hid_multitouch gf128mul glue_helper dcdbas ablk_helper cryptd pcspkr hci_uart snd_intel_sst_acpi mei_txe joydev input_leds snd_intel_sst_core btbcm snd_soc_rt5640 evdev snd_soc_sst_mfld_platform mousedev btintel mei lpc_ich mac_hid snd_soc_rl6231 thermal snd_soc_sst_match dw_dmac dw_dmac_core tpm_crb snd_soc_core bluetooth processor_thermal_device int3400_thermal int3403_thermal acpi_thermal_rel int3402_thermal i2c_hid int340x_thermal_zone snd_compress intel_soc_dts_iosf tpm_tis rfkill_gpio snd_pcm_dmaengine battery ac97_bus ac spi_pxa2xx_platform crc16 tpm snd_pcm i2c_designware_platform
[  164.283185]  acpi_pad i2c_designware_core 8250_dw snd_timer snd processor soundcore sch_fq_codel nfs lockd grace sunrpc fscache ip_tables x_tables overlay squashfs loop nls_iso8859_1 nls_cp437 vfat fat sd_mod uas usb_storage scsi_mod hid_generic usbhid hid i915 mmc_block button i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops crc32c_intel xhci_pci drm xhci_hcd intel_gtt wmi serio video sdhci_acpi sdhci led_class r8723bs(O) cfg80211 rfkill mmc_core
[  164.283978] CPU: 2 PID: 0 Comm: swapper/2 Tainted: G           O    4.5.0-byt #1
[  164.284073] Hardware name: Dell Inc. Venue 8 Pro 3845/XXXXXX, BIOS A02 12/29/2014
[  164.284169]  0000000000000086 9ad5a4512f59852f ffff880039d05b50 ffffffff812d25d1
[  164.284284]  0000000000000000 0000000000000000 ffff880039d05b68 ffffffff81116550
[  164.284399]  ffff880038ca8000 ffff880039d05ba0 ffffffff81156b4c 0000000000000001
[  164.284513] Call Trace:
[  164.284552]    [] dump_stack+0x63/0x82
[  164.284645]  [] watchdog_overflow_callback+0xe0/0xf0
[  164.284733]  [] __perf_event_overflow+0x8c/0x1d0
[  164.284815]  [] perf_event_overflow+0x14/0x20
[  164.284894]  [] intel_pmu_handle_irq+0x1e1/0x460
[  164.284980]  [] perf_event_nmi_handler+0x28/0x50
[  164.285062]  [] nmi_handle+0x5e/0x130
[  164.285133]  [] default_do_nmi+0x48/0x120
[  164.285207]  [] do_nmi+0xe2/0x130
[  164.285274]  [] end_repeat_nmi+0x1a/0x1e
[  164.285349]  [] ? poll_idle+0x39/0x80
[  164.285420]  [] ? poll_idle+0x39/0x80
[  164.285490]  [] ? poll_idle+0x39/0x80
[  164.285558]  <>  [] cpuidle_enter_state+0xf3/0x2f0
[  164.285655]  [] cpuidle_enter+0x17/0x20
[  164.285728]  [] call_cpuidle+0x2a/0x40
[  164.285800]  [] cpu_startup_entry+0x2c5/0x3a0
[  164.285878]  [] start_secondary+0x165/0x1a0
[  164.285964] INFO: NMI handler (perf_event_nmi_handler) took too long to run: 4.144 msecs
[  164.286069] perf interrupt took too long (32852 > 2495), lowering kernel.perf_event_max_sample_rate to 50100
[  172.707012] perf interrupt took too long (32619 > 4960), lowering kernel.perf_event_max_sample_rate to 25200

You’ll see a lot of these, however WiFi still continues to work, and the tablet didn’t kernel panic for me in the installer environment.

Hopefully someone finds this useful. I’ll have a write up on installing and using Arch Linux on the tablet in the coming weeks.

D-Link DAP-1520 hacking: Part 2

In Part 1 we looked at the hardware of the DAP-1520 and did some investigation into the stock D-Link firmware that runs on the device.

We found that there were two firmware images on the device, the main firmware (Image 1) and the recovery OS (Image 2) which is used when Image 1 fails verification.

Despite D-Link’s reputation for buggy firmwares, my infosec skills are still basic, and I wasn’t able to get telnetd running on the DAP-1520 to investigate the firmware more. Sure, we already have a dump of the firmware thanks to an SPI reader (and the update, available from D-Link’s website), but this only tells us what’s in the firmware, it doesn’t actually let us poke around at the hardware. Since my stated goal is to get OpenWrt running on the device, poking around at the hardware with a working OS is pretty important.

To accomplish this, I needed to build the firmware from the GPL source code published by D-Link. You can download the GPL source code for their routers from their Taiwanese website.

DAP-1520 GPL source code

DAP-1520 GPL source code

GPL source code for each firmware release

GPL source code for each firmware release

If you’re wondering why there’s a huge difference in the size of the source code between firmware versions, don’t. Firmware 1.05, despite the file extension, is just a tar file and is 185MB. Firmware 1.06 is a gzip compressed tar file and is 186MB. These inconsistencies are just the start of our wonderful journey with the D-Link source 😉

I’m building firmware 1.06, since newer is always better. I’ve just noticed that D-Link have published a file for firmware 1.07 on their Australian website. Hopefully they will release the GPL source for this firmware soon, I’m excited to see what vulnerabilities have been addressed in the web configurator!

Anyway, when you download the GPL source code, you will find that D-Link has included a README file, which describes how to build the firmware. This surprised me, I wasn’t expecting anything more than the source code, so some minor kudos go to D-Link for at least providing instructions.

Install & Build
===============
Environment: 
    1. Download Ubuntu 10.04.4 LTS from http://releases.ubuntu.com/lucid/
        http://releases.ubuntu.com/lucid/ubuntu-10.04.4-server-i386.iso
    2. Install Ubuntu 10.04.4 LTS server in your computer.
    3. Make sure your Ubuntu is 10.04.4 LTS(Lucid Lynx).
    4. Building image with ROOT privileges.	
	
Install:
    1.  Please update the list of available packages:
       ~#apt-get update
	   ~#apt-get install gcc build-essential zlib1g-dev bison flex subversion sharutils libncurses5-dev gawk help2man intltool pkg-config libglib2.0-dev	  
    2. Create a folder in root directory 
       ~#mkdir /tftpboot/
    3. Install the toolchain:
		~#cd /DAP-1520_A1_106b04_FOSS/toolchain
		A.	GCC
				1.	cp buildroot-gcc342.tar.bz2 /opt
				2.	tar jxvf buildroot-gcc342.tar.bz2
		B.	LZMA
			    1.  cp lzma-4.32.7.tar.gz /home/
			    2.  tar -xvf lzma-4.32.7.tar.gz
				3.  cd lzma-4.32.7
				4.	./configure
				5.	make
				6.	make install
			    7.  ldconfig	
		C.	XZ
				1.	cp xz-5.0.3.tar.bz2 /home/
				2.	tar jxvf xz-5.0.3.tar.bz2
				3.  cd xz-5.0.3
				4.	./configure
				5.	make
				6.	make install
		D.	mksquashfs
				1.  cp squashfs4.2.tar.bz2 /home/
				2.	tar jxvf squashfs4.2.tar.bz2
				3.	cd squashfs4.2/squashfs-tools
				4.	make
				5.	cp mksquashfs /opt/buildroot-gcc342/bin/mksquashfs_lzma-4.2
	4. Building the image & loader.
		(1). Please make sure the gcc-version is greater than 4.2.4
			 (You can type "~#gcc -v " to check the gcc-version)
		(2). Copy the DAP1520A1_GPL106b04.tar into /home/ directory
		     use following commands.
			~#tar -xvf DAP1520A1_GPL106b04.tar
		(3). You will get "AthSDK" directory.
			~#cd /home/AthSDK	
		(4). Into the AthSDK directory,and run following commands.
			(4-1). If you want to build normal image
			~#make clean
			~#make kernel_clean
			~#make 
			After make successfully, under "AthSDK/image/", you will get the normal image file "DAP1520A1_FW106B04.bin".
			(4-2). If you want to build backup image
			~#make -f Makefile.backup clean
			~#make kernel_clean
			~#make -f Makefile.backup 
			After make successfully, under "AthSDK/image/", you will get the backup image file "DAP1520A1_FW100B03.bin".
			(4-3). If you want to build loader
			~#make loader_clean
			~#make mtk_loader
			After make successfully, under "AthSDK/image/", you will get the loader file "DAP1520A1_FW100.boot".
	5. Update the new firmware by web interface provided by device.
	6. Congratulations! You got your specific image now.

Install Ubuntu 10.04? Thanks, I think I will pass. Ubuntu 10.04 isn't supported anymore, so good luck installing all the packages you need to support the build environment. So, instead I decided to build the firmware on my laptop, which runs a reasonably current version of Arch Linux. On Arch Linux /tmp is a ramdisk, so I just do all my work there and make symlinks when necessary. I wouldn't recommend using /tmp for work unless you have >8GB of RAM as the /tmp filesystem is by default 50% of your RAM, and the compiled source code is somewhere around 2GB give or take.

The first step is to decompress the toolchain and create a symlink from DAP-1520_A1_106b04_FOSS/toolchain/ to /opt/buildroot-gcc342 because a bunch of their makefiles are hard coded to look in this place for the toolchain.

$ cd $(mktemp -d)
$ tar -zxvf ~/Downloads/DAP-1520\ A1_ver1.06b04_FOSS.tar.gz
$ cd DAP-1520_A1_106b04_FOSS/toolchain
$ tar -jxf buildroot-gcc342.tar.bz2
$ tar -zxf lzma-4.32.7.tar.gz
$ tar -jxf squashfs4.2.tar.bz2
$ ln -s $(pwd)/buildroot-gcc342 /opt/buildroot-gcc342

Then you'll need to compile the versions of lzma and squashfs provided, for reasons which I will get into in a bit. Copy the lzma and mksquashfs_lzma-4.2 binaries into the bin folder of your toolchain. I don't recommend running make install as they do in the instructions, just run make and manually copy the binaries to the toolchain/bin directory.

$ cd lzma-4.32.7
$ ./configure
$ make
$ cp src/lzma/lzma ../buildroot-gcc342/bin/
$ cd ../squashfs4.2/squashfs-tools
$ make
$ cp mksquashfs ../../buildroot-gcc342/bin/mksquashfs_lzma-4.2

The copy of mksquashfs_lzma-4.2 included in the toolchain links against an ancient version of liblzma.so which has long since not existed in Arch Linux. Hence, it's easier just to compile the version from the source code included. Just install xz from your package manager, I didn't need to compile their specific version.

Now that we have "installed" the toolchain, we need to decompress the actual source code for the router firmware and "install" it in /home/AthSDK:

$ cd ../../../src/
$ tar -zxf DAP1520A1_GPL106b04.tar.gz
$ sudo ln -s $(pwd)/AthSDK /home/AthSDK

Now we are all set to start building it as per the D-Link instructions above:

$ cd AthSDK
$ make clean
$ make kernel_clean

At this point, we need to fix some of the source files or the compilation will fail. You will need to download and run the next few patches in the AthSDK directory or compilation will fail with errors.

$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/timeconst.patch
$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/busybox_makefile.patch
$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/busybox_features.patch
$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/timer_makefile.patch
$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/telnetd.patch
$ wget https://watchmysys.com/blog/wp-content/uploads/2016/03/nc.patch

Some explanation is in order:

  • Compiling the kernel will fail because Arch has a reasonably new verison of Perl, and the syntax in Perl >5.22 has changed since 2.6.36. You will need to apply the patch timeconst.patch to fix this.
  • Compiling busybox will fail because the syntax in the makefile is deprecated. You will need to apply the patch busybox_makefile.patch to fix this.
  • We want to have telnetd and nc in the image we create, for backdoors and stuff. You will need to apply the patch busybox_features.patch to enable these features.
  • D-Link includes an application called timer which expects an object file to compile, except this object file is never created. Removing the line fixes the error and as far as I know timer still works as intended. You will need to apply the patch timer_makefile.patch to fix this.
  • We need to create sysconfig scripts to start the telnetd and nc daemons on boot. The telnetd and nc patches create the sysconfig scripts in the rootfs folder of the D-Link OS
  • $ patch -p1 < timeconst.patch 
    patching file platform/MT7620/kernels/mips-linux-2.6.36.x/kernel/timeconst.pl
    $ patch -p1 < busybox_makefile.patch 
    patching file apps/busybox-1.6.1/Makefile
    $ patch -p1 < busybox_features.patch 
    patching file apps/busybox-1.6.1/.config
    $ patch -p1 < timer_makefile.patch   
    patching file apps/timer/Makefile
    $ sudo patch -p1 < telnetd.patch  
    patching file rootfs/target/etc/sysconfig/S3telnetd.sh
    $ sudo patch -p1 < nc.patch      
    patching file rootfs/target/etc/sysconfig/S4nc.sh
    $ chmod 755 rootfs/target/etc/sysconfig/*sh
    $ install -d rootfs/target/bin
    $ ln -s busybox_161 rootfs/target/bin/nc
    

    Now we can run the next command with sudo, as tar will attempt to create some files for the firmware which are owned by root. This will fail if not run as sudo:

    $ sudo make

    A long time later:

    
    =================== installing wireless ===================
    make -C wireless install || exit 1
    make[1]: Entering directory '/tmp/tmp.41xjcpqbrX/DAP-1520_A1_106b04_FOSS/src/AthSDK/wireless'
    make[1]: Leaving directory '/tmp/tmp.41xjcpqbrX/DAP-1520_A1_106b04_FOSS/src/AthSDK/wireless'
    =================== installing rootfs ===================
    make -C rootfs install || exit 1
    make[1]: Entering directory '/tmp/tmp.41xjcpqbrX/DAP-1520_A1_106b04_FOSS/src/AthSDK/rootfs'
    install -d /home/AthSDK/image
    rm -rf /home/AthSDK/rootfs/target/man/ /home/AthSDK/rootfs/target/lib/*.a
    Strip all .so
    find /home/AthSDK/rootfs/target/lib/ -name "*.so*" -exec mipsel-linux-uclibc-strip '{}' ';'
    cp -f /opt/buildroot-gcc342/lib/libdl* /home/AthSDK/rootfs/target/lib/
    mipsel-linux-uclibc-strip target/lib/libdl-0.9.30.so
    mipsel-linux-uclibc-strip: 'target/lib/libdl-0.9.30.so': No such file
    Strip all exec
    find /home/AthSDK/rootfs/target -type f -perm -u+x -exec mipsel-linux-uclibc-strip '{}' ';'
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/widget.cgi: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/tr069.cgi: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/save_configure.cgi: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/hnap.cgi: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/apply.cgi: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/www/library/test/success.html: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/usr/share/udhcpc/default.script: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/usr/share/udhcpc/default.bound-nodns: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/usr/share/udhcpc/default.bound-dns: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/lib/libavahi-core.la: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/lib/libavahi-common.la: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/lib/libexpat.la: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/lib/libdaemon.la: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/rdnssd-script: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/dhcp6c-script: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/host.conf: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/inittab: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/services: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/shadow: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/rdnssd/merge-hook: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/passwd: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/sysinfo: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/icon.ico: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/nvram.default: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/sysconfig/S2gpio.sh: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/issue: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/group: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/fstab: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/securetty: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/etc/rc.d/rcS: File format not recognized
    mipsel-linux-uclibc-strip: /home/AthSDK/rootfs/target/bin/gpio_event: File format not recognized
    Strip Atheros's *.ko
    find /home/AthSDK/rootfs/target/lib/modules/2.6.36.x -name "*.ko" -type f \
    	-exec mipsel-linux-uclibc-strip -g -S -d \
    	--strip-unneeded \
    	--remove-section=__kcrctab \
    	--remove-section=__kcrctab_gpl \
    	--remove-section=__param \
    	--remove-section=__ex_table \
    	--remove-section=__obsparm \
    	--remove-section=__versions \
    	--remove-section=.pdr \
    	--remove-section=.mdebug.abi32 \
    	--remove-section=.comment \
    	--remove-section=__ksymtab_gpl_future \
    	--remove-section=__kcrctab_gpl_future \
    	--remove-section=__ksymtab_unused \
    	--remove-section=__kcrctab_unused \
    	--remove-section=__ksymtab_unused_gpl \
    	--remove-section=__kcrctab_unused_gpl \
    	--remove-section=.ctors \
    	--remove-section=__markers \
    	--remove-section=__tracepoints \
    	--remove-section=_ftrace_events \
    	--remove-section=__mcount_loc \
    	-x '{}' ';'
    Strip Cameo's *.ko
    Remove unneeded files
    rm -f /home/AthSDK/rootfs/target/lib/modules/2.6.36.x/build
    rm -f /home/AthSDK/rootfs/target/lib/modules/2.6.36.x/modules.order
    rm -f /home/AthSDK/rootfs/target/lib/modules/2.6.36.x/source
    rm -rf /home/AthSDK/rootfs/target/include
    rm -rf /home/AthSDK/rootfs/target/lib/avahi
    rm -rf /home/AthSDK/rootfs/target/lib/pkgconfig
    rm -rf /home/AthSDK/rootfs/target/root
    rm -f /home/AthSDK/rootfs/target/lib/modules/2.6.36.x/net/ath_pktlog.ko
    cp /home/AthSDK/platform/MT7620/kernels/mips-linux-2.6.36.x/arch/mips/boot/vmlinux.* /home/AthSDK/image/ || exit 1;
    /home/AthSDK/tools/release_scripts/mkuImage.sh
    + case $BOARD_TYPE in
    + LDADDR=0x80000000
    ++ readelf -a /home/AthSDK/platform/MT7620/kernels/mips-linux-2.6.36.x/vmlinux
    ++ grep Entry
    ++ head -1
    ++ cut -d: -f 2
    + ENTRY='               0x8000c310'
    + /home/AthSDK/tools/release_scripts/mkimage -A mips -O linux -T kernel -C lzma -a 0x80000000 -e 0x8000c310 -n 'Linux Kernel Image' -d /home/AthSDK/image/vmlinux.lzma /home/AthSDK/image/vmlinux.lzma.ub
    Image Name:   Linux Kernel Image
    Created:      Sun Mar  6 23:20:20 2016
    Image Type:   MIPS Linux Kernel Image (lzma compressed)
    Data Size:    908125 Bytes = 886.84 kB = 0.87 MB
    Load Address: 0x80000000
    Entry Point:  0x8000C310
    /home/AthSDK/tools/release_scripts/release_rootfs.sh
    =================== Create SQUASHFS for DAP-1520 ===================
    Parallel mksquashfs: Using 4 processors
    Creating 4.0 filesystem on /home/AthSDK/image/MT7620-squash, block size 65536.
    [===========================================================================================\] 524/524 100%
    Exportable Squashfs 4.0 filesystem, xz compressed, data block size 65536
    	compressed data, compressed metadata, compressed fragments, compressed xattrs
    	duplicates are removed
    Filesystem size 3370.39 Kbytes (3.29 Mbytes)
    	28.91% of uncompressed filesystem size (11656.43 Kbytes)
    Inode table size 5286 bytes (5.16 Kbytes)
    	24.23% of uncompressed inode table size (21816 bytes)
    Directory table size 5948 bytes (5.81 Kbytes)
    	46.62% of uncompressed directory table size (12759 bytes)
    Number of duplicate files found 46
    Number of inodes 650
    Number of files 419
    Number of fragments 56
    Number of symbolic links  125
    Number of device nodes 53
    Number of fifo nodes 0
    Number of socket nodes 0
    Number of directories 53
    Number of ids (unique uids + gids) 1
    Number of uids 1
    	root (0)
    Number of gids 1
    	root (0)
    =================== MAX_ROOTFS_IMG_SIZE=3801062 Bytes =================== 
    0+1 records in
    1+0 records out
    3801062 bytes (3.8 MB) copied, 0.00318195 s, 1.2 GB/s
    =================== MT7620 Squashfs created for 8 MB FLASH ===================
    /home/AthSDK/tools/release_scripts/release_image.sh
    0+1 records in
    1+0 records out
    983040 bytes (983 kB) copied, 0.00105716 s, 930 MB/s
    make[1]: Leaving directory '/tmp/tmp.41xjcpqbrX/DAP-1520_A1_106b04_FOSS/src/AthSDK/rootfs'
    =================== Finish ===================
    

    We need to verify that the file produced matches the official firmware update available from D-Link. It really wouldn't do to flash an XZ compressed kernel when the bootloader expects an LZMA compressed kernel!

    $ binwalk image/DAP1520A1_FW106B04.bin 
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             uImage header, header size: 64 bytes, header CRC: 0x9D3D95E7, created: 2016-03-07 19:18:42, image size: 909160 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0x77D76472, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    64            0x40            LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2798288 bytes
    983040        0xF0000         Squashfs filesystem, little endian, version 4.0, compression:xz, size: 3451228 bytes, 650 inodes, blocksize: 65536 bytes, created: 2016-03-07 19:18:45

    Good, this matches the D-Link provided firmware update, so we should be all good to flash it to the router and see if we can login.

    Gotchas
    mksquashfs
    I had originally installed mksquashfs from pacman, because why would you want to use the old version included in the D-Link source?

    Hilariously, the version of mksquashfs_lzma-4.2 included in the D-Link source doesn't actually support LZMA compression at all, instead using xz compression by default!

    
    SYNTAX:/opt/buildroot-gcc342/bin/mksquashfs_lzma-4.2 source1 source2 ...  dest [options] [-e list of exclude
    dirs/files]
    
    Filesystem build options:
    -comp 		select  compression
    			Compressors available:
    				gzip
    				xz (default)
    
    (other options omitted)
    
    Compressors available and compressor specific options:
    	gzip (no options)
    	xz (default)
    	  -Xbcj filter1,filter2,...,filterN
    		Compress using filter1,filter2,...,filterN in turn
    		(in addition to no filter), and choose the best compression.
    		Available filters: x86, arm, armthumb, powerpc, sparc, ia64
    	  -Xdict-size 
    		Use  as the XZ dictionary size.  The dictionary size
    		can be specified as a percentage of the block size, or as an
    		absolute value.  The dictionary size must be less than or equal
    		to the block size and 8192 bytes or larger.  It must also be
    		storable in the xz header as either 2^n or as 2^n+2^(n+1).
    		Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
    		etc.
    

    This differs from mksquashfs included in Arch Linux, which uses gzip by default! If you just go ahead and build the image using mksquashfs provided by your package manager, you will end up with a filesystem which is too large, and the build process will fail!

    
    $ mksquashfs -h
    SYNTAX:mksquashfs source1 source2 ...  dest [options] [-e list of exclude
    dirs/files]
    
    Filesystem build options:
    -comp 		select  compression
    			Compressors available:
    				gzip (default)
    
    (other options omitted)
    
    Compressors available and compressor specific options:
    	gzip (default)
    	  -Xcompression-level 
    		 should be 1 .. 9 (default 9)
    	  -Xwindow-size 
    		 should be 8 .. 15 (default 15)
    	  -Xstrategy strategy1,strategy2,...,strategyN
    		Compress using strategy1,strategy2,...,strategyN in turn
    		and choose the best compression.
    		Available strategies: default, filtered, huffman_only,
    		run_length_encoded and fixed
    	lzma (no options)
    	lzo
    	  -Xalgorithm 
    		Where  is one of:
    			lzo1x_1
    			lzo1x_1_11
    			lzo1x_1_12
    			lzo1x_1_15
    			lzo1x_999 (default)
    	  -Xcompression-level 
    		 should be 1 .. 9 (default 8)
    		Only applies to lzo1x_999 algorithm
    	lz4
    	  -Xhc
    		Compress using LZ4 High Compression
    	xz
    	  -Xbcj filter1,filter2,...,filterN
    		Compress using filter1,filter2,...,filterN in turn
    		(in addition to no filter), and choose the best compression.
    		Available filters: x86, arm, armthumb, powerpc, sparc, ia64
    	  -Xdict-size 
    		Use  as the XZ dictionary size.  The dictionary size
    		can be specified as a percentage of the block size, or as an
    		absolute value.  The dictionary size must be less than or equal
    		to the block size and 8192 bytes or larger.  It must also be
    		storable in the xz header as either 2^n or as 2^n+2^(n+1).
    		Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
    		etc.
    

    At least the build process will fail and tell you the image is too large, instead of making an image which will brick your router...

    lzma
    Again I was wondering, why should I use the old version of LZMA included in the D-Link source, when Arch Linux ships which a much newer (and thus better) version of LZMA? It might occur to you, that I haven't learned my lesson yet from the previous experience with mksquashfs...

    The lzma included in D-Link's source:

    $ /opt/buildroot-gcc342/bin/lzma -h
    
    lzma 4.32.7 Copyright (C) 2005 Ville Koskinen
    Based on LZMA SDK 4.32 Copyright (C) 1999-2005 Igor Pavlov
    
    Usage: /opt/buildroot-gcc342/bin/lzma [flags and input files in any order]
      -c --stdout       output to standard output
      -d --decompress   force decompression
      -z --compress     force compression
      -k --keep         keep (don't delete) input files
      -f --force        force overwrite of output file and compress links
      -t --test         test compressed file integrity
      -S .suf  --suffix .suf   use suffix .suf on compressed files
      -q --quiet        suppress error messages
      -v --verbose      be verbose
      -h --help         print this message
      -L --license      display the license information
      -V --version      display version numbers of LZMA SDK and lzma
      -1 .. -2          fast compression
      -3 .. -9          good to excellent compression. -7 is the default.
         --fast         alias for -1
         --best         alias for -9 (usually *not* what you want)
    
      Memory usage depends a lot on the chosen compression mode -1 .. -9.
      See the man page lzma(1) for details.
    

    And lzma from Arch Linux:

    $ lzma -h
    Usage: lzma [OPTION]... [FILE]...
    Compress or decompress FILEs in the .xz format.
    
      -z, --compress      force compression
      -d, --decompress    force decompression
      -t, --test          test compressed file integrity
      -l, --list          list information about .xz files
      -k, --keep          keep (don't delete) input files
      -f, --force         force overwrite of output file and (de)compress links
      -c, --stdout        write to standard output and don't delete input files
      -0 ... -9           compression preset; default is 6; take compressor *and*
                          decompressor memory usage into account before using 7-9!
      -e, --extreme       try to improve compression ratio by using more CPU time;
                          does not affect decompressor memory requirements
      -T, --threads=NUM   use at most NUM threads; the default is 1; set to 0
                          to use as many threads as there are processor cores
      -q, --quiet         suppress warnings; specify twice to suppress errors too
      -v, --verbose       be verbose; specify twice for even more verbose
      -h, --help          display this short help and exit
      -H, --long-help     display the long help (lists also the advanced options)
      -V, --version       display the version number and exit
    
    With no FILE, or when FILE is -, read standard input.
    
    Report bugs to  (in English or Finnish).
    XZ Utils home page: 
    

    So, apart from the newer output looking much more like a standard GNU utility, you might have noticed that the older copy of lzma compresses with a default compression of -7 while the newer version compresses with a default compression of -6.

    If you think this doesn't make a difference, let me just tell you now, it does. A big one. The difference between -6 and -7 is the difference between a kernel that boots, and one that doesn't.

    This firmware was built with the D-Link SDK version of lzma and will boot:

    $ binwalk image/DAP1520A1_FW106B04.bin 
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             uImage header, header size: 64 bytes, header CRC: 0x46768407, created: 2016-03-05 22:33:15, image size: 909272 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0x4993B2D9, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    64            0x40            LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2798288 bytes
    983040        0xF0000         Squashfs filesystem, little endian, version 4.0, compression:xz, size: 3445784 bytes, 655 inodes, blocksize: 65536 bytes, created: 2016-03-05 22:33:20
    

    This firmware was built with the Arch Linux version of lzma and won't boot:

    $ binwalk image/DAP1520A1_FW106B04.bin 
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             uImage header, header size: 64 bytes, header CRC: 0x7A57558F, created: 2016-03-07 20:41:46, image size: 908147 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0x8E0F6C03, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    983040        0xF0000         Squashfs filesystem, little endian, version 4.0, compression:xz, size: 3451428 bytes, 653 inodes, blocksize: 65536 bytes, created: 2016-03-07 20:41:49
    

    So, what exactly happens when you flash this image to the device? Does it do some verification before flashing and stop you? Does it flash the image, and then when Image 1 fails to boot it boots the linux4b image and start rootfsb so you can recover the device?

    Not quite...

    Image1 Try Counter --> 0
    
    Image1: OK Image2: OK
    Both images are OK!!!
    
    =================================================
    
    Please choose the operation: 
       1: Load system code to SDRAM via TFTP. 
       2: Load system code then write to Flash via TFTP. 
       3: Boot system code via Flash (default).
       4: Entr boot command line interface.
       7: Load Boot Loader code then write to Flash via Serial. 
       9: Load Boot Loader code then write to Flash via TFTP. 
     1  0 
       
    3: System Boot system code via Flash.
    ## Booting image at bc050000 ...
    raspi_read: from:50000 len:40 
       Image Name:   Linux Kernel Image
       Image Type:   MIPS Linux Kernel Image (lzma compressed)
       Data Size:    908132 Bytes = 886.8 kB
       Load Address: 80000000
       Entry Point:  8000c310
    raspi_read: from:50040 len:ddb64 
       Verifying Checksum ... OK
       Uncompressing Kernel Image ... LZMA ERROR 1 - must RESET board to recover
    

    So, no. No verification of the validity of the kernel in the update before flashing. And no, it won't boot from Image 2. You will just see this error, over and over, while the device resets. You might think that Image1 Try Counter would increment, and after a threshold it would boot into the recovery environment, but no. Congratulations, you are now the proud owner of a brick. Get out your SPI flashing tool, because there's no other way around this disaster.

    This does beg the question, how do you get the device to boot into Image 2? Well, after the LZMA compression snafu on the kernel, I thought I would save some time and just flash the vmlinuz.ub file created by the build script to flash and be done with it... nope!

    
    Check image validation:
    Image1 Header Magic Number --> OK
    Image2 Header Magic Number --> OK
    Image1 Header Checksum --> OK
    Image2 Header Checksum --> OK
    Image1 Data Checksum --> raspi_read: from:50040 len:de000 
    Failed
    Image2 Data Checksum --> raspi_read: from:4f0040 len:ca8f4 
    OK
    Image1 Stable Flag --> Not stable
    Image1 Try Counter --> 0
    
    Image1: Broken Image2: OK
    Only Image1 is borken!!
    
    =================================================
    
    Please choose the operation: 
       1: Load system code to SDRAM via TFTP. 
       2: Load system code then write to Flash via TFTP. 
       3: Boot system code via Flash (default).
       4: Entr boot command line interface.
       7: Load Boot Loader code then write to Flash via Serial. 
       9: Load Boot Loader code then write to Flash via TFTP. 
     1  0 
       
    3: System Boot system code via Flash.
    ## Booting image at bc4f0000 ...
    raspi_read: from:4f0000 len:40 
       Image Name:   Linux Kernel Image
       Image Type:   MIPS Linux Kernel Image (lzma compressed)
       Data Size:    829684 Bytes = 810.2 kB
       Load Address: 80000000
       Entry Point:  8000c310
    raspi_read: from:4f0040 len:ca8f4 
       Verifying Checksum ... OK
       Uncompressing Kernel Image ... OK
    No initrd
    ## Transferring control to Linux (at address 8000c310) ...
    ## Giving linux memsize in MB, 64
    
    Starting kernel ...
    
    
    LINUX started...
    
     THIS IS ASIC
    Linux version 2.6.36.x ([email protected]) (gcc version 3.4.2) #1 Thu Sep 26 16:47:12 CST 2013
    

    Couple of things to note here:

    1. Only Image1 is borken!!
    2. The kernel in Image 2 is much older, and was built on a different host, than the kernel in Image 1 from D-Link

    When you do flash a working firmware back onto the router, you get a surprise when it boots. Because Image1 is borken!! the device rewrites all the nvram variables to their defaults.

    
    init NVRAM_SPACE from mtdblock size
    init nvram memory map size: 0x10000 order of pages: 0x4
    nvram module init:
    	/dev/nvram major number 225 glues to mtd: "nvram" size: 0x00010000
    	nvram_space: 0x00010000 mapped via mmap(2)
    openfile :/etc/sysinfo
    openfile :/etc/nvram.default
    nvram_sanity_check: restore key: uplink_set_by_user="0"
    nvram_sanity_check: restore key: language="default"
    nvram_sanity_check: restore key: ap_ipv6_wan_specify_dns="0"
    nvram_sanity_check: restore key: ap_ipv6_autoconfig_secondary_dns=""
    nvram_sanity_check: restore key: ap_ipv6_autoconfig_primary_dns=""
    nvram_sanity_check: restore key: ap_ipv6_autoconfig_dns_enable="0"
    nvram_sanity_check: restore key: ap_ipv6_static_secondary_dns=""
    nvram_sanity_check: restore key: ap_ipv6_static_primary_dns=""
    nvram_sanity_check: restore key: ap_ipv6_static_default_gw=""
    nvram_sanity_check: restore key: ap_ipv6_static_prefix_length=""
    nvram_sanity_check: restore key: ap_ipv6_static_lan_ip=""
    nvram_sanity_check: restore key: ap_ipv6_wan_proto="ipv6_autoconfig"
    nvram_sanity_check: restore key: pure_support_url="http://support.dlink.com/products/view.asp?productid=DAP-1520"
    nvram_sanity_check: restore key: pure_reboot_page="reboot.htm"
    nvram_sanity_check: restore key: pure_parental_url=""
    nvram_sanity_check: restore key: pure_block_url=""
    nvram_sanity_check: restore key: pure_wireless_url_new="/Wireless.htm"
    nvram_sanity_check: restore key: pure_wireless_url="/wireless.htm"
    nvram_sanity_check: restore key: pure_presentation_url="/Device_Info.htm"
    nvram_sanity_check: restore key: pure_model_description="Wireless Repeater"
    nvram_sanity_check: restore key: pure_vendor_name="D-Link"
    nvram_sanity_check: restore key: pure_device_name="D-Link Systems DAP-1520"
    nvram_sanity_check: restore key: pure_type_new="WiFiAccessPoint"
    nvram_sanity_check: restore key: pure_type="Repeater"
    nvram_sanity_check: restore key: default_downlink_ssid="1"
    nvram_sanity_check: restore key: wlan1_wps_wizard="0"
    nvram_sanity_check: restore key: setup_wizard_ap="1"
    nvram_sanity_check: restore key: log_response_type="system|debug|attack|dropped|notice"
    nvram_sanity_check: restore key: log_current_page="0"
    nvram_sanity_check: restore key: log_total_page="0"
    nvram_sanity_check: restore key: log_per_page="10"
    nvram_sanity_check: restore key: log_notice="1"
    nvram_sanity_check: restore key: log_dropped_packets="0"
    nvram_sanity_check: restore key: log_attacks="1"
    nvram_sanity_check: restore key: log_debug_information="0"
    nvram_sanity_check: restore key: log_system_activity="1"
    nvram_sanity_check: restore key: syslog_server="0/0.0.0.0"
    nvram_sanity_check: restore key: time_daylight_offset="3600"
    nvram_sanity_check: restore key: time_daylight_saving_end_time="1"
    nvram_sanity_check: restore key: time_daylight_saving_end_day_of_week="1"
    nvram_sanity_check: restore key: time_daylight_saving_end_week="2"
    nvram_sanity_check: restore key: time_daylight_saving_end_month="11"
    nvram_sanity_check: restore key: time_daylight_saving_start_time="1"
    nvram_sanity_check: restore key: time_daylight_saving_start_day_of_week="1"
    nvram_sanity_check: restore key: time_daylight_saving_start_week="3"
    nvram_sanity_check: restore key: time_daylight_saving_start_month="3"
    nvram_sanity_check: restore key: time_daylight_saving_enable="0"
    nvram_sanity_check: restore key: ntp_sync_interval="168"
    nvram_sanity_check: restore key: ntp_default_server="ntp1.dlink.com,ntp.dlink.com.tw"
    nvram_sanity_check: restore key: ntp_server=""
    nvram_sanity_check: restore key: time_zone_area="4"
    nvram_sanity_check: restore key: time_zone="-128"
    nvram_sanity_check: restore key: ntp_client_enable="0"
    nvram_sanity_check: restore key: session_timeout="180"
    nvram_sanity_check: restore key: graph_enable="none"
    nvram_sanity_check: restore key: system_time="2011/01/01/00/00/00"
    nvram_sanity_check: restore key: serial_number="none"
    nvram_sanity_check: restore key: model_url="http://support.dlink.com"
    nvram_sanity_check: restore key: model_name="D-Link Repeater"
    nvram_sanity_check: restore key: manufacturer_url="http://www.dlink.com"
    nvram_sanity_check: restore key: manufacturer="D-Link"
    nvram_sanity_check: restore key: friendlyname="DAP-1520"
    nvram_sanity_check: restore key: model_number="DAP-1520"
    nvram_sanity_check: restore key: hostname="DAP-1520"
    nvram_sanity_check: restore key: wlan1_11n_protection="auto"
    nvram_sanity_check: restore key: wlan1_wps_enable="1"
    nvram_sanity_check: restore key: wlan1_psk_pass_phrase="1234567890"
    nvram_sanity_check: restore key: wlan1_psk_cipher_type="both"
    nvram_sanity_check: restore key: wlan1_wep_display="hex"
    nvram_sanity_check: restore key: wlan1_wep128_key="00000000000000000000000000"
    nvram_sanity_check: restore key: wlan1_wep64_key="0000000000"
    nvram_sanity_check: restore key: wlan1_security="disable"
    nvram_sanity_check: restore key: wlan1_ssid=""
    nvram_sanity_check: restore key: wlan_repeater_mode="1"
    nvram_sanity_check: restore key: wlan0_disable_wps_pin="1"
    nvram_sanity_check: restore key: wlan0_wps_configured_mode="5"
    nvram_sanity_check: restore key: wlan0_wps_enable="1"
    nvram_sanity_check: restore key: wlan0_disablecoext="0"
    nvram_sanity_check: restore key: wlan0_rxchainmask="3"
    nvram_sanity_check: restore key: wlan0_txchainmask="3"
    nvram_sanity_check: restore key: wlan0_gkey_rekey_time="3600"
    nvram_sanity_check: restore key: wlan0_11n_protection="auto"
    nvram_sanity_check: restore key: wlan0_wmm_enable="1"
    nvram_sanity_check: restore key: wlan0_short_gi="1"
    nvram_sanity_check: restore key: wlan0_partition="0"
    nvram_sanity_check: restore key: wlan0_dtim="1"
    nvram_sanity_check: restore key: wlan0_fragmentation="2346"
    nvram_sanity_check: Raeth v3.0 (reTaskletst,SkbRecycleo)
    nvram_sanity_check: restore key: wlan0_beacon_interval="100"
    nvram_sanity_check: restore key: wlan0_txpower="100"
    nvram_sanity_check: restore key: wlan0_psk_cipher_type="both"
    nvram_sanity_check: restore key: wlan0_wep_display="hex"
    nvram_sanity_check: restore key: wlan0_wep128_key="00000000000000000000000000"
    nvram_sanity_check: restore key: wlan0_wep64_key="0000000000"
    nvram_sanity_check: restore key: wlan0_ssid_broadcast="1"
    nvram_sanity_check: restore key: wlan0_dot11_mode="11bgn"
    nvram_sanity_check: restore key: wlan0_auto_channel_enable="1"
    nvram_sanity_check: restore key: wlan0_channel="6"
    nvram_sanity_check: restore key: wlan0_enable="1"
    nvram_sanity_check: restore key: wlan0_5g_fragmentation="2346"
    nvram_sanity_check: restore key: wlan0_5g_rts_threshold="2347"
    nvram_sanity_check: restore key: wlan0_5g_dfs_enable="0"
    nvram_sanity_check: restore key: wlan0_5g_11n_protection="adevice eth2 entered promiscuous mode
    uto"
    nvram_sanity_check: restore key: wlan0_5g_wep128_key="00000000000000000000000000"
    nvram_sanity_check: restore key: wlan0_5g_wep64_key="0000000000"
    nvram_sanity_check: restore key: wlan0_5g_psk_cipher_type="both"
    nvram_sanity_check: restore key: wlan0_5g_wep_display="hex"
    nvram_sanity_check: restore key: wlan0_5g_wmm_enable="1"
    nvram_sanity_check: restore key: wlan0_5g_txpower="100"
    nvram_sanity_check: restore key: wlan0_5g_dtim="1"
    nvram_sanity_check: restore key: wlan0_5g_beacon_interval="100"
    nvram_sanity_check: restore key: wlan0_5g_auto_channel_enable="1"
    nvram_sanity_check: restore key: wlan0_5g_channel="36"
    nvram_sanity_check: restore key: wlan0_5g_dot11_mode="11anac"
    nvram_sanity_check: restore key: dhcpc_enable="1"
    nvram_sanity_check: restore key: ap_device_name="dlinkap"
    nvram_sanity_check: restore key: ap_secondary_dns="0.0.0.0"
    nvram_sanity_check: restore key: ap_primary_dns="0.0.0.0"
    nvram_sanity_check: restore key: ap_gateway="0.0.0.0"
    nvram_sanity_check: restore key: ap_netmask="255.255.255.0"
    nvram_sanity_check: restore key: ap_ipaddr="192.168.0.50"
    nvram_sanity_check: restore key: lan_bridge="br0"
    nvram_sanity_check: restore key: lan_eth="eth2"
    nvram_sanity_check: restore key: admin_password=""
    nvram_sanity_check: restore key: admin_username="admin"
    

    Solid defaults there, D-Link. I think this is the first device I've ever encountered where the admin password was actually blank. I setup the device initially, and then it rebooted and asked me for a password to login. Even working in IT, I never thought to try an empty password. I mean, who does that?! D-Link does that.

    And just while writing this I realized that I can look at the nvram defaults any time I want to in /etc/nvram.defaults. This is why you use the 15 minute rule, people.

    Below is a firmware with nc running on port 8023 if you have a DAP-1520 and you want to poke around the D-Link firmware. Telnet asks for a username and password, and none of the combinations I could think of let me login.

    nc 192.168.0.50 8023

    Enjoy your root shell!

    Firmware DAP1520A1_FW106B04.bin (gzip compressed) with nc backdoor.
    md5sum: 31397369d0631183c3823d9933bede5f
    sha1sum: 2951f4e36b05014cbc327acf5c9d6e860ac2f0a5
    sha256sum: 6dd416c6f26e17f059dcc531e2a882a64e3af0594bd3720da669af631c34e50b

    D-Link DAP-1520 hacking: Part 1

    What do you do with a device you never would have bought for yourself, but received for free? Say welcome the D-Link DAP-1520, a “WiFi Extender” that was given to me by O2 as a bonus for signing up with them. Hopefully they aren’t expecting it back in one piece…

    So, what is the DAP-1520? Executive summary:

  • Supports 2.4GHz at 300MBps and 5GHz at 433MBps (thanks to SmallNetBuilder for demystifying this)
  • Repeats the packets from your existing WiFi network for extending range
  • Will also turn a 2.4GHz network into 5GHz through the repeating process (or vice versa)
  • No Ethernet ports because Ethernet is so 2014
  • Right, now that we’ve got the useless D-Link page out of the way, let’s talk about what’s actually in the DAP-1520:

  • MediaTek MT7260A SoC running at 580MHz (includes 2.4GHz radio)
  • 64MB RAM (Winbond W9751G6KB-25 64471X600ZY2)
  • 8MB Flash (MXIC MX 25L640GE)
  • MediaTek MT7610EN (5GHz radio)
  • Skyworks 5GHz Frontend module (datasheet [PDF])
  • This all sounds great, but what do we actually have here? I will preface this post by saying that I started out wanting to port OpenWrt to this device, and I still do, but I got side tracked in my investigation and you’ll have to wait for a follow up post if I ever succeed to port OpenWrt.

    PCB front

    PCB front

    PCB Rear

    PCB Rear

    The UART runs at 57600 8N1.

    No pictures of the power supply because it’s just a boring 5V power source.

    Okay, so now that we know the UART pinout, what does the device say when it boots?

    Boot log:

    U-Boot 1.1.3 (Aug  8 2013 - 10:32:46)
    
    Board: Ralink APSoC DRAM:  64 MB
    relocate_code Pointer at: 83fb0000
    enable ephy clock...done. rf reg 29 = 5
    SSC disabled.
    spi_wait_nsec: 29 
    spi device id: c2 20 17 c2 20 (2017c220)
    find flash: MX25L6405D
    raspi_read: from:30000 len:1000 
    *** Warning - bad CRC, using default environment
    
    ============================================ 
    Ralink UBoot Version: 4.1.1.0
    -------------------------------------------- 
    ASIC 7620_MP (Port5None)
    DRAM component: 512 Mbits DDR, width 16
    DRAM bus: 16 bit
    Total memory: 64 MBytes
    Flash component: SPI Flash
    Date:Aug  8 2013  Time:10:32:46
    Cameo Version: v1.00 Build:01
    Module Name: D-Link DAP-1520A1
    ============================================ 
    icache: sets:512, ways:4, linesz:32 ,total:65536
    dcache: sets:256, ways:4, linesz:32 ,total:32768 
    
     ##### The CPU freq = 580 MHZ #### 
     estimate memory size =64 Mbytes
    raspi_read: from:50000 len:40 
    raspi_read: from:4f0000 len:40 
    
    =================================================
    Check image validation:
    Image1 Header Magic Number --> OK
    Image2 Header Magic Number --> OK
    Image1 Header Checksum --> OK
    Image2 Header Checksum --> OK
    Image1 Data Checksum --> raspi_read: from:50040 len:ddf98 
    OK
    Image2 Data Checksum --> raspi_read: from:4f0040 len:ca8f4 
    OK
    Image1 Stable Flag --> Not stable
    Image1 Try Counter --> 0
    
    Image1: OK Image2: OK
    Both images are OK!!!
    
    =================================================
    
    Please choose the operation: 
       1: Load system code to SDRAM via TFTP. 
       2: Load system code then write to Flash via TFTP. 
       3: Boot system code via Flash (default).
       4: Entr boot command line interface.
       7: Load Boot Loader code then write to Flash via Serial. 
       9: Load Boot Loader code then write to Flash via TFTP. 
     1  0 
       
    3: System Boot system code via Flash.
    ## Booting image at bc050000 ...
    raspi_read: from:50000 len:40 
       Image Name:   Linux Kernel Image
       Image Type:   MIPS Linux Kernel Image (lzma compressed)
       Data Size:    909208 Bytes = 887.9 kB
       Load Address: 80000000
       Entry Point:  8000c310
    raspi_read: from:50040 len:ddf98 
       Verifying Checksum ... OK
       Uncompressing Kernel Image ... OK
    No initrd
    ## Transferring control to Linux (at address 8000c310) ...
    ## Giving linux memsize in MB, 64
    
    Starting kernel ...
    
    
    LINUX started...
    
     THIS IS ASIC
    Linux version 2.6.36.x ([email protected]) (gcc version 3.4.2) #1 Fri Aug 22 16:26:27 CST 2014
    
     The CPU feqenuce set to 580 MHz
    
     MIPS CPU sleep mode enabled.
     PCIE: bypass PCIe DLL.
     PCIE: Elastic buffer control: Addr:0x68 -> 0xB4
     disable all power about PCIe
    CPU revision is: 00019650 (MIPS 24Kc)
    Determined physical RAM map:
     memory: 04000000 @ 00000000 (usable)
    Zone PFN ranges:
      Normal   0x00000000 -> 0x00004000
    Movable zone start PFN for each node
    early_node_map[1] active PFN ranges
        0: 0x00000000 -> 0x00004000
    Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
    Kernel command line: console=ttyS1,57600n8 root=/dev/mtdblock5 console=ttyS0,57600 root=31:05 rootfstype=squashfs init=/sbin/init
    PID hash table entries: 256 (order: -2, 1024 bytes)
    Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
    Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
    Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
    Primary data cache 32kB, 4-way, PIPT, no aliases, linesize 32 bytes
    Writing ErrCtl register=0007efde
    Readback ErrCtl register=0007efde
    Memory: 62028k/65536k available (2225k kernel code, 3508k reserved, 338k data, 168k init, 0k highmem)
    NR_IRQS:128
    MTK/Ralink System Tick Counter init... cd:80271d98, m:214748, s:32
    console [ttyS1] enabled
    Calibrating delay loop... 386.04 BogoMIPS (lpj=772096)
    pid_max: default: 32768 minimum: 301
    Mount-cache hash table entries: 512
    NET: Registered protocol family 16
    RALINK_GPIOMODE = 1a311d
    RALINK_GPIOMODE = 18311d
    PPLL_CFG1=0xe90000
    MT7620 PPLL lock
    PPLL_DRV =0x80080504
    start PCIe register access
    RALINK_PCI_PCICFG_ADDR = 1000f0
    
    *************** MT7620 PCIe RC mode *************
    bio: create slab  at 0
    vgaarb: loaded
    pci 0000:00:00.0: BAR 8: assigned [mem 0x20000000-0x201fffff]
    pci 0000:00:00.0: BAR 1: assigned [mem 0x20200000-0x2020ffff]
    pci 0000:00:00.0: BAR 1: set to [mem 0x20200000-0x2020ffff] (PCI address [0x20200000-0x2020ffff]
    pci 0000:01:00.0: BAR 0: assigned [mem 0x20000000-0x200fffff]
    pci 0000:01:00.0: BAR 0: set to [mem 0x20000000-0x200fffff] (PCI address [0x20000000-0x200fffff]
    pci 0000:01:00.1: BAR 0: assigned [mem 0x20100000-0x201fffff]
    pci 0000:01:00.1: BAR 0: set to [mem 0x20100000-0x201fffff] (PCI address [0x20100000-0x201fffff]
    pci 0000:00:00.0: PCI bridge to [bus 01-01]
    pci 0000:00:00.0:   bridge window [io  disabled]
    pci 0000:00:00.0:   bridge window [mem 0x20000000-0x201fffff]
    pci 0000:00:00.0:   bridge window [mem pref disabled]
    BAR0 at slot 0 = 0
    bus=0x0, slot = 0x0
    res[0]->start = 0
    res[0]->end = 0
    res[1]->start = 20200000
    res[1]->end = 2020ffff
    res[2]->start = 0
    res[2]->end = 0
    res[3]->start = 0
    res[3]->end = 0
    res[4]->start = 0
    res[4]->end = 0
    res[5]->start = 0
    res[5]->end = 0
    bus=0x1, slot = 0x0
    res[0]->start = 20000000
    res[0]->end = 200fffff
    res[1]->start = 0
    res[1]->end = 0
    res[2]->start = 0
    res[2]->end = 0
    res[3]->start = 0
    res[3]->end = 0
    res[4]->start = 0
    res[4]->end = 0
    res[5]->start = 0
    res[5]->end = 0
    bus=0x1, slot = 0x0
    res[0]->start = 20100000
    res[0]->end = 201fffff
    res[1]->start = 0
    res[1]->end = 0
    res[2]->start = 0
    res[2]->end = 0
    res[3]->start = 0
    res[3]->end = 0
    res[4]->start = 0
    res[4]->end = 0
    res[5]->start = 0
    res[5]->end = 0
    Switching to clocksource Ralink external timer
    NET: Registered protocol family 2
    IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
    TCP established hash table entries: 2048 (order: 2, 16384 bytes)
    TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
    TCP: Hash tables configured (established 2048 bind 2048)
    TCP reno registered
    UDP hash table entries: 256 (order: 0, 4096 bytes)
    UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
    NET: Registered protocol family 1
    squashfs: version 4.0 (2009/01/31) Phillip Lougher
    msgmni has been set to 121
    Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
    io scheduler noop registered (default)
    Ralink gpio driver initialized
    Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
    serial8250: ttyS0 at MMIO 0x10000500 (irq = 37) is a 16550A
    serial8250: ttyS1 at MMIO 0x10000c00 (irq = 12) is a 16550A
    brd: module loaded
    deice id : c2 20 17 c2 20 (2017c220)
    MX25L6405D(c2 2017c220) (8192 Kbytes)
    mtd .name = raspi, .size = 0x00800000 (0M) .erasesize = 0x00000008 (0K) .numeraseregions = 65536
    Creating 9 MTD partitions on "raspi":
    0x000000000000-0x000000800000 : "ALL"
    0x000000000000-0x000000030000 : "u-boot"
    0x000000030000-0x000000040000 : "nvram"
    0x000000040000-0x000000050000 : "Factory"
    0x000000050000-0x000000140000 : "linux4"
    0x000000140000-0x0000004e0000 : "rootfs"
    0x0000004e0000-0x0000004f0000 : "LANG"
    0x0000004f0000-0x0000005c0000 : "linux4b"
    0x0000005c0000-0x000000800000 : "rootfsb"
    rdm_major = 253
    SMACCR1 -- : 0x0000000c
    SMACCR0 -- : 0x43762077
    Ralink APSoC Ethernet Driver Initilization. v3.0  256 rx/tx descriptors allocated, mtu = 1500!
    SMACCR1 -- : 0x0000000c
    SMACCR0 -- : 0x43762077
    PROC INIT OK!
    TCP cubic registered
    NET: Registered protocol family 10
    IPv6 over IPv4 tunneling driver
    NET: Registered protocol family 17
    VFS: Mounted root (squashfs filesystem) readonly on device 31:5.
    Freeing unused kernel memory: 168k freed
    init started:  BusyBox v1.01 (2014.08.22-08:26+0000) multi-call binary
    Algorithmics/MIPS FPU Emulator v1.5
    devpts: called with bogus options
    init NVRAM_SPACE from mtdblock size
    init nvram memory map size: 0x10000 order of pages: 0x4
    nvram module init:
        /dev/nvram major number 225 glues to mtd: "nvram" size: 0x00010000
        nvram_space: 0x00010000 mapped via mmap(2)
    openfile :/etc/sysinfo
    openfile :/etc/nvram.default
    
    
    BusyBox v1.01 (2014.08.22-08:26+0000) Built-in shell (ash)
    Enter 'help' for a list of built-in commands.
    
    / # rm: cannot remove `/var/wizard_lang.js': No such file or directory
    umount: cannot umount /tmp/lang_pack: No such file or directory
    eth2: Cannot assign requested address
    Raeth v3.0 (Tasklet,SkbRecycle)
    
    phy_tx_ring = 0x03f4b000, tx_ring = 0xa3f4b000
    
    phy_rx_ring0 = 0x03f4c000, rx_ring0 = 0xa3f4c000
    SMACCR1 -- : 0x000054b8
    SMACCR0 -- : 0x0a7d19a6
    CDMA_CSG_CFG = 81000000
    GDMA1_FWD_CFG = 20710000
    umount: cannot umount /tmp/lang_pack: No such file or directory
    mount: mounting /dev/mtdblock6 on /tmp/lang_pack failed
    eth2: Cannot assign requested address
    device eth2 entered promiscuous mode
    TFTP main
    standard_tftp_server launched on port 69.
    killall: syslogd: no process killed
    killall: klogd: no process killed
    Sat Jan  1 00:00:00 UTC 2011
    /tmp/password has been created
    br0: port 1(eth2) entering forwarding state
    br0: port 1(eth2) entering forwarding state
    Set: phy[0].reg[0] = 3900
    Set: phy[1].reg[0] = 3900
    Set: phy[2].reg[0] = 3900
    Set: phy[3].reg[0] = 3900
    Set: phy[4].reg[0] = 3900
    Set: phy[0].reg[0] = 3100
    2011-01-01 00:00:00: (network.c.247) warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes 
    rt2860v2_ap: module license 'unspecified' taints kernel.
    Disabling lock debugging due to kernel taint
    
    
    === pAd = c04cd000, size = 1278080 ===
    
    <-- RTMPAllocTxRxRingMemory, Status=0
    <-- RTMPAllocAdapterBlock, Status=0
    AP Driver version-2.7.1.6_edcca_monitor_20131222
    
    
    === pAd = c0b02000, size = 2010752 ===
    
    <-- RTMPAllocTxRxRingMemory, Status=0
    MT76x0_WLAN_ChipOnOff(): OnOff:1, pAd->WlanFunCtrl:0x0, Reg-WlanFunCtrl=0xff000002
    MACVersion = 0x76502000
    RX DESC a3672000  size = 2048
    RTMP_TimerListAdd: add timer obj c05a1e20!
    RTMP_TimerListAdd: add timer obj c053b694!
    RTMP_TimerListAdd: add timer obj c053f78c!
    RTMP_TimerListAdd: add timer obj c053f84c!
    RTMP_TimerListAdd: add timer obj c053f90c!
    RTMP_TimerListAdd: add timer obj c053f9cc!
    RTMP_TimerListAdd: add timer obj c053fa8c!
    RTMP_TimerListAdd: add timer obj c053fb4c!
    RTMP_TimerListAdd: add timer obj c053fc0c!
    RTMP_TimerListAdd: add timer obj c053fccc!
    RTMP_TimerListAdd: add timer obj c053fd8c!
    RTMP_TimerListAdd: add timer obj c053fe4c!
    RTMP_TimerListAdd: add timer obj c053ff0c!
    RTMP_TimerListAdd: add timer obj c053ffcc!
    RTMP_TimerListAdd: add timer obj c054008c!
    RTMP_TimerListAdd: add timer obj c054014c!
    RTMP_TimerListAdd: add timer obj c054020c!
    RTMP_TimerListAdd: add timer obj c05402cc!
    RTMP_TimerListAdd: add timer obj c0569e9c!
    RTMP_TimerListAdd: add timer obj c056df94!
    RTMP_TimerListAdd: add timer obj c056e054!
    RTMP_TimerListAdd: add timer obj c056e114!
    RTMP_TimerListAdd: add timer obj c056e1d4!
    RTMP_TimerListAdd: add timer obj c056e294!
    RTMP_TimerListAdd: add timer obj c056e354!
    RTMP_TimerListAdd: add timer obj c056e414!
    RTMP_TimerListAdd: add timer obj c056e4d4!
    RTMP_TimerListAdd: add timer obj c056e594!
    RTMP_TimerListAdd: add timer obj c056e654!
    RTMP_TimerListAdd: add timer obj c056e714!
    RTMP_TimerListAdd: add timer obj c056e7d4!
    RTMP_TimerListAdd: add timer obj c056e894!
    RTMP_TimerListAdd: add timer obj c056e954!
    RTMP_TimerListAdd: add timer obj c056ea14!
    RTMP_TimerListAdd: add timer obj c056ead4!
    RTMP_TimerListAdd: add timer obj c053b668!
    RTMP_TimerListAdd: add timer obj c053b6c0!
    RTMP_TimerListAdd: add timer obj c053f760!
    RTMP_TimerListAdd: add timer obj c053f820!
    RTMP_TimerListAdd: add timer obj c053f8e0!
    RTMP_TimerListAdd: add timer obj c053f9a0!
    RTMP_TimerListAdd: add timer obj c053fa60!
    RTMP_TimerListAdd: add timer obj c053fb20!
    RTMP_TimerListAdd: add timer obj c053fbe0!
    RTMP_TimerListAdd: add timer obj c053fca0!
    RTMP_TimerListAdd: add timer obj c053fd60!
    RTMP_TimerListAdd: add timer obj c053fe20!
    RTMP_TimerListAdd: add timer obj c053fee0!
    RTMP_TimerListAdd: add timer obj c053ffa0!
    RTMP_TimerListAdd: add timer obj c0540060!
    RTMP_TimerListAdd: add timer obj c0540120!
    RTMP_TimerListAdd: add timer obj c05401e0!
    RTMP_TimerListAdd: add timer obj c05402a0!
    RTMP_TimerListAdd: add timer obj c0569e70!
    RTMP_TimerListAdd: add timer obj c0569ec8!
    RTMP_TimerListAdd: add timer obj c056df68!
    RTMP_TimerListAdd: add timer obj c056e028!
    RTMP_TimerListAdd: add timer obj c056e0e8!
    RTMP_TimerListAdd: add timer obj c056e1a8!
    RTMP_TimerListAdd: add timer obj c056e268!
    RTMP_TimerListAdd: add timer obj c056e328!
    RTMP_TimerListAdd: add timer obj c056e3e8!
    RTMP_TimerListAdd: add timer obj c056e4a8!
    RTMP_TimerListAdd: add timer obj c056e568!
    RTMP_TimerListAdd: add timer obj c056e628!
    RTMP_TimerListAdd: add timer obj c056e6e8!
    RTMP_TimerListAdd: add timer obj c056e7a8!
    RTMP_TimerListAdd: add timer obj c056e868!
    RTMP_TimerListAdd: add timer obj c056e928!
    RTMP_TimerListAdd: add timer obj c056e9e8!
    RTMP_TimerListAdd: add timer obj c056eaa8!
    RTMP_TimerListAdd: add timer obj c053b63c!
    RTMP_TimerListAdd: add timer obj c0569e44!
    RTMP_TimerListAdd: add timer obj c053f7b8!
    RTMP_TimerListAdd: add timer obj c053f878!
    RTMP_TimerListAdd: add timer obj c053f938!
    RTMP_TimerListAdd: add timer obj c053f9f8!
    RTMP_TimerListAdd: add timer obj c053fab8!
    RTMP_TimerListAdd: add timer obj c053fb78!
    RTMP_TimerListAdd: add timer obj c053fc38!
    RTMP_TimerListAdd: add timer obj c053fcf8!
    RTMP_TimerListAdd: add timer obj c053fdb8!
    RTMP_TimerListAdd: add timer obj c053fe78!
    RTMP_TimerListAdd: add timer obj c053ff38!
    RTMP_TimerListAdd: add timer obj c053fff8!
    RTMP_TimerListAdd: add timer obj c05400b8!
    RTMP_TimerListAdd: add timer obj c0540178!
    RTMP_TimerListAdd: add timer obj c0540238!
    RTMP_TimerListAdd: add timer obj c05402f8!
    RTMP_TimerListAdd: add timer obj c053b710!
    RTMP_TimerListAdd: add timer obj c053b73c!
    RTMP_TimerListAdd: add timer obj c053b768!
    RTMP_TimerListAdd: add timer obj c056dfc0!
    RTMP_TimerListAdd: add timer obj c056e080!
    RTMP_TimerListAdd: add timer obj c056e140!
    RTMP_TimerListAdd: add timer obj c056e200!
    RTMP_TimerListAdd: add timer obj c056e2c0!
    RTMP_TimerListAdd: add timer obj c056e380!
    RTMP_TimerListAdd: add timer obj c056e440!
    RTMP_TimerListAdd: add timer obj c056e500!
    RTMP_TimerListAdd: add timer obj c056e5c0!
    RTMP_TimerListAdd: add timer obj c056e680!
    RTMP_TimerListAdd: add timer obj c056e740!
    RTMP_TimerListAdd: add timer obj c056e800!
    RTMP_TimerListAdd: add timer obj c056e8c0!
    RTMP_TimerListAdd: add timer obj c056e980!
    RTMP_TimerListAdd: add timer obj c056ea40!
    RTMP_TimerListAdd: add timer obj c056eb00!
    RTMP_TimerListAdd: add timer obj c0569f18!
    RTMP_TimerListAdd: add timer obj c0569f44!
    RTMP_TimerListAdd: add timer obj c0569f70!
    RTMP_TimerListAdd: add timer obj c04d5014!
    RTMP_TimerListAdd: add timer obj c04d4bf8!
    RTMP_TimerListAdd: add timer obj c04d4fe4!
    RTMP_TimerListAdd: add timer obj c04d5320!
    RTMP_TimerListAdd: add timer obj c04d5260!
    RTMP_TimerListAdd: add timer obj c04d5290!
    RTMP_TimerListAdd: add timer obj c04d8fbc!
    RTMP_TimerListAdd: add timer obj c04d8ba0!
    RTMP_TimerListAdd: add timer obj c04d8f8c!
    RTMP_TimerListAdd: add timer obj c04d92c8!
    RTMP_TimerListAdd: add timer obj c04d9208!
    RTMP_TimerListAdd: add timer obj c04d9238!
    RTMP_TimerListAdd: add timer obj c04dcf64!
    RTMP_TimerListAdd: add timer obj c04dcb48!
    RTMP_TimerListAdd: add timer obj c04dcf34!
    RTMP_TimerListAdd: add timer obj c04dd270!
    RTMP_TimerListAdd: add timer obj c04dd1b0!
    RTMP_TimerListAdd: add timer obj c04dd1e0!
    RTMP_TimerListAdd: add timer obj c04e0f0c!
    RTMP_TimerListAdd: add timer obj c04e0af0!
    RTMP_TimerListAdd: add timer obj c04e0edc!
    RTMP_TimerListAdd: add timer obj c04e1218!
    RTMP_TimerListAdd: add timer obj c04e1158!
    RTMP_TimerListAdd: add timer obj c04e1188!
    RTMP_TimerListAdd: add timer obj c04e4eb4!
    RTMP_TimerListAdd: add timer obj c04e4a98!
    RTMP_TimerListAdd: add timer obj c04e4e84!
    RTMP_TimerListAdd: add timer obj c04e51c0!
    RTMP_TimerListAdd: add timer obj c04e5100!
    RTMP_TimerListAdd: add timer obj c04e5130!
    RTMP_TimerListAdd: add timer obj c04e8e5c!
    RTMP_TimerListAdd: add timer obj c04e8a40!
    RTMP_TimerListAdd: add timer obj c04e8e2c!
    RTMP_TimerListAdd: add timer obj c04e9168!
    RTMP_TimerListAdd: add timer obj c04e90a8!
    RTMP_TimerListAdd: add timer obj c04e90d8!
    RTMP_TimerListAdd: add timer obj c04ece04!
    RTMP_TimerListAdd: add timer obj c04ec9e8!
    RTMP_TimerListAdd: add timer obj c04ecdd4!
    RTMP_TimerListAdd: add timer obj c04ed110!
    RTMP_TimerListAdd: add timer obj c04ed050!
    RTMP_TimerListAdd: add timer obj c04ed080!
    RTMP_TimerListAdd: add timer obj c04f0dac!
    RTMP_TimerListAdd: add timer obj c04f0990!
    RTMP_TimerListAdd: add timer obj c04f0d7c!
    RTMP_TimerListAdd: add timer obj c04f10b8!
    RTMP_TimerListAdd: add timer obj c04f0ff8!
    RTMP_TimerListAdd: add timer obj c04f1028!
    RTMP_TimerListAdd: add timer obj c053e5ec!
    RTMP_TimerListAdd: add timer obj c053e1d0!
    RTMP_TimerListAdd: add timer obj c053e5bc!
    RTMP_TimerListAdd: add timer obj c053e8f8!
    RTMP_TimerListAdd: add timer obj c053e61c!
    RTMP_TimerListAdd: add timer obj c053e64c!
    RTMP_TimerListAdd: add timer obj c053e67c!
    RTMP_TimerListAdd: add timer obj c056cdf4!
    RTMP_TimerListAdd: add timer obj c056c9d8!
    RTMP_TimerListAdd: add timer obj c056cdc4!
    RTMP_TimerListAdd: add timer obj c056d100!
    RTMP_TimerListAdd: add timer obj c056ce24!
    RTMP_TimerListAdd: add timer obj c056ce54!
    RTMP_TimerListAdd: add timer obj c056ce84!
    RTMP_TimerListAdd: add timer obj c057878c!
    RTMP_TimerListAdd: add timer obj c05788a8!
    RTMP_TimerListAdd: add timer obj c05787b8!
    RTMP_TimerListAdd: add timer obj c056fcc4!
    RTMP_TimerListAdd: add timer obj c04d24c4!
    RTMP_TimerListAdd: add timer obj c04d646c!
    RTMP_TimerListAdd: add timer obj c04da414!
    RTMP_TimerListAdd: add timer obj c04de3bc!
    RTMP_TimerListAdd: add timer obj c04e2364!
    RTMP_TimerListAdd: add timer obj c04e630c!
    RTMP_TimerListAdd: add timer obj c04ea2b4!
    RTMP_TimerListAdd: add timer obj c04ee25c!
    RTMP_TimerListAdd: add timer obj c056f9d0!
    APSDCapable[0]=0
    APSDCapable[1]=0
    APSDCapable[2]=0
    APSDCapable[3]=0
    APSDCapable[4]=0
    APSDCapable[5]=0
    APSDCapable[6]=0
    APSDCapable[7]=0
    APSDCapable[8]=0
    APSDCapable[9]=0
    APSDCapable[10]=0
    APSDCapable[11]=0
    APSDCapable[12]=0
    APSDCapable[13]=0
    APSDCapable[14]=0
    APSDCapable[15]=0
    default ApCliAPSDCapable[0]=0
    default ApCliAPSDCapable[1]=0
    start ch = 1, ch->num = 2
    30 30 30 30 
    30 30 30 30 30 30 30 30 
    26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    start ch = 3, ch->num = 9
    30 30 30 30 
    30 30 30 30 30 30 30 30 
    26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 
    26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 
    start ch = 12, ch->num = 2
    30 30 30 30 
    30 30 30 30 30 30 30 30 
    26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    start ch = 14, ch->num = 1
    30 30 30 30 
    0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    1. Phy Mode = 9
    2. Phy Mode = 9
    E2PROM: D0 target power=0xff20 
    E2PROM: 40 MW Power Delta= 0 
    3. Phy Mode = 9
    AntCfgInit: primary/secondary ant 0/1
    Initialize RF Central Registers for E2 !!!
    Initialize RF Central Registers for E3 !!!
    Initialize RF Channel Registers for E2 !!!
    Initialize RF Channel Registers for E3 !!!
    Initialize RF DCCal Registers for E2 !!!
    Initialize RF DCCal Registers for E3 !!!
    D1 = -1, D2 = 16, CalCode = 40 !!!
    RT6352_Temperature_Init : BBPR49 = 0xffffffff
    RT6352_Temperature_Init : TemperatureRef25C = 0xfffffff5
    Current Temperature from BBP_R49=0xffffffec
    RT6352_TemperatureCalibration:: CurrentTemper 
    @@@ ed_monitor_init : <===
    Main bssid = 54:b8:0a:7d:19:a6
    
    @@@ ed_monitor_init : num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 38, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 40, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 42, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 44, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 46, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 48, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 52, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 54, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 56, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 58, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 60, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 62, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 64, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 100, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 102, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 104, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 106, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 108, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 110, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 112, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 116, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 118, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 120, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 122, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 124, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 126, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 128, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 132, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 134, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 136, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 140, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 149, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 151, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 153, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 155, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 157, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 159, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 161, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 165, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 169, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    start ch = 173, ch->num = 1
    0 0 0 0 
    38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    38 38 38 38 36 36 32 32 38 38 38 38 36 36 32 32 
    32 32 32 32 32 32 32 32 26 26 
    1. Phy Mode = 49
    2. Phy Mode = 49
    ext_pa_current_setting = 1
    3. Phy Mode = 49
    AntCfgInit: primary/secondary ant 0/1
    ChipStructAssign(): RALINK6590 hook !
    MCS Set = ff 00 00 00 01
    MT76x0_ChipBBPAdjust():rf_bw=2, ext_ch=1, PrimCh=36, HT-CentCh=38, VHT-CentCh=42
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    APStartUp(): AP Set CentralFreq at 42(Prim=36, HT-CentCh=38, VHT-CentCh=42, BBP_BW=2)
    Main bssid = 54:b8:0a:7d:19:a8
    <==== rt28xx_init, Status=0
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    RT6352_TemperatureCalibration:: CurrentTemper < 20 
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    MT76x0_ChipBBPAdjust():rf_bw=2, ext_ch=1, PrimCh=44, HT-CentCh=46, VHT-CentCh=42
    MT76x0_ChipSwitchChannel: DefaultTargetPwr = 30
    APStartUp(): AP Set CentralFreq at 42(Prim=44, HT-CentCh=46, VHT-CentCh=42, BBP_BW=2)
    0x1300 = 00064380
    RTMPDrvOpen(1):Check if PDMA is idle!
    RTMPDrvOpen(2):Check if PDMA is idle!
    device rai0 entered promiscuous mode
    br0: port 4(rai0) entering forwarding state
    br0: port 4(rai0) entering forwarding state
    device apclii0 entered promiscuous mode
    br0: port 5(apclii0) entering forwarding state
    br0: port 5(apclii0) entering forwarding state
    Interface doesn't accept private ioctl...
    set (8BE2): Invalid argument
    killall: udhcpc: no process killed
    SIOCSIFFLAGS: Cannot assign requested address
    rm: cannot remove `/var/tmp/previous_dn': No such file or directory
    rm: cannot remove `/var/tmp/previous_dns': No such file or directory
    rm: cannot remove `/var/tmp/m_flag': No such file or directory
    rm: cannot remove `/var/tmp/o_flag': No such file or directory
    RTNETLINK answers: No such file or directory
    cat: /var/etc/resolv.conf: No such file or directory
    sh: cannot create /proc/sys/net/ipv6/conf/br0/disable_ipv6: Directory nonexistent
    Start IPv6 dhclient
    Sat Jan  1 00:00:00 UTC 2011
    rdnssd is already active !
    RT6352_TemperatureCalibration:: CurrentTemper < 20 
    Start IPv6 dhclient
    DHCP server start.
    device_lan_ip=192.168.0.50 , device_lan_subnet_mask=255.255.255.0
    max_leases value (254) not sane, setting to 20 instead
    Unable to open /var/misc/udhcpd.leases for reading
    llmnr: have no available linklocal address. wait count=0
    /tmp/password has been created
    2011-01-01 00:00:00: (network.c.247) warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes 
    Failed to kill daemon: No such file or directory
    Daemon already running on PID 315
    RT6352_TemperatureCalibration:: CurrentTemper < 20 
    RT6352_TemperatureCalibration:: CurrentTemper < 20 
    RT6352_TemperatureCalibration:: CurrentTemper < 20
    

    Warning: Do not attempt to modify the firmware of this device if you do not have hardware to rewrite the firmware to the SPI flash. Before I even powered up the device the first time, I took a dump of the SPI flash in case I ended up “bricking” the device (which I did, many times).

    You can build your own SPI flash reader/writer with a Teensy and a chip clip. I am using the work of Trammell Hudson who gave an awesome talk at 31C3 on manipulating UEFI on MacBook Pros for fun and profit.

    You can find a copy of the SPI dump of my device (firmware 1.05) here. You cannot flash this image without hardware tools as described above. If you flash this dump, your device will have the same MAC address as mine. This dump should be used only as an option of last resort.

    Poking around the D-Link firmware for vulnerabilities

    I would love to say that I’m an infosec god, and that I can hack anything that moves. Really though, I’m not. I tried to find exploits for D-Link, and it doesn’t seem that there is any shortage of HNAP exploits and other nasty things, but I was unable to get the device to do any interesting things for me, like start a telnet server.

    Disassembling the firmware to learn more about installed software

    Since my Google-fu is weak, I couldn’t find the firmware images for this device on D-Link’s website at first, so I just disassembled the firmware I dumped from the MXIC.

    $ binwalk dlink-dap1520.bin 
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    99968         0x18680         U-Boot version string, "U-Boot 1.1.3 (Aug  8 2013 - 10:32:46)"
    100732        0x1897C         HTML document header
    101832        0x18DC8         HTML document footer
    101954        0x18E42         HTML document header
    102754        0x19162         HTML document footer
    102878        0x191DE         HTML document header
    105248        0x19B20         HTML document footer
    105367        0x19B97         HTML document header
    106050        0x19E42         HTML document footer
    106174        0x19EBE         HTML document header
    106255        0x19F0F         HTML document footer
    196962        0x30162         Unix path: /01/01/00/00/00
    327680        0x50000         uImage header, header size: 64 bytes, header CRC: 0xC9616E23, created: 2014-08-22 08:41:24, image size: 909208 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0x895D3AE, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    327744        0x50040         LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2798288 bytes
    1310720       0x140000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 3459080 bytes, 649 inodes, blocksize: 65536 bytes, created: 2014-08-22 08:41:35
    5177344       0x4F0000        uImage header, header size: 64 bytes, header CRC: 0x225D8E97, created: 2013-09-26 08:58:51, image size: 829684 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0xA98529B2, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    5177408       0x4F0040        LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2544052 bytes
    6029312       0x5C0000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 2192260 bytes, 345 inodes, blocksize: 65536 bytes, created: 2013-09-26 08:59:04
    

    Something interesting, there are two Squashfs filesystems on this device. This makes some sense, given what we saw earlier in the uboot logs:

    Check image validation:
    Image1 Header Magic Number --> OK
    Image2 Header Magic Number --> OK
    Image1 Header Checksum --> OK
    Image2 Header Checksum --> OK
    Image1 Data Checksum --> raspi_read: from:50040 len:ddf98 
    OK
    Image2 Data Checksum --> raspi_read: from:4f0040 len:ca8f4 
    OK
    Image1 Stable Flag --> Not stable
    Image1 Try Counter --> 0
    
    Image1: OK Image2: OK
    Both images are OK!!!

    Using dd, we can extract both Squashfs images from the firmware file. I used my dump, but actually I would recommend you just head over to D-Link's website and download the 1.06 firmware image [ZIP] and dump that instead. However, D-Link's firmware is missing the second Squashfs filesystem.

    Squashfs #1

    $ dd if=dlink-dap1520.bin of=squashfs1.bin bs=1 skip=1310720

    Squashfs #2

    $ dd if=dlink-dap1520.bin of=squashfs2.bin bs=1 skip=6029312

    Run the 'ol unsquashfs on squashfs1.bin and squashfs2.bin, and you'll have the extracted filesystems of the squashfs images in my dump the firmware. Remember to rename the directory squashfs-root between runs, or specify unsquashfs -d with a different directory name to decompress the images into respective directories.

    If you're using the D-Link firmware from their website, the dd command is a bit different due to offsets and all:

    $ binwalk DAP1520A1_FW106B04.bin
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             uImage header, header size: 64 bytes, header CRC: 0xBA3B64BA, created: 2015-01-22 03:48:48, image size: 909200 bytes, Data Address: 0x80000000, Entry Point: 0x8000C310, data CRC: 0x310BA125, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
    64            0x40            LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2798288 bytes
    983040        0xF0000         Squashfs filesystem, little endian, version 4.0, compression:xz, size: 3460300 bytes, 649 inodes, blocksize: 65536 bytes, created: 2015-01-22 03:48:54
    $ dd if=DAP1520A1_FW106B04.bin of=dlink106.bin bs=1 skip=983040

    Now unsquashfs that, and you'll have firmware 1.06 from D-Link.

    I'm going to leave investigation of the individual files in the firmware to the reader, but I'd like to state some facts I learned while investigating the firmware:

  • There are two copies of busybox on the firmware, versions 1.01 and 1.6.1. Some programs in /bin are linked to 1.01 and others to 1.6.1. I have no idea why D-Link would do this.
  • Pretty much everything on the device is run from /bin/cli and /bin/ssi. Other people on the web have analyzed these binaries and can tell you what they do (and how insecure they are).
  • The second squashfs image is the D-Link recovery OS. This OS will boot if the first kernel fails the integrity check performed in uboot. Hilariously, it won't boot into the recovery environment if you flash a bad kernel to the device in Image 1 as I found out.

    You might have noticed the flash layout from the binwalk of the firmware dump I made, but here is the actual firmware layout as reported by Linux:

    0x000000000000-0x000000800000 : "ALL"
    0x000000000000-0x000000030000 : "u-boot"
    0x000000030000-0x000000040000 : "nvram"
    0x000000040000-0x000000050000 : "Factory"
    0x000000050000-0x000000140000 : "linux4"
    0x000000140000-0x0000004e0000 : "rootfs"
    0x0000004e0000-0x0000004f0000 : "LANG"
    0x0000004f0000-0x0000005c0000 : "linux4b"
    0x0000005c0000-0x000000800000 : "rootfsb"

    To summarize:
    ALL: This spans from 0x000000 to 0x800000 which is the entire 8MB of the MXIC chip
    u-boot: u-boot loader
    nvram: Storage space for configuration variables. More on this in part 2
    Factory: No idea.
    linux4: This is the primary kernel on the device, and the one that will boot if your device has a valid Image 1. This is the firmware that you download from D-Link's website. Despite the label, it is not Linux 4.x, but 2.6.36.
    rootfs: Squashfs compressed filesystem of the primary OS (Image 1)
    LANG: No idea.
    linux4b: Recovery kernel. This kernel will be booted if Image 1 kernel fails verification.
    rootfsb: Squashfs compressed recovery filesystem. This, along with linux4b boot if Image 1 is corrupt and allow you to flash a firmware through the web interface to restore the device.

    I must say that the inclusion of a recovery OS is an interesting move on D-Link's part. Since I don't buy their products normally, I'm not sure if other D-Link devices also have this recovery OS on them. It seems like a good idea to include on this device, since if a firmware update fails, since there are no Ethernet ports on the device it's not possible to recover via TFTP, as it would be on a normal router. The firmware update from D-Link's website only updates Image 1 squashfs and kernel. Image 2 on my device is firmware version 1.00, and the squashfs filesystem is smaller than the Image 1 OS.

    If you do some maths on the mtd blocks, you will see that with the stock D-Link layout, the Image 1 kernel can only be 983040 bytes (0xF0000) large. Any larger, and the kernel will not fit in flash. The recovery kernel has to be even smaller, maximum 851968 bytes (0xD0000).

    Since this device lacks Ethernet ports, it doesn't include some of the features one would consider necessary on a home router, such as port forwarding, firewall configuration, and the like. I suspect that not needing to include these features gave D-Link the space on flash to store a recovery OS. As you can see though, they did have to make some compromises in the allocation of flash to fit the main and recovery OS within 8MB. The device does not function as a WiFi repeater in the recovery OS, only allowing you to reflash a firmware.

    As much as I would love to cram all of what I did into one post, this is getting long already.

    Stay tuned for part 2 where I compile the D-Link GPL firmware from source and backdoor the device to allow shell access without a login (infosec is hard). If you've heard horror stories about GPL firmwares before, they're all true...

    Come back soon!