针对某款摄像头设备的固件重打包及root shell获取

<返回列表

最近在研究一款 IPC 摄像头的固件,想要固件模拟进行动态调试然后发现由于固件给的 lib 库出了一些问题,导致无法模拟。手里正好有实体设备,于是想到使用拆设备找串口的方式来进行调试,找到串口调试发现需要登录密码,且弱口令无法进入系统,于是又想到了使用固件重打包的方式去掉登录密码,所以对固件进行结构的分析以及对固件修改后的重打包,同时记录下遇到的问题和相应的解决方法,最终获取 root shell 的过程。

串口调试信息

在一波物理攻击拆开设备外壳之后,找到 uart 串口后进行终端调试的常规操作。根据串口的输出,在启动内核时,可以看到这里的内存地址映射:

[0.394000]0x000000000000-0x00000001d800:"factory_boot"

[0.405000] mtd: partition "factory_boot" doesn't end on an erase block -- force read-only

[ 0.422000] 0x00000001d800-0x000000020000 : "factory_info"

[ 0.433000] mtd: partition "factory_info" doesn't start on an erase block boundary -- force read-oy

[0.453000]0x000000020000-0x000000040000:"art"

[0.463000]0x000000040000-0x000000050000:"config"

[0.473000]0x000000050000-0x000000060000:"boot"

[0.484000]0x000000060000-0x0000001c7c00:"kernel"

[0.494000] mtd: partition "kernel" doesn't end on an erase block -- force read-only

[ 0.510000] 0x0000001c7c00-0x0000007b0000 : "rootfs"

[ 0.520000] mtd: partition "rootfs" doesn't start on an erase block boundary -- force read-only

[0.538000]0x0000007b0000-0x000000800000:"rootfs_data"

[0.549000] mtd: partition "rootfs_data" doesn't start on an erase block boundary -- force read-ony

[ 0.568000] 0x000000060000-0x000000800000 : "firmware"

这里我们需要关注以下 kernel 以及 rootfs 的内存地址范围,kernel:0x000000060000-0x0000001c7c00,rootfs:0x0000001c7c00-0x0000007b0000。这里的内存地址范围对我们后续的重打包工作非常重要。

在启动内核、系统初始化之后,我们会发现这里有一个需要输入密码的 shell 登录命令行,使用 root 用户加上弱口令进行登录不成功。于是对固件重打包并重新刷上固件就是本文需要研究的内容。

固件分析

在主板上找到了存储文件系统的 flash 芯片,发现此芯片的存储容量只有 8M,用热风枪一顿操作将其取下并使用编程器连接得到其中的固件内容。

按照惯例,直接使用 binwalk 来分析其固件结构:

这里发现此固件文件由五个部分组成,前四个部分都是压缩数据,uboot 和 kernel 都位于此部分的压缩数据中。最后一部分是 Squashfs 文件系统,我们这边的目的就是在文件系统中留下后门方便进行后续调试。

文件系统修改

常见留后门的操作主要有:修改 passwd 文件、在文件系统的初始化启动脚本中加入 telnet 后门。比较方便的是改动 passwd 文件中的 root 用户的密码,这里可以选择修改也可以选择删除密码(删除之后就无需密码就可以登录)。

查看 passwd 文件,这里的密码破解不出来,于是这里就进行了删除处理:

root:$1$aG9UJ4ev$gDdMidRwq4Rm6wrrfxcfT0:0:0:root:/root:/bin/ash

nobody:*:65534:65534:nobody:/var:/bin/false

admin:*:500:500:admin:/var:/bin/false

guest:*:500:500:guest:/var:/bin/false

ftp:*:55:55:ftp:/home/ftp:/bin/false

即将第一行改成:

root::0:0:root:/root:/bin/ash

在修改完此文件之后,需要做的就是对根目录进行重打包。因为目标文件系统是 Squashfs 格式的,所以很自然就想到了用 mkquashfs 这个工具对根目录进行打包。

mkquashfs 打包文件系统

squashfs-tools 项目编译和安装

在这里可以找到 squashfs-tools 项目的源码:https://sourceforge.net/projects/squashfs/files/squashfs/下载 4.4 版本的项目到本地之后解压,进入 /squashfs-tools 子文件夹下,修改 Makefile 文件,去掉 XZ_SUPPORT = 1 这个注释(为了让此工具支持 xz 压缩算法)。

