一个关于 WiFi 密钥、UART 锁定与受损闪存芯片的故事

Hacker News Top 新闻

摘要

安全研究人员详细讲述了提取并逆向分析 Eufy HomeBase 2 固件的过程,包括通过 SPI 读取闪存(并意外损坏芯片)、逆向 Wi-Fi 凭据生成算法,以及重刷 U-Boot 镜像绕过 UART 锁定获取 shell。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/17 07:45

# WiFi密码、UART封锁与一颗损坏的闪存芯片的故事 | 来源:https://adepts.of0x.cc/eufy-homestation2-firmware/ 亲爱的Fell**owl**ship成员们,正如我们在上一篇文章中所预告的那样,我们敬爱的上帝调试器(God Debugger)似乎投射了他神圣的光芒——载满了操作码——降临在他卑微的仆人身上,于是我们在寺庙中又迎来了一篇新的布道文。请坐下来,听听我们的一位猫头鹰兄弟和他的妹夫是如何继续解剖Eufy生态系统的下一个组件:Eufy HomeBase 2的。 *我们选择分析Eufy HomeBase 2而不是HomeBase 3,是因为正如我们在《从你的门铃到你的家庭网络》(https://adepts.of0x.cc/eufy-doorbell-hacking/)中解释的那样,这是我在我的城市中发现已经部署的版本,而且也是最容易买到的。我们在此分析的固件版本为 **3.4.2.2h(2026-06-05 18:45:48)**。 *这是我第一次接触与"硬件"相关的东西。我之前一直是对可以从网上下载的固件进行漏洞挖掘,所以请原谅我所犯下的罪过。 **重要!!!**在继续阅读本文之前,请务必先阅读前一篇文章。 ## https://adepts.of0x.cc/eufy-homestation2-firmware/#table-of-contents目录 与上一篇文章一样,我们将在这里涵盖多种主题;因此,本组织的某些成员可能对某些章节不感兴趣,或许希望直接跳转到最吸引他们的部分。这就是我们呈现这份神圣献礼的原因:目录。 - 0x00 固件提取 (https://adepts.of0x.cc/eufy-homestation2-firmware/#0x00-firmware-extraction)。描述固件的提取过程(以及我们如何损坏了部分闪存)。 - 0x01 识别WiFi凭据生成算法 (https://adepts.of0x.cc/eufy-homestation2-firmware/#0x01-identifying-the-wifi-credential-generation-algorithm)。逆向工程用于生成OCEAN\_XXXXXX管理WiFi凭据的算法。 - 0x02 对UART封锁进行"橡皮管式"审讯 (https://adepts.of0x.cc/eufy-homestation2-firmware/#0x02-rubber-hose-treatment-to-the-uart-lockdown)。刷新一个植入后门的U-Boot镜像,以通过UART获取shell。 - 0x03 为调试目的进行文件系统修整 (https://adepts.of0x.cc/eufy-homestation2-firmware/#0x03-filesystem-gardening-for-debugging-purposes)。恢复镜像功能有限,需要一些文件系统调整才能运行主程序。 - 0x04 结语 (https://adepts.of0x.cc/eufy-homestation2-firmware/#0x04-final-words)。我分享了在AI时代写文章缺乏热情的感受。 在这个系列的上一章 (https://adepts.of0x.cc/eufy-doorbell-hacking/)中,我提到我们之所以能提取门铃的固件,是因为HomeBase的闪存直接焊接在电路板上,没有暴露任何引脚可以连接测试钩子来转储固件。起初我们并不想拆焊它(尽管最终不得不这样做,正如我稍后将解释的那样),所以我不得不等待获取额外的硬件。Craig S. Blackie (https://x.com/craigsblackie)为我指出了适合Winbond 25Q256JWEQ(WSON8 8.6mm)尺寸的弹簧夹。夹子到货后,我创建了一个你能想象到的最简陋的装置,用橡皮筋固定夹子,然后使用Raspberry Pi的SPI0接口读取数据,就像我们对门铃所做的那样。 使用橡皮筋的设置来读取固件。使用橡皮筋的设置来读取固件。我进行了三次读取,三次的校验和都不相同,这通常意味着出了什么问题。检查转储后,我很快意识到它们全是垃圾数据,完全没用。与门铃不同的是,在这种情况下,读取操作提供的电源同时激活了与内存交互的电路板上的其他组件;因此,为了提取固件,我们必须拆焊芯片并在板外读取。 我的妹夫Óscar Lestón (https://www.linkedin.com/in/%C3%B3scar-lest%C3%B3n-casais-5b970b243/?isSelfProfile=false)是那个勇敢的人,拆焊了闪存芯片(我甚至害怕得不敢尝试): 拆焊。拆焊。虽然内存模块拆下来后看起来很糟糕,但实际上几乎没怎么损坏... 取出的闪存芯片。取出的闪存芯片。最后我们使用Raspberry Pi SPI 0复现了相同的设置来转储固件。这一次三个转储的校验和都相同,并且包含"有趣的数据"。唯一的问题是......有一个64KB的块被擦除/无法读取,很可能是因为我们在取下芯片时损坏了它。 使用Raspberry Pi在板外读取芯片。使用Raspberry Pi在板外读取芯片。大部分固件可以使用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 `` 部分内容被恢复了,有些没有。由于有大量XZ流,我们选择了一个愚蠢的(也许没那么愚蠢的)想法:直接使用`7z`将所有内容提取到一个大块中(希望能在里面通过关键词搜索来雕刻文件)。 `` ➜ 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 `` ## https://adepts.of0x.cc/eufy-homestation2-firmware/#0x01-identifying-the-wifi-credential-generation-algorithm0x01 识别WiFi凭据生成算法 我们想通过固件调查的关键问题之一是用于生成**OCEAN\_XXXXXX**管理WiFi凭据的新算法,因为我们发现它与基于2024年论文所预期的完全不同。特别有趣的是,我们想知道这个算法是否像之前那样脆弱。 写完上一篇文章后,我意识到我作为导师辜负了我的妹夫。我向他展示了如何使用Ghidra和伪C代码,而不是引导他走上光明与理性之路。我为此赎罪,拿出了radare2。 首先我们检查了WiFi网络的ESSID和密码在固件中的位置。一个简单的`grep`就足以识别出包含WiFi名称和凭据的NVRAM变量(用于注册门铃的临时WiFi和管理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 `` 然后我们进行了一次更广泛的搜索,试图找到与WiFi设置相关的二进制文件和/或脚本: `` ➜ 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 & `` 沿着这条线索进一步展开,我们发现了一个有趣的行,与我们已知的信息吻合:ESSID是"OCEAN\_"和MAC地址的拼接: `` ➜ 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 `` 让我们缩小视图以获取完整的脚本: `` ➜ 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 `` 因此,一方面我们找到了ESSID的生成方式,另一方面我们发现密码是从NVRAM变量中读取的(`wpapsk="nvram_get WPAPSK1"`)。让我们用这个名字做一个快速搜索: `` ➜ 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 -- (...) `` 在第一个结果中就有一个看起来像是编译过的二进制文件的东西(那个`%d`很可疑)。我编写了这个简陋的脚本来提取搜索该字符串的ELF文件,然后向后搜索找到魔数头,并假设块中的下一个文件也是ELF: `` #!/usr/bin/env python3 with open("homestation2_firmware00", "rb") as file: data = file.read() needle = data.find(b"WPAPSK1") magic = data.rfind(b"\x7fELF", 0, needle) end = data.find(b"\x7fELF", needle) binary = open("elf_wifi_01", "wb") binary.write(data[magic:end]) binary.close() `` 它完美地工作了: `` ➜ 7z_mandanga python3 carve_wifi.py ➜ 7z_mandanga file elf_wifi_01 elf_wifi_01: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped `` 快速检查符号后,我们发现了大量与WiFi相关的内容,尤其是那个诱人的`set_wifi_pwd`。 `` ➜ 7z_mandanga r2 elf_wifi_01 WARN: Relocs has not been applied. Please use `-e bin.relocs.apply=true` or `-e bin.cache=true` next time -- Use 'zoom.byte=printable' in zoom mode ('z' in Visual mode) to find strings [0x0045d130]> aaa INFO: Analyze all flags starting with sym. and

相似文章

入侵 Eufy 门铃与基站

Lobsters Hottest

深入的安全研究文章,详细讲解了对 Eufy Security 视频门铃及 Homebase 2 基站的逆向工程过程,涵盖远程信号干扰、声音同步协议逆向,以及从闪存转储中提取并解密门铃连接隐藏网络所用凭据的方法。

From your doorbell to your home network

Hacker News Top

A security researcher details how they reverse-engineered the Eufy Security Video Doorbell ecosystem, demonstrating jamming attacks, decoding the soundwave sync protocol, and extracting decrypted credentials from memory dumps.

本来只想下载个 ISO,结果写了个内核漏洞利用

Hacker News Top

作者发现自家 TP-Link EAP770 Wi-Fi 7 存在缺陷会静默损坏大文件下载,而 TP-Link 在开箱前就已将该设备 EOL 且不再提供固件更新。由于官方限制无法通过 shell 写入修复,作者最终自己编写了一个内核漏洞利用来获取自己路由器的 root 权限。

一个解析器,两个漏洞,众多路由器:跨eCos与Linux利用Broadcom WiFi

Hacker News Top

安全研究人员披露了Broadcom 802.11驱动中存在十余年之久的WPS解析器漏洞,可在无认证情况下远程实现代码执行,影响eCos与Linux平台上的数百万台路由器;Broadcom自2018年起便已知晓该漏洞,却拒绝分配CVE或进行公开沟通,导致大量OEM设备至今仍未修补。

TP-Link TL-841N 的 Root、固件分析与持久化凭据

Hacker News Top

通过 UART 对 TP-Link TL-841N 路由器进行 Root 的详细步骤,使用两种方法转储固件,并发现即使恢复出厂设置后仍存在的硬编码凭据,揭示了常见的物联网安全弱点。