返回资讯中心
外部精选
软件工程
#社区热议

A story of WiFi secrets, UART lockdowns and a damaged flash memory chip

Dear Fellowlship, just as we announced in our previous post, our beloved God Debugger has seen fit to cast his divine light -laden with opcodes- upon his humble servant, and we have a new homily here in our temple. Plea…

Hacker Newsmichidk2026/10/16 · 11 天前30 分钟阅读

以下正文同步自 Hacker News,版权归原站所有,已转换为易读排版。

Dear Fellowlship, just as we announced in our previous post, our beloved God Debugger has seen fit to cast his divine light -laden with opcodes- upon his humble servant, and we have a new homily here in our temple. Please, take a seat and listen to the story of how one of our owls, along with his brother-in-law, moved on to dissect the next component of the Eufy ecosystem: the Eufy HomeBase 2.

Dear Fellowlship, just as we announced in our previous post, our beloved God Debugger has seen fit to cast his divine light -laden with opcodes- upon his humble servant, and we have a new homily here in our temple. Please, take a seat and listen to the story of how one of our owls, along with his brother-in-law, moved on to dissect the next component of the Eufy ecosystem: the Eufy HomeBase 2.

We focused our analysis in Eufy Homebase2 instead of Homebase3 because, as we explained in From your doorbell to your home network it’s the version that I found deployed in my city, and also was the easiest to buy. The firmware version we analyze here is 3.4.2.2h (2026-06-05 18:45:48)

This is the first time I touched something related to “hardware”. I always did vuln hunting on firmwares that I could download from internet, so please forgive the sins I commited

Just as with the previous article, we will be covering a variety of topics here; consequently, some members of this fellowlship may have no interest in certain sections and might wish to skip straight to whatever appeals to them most. That is why we present this blessed offering: a table of contents.

In the previous chapter of this saga, I mentioned that we were only able to extract the doorbell’s firmware because the Homebase’s flash memory was soldered directly to the board and did not expose any pins to which we could attach test hooks to dump the firmware. Initially we did not want to desolder it (although in the end we had to as I will explain later) so I had to wait to acquire additional hardware. Craig S. Blackie pointed me to spring clips that would suit the size needed for the Winbond 25Q256JWEQ (WSON8 8.6mm). Once the clip arrived I created the most makeshift setup imaginable, using rubber bands to secure the clip while I proceeded to take readings using the Raspberry Pi’s SPI0 interface, just as we did with the doorbell.

I performed three reads and the three had different checksums, which usually is a indicator of something being wrong. After inspecting the dumps, I quickly realized they were garbage and completely useless. Unlike with the doorbell, in this case, the power supplied for the read operation also activated other components on the board that interacted with the memory; therefore, to extract the firmware, we had to desolder the chip and read it off-board.

My brother-in-law (Óscar Lestón) was the brave soul that desoldered the flash memory (I was too scared to even try it):

Although the memory module looked terrible once removed, it was barely damaged…

And finally we replicated the same setup using the Raspberry Pi SPI 0 to dump the firmware. This time the three dumps had the same checksum and contained “interesting data”. The only problem was… a block of 64kbs was erased / could not be read, highly probable because we damaged it when extracting the chip.

Most of the firmware could be extracted using Binwalk.

/home/psyconauta/research/Eufy/homebase2-firmware/chipoff/STUFF/extractions/homestation2_firmware00.bin

DECIMAL HEXADECIMAL DESCRIPTION

83328 0x14580 U-Boot version string: 1.1.3 (Jun 14 2023 - 16:18:56) 3022226 0x2E1D92 XZ compressed data, total size: 11514432 bytes 14536660 0xDDCFD4 XZ compressed data, total size: 2200 bytes 14538862 0xDDD86E XZ compressed data, total size: 2084 bytes 14540948 0xDDE094 XZ compressed data, total size: 1868 bytes 14542818 0xDDE7E2 XZ compressed data, total size: 168 bytes 14542988 0xDDE88C XZ compressed data, total size: 4192 bytes 14547182 0xDDF8EE XZ compressed data, total size: 2844 bytes 14550028 0xDE040C XZ compressed data, total size: 244 bytes 14550282 0xDE050A XZ compressed data, total size: 1452 bytes 15138816 0xE70000 uImage firmware image, header size: 64 bytes, data size: 3429849 bytes, compression: lzma, CPU: MIPS32, OS: Linux, image type: OS Kernel Image, load address: 0x80000000, entry point: 0x802E04F0, creation time: 2023-06-14 12:56:37, image name: "Linux Kernel Image" 21233664 0x1440000 JFFS2 filesystem, little endian, nodes: 3426, total size: 12255244 bytes

