文章总结: 本文详细介绍了对VStarcamCB73安防摄像头的逆向工程过程,包括设备拆解、固件提取、UART接口访问、修改启动参数获取shell以及通过逆向工程分析二进制文件找出管理员密码。作者通过硬件拆解获取了SPI闪存芯片中的固件,通过UART接口访问设备控制台,并利用引导程序漏洞修改启动参数直接进入shell。通过Ghidra逆向工程工具分析固件中的二进制文件,发现了管理员密码的生成逻辑,最终成功破解了管理员密码20170912,获取了设备的root权限。这篇文章展示了物联网设备渗透测试的完整流程,包括硬件破解、固件分析和逆向工程等技术。
综合评分: 100
文章分类: 逆向分析,IoT安全,硬件破解,渗透测试,固件分析
逆向工程 | 从拆解摄像头到获取超管密码
原创
白帽子左一
白帽子左一
2025年7月21日 12:01
美国
扫码领资料
获网安教程
来Track安全社区投稿~
赢千元稿费!还有保底奖励~(https://bbs.zkaq.cn)
我经常被问到如何入门物联网渗透测试或硬件破解。我的常规建议是直接拿一个设备,拆开它,然后深入研究!最近,我在东南亚旅行时获得了一个有趣的安防摄像头:VStarcam CB73。我认为这个摄像头是一个非常适合逆向工程和寻找安全漏洞的目标。
从下图可以看到,这是一个非常小的摄像头,尺寸大约为1平方英寸。
The VStarcam CB73 – an extremely 小型WiFi安防摄像头
该设备通过WiFi连接互联网,并通过一个移动应用程序与摄像头进行交互。事实上,我之所以选择这个设备作为目标,其中一个原因是它的宣传资料中声称可以通过移动应用随时随地查看摄像头的视频画面并接收警报。这表明设备会与某个云服务器进行通信。作为一名黑客,我非常喜欢设备具有复杂性。设备越复杂,其攻击面就越大,我发现有趣漏洞的可能性也就越高。
设备分析
对任何物联网设备进行分析的第一步就是拆开它,取出所有的印刷电路板(PCB)以及其他连接组件。拧下几颗螺丝并撬开外壳后,我们可以取出主电路板和电池。
Vstarcam CB73的内部构造
在设备电路板的顶部,我们可以看到一个Micro USB接口、两个开关(一个用于电源,一个用于WiFi)、一个标有“Ingenic T31”的芯片隐藏在摄像头后面,以及一组标有“G”、“R”、“T”、“N”的4个测试点。在电路板这一侧,最引起我兴趣的两样东西是T31芯片和这4个带标签的测试点。
标注好的UART测试点
通过在互联网上进行一些搜索,我们可以了解到Ingenic T31芯片是一个集成了MIPS CPU、板载内存和视频编码功能的模块,所有功能都封装在一个单一芯片中。
然而,PCB顶部最有趣的部分是这些标注好的测试点。对于一个经验丰富的物联网黑客来说,这些测试点显然是UART接口。UART是一种串行通信协议,通常用于Linux物联网设备上,供开发人员访问设备控制台以进行调试。我们稍后会回到这个UART接口。
在设备电路板的底部,我们可以看到WiFi模块、一个SD卡插槽,以及一个8引脚的SPI闪存芯片。在这一侧,最有趣的显然是标有“25QH64CHIQ”的闪存芯片。这很可能就是设备固件的存储位置。
固件提取
既然我们已经确认了SPI闪存芯片的存在,接下来我们将从PCB上取下这个芯片,并读取其中的固件,以便分析其中的安全问题。
为了移除SPI闪存芯片,我们将使用热风重工台。
我的热风重工台
我通常将热风台的温度设置为465摄氏度(869华氏度),风速设置在中等范围。然后用热风吹向闪存芯片,并保持耐心!我们不希望过快地将芯片拔下,以免把连接芯片与PCB的焊盘一起撕下来。
从PCB上拆下后的XMC 25QH64C SPI闪存芯片
芯片拆下后,我们将其放入XGecu T56通用编程器中,使用匹配尺寸的插槽进行读取。
SPI闪存芯片已放入XGecu T56通用编程器
现在将SPI闪存芯片放入XGecu T56之后,我们需要运行其仅支持Windows的Xgpro软件。我们可以直接在Windows上运行它,或者在Linux中借助Wine运行。在Wine中,我们需要使用radiomanV的TL866 GitHub项目中提供的特殊DLL。
首先,在“Search Device”(搜索设备)菜单中查找我们的闪存芯片。
在XGpro中选择25QH64C设备
然后我们执行“读取”(READ)操作。
在XGpro中执行读取操作
最后,我们将文件保存为“cb73fw.bin”。
将固件保存到文件中
在成功读取闪存芯片的内容后,我们使用热风重工台将这个8引脚的SPI闪存芯片重新焊接回设备的PCB上。
UART控制台
现在我们将注意力重新转向PCB顶部4个测试点中发现的UART接口。
首先,在设备通电的状态下,我们使用万用表测量电压,将黑色探针接触GND测试点(标为“G”),红色探针接触TX测试点(标为“T”)或VCC测试点(标为“N”)。我们测得的电压为3.3伏,这是Linux物联网设备中最常见的UART电压。我们将使用FTDI TTL-232R-3V3线缆将其连接到我们的PC。
通过查看 TTL-232R-3V3 数据手册,我们找到了该线缆的引脚排列,如下所示。
3.3伏UART转USB线缆引脚排列
需要注意的一点是,将目标设备的TX和RX线连接到线缆时,信号需要交叉连接。也就是说,设备的TX需要连接到线缆的RX,反之亦然。这样我们的线缆连接如下:
| Device Pad | UART Cable Pin | UART Cable Color |
| — | — | — |
| TX | RX | Yellow |
| RX | TX | Orange |
| GND | GND | Black |
现在的问题是:我们如何将这三根线连接到那些微小的测试点上?首先,我们不必非得连接到GND测试点,可以在电路板上找到更方便的地方接地。就我们而言,我们选择将鳄鱼夹夹在WiFi天线上。
鳄鱼夹连接到WiFi天线
这样我们只剩下两个测试点需要连接到我们的UART适配器。一种方法是将细小的焊线焊接到测试点上。这需要我们具备一定的焊接设备,并且小心不要把测试点从PCB上撕下来。对此任务,我们将使用PCBite探针。这些探针末端有一个带弹簧的小针头,使我们能够小心地将探针放置在测试点上。
PCBite探针连接到微小的测试点
现在,将我们的UART适配器插入Linux电脑后,我们运行以下命令:
picocom -b 115200 /dev/ttyUSB0
Picocom是一款终端仿真器,允许我们通过UART线缆发送和接收串行数据。115200是物联网设备上最常见的波特率(每秒传输的比特数)。在本例中,我们的猜测是正确的。“/dev/ttyUSB0”是我们的USB设备在Linux电脑上的映射位置。
现在picocom正在运行,我们可以开启设备电源,观察设备启动时的Linux控制台日志。我习惯将设备启动时UART控制台的全部输出复制到文本文件中,以便后续查看。
U-Boot SPL 2013.07-00014-g7e49a25-dirty (Jul 15 2022 - 19:00:08)
[ ---------- TRUNCATED ---------- ]
U-Boot 2013.07 (Aug 30 2022 - 17:52:01)
[ ---------- TRUNCATED ---------- ]
Hit any key to stop autoboot: 0
the manufacturer 20
SF: Detected XM25QH64C
--->probe spend 4 ms
SF: 2097152 bytes @ 0x40000 Read: OK
--->read spend 675 ms
## Booting kernel from Legacy Image at 80600000 ...
Image Name: Linux-3.10.14__isvp_swan_1.0__
Image Type: MIPS Linux Kernel Image (lzma compressed)
Data Size: 1480678 Bytes = 1.4 MiB
Load Address: 80010000
Entry Point: 803283a0
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
[ ---------- TRUNCATED ---------- ]
veepai login:
注意,系统完全启动后,UART控制台会提示我们登录。当前我们没有用户名和密码,因此无法登录。
从引导程序菜单进入Shell
在让设备正常启动一次后,我们想查看引导程序是否允许我们进入其引导菜单。为此,我们先关闭设备电源,在picocom窗口中按住回车键,然后重新开机。这样会使启动过程暂停,进入U-Boot引导菜单:
[ ---------- TRUNCATED ---------- ]
Hit any key to stop autoboot: 0
isvp_t31#
isvp_t31#
isvp_t31#
在这个引导菜单中,我们可以运行help命令来显示该设备支持的命令。这非常有用,因为设备开发者在编译U-Boot时,有许多可选的工具程序,这些工具可能会包含或不包含在某个特定的构建版本中。
isvp_t31# help
? - alias for 'help'
boot - boot default, i.e., run 'bootcmd'
boota - boot android system
bootd - boot default, i.e., run 'bootcmd'
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
chpart - change active partition
coninfo - print console devices and information
echo - echo args to console
env - environment handling commands
ethphy - ethphy contrl
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
getadc - get adc val elapsed,
gettime - get timer val elapsed,
go - start application at address 'addr'
help - print command description/usage
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loady - load binary file over serial line (ymodem mode)
mmc - MMC sub system
mmcinfo - display MMC info
mtdparts- define flash/nand partitions
printenv- print environment variables
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv - set environment variables
sf - SPI flash sub-system
sleep - delay execution for some time
source - run script from memory
tftpboot- boot image via network using TFTP protocol
version - print monitor, compiler and linker version
watchdog- open or colse the watchdog
我们将运行的第一个命令是printenv。该命令会打印出当前加载的U-Boot环境变量。执行后,我们看到如下输出:
isvp_t31# printenv
adcthreshold=1240
baudrate=115200
bootargs=console=ttyS1,115200n8 quiet mem=42M@0x0 rmem=22M@0x2A00000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(boot),1536k(kernel),4608k(root),1792k(appfs),8m@0(all)
bootcmd=sf probe;sf read 0x80600000 0x40000 0x200000; bootm 0x80600000
bootdelay=1
ethact=Jz4775-9161
ethaddr=00:d0:d0:00:95:27
gatewayip=192.168.1.1
ipaddr=192.168.1.126
loads_echo=1
netmask=255.255.255.0
serverip=192.168.1.101
stderr=serial
stdin=serial
stdout=serial
Environment size: 537/16380 bytes
我感兴趣的主要环境变量是bootargs:
bootargs=console=ttyS1,115200n8 quiet mem=42M@0x0 rmem=22M@0x2A00000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(boot),1536k(kernel),4608k(root),1792k(appfs),8m@0(all)
注意bootargs变量中的init=/linuxrc部分。该参数告诉Linux内核如何执行初始化系统,初始化系统是Linux内核在系统启动后启动的第一个进程。在这台设备以及大多数物联网设备中,该初始化系统由BusyBox提供。这个初始化系统会执行各种操作,比如挂载除根文件系统之外的其他文件系统,并在UART控制台上运行login程序。我们可以将初始化系统从init=/linuxrc修改为init=/bin/sh,使设备启动时直接进入shell,而不是默认的初始化系统。为此,我们使用如下setenv命令:
setenv bootargs console=ttyS1,115200n8 quiet mem=42M@0x0 rmem=22M@0x2A00000 init=/bin/sh rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:256k(boot),1536k(kernel),4608k(root),1792k(appfs),8m@0(all)
然后我们运行boot命令,使用新的环境变量启动设备:
isvp_t31# boot
the manufacturer 20
SF: Detected XM25QH64C
--->probe spend 4 ms
SF: 2097152 bytes @ 0x40000 Read: OK
--->read spend 675 ms
## Booting kernel from Legacy Image at 80600000 ...
Image Name: Linux-3.10.14__isvp_swan_1.0__
Image Type: MIPS Linux Kernel Image (lzma compressed)
Data Size: 1480678 Bytes = 1.4 MiB
Load Address: 80010000
Entry Point: 803283a0
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ...
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Initializing cgroup subsys cpuacct
[ 0.000000] Linux version 3.10.14__isvp_swan_1.0__ (madong@vstarcam) (gcc version 4.7.2 (Ingenic r2.3.3 2016.12) ) #7 PREEMPT Tue Aug 30 16:11:27 CST 2022
[ 0.000000] bootconsole [early0] enabled
[ 0.000000] CPU0 RESET ERROR PC:DA64E93C
[ 0.000000] CPU0 revision is: 00d00100 (Ingenic Xburst)
[ 0.000000] FPU revision is: 00b70000
[ 0.000000] CCLK:1104MHz L2CLK:552Mhz H0CLK:250MHz H2CLK:250Mhz PCLK:125Mhz
[ 0.000000] Determined physical RAM map:
[ 0.000000] memory: 00429000 @ 00010000 (usable)
[ 0.000000] memory: 00037000 @ 00439000 (usable after init)
[ 0.093754] drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
/bin/sh: can't access tty; job control turned off
/ #
接着我们需要运行几个命令来挂载/system分区:
mount -t proc proc /proc
mount -t tmpfs tmpfs /dev
mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
mount -t jffs2 /dev/mtdblock3 /system
让我们来看看/etc/passwd文件:
~ # ls -l /etc/passwd
lrwxrwxrwx 1 1001 1001 20 Jun 16 2021 /etc/passwd -> /system/param/passwd
注意,/etc/passwd是指向/system/param/passwd的符号链接。为什么它是一个符号链接呢?让我们检查一下mount命令:
~ # mount
rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
proc on /proc type proc (rw,relatime)
tmpfs on /dev type tmpfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,relatime)
tmpfs on /run type tmpfs (rw,nosuid,nodev,relatime,mode=755)
sysfs on /sys type sysfs (rw,relatime)
/dev/mtdblock3 on /system type jffs2 (rw,relatime)
注意,第一行显示根文件系统(挂载在/上)是只读的。实际上,我们还能通过它是SquashFS文件系统这一点确认该分区为只读。SquashFS因其设计原因,不能以写权限挂载。因此,如果系统开发者希望某些通常位于根文件系统中的内容可写,他们会将根文件系统中的该文件做成符号链接,指向挂载在/system上的JFFS2分区中可写的文件。
现在我们可以获取管理员用户的密码哈希值:
# cat /etc/passwd
vstarcam2017:uTV43RfKc73oM:0:0:Administrator:/:/bin/sh
我尝试使用hashcat和多个字典破解该密码哈希,但未成功:
echo 'uTV43RfKc73oM' > hash.txt
hashcat -m1500 hash.txt wordlist1.txt wordlist2.txt ...
让我们看看是否能通过另一种方式找出密码……
固件分析
现在我们回到从SPI闪存芯片中读取的固件。让我们使用binwalk从固件镜像中提取所有可以提取的文件系统:
$ binwalk -e cb73fw.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
48253 0xBC7D JBOOT STAG header, image id: 2, timestamp 0x1400A420, image size: 554991616 bytes, image JBOOT checksum: 0xA020, header JBOOT checksum: 0x400
125385 0x1E9C9 JBOOT STAG header, image id: 2, timestamp 0x18A3A200, image size: 682602496 bytes, image JBOOT checksum: 0xA400, header JBOOT checksum: 0x2127
127813 0x1F345 JBOOT STAG header, image id: 2, timestamp 0xB024318, image size: 4278207360 bytes, image JBOOT checksum: 0x620F, header JBOOT checksum: 0x432
129749 0x1FAD5 JBOOT STAG header, image id: 5, timestamp 0x250411FA, image size: 537010824 bytes, image JBOOT checksum: 0xBC00, header JBOOT checksum: 0x218F
164817 0x283D1 JBOOT STAG header, image id: 3, timestamp 0xA004918, image size: 553722640 bytes, image JBOOT checksum: 0x4018, header JBOOT checksum: 0x2B00
186521 0x2D899 JBOOT STAG header, image id: 3, timestamp 0xD006210, image size: 2148810752 bytes, image JBOOT checksum: 0x9980, header JBOOT checksum: 0x308F
190224 0x2E710 CRC32 polynomial table, little endian
194484 0x2F7B4 LZO compressed data
197912 0x30518 Android bootimg, kernel size: 0 bytes, kernel addr: 0x70657250, ramdisk size: 543519329 bytes, ramdisk addr: 0x6E72656B, product name: "mem boot start"
262144 0x40000 uImage header, header size: 64 bytes, header CRC: 0xC5F74AE8, created: 2022-08-30 08:11:32, image size: 1480678 bytes, Data Address: 0x80010000, Entry Point: 0x803283A0, data CRC: 0x42B2FB8A, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux-3.10.14__isvp_swan_1.0__"
262208 0x40040 LZMA compressed data, properties: 0x5D, dictionary size: 67108864 bytes, uncompressed size: -1 bytes
1301640 0x13DC88 JBOOT STAG header, image id: 14, timestamp 0xEA9BD3FE, image size: 3424185437 bytes, image JBOOT checksum: 0x9F9A, header JBOOT checksum: 0xD67
1835008 0x1C0000 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4680592 bytes, 376 inodes, blocksize: 65536 bytes, created: 2022-04-08 13:34:09
6553612 0x64000C JFFS2 filesystem, little endian
6586448 0x648050 Zlib compressed data, compressed
6586552 0x6480B8 JFFS2 filesystem, little endian
6587300 0x6483A4 Zlib compressed data, compressed
[ ---------- TRUNCATED ---------- ]
查看binwalk创建的_cb73fw.bin.extracted目录,我们可以看到大量的文件和文件夹。
$ ls -l _cb73fw.bin.extracted/
total 657080
-rw-r--r-- 1 nmatt nmatt 4680592 Jul 26 15:18 1C0000.squashfs
[ ---------- TRUNCATED ---------- ]
drwxr-xr-x 6 nmatt nmatt 4096 Jul 26 15:18 jffs2-root
drwxr-xr-x 6 nmatt nmatt 4096 Jul 26 15:18 jffs2-root-0
drwxr-xr-x 6 nmatt nmatt 4096 Jul 26 15:18 jffs2-root-1
drwxr-xr-x 6 nmatt nmatt 4096 Jul 26 15:18 jffs2-root-2
[ ---------- TRUNCATED ---------- ]
drwxr-xr-x 19 nmatt nmatt 4096 Jun 15 2021 squashfs-root
其中有些是重复的和误报。比较突出的项目是squashfs文件系统(_cb73fw.bin.extracted/squashfs-root/)和第一个列出的JFFS2文件系统(_cb73fw.bin.extracted/jffs2-root)。
查看SquashFS文件系统,我们看到看起来像是Linux根文件系统:
$ ls -l _cb73fw.bin.extracted/squashfs-root/
total 68
drwxr-xr-x 3 nmatt nmatt 4096 Jun 15 2021 addfs
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 bin
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 dev
drwxr-xr-x 4 nmatt nmatt 4096 Jul 26 15:18 etc
drwxr-xr-x 5 nmatt nmatt 4096 Jun 15 2021 lib
lrwxrwxrwx 1 nmatt nmatt 11 Jun 15 2021 linuxrc -> bin/busybox
drwxr-xr-x 2 nmatt nmatt 4096 Jun 29 2021 media
drwxr-xr-x 6 nmatt nmatt 4096 Jun 15 2021 mnt
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 opt
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 proc
drwx------ 2 nmatt nmatt 4096 Jun 15 2021 root
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 run
drwxr-xr-x 2 nmatt nmatt 4096 Jun 16 2021 sbin
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 sys
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 system
drwxr-xr-x 2 nmatt nmatt 4096 Jun 15 2021 tmp
drwxr-xr-x 6 nmatt nmatt 4096 Jun 15 2021 usr
drwxr-xr-x 4 nmatt nmatt 4096 Jun 15 2021 var
但请记住,passwd文件的内容实际上并不存放在这个只读的根文件系统中。现在让我们查看JFFS2文件系统:
$ ls -l _cb73fw.bin.extracted/jffs2-root
total 16
drwxr-xr-x 2 nmatt nmatt 4096 Jul 26 15:18 init
drwxr-xr-x 2 nmatt nmatt 4096 Jul 26 15:18 param
drwxr-xr-x 4 nmatt nmatt 4096 Jul 26 15:18 system
drwxr-xr-x 3 nmatt nmatt 4096 Jul 26 15:18 www
如果还记得上文,/etc/passwd文件是指向/system/param/passwd的符号链接,而JFFS2文件系统挂载在/system上。这意味着我们应该能在_cb73fw.bin.extracted/jffs2-root/param/passwd找到同样的passwd文件。
$ cat _cb73fw.bin.extracted/jffs2-root/param/passwd
vstarcam2017:uTV43RfKc73oM:0:0:Administrator:/:/bin/sh
好的,我们看到了同样的文件。但是否有东西会生成这个文件呢?毕竟它是存储在可写文件系统中的。让我们在提取的固件中查找任何与passwd相关的内容。
$ sudo grep -r "/etc/passwd" _cb73fw.bin.extracted/
grep: _cb73fw.bin.extracted/jffs2-root/system/bin/encoder: binary file matches
grep: _cb73fw.bin.extracted/jffs2-root-63/system/bin/encoder: binary file matches
grep: _cb73fw.bin.extracted/jffs2-root-4/system/bin/encoder: binary file matches
[ ---------- TRUNCATED ---------- ]
有趣的是,在_cb73fw.bin.extracted/jffs2-root/system/bin/encoder文件中包含了字符串/etc/passwd。仔细查看该文件,我们发现它是一个MIPS架构的二进制可执行文件。
$ file _cb73fw.bin.extracted/jffs2-root/system/bin/encoder
_cb73fw.bin.extracted/jffs2-root/system/bin/encoder: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
使用Ghidra逆向工程Root密码
接下来,我们将在Ghidra中打开这个程序,Ghidra是一款由NSA开发并开源的逆向工程工具。打开二进制文件后,工具会提示我们允许执行一些初步分析。我们将保持所有默认的分析选项。
Ghidra提示我们对该二进制文件进行分析
选择默认设置并点击“Analyze”(分析)
搜索字符串“/etc/passwd”
我们看到该字符串位于二进制文件的地址0x004ef7d0。点击该字符串后,Ghidra主窗口会跳转到该地址,并显示一个名为s_/etc/passwd_004ef7d0的字符串引用。针对该字符串,我们执行以下操作:右键点击 -> References(引用)-> Show References to s_/etc/passwd_004ef7d0(显示对s_/etc/passwd_004ef7d0的引用)。这将搜索二进制文件中所有引用该字符串的代码。
对s_/etc/passwd_004ef7d0的引用搜索
结果显示字符串/etc/passwd在地址0x0047bcdc处有一个引用。点击该地址后,Ghidra主窗口会跳转到引用该字符串的汇编指令位置,同时反编译窗口会显示对应的函数。
Ghidra反编译窗口中的/etc/passwd引用
Ghidra反编译出的整个函数如下:
void FUN_0047bcc4(void)
{
FILE *pFVar1;
uint __seed;
long lVar2;
char *pcVar3;
size_t sVar4;
undefined4 local_10c;
undefined4 local_108;
undefined4 local_104;
undefined2 local_100;
char local_fe;
undefined4 local_8c;
undefined4 local_88;
undefined local_84;
undefined auStack_83 [119];
char acStack_c [4];
pFVar1 = fopen("/etc/passwd","wb");
if (pFVar1 != (FILE *)0x0) {
memset(&local_10c,0,0x80);
local_8c = 0x37313032;
local_88 = 0x32313930;
local_84 = 0;
memset(auStack_83,0,0x77);
__seed = time((time_t *)0x0);
srandom(__seed);
lVar2 = random();
FUN_0047bc60(acStack_c,lVar2,2);
pcVar3 = crypt((char *)&local_8c,acStack_c);
sprintf((char *)&local_10c,"vstarcam2017:%s:0:0:Administrator:/:/bin/sh",pcVar3);
sVar4 = strlen((char *)&local_10c);
fwrite(&local_10c,1,sVar4,pFVar1);
fclose(pFVar1);
pFVar1 = fopen("/etc/group","wb");
if (pFVar1 != (FILE *)0x0) {
memset(&local_10c,0,0x80);
local_10c._0_1_ = 'r';
local_10c._1_1_ = 'o';
local_10c._2_1_ = 'o';
local_10c._3_1_ = 't';
local_108._0_1_ = ':';
local_108._1_1_ = 'x';
local_108._2_1_ = ':';
local_108._3_1_ = '0';
local_104._0_1_ = ':';
local_104._1_1_ = 'a';
local_104._2_1_ = 'd';
local_104._3_1_ = 'm';
local_100._0_1_ = 'i';
local_100._1_1_ = 'n';
local_fe = '\0';
sVar4 = strlen((char *)&local_10c);
fwrite(&local_10c,1,sVar4,pFVar1);
fclose(pFVar1);
}
}
return;
}
从上方高亮的代码行中可以看到,/etc/passwd文件是以写入模式打开的。然后设置了几个看起来很有趣的局部变量。其中一个原因是local_8c被传入了负责生成密码哈希的crypt()函数。另一个原因是,虽然Ghidra展示的是这些数据的十六进制表示,但细心的人会注意到这些十六进制数据都在可打印的ASCII范围内。实际上,这些代码行可以重写为以下形式:
23 local_8c = "7102";
24 local_88 = "2190";
25 local_84 = 0;
这显然看起来像一个以空字符结尾的字符串。然而,我们需要注意这里存在一个字节序的问题。通过反转这两个4字节字符串的顺序,我们得到2017和0912。
等等……管理员用户名是vstarcam2017。开发者肯定不会选择一个和他们开发设备时间相同的密码吧??
让我们尝试用hashcat来确认这个密码。
echo 'uTV43RfKc73oM' > hash.txt
echo '20170912' > passwd.txt
hashcat -m1500 hash.txt passwd.txt
破解成功!
通过UART以Root身份登录
现在我们只需断电重启设备,等待其正常启动并出现登录提示。
输入用户名vstarcam2017和密码20170912即可登录。
veepai login: vstarcam2017
Password:
Nov 28 15:35:16 login[65]: root login on 'console'
[vstarcam2017@veepai:~]# id
uid=0(vstarcam2017) gid=0(root) groups=0(root)
尾声
既然我们已经获得了完整的固件镜像和一个root shell,我们就拥有了开始寻找设备上各种网络服务软件漏洞的所有工具。我们还可以检查设备与云服务器通信的安全性。这些都是在物联网渗透测试过程中可能发现的多种风险。
获取更多精彩内容,尽在Track安全社区~:https://bbs.zkaq.cn
声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权!
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:白帽子左一 白帽子左一《逆向工程 | 从拆解摄像头到获取超管密码》