安基网 首页 安全 攻防案例 查看内容

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

2020-8-2 03:46| 投稿: xiaotiger |来自: 互联网


免责声明:本站系公益性非盈利IT技术普及网,本文由投稿者转载自互联网的公开文章,文末均已注明出处,其内容和图片版权归原网站或作者所有,文中所述不代表本站观点,若有无意侵权或转载不当之处请从网站右下角联系我们处理,谢谢合作!

摘要: 最近在研究一款 IPC 摄像头的固件,想要固件模拟进行动态调试然后发现由于固件给的 lib 库出了一些问题,导致无法模拟。手里正好有实体设备,于是想到使用拆设备找串口的方式来进行调试,找到串口调试发现需要登录密码,且弱口令无法进入系统,于是又想到了使用 ...

Aodzip@海特实验室 H4lo@海特实验室

概述

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

串口调试信息

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  14. [ 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 文件,这里的密码破解不出来,于是这里就进行了删除处理:

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

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

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

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

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

即将第一行改成:

  1. 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即可。

打包命令

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

  1. Squashfsfilesystem,// Squashfs 格式文件系统

  2. little endian,// 小端架构

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

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

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

  6. 1179 inodes,// 节点数

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

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

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

  • 文件系统压缩算法为 xz

  • 块大小为 256K(262144 bytes)

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

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

打包的情况:

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

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

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

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

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

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

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



  4. h4lo@ubuntu:xxx$ binwalk firmware.bin

  5. ...

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



  7. h4lo@ubuntu:xxx$ binwalk header.bin

  8. ...

  9. 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 串口的信息如下:

  1. Autobootingin1 seconds

  2. copying flash to 0x81500000

  3. flash status is0,0,0

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

  5. SF:8388608 bytes @0x0Read: OK

  6. verifying uboot partition...

  7. ok

  8. verifying kernel andromfs partition...

  9. failed


  10. Firmwarecheck failed!

  11. Enterrecovery mode.

  12. In: serial

  13. Out: serial

  14. Err: serial

  15. Net:RealtekPCIeGBEFamilyControllermcfg =0024

  16. nohw config header

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

  18. r8168#0

  19. Usingdefaultenvironment

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

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

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

  1. rlxboot# printenv

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

  3. baudrate=57600

  4. bootaddr=(0xBC000000+0x120000)

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

  6. bootcmd=jmpaddr 0xbfc50000

  7. bootdelay=1

  8. bootfile=/vmlinux.img

  9. ethact=r8168#0

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

  11. gatewayip=192.168.1.1

  12. ipaddr=192.168.1.60

  13. load=tftp 80500000 ${u-boot}

  14. loadaddr=0x81500000

  15. netmask=255.255.255.0


  16. Environmentsize:435/131068 bytes

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

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

mtd 分区表修复

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

mtd 分区表分析

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

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

  1. 地址开头+ 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 的地址位置。

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

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

固件重打包

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

"

小编推荐:欲学习电脑技术、系统维护、网络管理、编程开发和安全攻防等高端IT技术,请 点击这里 注册账号,公开课频道价值万元IT培训教程免费学,让您少走弯路、事半功倍,好工作升职加薪!

本文出自:https://www.toutiao.com/a6855845466425426446/

免责声明:本站系公益性非盈利IT技术普及网,本文由投稿者转载自互联网的公开文章,文末均已注明出处,其内容和图片版权归原网站或作者所有,文中所述不代表本站观点,若有无意侵权或转载不当之处请从网站右下角联系我们处理,谢谢合作!


鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论

 最新
返回顶部