0%

记录RAX30-V1.0.7.78固件的逆向(一)

工控安全的学习进入了新的阶段,老师给我们发了三篇文章,一篇是常见漏洞发生情况,比较偏理论,另外两篇是对路由器漏洞的分析和复现,比较偏实践。我比较不喜欢理论,更喜欢动手,所以先从路由器漏洞复现开始了。参考文章

需要用到的工具

  • ghidra
  • binwalk
  • qemu

获取固件

按照文章说的下载RAX30-V1.0.7.78.zip,并解压得到RAX30-V1.0.7.78_1.img镜像文件和一份说明书,我们主要关注这个镜像文件。

首先,先用binwalk把镜像拆了。

binwalk命令我也才刚接触,大概就是用来从二进制文件里面拆出文件,通常拆img或者bin等镜像文件,对于file查不出是什么文件类型的二进制文件都可以用来试一试,听说也能用于分离某些数据隐写

1
binwalk -Me RAX30-V1.0.7.78_1.img

之后可以看见当前目录下生成了一个_RAX30-V1.0.7.78_1.img.extracted目录,

这些压缩文件我都试过了,都打不开,squashfs-root则是路由器的文件系统,之后主要研究这个。

找到入口函数

出于好奇心,我先chroot进去跑了一下pucfu,貌似要加参数,但是不知道加什么,

不知道怎么跑,继续看文章。

命令注入漏洞出现在/lib/libpu_util.so中定义的SetFileValue()函数中,该函数由/bin/pucfu导入。此函数将传递一个用户控制的值,该值将附加到已执行的execve shell命令中。

然后文章还给出了调用关系图,

为了方便操作,我们先新建个项目目录,再把涉及到的库和程序复制到项目目录里,再对复制出来的进行反编译。

先用ghidra对/bin/pucfu进行反编译,然后我在函数列表中并没找到main,但是找到了个疑似入口函数的,

查查__libc_start_main是什么,参考了一下这篇文章:入口函数与程序初始化浅析

1
2
3
4
5
6
7
8
int __libc_start_main(
int (*main) (int, char**, char**),
int argc,
char* __unbounded* __unbounded ubp_av,
__typeof (main) init,
void (*fini) (void),
void (*rtld_fini) (void),
void* __unbounded stack_end)

这是__libc_start_main的函数头部,可见和_start函数里的调用一致,一共有7个参数,其中main由第一个参数传入,紧接着是argc和argv(这里称为ubp_av,因为其中还包含了环境变量表)。除了main的函数指针外,外部还要传入3个函数指针,分别是:

  • init:main调用前的初始化工作。
  • fini:main结束后的收尾工作。
  • rtld_fini:和动态加载有关的收尾工作,rtld是RunTime LoaDer。

所以FUN_FUN0010dfc就是我们要找的主函数

阅读反编译代码

跳转之后,超长if和多重if嵌套使人无法阅读,

把代码复制到现代的代码编辑器里面,稍微折叠一下,使代码稍微可读一点,

稍微正常了一点,前面一堆编号命名的变量没有截图,接下来看一下主函数的流程。

首先前面两个fopen打开了一个控制台和一个日志文件,应该是用于调试时输出信息还有输出日志的,没有什么好说的。

param_1param_2分别对应argcargv,如果参数个数大于1,就用getopt来获取控制台参数的,如果参数读取完,则返回-1,未知参数返回'?',将获取到的参数保存在iVar1内。

if(iVar1==-1),是个超长的语句块,而且里面有多重if嵌套,目测是用于在参数读取完毕之后进行初始化的,我们最后再看这个。

if(iVar1==0x49),0x49对应的ASCII是I大写字母i,就是修改了puVar6=0x4,然后跳到getopt前的那个标签,不知道为什么不放在switch里面。

switch(iVar1),很显然就是对对每个参数进行的操作,且几乎对每个参数的操作里面都有个goto,跳到getopt前的那个标签。

0x68那个对应的字符是h也就是help,会跳到LAB_00010f3c处理pcVar5之后跳到LAB_00010e74输出帮助信息然后退出程序,返回-1。

还有一个特例是0x72,对应字符r

iVar1设为了随机数,之后传递给了FUN_FUN0001189c,修改了local_local1830,break之后执行local_1834=1,然后跳到getopt前的那个标签。

然后看看FUN_FUN0001189c

这样调用显然直接跳到了FUN_FUN000117c8,它的两个参数一并传过去了,然后执行了一系列我看不懂的计算,先暂时不管

接下来看看之前的超长if的前面一段,

它首先调用了DBG_DBGPRINT,从名字还有之前运行的结果也能猜到,这是个在debug模式下输出信息用的函数,并且我们也知道了这几个变量分别是什么意思,去试试参数。

可以看见输出的信息有在变化,但是不知道为什么依然跑不起来,继续往下看。

日志输出模式时,将fw_debug设为一个函数的地址

这个显然就是用于写入日志文件的,前情回顾

再往下看,之后调用了fw_check_init,这个函数在libfwcheck.so里面,用ghidra打开看一下。

显然,如果开了日志模式,到这里肯定会写入日志的,跑一下看看,

日志文件是创建了,但是并没有向日志写入任何内容,而且文件有写入权限,看来是在fw_check_init附近出现了段错误,这次暂时止步于此,之后我再去学习用gdb调试程序。

后续

第二天我没开电脑,用手机连着服务器调试的时候,发现好像意外的运行了一下,但是换成电脑上复现不出来,不知道为什么会报段错误,等有时间再去看看。