Some parts were recovered, others no. Because there was a lot of XS streams, we choose the stupid (or maybe not so stupid) idea of using directly 7z to extract all in a big blob (and hopefully search by needles there to carve files).

➜ extractions mkdir -p 7z_mandanga ➜ extractions 7z e -y -o7z_mandanga homestation2_firmware00.bin

7-Zip 23.01 (x64) : Copyright (c) 1999-2023 Igor Pavlov : 2023-06-20 64-bit locale=es_ES.UTF-8 Threads:16 OPEN_MAX:1024

Scanning the drive for archives: 1 file, 33554432 bytes (32 MiB)

Extracting archive: homestation2_firmware00.bin

ERRORS: There are data after the end of archive

-- Path = homestation2_firmware00.bin Type = xz ERRORS: There are data after the end of archive Offset = 3022226 Physical Size = 11514432 Tail Size = 19017774 Method = LZMA2:17 Streams = 358 Blocks = 358 Characteristics = BlockPackSize BlockUnpackSize

ERROR: There are some data after the end of the payload data : homestation2_firmware00

Sub items Errors: 1

Archives with Errors: 1

Open Errors: 1

Sub items Errors: 1 ➜ extractions ls -hal 7z_mandanga total 42M drwxrwxr-x 2 psyconauta psyconauta 4,0K sep 19 19:41 . drwxrwxr-x 4 psyconauta psyconauta 4,0K sep 19 19:34 .. -rw-rw-r-- 1 psyconauta psyconauta 42M sep 19 18:35 homestation2_firmware00

0x01 Identifying the Wifi credential generation algorithm

One of the key questions we wanted to investigate via the firmware was the new algorithm for generating credentials for the OCEAN_XXXXXX management Wi-Fi, as we observed that it differed completely from what was expected based on the 2024 paper. Specially, we were interesting in know if the algorithm was weak as previously or no.

After writing the previous article, I realized I had failed my brother-in-law as a mentor. I had shown him how to use Ghidra and pseudo-C instead of leading him down the path of light and reason. I atoned for my sin and drew radare2.

First we checked where the Wifi network ESSID and password could be found in the firmware. A simple grep was enough to identify NVRAM variables containing both wifi names and credentials (the ad-hoc wifi used to enroll the doorbell and the mangement wifi):

➜ extractions strings homestation2_firmware00.bin_83358_unknown.raw | grep F1VSXfNQL_ZCd3k -A5 -B5 AuthMode=WPAPSKWPA2PSK;WPAPSKWPA2PSK EncrypType=AES;AES RekeyInterval=3600 RekeyMethod=TIME PMKCachePeriod=10 WPAPSK1=F1VSXfNQL_ZCd3k WPAPSK2=913ceac3 DefaultKeyID=2;2 Key1Type=0 Key1Str1= Key2Type=0 ➜ extractions strings homestation2_firmware00.bin_83358_unknown.raw | grep OCEAN_E9A5FC -A5 -B5 DDNSPassword= CountryRegion=0 CountryRegionABand=7 CountryCode=ES BssidNum=2 SSID1=OCEAN_E9A5FC SSID2=4350c372 WirelessMode=9 TxRate=0 Channel=0 BasicRate=15

Then we tried a more broad search to try to identify binaries and/or scripts related to the wifi setup:

➜ 7z_mandanga strings homestation2_firmware00 | rg 'OCEAN_' %s,<%d>file not exist.return OCEAN_TEST_TF_CARD_NOT_FOUND. OCEAN_MAGIC out of memory of OCEAN_TEST_DATA OCEAN_TF_CARD_FORMAT_SUCCESSED OCEAN_TF_CARD_FORMAT_FAIL_MOUNT OCEAN_TF_CARD_FORMAT_FAIL OCEAN_TF_CARD_FORMAT_IN_USE ssid_prefix="OCEAN_" ssid_prefix="OCEAN_" CONFIG_LEDS_OCEAN_PWM=y OCEAN_RUN=/mnt/ocean.sh if [ -f $OCEAN_RUN ];then chmod +x $OCEAN_RUN sh $OCEAN_RUN &

Unraveling a bit more this thread we found an interesting line that matches what we already knew: the ESSID is the concatenation of “OCEAN_” and MAC address:

➜ 7z_mandanga strings homestation2_firmware00 | rg 'ssid_prefix' ssid_prefix="ocean_factory_" ssid_prefix="OCEAN_" WIFI_SSID=$ssid_prefix$SSID_MAC ssid_prefix="nvram_get ssid_pre" if [ "$ssid_prefix" = "" ];then ssid_prefix="OCEAN_" WIFI_SSID=$ssid_prefix$SSID_MAC

Let’s zoom out to get the full script:

➜ 7z_mandanga strings homestation2_firmware00 | rg 'ssid_prefix' -B30 -A40 .sdata .sbss .bss .pdr .gnu.attributes .mdebug.abi32 #!/bin/sh work_mode="cat /var/run/testmode" def_mode="nvram_get 2860 work_mode" SSID_MAC="" nvram_ssid="nvram_get 2860 SSID1" bssid_num=nvram_get 2860 BssidNum eth_state=apcliDaemon get_eth_state repeater_enable=nvram_get 2860 RepeaterEnable wifi_11bgn_enabled=nvram_get 2860 wifi_11bgn_enabled Wireless_Mode=9 if [ "$eth_state" = "0" -a "$repeater_enable" = "1" -a "$wifi_11bgn_enabled" = "1" ]; then Wireless_Mode=9 get_wifi_mac() SSID_MAC="ifconfig ra0 | grep HWaddr | awk '{print $5}' | awk -F ':' '{print $4$5$6}'" #SSID_MAC="ifconfig ra0 | grep HWaddr | awk '{print $5}' | awk -F ':' '{print $5$6}'" while [ "$SSID_MAC" = "" ] sleep 1 SSID_MAC="ifconfig ra0 | grep HWaddr | awk '{print $5}' | awk -F ':' '{print $4$5$6}'" #SSID_MAC="ifconfig ra0 | grep HWaddr | awk '{print $5}' | awk -F ':' '{print $5$6}'" done echo "get mac in ssid is:$SSID_MAC" if [ "$bssid_num" = "2" ];then ifconfig ra1 down>/dev/null 2>&1 if [ "$work_mode" = "1" ];then ssid_prefix="ocean_factory_" if [ "$def_mode" = "factory_mode" ];then ssid_prefix="OCEAN_" get_wifi_mac WIFI_SSID=$ssid_prefix$SSID_MAC echo "set wifi SSID:$WIFI_SSID" iwpriv ra0 set SSID="$WIFI_SSID" if [ "$def_mode" = "factory_mode" ];then

only WIFI_SSID is not equ nvram_ssid, update nvram.

if [ "$nvram_ssid" != $WIFI_SSID ];then nvram_set SSID1 $WIFI_SSID fi else ssid_prefix="nvram_get ssid_pre" if [ "$ssid_prefix" = "" ];then ssid_prefix="OCEAN_" get_wifi_mac WIFI_SSID=$ssid_prefix$SSID_MAC echo "set wifi SSID:$WIFI_SSID"

only WIFI_SSID is not equ nvram_ssid, update nvram.

if [ "$nvram_ssid" != $WIFI_SSID ];then nvram_set SSID1 $WIFI_SSID iwpriv ra0 set SSID="$WIFI_SSID" if [ "$nvram_ssid" = "MT7628_AP" ];then sleep 3 #reboot /sbin/umount_and_reboot.sh fi iwpriv ra0 set HtBw=0 iwpriv ra0 set WirelessMode=$Wireless_Mode iwpriv ra0 set HtGi=0 iwpriv ra0 set SCSEnable=1 if [ "$bssid_num" = "2" ];then

ssid and pwd set in RT2860_default_vlan

#iwpriv ra1 set SSID="AKDevbind" #iwpriv ra1 set WPAPSK="Anker39#*" #iwpriv ra1 set SSID="XMCONFIG" #iwpriv ra1 set WPAPSK="12345678" iwpriv ra1 set HtBw=0 iwpriv ra1 set WirelessMode=$Wireless_Mode iwpriv ra1 set HtGi=0 iwpriv ra1 set SCSEnable=1 iwpriv ra1 set WmmCapable=1 iwpriv ra1 set ed_chk=0 wpapsk="nvram_get WPAPSK1" if [ "$wpapsk" != "" ];then #echo "iwpriv ra0 set WPAPSK=$wpapsk" iwpriv ra0 set WPAPSK=$wpapsk

set wmm

iwpriv ra0 set WmmCapable=1

change wan mode,

wifi apcli iwpriv home_security if [ "$eth_state" = "0" -a "$repeater_enable" = "1" ]; then switch_wan_interface.sh eth2.2 apcli0 0 #!/bin/sh

So, from one side we found how the ESSID is created and from the other side we found that the password is read from a NVRAM variable (wpapsk="nvram_get WPAPSK1"). Let’s do a quick search with that name:

➜ 7z_mandanga strings homestation2_firmware00 | rg -i 'WPAPSK1' -A5 -B5 wifi channel:%d. fail to get freq. /dev/gpio Error while opening /dev/gpio SSID1 WPAPSK1 SSID2 ra1 SSID=%s WPAPSK2 ra1 WIFI_PWD=%s /sbin/chpasswd.sh eufycamera %s

(...)

Just in the first hit there is something that looks like a compiled binary (that %d is

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →