FreeRTOS-Plus-TCP Fuzz

FreeRTOS介绍

FreeRTOS由美国的Richard Barry于2003年发布。2018年,亚马逊收购了FreeRTOS并将其改名为AWS FreeRTOS。收购前FreeRTOS经历了9个版本,收购后更新到V10。

优点

  • FreeRTOS开源免费,轻量小巧,其代码只有3000多行,编译后的所占空间只有6-12K字节,十分适用于小型的星载计算机设备。
  • FreeRTOS具有实时性,该操作系统内核的多任务调度系统采用抢占式内核,能够保证高优先级任务在预期运行的时间进行唤醒运行,达到强实时的特性,有利于对时间敏感的姿轨控任务能够按精确的步骤执行,确保了姿轨控的精度。
  • FreeRTOS相比于其他系统的一个显著优势是它没有对任务数量和优先级设置限制,可以灵活地分配相同优先级给多个任务。
  • FreeRTOS支持多平台,该操作系统大部分采用C语言编写,可以很方便的进行移植,目前已经移植到40多款不同类型的处理器平台上。
  • FreeRTOS具有可靠性,该操作系统代码遵循MISRA-C标准的编程规范,确保系统的安全可靠,它具备强大的行跟踪功能,可以有效检测堆栈溢出问题。FreeRTOS还支持静态任务功能,进一步提高了操作系统的可靠性,对于在轨长期运行的业务方式有很强的适应性。

此外,FreeRTOS还衍生出了另外两个操作系统:OpenRTOS和SafeRTOS,其中,OpenRTOS使用了FreeRTOS相同的代码,但却受到商业授权的保护;SafeRTOS同样是FreeRTOS的衍生版本,其符合工业、医疗、汽车和其他国际安全标准的严格要求,具有更高的安全性。

代码构成

FreeRTOS主要包含任务调度模块,平台移植模块,列表数据结构支持模块,软件定时器模块,消息队列模块,事件驱动模块,协程模块。

image-20260115210950752

FreeRTOS内核文件包括三部分,Demo文件夹中是FreeRTOS的演示工程,License文件夹包含FreeRTOS的相关许可信息,Source文件夹中的内容是FreeRTOS的源代码。

对Source文件夹中各文件和文件夹的描述如下:

  • include文件夹下的文件为FreeRTOS的主要头文件。
  • portable文件夹下的内容为FreeRTOS针对不同的芯片架构和不同的编译器提供的移值文件,是连接软件层面的FreeRTOS操作系统和硬件层面芯片的桥梁。
  • croutine.c:协程相关文件,在较新版本的FreeRTOS中默认宏定义关闭。
  • event_groups.c:事件相关文件,实现进程间通信,由若干“事件位”组成,每个位代表一个事件的发生状态,比如就绪、执行、完成等。多个任务可以共享一个事件组,利于实现进程间的协作与同步。
  • stream_buffer.c:流式缓冲区相关文件,用于进程间通信,适用于一对一消息传输,基于中断的消息传输。
  • list.c:列表相关文件,FreeRTOS内核中使用的链表结构,如进程的暂停列表、就绪列表等使用链表数据结构实现。
  • queue.c:队列相关文件,用于进程间通信,两个进程之间传输数据。
  • tasks.c:任务相关文件,是FreeRTOS中最重要的部分。包含任务的操作(创建、暂停等),任务调度器,上下文切换策略等。
  • timers.c:FreeRTOS内部实现的软件定时器,较新版本中默认宏定义关闭。

操作系统内核是操作系统的核心,负责CPU的调度等核心功能,除此之外,如Windows、VxWorks大中型操作系统一般还包括文件系统、输入输出系统、网络系统等其他系统。FreeRTOS作为一个轻小型的操作系统将除内核以外的各个功能独立集成在附加库中,用户可根据需要灵活选择开发,十分适用于一些资源受限的设备。

FreeRTOS-Plus库实现了FreeRTOS内核的附加功能,包括FreeRTOS-Plus-TCP(TCP/IP 协议组件)、FreeRTOS-Plus-CLI(命令行解释器)、FreeRTOS-Plus-Trace(可视化跟踪)、FreeRTOS-Plus-IO(提供硬件I/O引脚的通信接口)等操作系统常用功能库。

其中,FreeRTOS的TCP/IP协议栈是本文漏洞研究的重点,对FreeRTOS Plus库中TCP/IP协议栈的部分源码简单描述如下:

  • FreeRTOS_ARP.c:用于解析IP地址,获取MAC地址信息,FreeRTOS的TCP/IP协议栈通过维护一个ARP表建立IP地址到MAC地址的映射。
  • FreeRTOS_DHCP.c:用于为网络节点动态分配IP地址,在产品实际应用过程中灵活实用,但在实验开发时,可以通过预分配静态配置网络IP。
  • FreeRTOS_Sockets.c:FreeRTOS-Plus-TCP使用标准伯克利套接字的接口,代码中包含套接字的创建、绑定、连接、发送、接收等一系列函数。
  • BufferAllocation.c:用于实现网络缓冲区的分配,文件位于FreeRTOS_Plus_TCP/source/portable/BufferManagement/文件夹中。
  • NetworkInterface.c:网络接口的驱动程序,文件位于FreeRTOS_Plus_TCP/source/portable/NetworkInterface/文件夹中。

网络通信

image-20260115211642401

在FreeRTOS+TCP架构的设计中,IP任务被设计为一个事件驱动任务,它会在一个FreeRTOS队列上超时阻塞(挂起),等待应用程序通过网络接口(应用程序和网络协议通信的接口)向该队列发布事件。一旦接收到事件,IP任务会解除阻塞并处理这些事件,实现网络通信的功能。

通过套接字发送和接收事件就是一种可以驱动的事件,由于FreeRTOS只维护一个队列,所以队列所收到的所有事件都以相同的优先级进行处理,与流量类型、套接字配置、协议等无关。FreeRTOS中使用标准伯克利套接字的接口连接应用层和传输层:FreeRTOS_socket()创建TCP套接字、FreeRTOS_bind()绑定本机IP地址的端口号、FreeRTOS_connect()远程连接套接字、FreeRTOS_send()发送数据、FreeRTOS_recv()接收数据。

数据包依次通过应用层、运输层、网络层、数据链路层,先自上而下而后自下而上的进行传输。通信两端都分配有网络缓冲区,FreeRTOS提供了两种缓冲区分配方案,BufferAllocation_1.c按照以太网硬件要求在编译时静态分配,BufferAllocation_2.c则按照需要动态分配和释放缓冲区。缓冲区中存放着已经到达但是还没有接收的数据,当套接字调用FreeRTOS_recv()函数时,即可从缓冲区中读取数据。

模糊测试

Boofuzz

介绍

基于生成的协议模糊测试工具

Boofuzz框架主要可以分为三个部分:

(1) 数据生成模块Data Generation

Boofuzz的数据生成模块继承自SPIKE框架中“块”结构的方式,消息的定义由基元(Primitives)、块(blocks)来构造,多个基元可以组成块,块除了构成基元也可以单独作为消息请求中的元素,块与块之间也可以相互嵌套。Boofuzz中协议消息的定义有两种方式:一种是早期版本中的静态协议定义方式,一种是新版本中对原有SPIKE框架做简化改进的协议定义方式。静态协议的定义包括有请求操作、区块操作和一些原始定义项,比如s_initialize()初始化块请求,s_block_start()在当前请求下打开一个新块,s_block_end()关闭上次打开的块,s_binary()将二进制串推送到当前块的堆栈上,s_static()静态值,s_string()字符串等等函数命令。而新版本中的协议定义是基于Request、Blocks、Primitives层层嵌套树状构造的,两种协议定义方式在逻辑上是一致的,只是调用的函数语法不同,本文中主要使用早期版本中的静态协议定义方式。

(2) 会话管理模块Session

Session对象继承自pgraph.graph,是会话模块的核心,协议测试时发出的每一条消息都是一个Request对象,会话管理的思想是利用Session对象将各消息请求连接起来,组成状态图。通过构建的状态图将协议分解为各个请求,图中的路径反映了各消息请求节点的先后顺序,数据发送后即fuzz遍历所有可能路径。通过状态图既可以对每个节点进行操作,也可以添加回调函数从前一消息节点获取反馈信息。实际流程中首先创建Session对象,Session对象中会传入一个Target对象,Target对象又接收了Connection族中的对象,比如本文中使用TCPSocketConnection对象,用于连接靶机。然后将数据生成模块定义好的消息利用session.connect()连接到状态图中,随后运行session.fuzz()即可按照状态图的逻辑依次测试。

(3) 代理模块Agents

代理模块主要实现的是对目标服务器的监控程序,只有监控目标服务器在收到测试样例后的状态变化才能更精确的找到漏洞位置。代理模块包括网络监控代理、进程监控代理、虚拟机(VMware)控制代理三个子模块。网络监控代理模块负责捕捉网络通信流量,在测试之前此模块就向目标靶机发送请求,监控得到的数据包内容及流量信息将被记录保存,此模块可以使用外部的网络分析抓包工具替代,本文中使用Wireshark工具进行数据包流量的监控和分析。进程监控模块负责检测模糊测试过程中触发的崩溃,检测到关于崩溃的信息会被传送回Session对象并保存,此信息也可以在web监控服务页面中显示。虚拟机控制代理负责控制虚拟机,模糊测试过程中为保障安全、降低成本,常常将目标系统部署在虚拟机上,此模块用于控制虚拟机的启动、关闭等状态,最重要的功能用于在目标主机崩溃时自动恢复主机状态。

安装

更新pip工具并安装boofuzz模块

image-20260115213633484

下载boofuzz源码,在开发者模式下安装更新,通过这一步可以对boofuzz模块中的各函数进行修改调整

image-20260115213658863

image-20260115213702772

测试环境

image-20260115212420212

主机为一台联想笔记本电脑,使用Win11操作系统,其上运行着VMware Workstation 17.5虚拟机应用程序和用于网络通信监控的Wireshark 4.2.3网路抓包工具。

虚拟机平台中运行着两台虚拟机。作为攻击机的虚拟机使用kali Linux操作系统,其上运行着Boofuzz各个模块,是发送数据包的客户端。作为目标机的虚拟机使用Windows 10操作系统,其上通过对FreeRTOS源代码的配置,实现了Windows环境下FreeRTOS-Plus-TCP的运行,是接收和处理数据包的服务器。

虚拟机平台中的两台虚拟机使用NAT(网络地址转换)模式连接网络,此模式下虚拟机与真实主机之间不能通信,但平台内的各虚拟机之间可以共享网络相互通信,网络流量信息通过VMware Network Adapter VMnet8接口监控查看。

目标移植

FreeRTOS-Plus-TCP在Windows下的移植运行

  1. FreeRTOS的内核和TCP/IP堆栈都是开源的,构建FreeRTOS TCP模拟服务器的核心就是这两部分源代码,所以首先将FreeRTOS内核源码和FreeRTOS TCP源码加入到项目中。

  2. 然后为FreeRTOS内核项目选择heap_4.c的内存分配方案,为FreeRTOS Plus TCP添加网络接口驱动程序NetworkInterface.c和网络缓冲区分配方案BufferAllocation_2.c。

image-20260115212748283

  1. 然后,向项目中添加配置文件、主程序和建立TCP服务器的代码,另外注意在include文件夹中引入相关代码的头文件。

image-20260115212814884

image-20260115212821581

  1. 设置IP地址(为方便测试,预分配静态网络IP地址)

首先在FreeRTOSIPConfig.h配置文件中将ipconfigUSE_DHCP设置为0,即不使用网络中的DHCP服务器,然后在FreeRTOSConfig.h配置文件中中设置应用的IP地址、网络掩码和网关地址。虚拟机主机地址为×××,IP地址前三个八位字节需要与主机地址的前三个八位字节相同,网络掩码和网关地址也需要与IP地址相兼容。

  1. 设置网络端口

计算机一般有多个网络端口,所以需要为应用程序设置需要使用的接口。试运行应用程序后,控制台窗口会显示可用的网络接口。

image-20260115213150116

从图中信息可以看到,接口4是Intel PRO/1000 MT虚拟网卡创建的用于网络连接的接口,所以在FreeRTOSConfig.h文件中将应用程序的网络接口设为4。

image-20260115213217160

  1. 对TCP服务的设置

由于新版本示例中给了TCP、UDP等多个演示任务,并且任务配置均适配于当前版本,所以对建立的TCP模拟服务器任务进行设置。首先在main.c主程序文件中引入SimpleTCPEchoServer.h的配置文件并选中TCP_ECHO_SERVER这个任务。然后在SimpleTCPEchoServer.c文件中为TCP服务器任务设置指定端口为7端口

  1. 运行测试
  • 使用本地Windows调试器运行项目,生成的RTOS可执行文件。
  • 在另一台kali虚拟机上使用ping命令测试FreeRTOS模拟服务器的运行状态和网络联通情况,通信情况正常。
  • 在本机上的Wireshark软件中通过VMnet8接口能够监控到通信双方及数据包信息。

测试和异常

  • 开启虚拟机,启动FreeRTOS服务器
  • 开启另一台虚拟机,运行测试脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from boofuzz import *

def main():
#创建Session对象,连接靶机服务器的IP地址和端口
session = Session(
target=Target(
connection = TCPSocketConnection("tcp服务ip", 7),
),
)
#初始化请求
s_initialize("trun")

s_string("TRUN", fuzzable=False)

s_delim(" ", fuzzable=False)
#推送字符串"FUZZ"到当前块堆栈上,默认启用模糊测试
s_string("FUZZ")

s_static("\r\n")

#使用刚刚创建的session对象将请求"TRUN"连接到图中
session.connect(s_get("trun"))

session.fuzz()

if __name__ == "__main__":
main()

第一步,实例化Session对象,Session对象是boofuzz测试的核心,所有的消息定义,消息连接,消息模糊,消息发送,目标监视,日志记录等boofuzz大部分的功能都集成在Session对象中。Session对象中传入了一个Target对象,Target对象是一个描述目标状态的容器,Target对象中还接收了一个connection对象,connection对象定义了目标系统与当前系统之间的连接,并将节点之间传输的数据回显。

第二步,定义协议消息,在假设没有任何先验信息的情况下随机定义输入种子,利用s_initialize()初始化消息请求,命名为“trun”,作为节点易于检索的标识。利用各协议定义函数初始化消息内容,并选择要进行模糊测试的消息部分。Boofuzz中有三种数据生成器:_fuzz_library、_mutators_of_default_value和_magic_debug_values,string数据类型主要调用的是_fuzz_library生成器。_fuzz_library维护了一个包含有临界值、超长值等特殊字符串的的列表,在此基础上进行长度和内容的替换和组合。Boofuzz对启用模糊化的数据部分进行变异,生成大量的测试数据保存在迭代器中,为后续测试做准备。

第三步,利用Session内置的connect()方法连接消息节点,由于只定义了简单的一个消息请求,所以直接将“trun”请求代表的节点连接到根节点,根节点“root”在Session初始化时自行创建,作为测试的状态转移图的起点。

Boofuzz运行

image-20260115214337425

异常

在发送第二十三个数据包时,客户端多次尝试发送请求均无法连接服务器。经过多次测试实验,服务器基本稳定在第二十三个请求处拒绝连接。FreeRTOS控制台显示异常:

image-20260115214420408

原因分析

利用套接字发送数据实际上是利用send函数将数据发送到操作系统的TCP协议缓冲区中,由协议栈缓冲区负责通过建立好的网络将数据发送到对方机器中。接收数据时实际上是协议栈缓冲区生成一个套接字利用recv函数接收数据,然后程序利用accept函数从协议栈缓冲区中取出套接字。但是协议栈缓冲区的空间的有限的。

而为了确认客户端中Boofuzz模块发送的数据成功被接收,同时为了对FreeRTOS网络协议栈做更全面的测试,在SimpleTCPEchoServer.c中调用服务器各功能函数时对TCP模拟服务器设置了自动回复功能,即服务器成功接收到数据后,会将相同的数据再发送给客户端程序。

服务器进程调用listen函数时,设置了参数xBacklog,表示协议栈缓冲区中能存放的连接成功的最大套接字数量。本项目中设置xBacklog为20,也就是说当协议栈中套接字数量达到20后,操作系统将直接拒绝来自之后客户端的连接请求。

客户端中的Boofuzz模块利用Session对象与服务器进行连接并发送数据,但是Boofuzz工具不会主动接收服务器发送的数据。即数据发送到FreeRTOS操作系统的TCP协议缓冲区后将无法成功发送给客户端一方,数据套接字将一直留存在FreeRTOS服务器中的协议缓冲区内。所以当缓冲区内套接字数量达到20时,服务器将拒绝之后的连接请求。

为Boofuzz模块添加接收功能,使缓冲区内的套接字不断地被取出,可以继续进行测试

打开boofuzz-results文件夹中的运行日志数据库,可以查看测试结果。