网址
https://visionspace.com/remote-code-execution-and-critical-vulnerabilities-in-nasa-fprime-v3-4-3/
目标
-
fPrime≤v3.4.3
解释
fPrime 是一个用于航天应用和嵌入式系统的轻量级开发和部署软件框架。系统由通过通信端口连接的组件组成,组件之间的通信通过事件和命令进行。组件提供输入/输出端口来联系(或与其他元素通信),通知它们事件或发送和接收命令。
命令调度服务也是 fprime 框架内的一个组件,它从外部接收命令并将其传递到内部组件。此时,传输到指挥调度服务的外部命令是从地面系统传输的,如下图所示。地面系统负责与配备飞行软件的外部系统(例如卫星)进行通信和控制。
根本原因
当命令调度服务处理大量来自外部的命令时,就会出现内部命令队列(m_queue或m_seqCmdBuffQueue)溢出的问题。该服务从外部源(例如地面系统)接收命令并将其转发到目标组件进行处理。
pipeline/standard.py通过将函数修改send_command为无限循环 [1](该循环快速地重复发送命令),可以重现该问题。当短时间内出现异常大量的命令时,命令调度服务将不堪重负地使用其内部队列来处理这些命令。
def send_command(self, command, args):
"""Sends commands to the encoder and history.
:param command: command id from dictionary togetcommand template
:param args: arguments to process
"""
if isinstance(command, str):
command_template = self.dictionaries.command_name[command]
else:
command_template = self.dictionaries.command_id[command]
cmd_data = fprime_gds.common.data_types.cmd_data.CmdData(
tuple(args), command_template
)
cmd_data.time = fprime.common.models.serialize.time_type.TimeType()
cmd_data.time.set_datetime(datetime.datetime.now(), 2)
while1: // [1] Infinite Loop
self.coders.send_command(cmd_data)
根本原因在于将接收到的命令放入内部队列的逻辑。以下代码用于CommandDispatcherComponentBase::seqCmdBuff_handlerBase()对函数中接收到的命令消息()进行排队:msg
Os::Queue::QueueBlocking _block = Os::Queue::QUEUE_NONBLOCKING; // [2]
Os::Queue::QueueStatus qStatus = this->m_queue.send(msg, 0, _block); // 메시지를 큐에 전송 시도
FW_ASSERT(
qStatus == Os::Queue::QUEUE_OK,
static_cast<FwAssertArgType>(qStatus)
);
这段代码中,队列传输模式Os::Queue::QUEUE_NONBLOCKING设置为[2],即当队列满的时候,立即返回状态,不会阻塞。在内部,this->m_queue.send()它bareSendNonBlock()调用[3],
Queue::QueueStatus Queue::send(const Fw::SerializeBufferBase &buffer, NATIVE_INT_TYPE priority, QueueBlocking block) {
const U8* msgBuff = buffer.getBuffAddr();
NATIVE_INT_TYPE buffLength = buffer.getBuffLength();
return this->send(msgBuff, buffLength, priority, block);
}
Queue::QueueStatus Queue::send(const U8* buffer, NATIVE_INT_TYPE size, NATIVE_INT_TYPE priority, QueueBlocking block) {
//Check if the handle is null or check the underlying queue is null
/* ... */
//Send to the queue
if (QUEUE_NONBLOCKING == block) { //[3]
return bareSendNonBlock(handle, buffer, size, priority);
}
return bareSendBlock(handle, buffer, size, priority);
}
Queue::QueueStatus bareSendNonBlock(BareQueueHandle& handle, const U8* buffer, NATIVE_INT_TYPE size, NATIVE_INT_TYPE priority){
/ * ... */
Queue::QueueStatus status = Queue::QUEUE_OK;
bool success = queue.push(buffer, size, priority); // [4]
if(!success) { // push가 실패했다면 (e.g., 큐가 가득 찼다면)
status = Queue::QUEUE_FULL; // 상태를 QUEUE_FULL로 설정
}
return status; // 최종 상태 반환
}
如果队列已满,push()则返回false,bareSendNonBlock()通过函数Os::Queue::QUEUE_FULL转换为状态(错误代码8)并存储在[5]m_queue.send()的返回值中。qStatus
问题FW_ASSERT出现在下一行语法中。这个断言qStatus断言。Os::Queue::QUEUE_OK但是,如果队列溢出到(8),则此断言条件变为假,并且断言失败。qStatusOs::Queue::QUEUE_FULL
Os::Queue::QueueBlocking _block = Os::Queue::QUEUE_NONBLOCKING;
Os::Queue::QueueStatus qStatus = this->m_queue.send(msg, 0, _block);
FW_ASSERT( // 전송 상태에 대한 어설션 체크
qStatus == Os::Queue::QUEUE_OK, // [5] 상태가 QUEUE_OK (성공)인지 확인
static_cast<FwAssertArgType>(qStatus) // 실패 시 상태 값 출력
);
综上所述,队列溢出时的处理应该合理设置为block、drop或者hook,而不是assert,但默认设置是应用assert,导致队列满的时候程序崩溃,从而造成漏洞。
参考
-
https://www.cve.org/CVERecord?id=CVE-2024-55030
原文始发于微信公众号(Ots安全):CVE-2024-55030:NASA fPrime 队列溢出导致 DoS 漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论