绕过某摄像头校验机制软刷固件

缘起: 因需求需要获取某种arm智能设备(路由器除外)的shell权限,选择了某摄像头。
缘灭: 在硬刷和软刷之间最终还是通过软刷绕过升级校验机制实现重刷固件。

第一步

nmap扫描走起(部分摄像头关闭了ping,需要-sS -sT命令),没有telnet和ssh,开启了80端口,登陆web界面,发现可以通过web界面更新固件。

第二步

从官网下载相应版本的固件,binwalk解包,简单分析一波固件内容,grep -rn和find命令走起,发现固件包中是有telnet和telnetd命令的,且所在目录添加进了path环境变量中,qemu跑busybox发现其拥有telnet和telnetd功能。

第三步

选择获取shell的方式:1.pwn it 2.重打包刷固件

一波三折

肯定是选择重打包刷固件啦,怎么会pwn呢?pwn是不可能pwn的,233。由于固件自带telnetd功能,我们只需要在自启动的脚本中选择其一,添加一条

1
telnetd -p 23 -l /bin/sh

命令,然后重打包固件,往摄像头里面刷即可。

一次尝试

利用firmware-mod-kit第三方打包工具重打包修改后的固件,80端口上升级固件,失败!摄像头虽然提示升级成功,但是并没有完成固件的更新。软刷不行就硬刷,芯片夹,编程器,RT809F软件走起,费了九牛二虎之力,从flash芯片中读取了固件,发现果然没有刷上。

二次尝试

猜测是不是重打包的问题,Seebug上找到一篇文章,见文末,学习了一波。于是手动重打包固件,通过编程器硬刷修改后的固件,emmmm,摄像头就成砖了。于是刷回原固件,中间省略一顿折腾……

三次尝试

重打包暂时放弃,寻找命令执行的漏洞,反正固件都拿到了,httpd文件和luac(5.1版本)进行交互,luadec反编译5.1版本,没找到命令执行的API,选择放弃。

山重水复疑无路,柳暗花明又一村

通过比较编程器提取的固件以及网上下载的固件的二进制,反复思考手动重打包的过程,发现了可疑的0x100字节

important_100bytes

并且比对binwalk结果进行猜测和分析。

binwalk_result

看到一篇破解智能手环的文章,学习了一波,可以通过绕过crc32校验的方式软刷固件,在0x100字节中猜测crc32的存储位置以及crc32计算范围,通过写脚本计算crc32的值,知道了uboot和kernel的crc32值存储位置。

important_100bytes_guess

这里比较遗憾,忽略了header的crc32校验,仅修改了uboot和kernel的校验值进行软刷和硬刷均以失败告终。最终通过逆向固件的方式找到了校验crc32算法的位置,过程如下:

camera1

首先校验开头的16字节是否为0x99999999,0x44444444,0x55555555,0xAAAAAAAA,17-20字节没有校验,但这4个字节会作为lseek函数参数中offset的一部分。

camera2

其次计算0x14-0xb8的CRC32值,和0xb8处的4字节进行比较,也就是header的crc32算法校验,接下来进行uboot的CRC32计算,与0x50处的4字节比较。

camera3

最后计算文件系统Squashfs system的CRC32值,和0x5C处的4字节比较,至此,更新固件的校验算法结束。我们通过脚本计算得到的值将重打包的固件进行修改,绕过固件升级时的校验机制,成功软刷固件,开启了telnet。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import binascii

def getCRC32(v):
return '0x%8x' % (binascii.crc32(v) & 0xffffffff)

fp = open("T520_v214t1_firmware.bin",'rb')
content = fp.read()

# CRC32 of Squashfs filesystem
tmp1 = content[0x280000:]
print hex(len(tmp1))
print "CRC32 of Squashfs filesystem:",getCRC32(tmp1)
# CRC32 of Squashfs filesystem

# CRC32 of boot
tmp2 = content[0x100:0x280000]
print "CRC32 of boot to Squashfs filesystem:",getCRC32(tmp2)
# CRC32 of boot

#CRC32 of header

tmp3 = content[0x14:0xb8]
print "CRC32 of header:",getCRC32(tmp3)
#print 0xffffffff^getCRC32(tmp3)
#CRC32 of header

telnetd

参考资料

Seebug-摄像头漏洞挖掘入门教程(固件篇)
破解智能手环