接着 make & make install即可。

打包命令

根据原来固件的输出,我们可以得到一些信息:

Squashfsfilesystem,// Squashfs 格式文件系统

little endian,// 小端架构

version 4.0,// 文件系统打包版本为 4.0

compression:xz,// 此文件系统中采用压缩算法为 xz

size:6192298 bytes,// 此文件系统的大小

1179 inodes,// 节点数

blocksize:262144 bytes,// 文件系统块大小页大小

created:2020-03-1802:05:12// 创建时间

这里的信息对我们来说比较重要的两个地方是:

文件系统压缩算法为 xz

块大小为 256K(262144 bytes)

那么我们在使用 mkquashfs 命令如下:

mksquashfs squashfs-root/ squashfs-root.fs -comp xz -b 256K-nopad

打包的情况:

在得到打包好的文件系统之后,按照常理,头部数据不改动,将其拼接到新的固件上:

dd if=firmware.bin of=header.bin bs=1 count=1866752// 提取固件头部

cat squashfs-root.fs >> header.bin // 将文件系统拼接到头部

对比整个固件文件的大小和文件系统的大小我们会发现:打包好的整个固件的大小是小于原始固件的,但是 Squashfs 文件系统的大小却比原始的文件系统大。导致这里原因的问题目前还不太清楚,可能是由于原始固件的文件系统打包方式并由不是与运行此命令的方式相同。

h4lo@ubuntu:xxx$ ls -al header.bin firmware.bin

-rw-rw-r--1 h4lo h4lo 8388608Jul1611:45 firmware.bin

-rw-rw-r--1 h4lo h4lo 8084480Jul1612:24 header.bin

h4lo@ubuntu:xxx$ binwalk firmware.bin

...

18667520x1C7C00Squashfsfilesystem, little endian, version 4.0, compression:xz, size:6192298 bytes,1179 inodes, blocksize:262144 bytes, created:2020-03-1802:05:12

h4lo@ubuntu:xxx$ binwalk header.bin

...

18667520x1C7C00Squashfsfilesystem, little endian, version 4.0, compression:xz, size:6214768 bytes,1179 inodes, blocksize:262144 bytes, created:2020-07-1604:21:25

这样会导致一些问题,我们不妨先使用编程器将固件刷回 flash,用烙铁重新焊上 flash 到板子上。

问题定位

重新启动设备,查看 uart 串口的信息如下:

Autobootingin1 seconds

copying flash to 0x81500000

flash status is0,0,0

SF:DetectedXM25QH64Awithpage size 256Bytes, erase size 64KiB, total 8MiB

SF:8388608 bytes @0x0Read: OK

verifying uboot partition...

ok

verifying kernel andromfs partition...

failed

Firmwarecheck failed!

Enterrecovery mode.

In: serial

Out: serial

Err: serial

Net:RealtekPCIeGBEFamilyControllermcfg =0024

nohw config header

new_ethaddr =00:00:23:34:45:66

r8168#0

Usingdefaultenvironment

这里发现 verifying kernel and romfs partition.是 failed 的状态。出现这种情况的原因主要有两种:一种是由于打包文件系统之后,在固件的某个地方做了某些验证或者类似 crc 的校验;还有一种情况我们在处理文件系统的时候内存偏移没有计算好导致的。

此时窗口是处于 boot 模式,可以操作一些命令:

查看一下关于 boot 的启动参数,这里的 bootcmd=jmpaddr 0xbfc50000代表跳转 0xbfc50000 这个地址中,而这个地址即内核在内存中的绝对加载地址中:

rlxboot# printenv

addmisc=setenv bootargs ${bootargs}console=ttyS0,${baudrate}panic=1

baudrate=57600

bootaddr=(0xBC000000+0x120000)

bootargs=console=ttyS1,57600 root=/dev/mtdblock6 rts-quadspi.channels=quad

bootcmd=jmpaddr 0xbfc50000

bootdelay=1

bootfile=/vmlinux.img

ethact=r8168#0

ethaddr=74:05:a5:4c:e1:b0

gatewayip=192.168.1.1

ipaddr=192.168.1.60

load=tftp 80500000 ${u-boot}

loadaddr=0x81500000

netmask=255.255.255.0

Environmentsize:435/131068 bytes

这里我们手动执行一下这个命令,会发现这里确实会跳转到内核启动的地方,但是很快又会发现这里 kernel 出现了 panic,出现问题的原因是文件系统的位置没加载对。

这里具体的原因是因为在使用 mkquashfs 命令进行文件系统打包之后的文件比原文件文件大,导致 kernel 在访问分区表时,找到了 squashfs 文件系统的绝对加载地址,以及 size 的具体数值,但是由于整个文件系统的 size 变大导致无法正常进行解压导致的错误提示。

mtd 分区表修复

由于这里的文件系统在分区表中的 size 数值增大,因此这里我们研究的重点就是关于此固件 kernel 部分 mtd 分区表的修复。

mtd 分区表分析

知道了整个固件文件的内存映射之后,我们在 010 editor 中打开原始固件,跳转到 kernel 部分:

0x60000 地址开始的前 0x200 个字节就是 mtd 分区表,关注到 0x60028 到 0x6006f 部分的数据,发现这其实就是分区表项的各个偏移。具体的格式为:

地址开头+ size +地址开头+ size +地址开头+...

每个地址的开头对应了各个区段的起始位置,再加上一个 size 值之后对应了该区段的结束位置。这里总共是 8 个地址,正好就对应了上文 kernel 打印出来的分区地址信息。

地址 size 地址 size 地址 size 地址 size 地址 size 地址 size 地址 size 地址 size 地址0x0 0x1d800 0x1d800 0x2800 0x20000 0x20000 0x40000 0x10000 0x50000 0x10000 0x60000 0x200 0x60200 0x167a00 0x1c7c00 0x5e8400 0x7b0000

多出的 0x60000 - 0x60200 这个区段为 mtd 分区表本身,在 kernel 的输出信息中没有体现。从 0x60200 地址开始的数据为内核的 lzma 压缩数据(使用 binwalk 可以快速定位)。

另外,这里 0x7b0000 这个地址就是文件系统的结尾地址,我们可以跳转过去看看,比较有意思的是这里有一个类似标志位的十六进制数,猜测 uboot 就是通过此标志来判断加载的文件系统开头地址和结束地址是否正确。

分区修改

同样查看一下修改之后的固件,首先 mtd 分区表不变(没有进行修改),我们还是跳转到 0x7b0000 这个原来文件系统的结束位置:

发现其实这里的文件系统的数据并没有结束,因为在上文也可以知道打包后的 squashfs 文件系统的 size 大了很多,所以在分区表对应的位置需要对文件系统的结束地址进行扩充。

假设这里指定文件系统结束地址为 0x7b6000,那么 size 大小为:0x7b6000-0x1c7c00=0x5ee400。同时最后的 size 的值也要进行修改,这个大小为 rootfs_data 用户空间的数据段的长度,因为整体的 flash 空间大小是 0x000000800000,所以这个值需要变小,具体为:0x800000-0x7b6000=0x4a000。

因此这里在相应的位置进行修改,修改后的结果如下:

修改完分区表项之后,需要在固件文件末尾以 0xff 进行填充到 0x7fffff 的地址位置。

python -c "print('xff'*(0x7fffff-0x7b507c))">> modify.bin

之后在文件系统的结尾,也就是 0x7b6000 位置补上十六进制:0xDEADC0DE。

固件重打包

将得到的固件重新刷入 flash 中就行了,开机查看 uart 串口信息,可以正常解压内核运行系统,同时使用 root 用户进行登录之后,就成功获取了摄像头的 root shell。最后,感谢 aodzip 师傅的帮助和指导。

国内免备案VPS301跳转服务器国内免备案服务器域名被墙跳转301,绕过信息安全中心不能放违反法律法规内容!(北京免备案  镇江免备案 江苏免备案 辽宁免备案vps 山东联通免备案) 
分享新闻到:

更多帮助

针对某款摄像头设备的固件重打包及roo

新闻中心 2024-02-07
最近在研究一款 IPC 摄像头的固件,想要固件模拟进行动态调试然后发现由于固件给的 lib 库出...
查看全文

新建三座超级数据中心,增超百万台服务

新闻中心 2024-02-07
阿里云正式宣布,其位于南通、杭州和乌兰察布的三座超级数据中心正式落成,陆续开服,新...
查看全文

云服务器对于互联网基础设施意味着什么

新闻中心 2024-02-06
根据RightScale公司的调研报告,云服务器应用日益广泛,并不断增长。报告指出,超过75%的受...
查看全文