飞塔 FortiGate 7.4.1 格式化字符串漏洞

格式化字符串命令执行
2026-09-02 20:50
44023

1、下载飞塔 FortiGate 7.4.1 的 VM 虚拟机镜像

图片.png
第一步、下载完解压放到 linux 虚拟机里
把 vmdk 这种 "VMware 专有包装格式" 变成 Linux 标准工具能直接读的 "裸盘镜像"
执行命令:qemu-img convert -f vmdk -O raw fortios.vmdk fortios.raw

图片.png
第二步、查看看分区表
执行命令:fdisk -l fortios.raw

图片.png
第三步、抠出分区 fortios.raw1 ,起始扇区 2048、长度 524288 扇区正好是 256MB
执行命令:dd if=fortios.raw of=fortios_p1.ext2 bs=512 skip=2048 count=524288

图片.png
第四步、从分区 fortios.raw1 里 dump 内核和加密的 rootfs
执行命令:
mkdir FORTIOS741
debugfs -R "dump /flatkc FORTIOS741/flatkc" fortios_p1.ext2
debugfs -R "dump /rootfs.gz FORTIOS741/rootfs.gz" fortios_p1.ext2
debugfs -R "dump /flatkc.chk FORTIOS741/flatkc.chk" fortios_p1.ext2
debugfs -R "dump /rootfs.gz.chk FORTIOS741/rootfs.gz.chk" fortios_p1.ext2
第五步、tail 抠出 gzip 流再解压
flatkc 是 bzImage(MZ 引导头 + setup 段 + 内层 gzip 流)
gzip 流不在文件开头,而在偏移 0x41b4
然后 kernel_741.gz 开头才是 1f 8b 方能解压了
把 seed 从内核 ELF 里抠出来(48 字节)
执行命令:

tail -c +$((0x41b4 + 1)) FORTIOS741/flatkc > kernel_741.gz

gzip -d -c kernel_741.gz > kernel_741.elf
dd if=kernel_741.elf of=seed.bin bs=1 skip=$((0x19952e0)) count=48
xxd seed.bin

图片.png
第六步、/dev/zero 是内核提供的一个 "特殊设备文件"
从它里面读数据,它永远返回 0x00 字节,要多少给多少
再用 chacha_dec.py 去解密生成两个文件 rootfs_741.decrypted(重要)、pub_key_741.der
执行命令:
dd if=/dev/zero of=enc_pub_key.bin bs=1 count=270
python3 chacha_dec.py

图片.png
第七步、gzip 解出 cpio 归档
执行命令:
gzip -d -c rootfs_741.decrypted > rootfs_741.cpio
xxd rootfs_741.cpio | head -1

图片.png
第八步、cpio 解包
执行命令:
mkdir rootfs741
cd rootfs741
sudo cpio -idmv < ../rootfs_741.cpio

图片.png
第九步、解开里面的 tar.xz
严格说普通用户也能解,sudo su 主要是为了,保持文件属主是 root(和固件里一致)
普通用户(iotsec-zone)解包时,
没法把属主设回 root,解出来的文件就变成自己的:
-rwxr-xr-x 1 iotsec-zone iotsec-zone 12345 ... ← 属主变了

root 有权限执行 chown,
所以 tar 能把属主恢复成固件里记录的样子(root : root):
-rwxr-xr-x 1 root root 12345 ... ← 和固件一致
执行命令:
xz -d bin.tar.xz && tar -xvf bin.tar && rm -f bin.tar
xz -d usr.tar.xz && tar -xvf usr.tar && rm -f usr.tar
xz -d node-scripts.tar.xz && tar -xvf node-scripts.tar && rm -f node-scripts.tar
xz -d migadmin.tar.xz && tar -xvf migadmin.tar && rm -f migadmin.tar

图片.png

图片.png
打开 VM 界面点击文件 → 打开

图片.png
接着用把 FortiGate-VM64.hw17.ovf 启动

图片.png
点击同意,再填入虚拟机名称和存放文件路径,点击导入即可

图片.png
配置不需要做任何更改

图片.png
初次登录设置密码

图片.png
查看分配了什么 IP 手动输入命令:diagnose ip address list
这里可以看到分配了 192.168.2.32

图片.png
网页输入 192.168.2.32 再输入 admin admin 登录

图片.png
点击上传(英文网页翻译成中文)

图片.png
选择 license.lic 文件后,再点击 OK

图片.png
接着点击确认再系统重启一次(虚拟机会自动重启)

图片.png
重启名称会变(无关要紧)

图片.png
然后重新登录就能进入管理页面

图片.png
补充:有时候启动会报磁盘错误
手动输入命令:
execute disk list(查看 ref: 16)
execute disk scan 16(执行完会重启)

图片.png
2、格式化字符串漏洞
在 bin/init 程序中的 sub_2B1D9E0() 里面有一个格式化字符串漏洞

图片.png

图片.png
这里直接把 v12 填到 snprintf() 把 v12 当数据传
这里漏了 "%s",导致 v12 里的 % 全被当作格式指令解析
正确写法应该是 snprintf(&s[17], 0xC9, "%s", v12)

图片.png
交叉引用能看到有 4 处地方引用了 sub_2B1D9E0()

图片.png
先从倒数第一 sub_27E2CC0() 函数看起,这里有一大串字符都在提示 USB 磁盘
这里实际是需要通过管理员从 USB 拷贝才能触发,所以这里触发漏洞点不可靠

图片.png
倒数第二个函数 sub_27E2B70() 函数需要攻击者先架好恶意 FTP 服务器
攻击者不能直接传内容,
只能让管理员在设备上敲一条 CLI 命令从指定的 FTP 服务器下载到 /tmp/ftkseed 才行

图片.png
顺数第二 sub_27E2A80() 函数同倒数第二 sub_27E2B70() 函数原理一样
只不过这个开的是 TFTP 服务器

图片.png
顺数第一 sub_E7A470() 函数才是用户可控的点,再交叉引用找到一段提示词

图片.png

图片.png

图片.png
在 .data:00000000047E2190 有 "user" 、"fortitoken" 、"import-seed" 关键词
用 burp 可以看到有这样的接口,那么就可以知道该 sub_E7A470() 函数的触发接口是
/api/v2/monitor/user/fortitoken/import-seed

图片.png
接下来就简单讲解一下,如何触发这里漏洞 sub_2B1D9E0(char *filename) 函数

当通过接口上传一个恶意文件到这里,filename 会被打开给 v1 赋值文件指针

图片.png
接着 v1 文件指针会按照末尾 '\n' 切割出一行内容再赋值 lineptr 指针

图片.png
这里会把 lineptr 内容按 ' , '(逗号)分别切割赋值给
format(serial 指纹)、v11(密文)、v8(IV)

图片.png
因为上传的内容是密文,需要对 v11 进行 AES-256-CBC 解密
v12 将会得到解密后的明文

图片.png
v10 是 64 字节 key

图片.png

图片.png
然后这里恶意构造文件内容注入就能通过 %hhn 对内存进行改写

图片.png
这里再提前了解一下飞塔 FortiGate 的登录获取 Cookie 和 token 两道防线:
① APSCOOKIE 管身份认证,APSCOOKIE → httpsd 校验通过,放行进 API
② ccsrftoken 管CSRF 防护码

图片.png
CSRF 全称 Cross-Site Request Forgery(跨站请求伪造)
ccsrftoken_10443_2853b1b6 和请求头 X-Csrftoken 做双提交校验
X-Csrftoken == ccsrftoken ,则表示 CSRF 校验通过,允许执行导入
对于写入路由器操作必须带 X-Csrftoken:
第 1 条、后端强制要
POST / PUT / DELETE(会改东西的)→ 必须带,不带就 403
GET(只读)→ 不强制,带不带都行
第 2 条、前端带不带(发不发)
页面 JS 发的 XHR 请求
(有 X-Requested-With: XMLHttpRequest) → 统一会带(前端拦截器顺手加的)
浏览器自己发的资源请求如:
(图片、JS、service-worker,Sec-Fetch-Dest 不是空/cors) → 不带

图片.png
这里 exp.py 脚本的 IP 地址要对应上飞塔 VM 虚拟机分配的 IP 地址

图片.png
python exp.py --file /tmp/IOTsec --content 123456789 --sleep 20
用途:
--file 往指定目录下写入文件
--content 往文件里面写入指定的内容
--sleep 等待时间可写可不写

图片.png
在 VM 模拟的飞塔终端里面输入 fnsysctl ls -l /tmp/IOTsec 的确看到有 9 字节文件
但是输入 fnsysctl cat /tmp/IOTsec 却没有权限查看 "123456789" 内容
也算是成功利用格式化字符串提权对内部做某些操作

图片.png
补充内容:
① 这里逻辑 httpsd 的 GOT 表被劫持 strchr() 全局指针 被替换成了 setcontext()
当去执行 setcontext() ,它就会由 /bin/httpsd 通过 execve() 替换成 /bin/node 执行
execve() 也就是 "当前这个进程" 的代码和数据整个替换成 node 且 PID 不变
当 /bin/node 跑完后 JS ,那么 PID 消失父进程知道 /bin/httpsd 已经挂掉了
父进程就会立马 fork 一个新的 PID 将 /bin/httpsd 重新拉起
② 这个 fnsysctl 命令是由 /bin/sysctl 程序处理

图片.png
这里对于一些常用 linux 命令进行重写,还有权限限制不是都能使用的

图片.png

图片.png

分享到

参与评论

0 / 200

全部评论 1

皮皮先生的头像
牛🤬
2026-09-03 02:54
投稿
签到
联系我们
关于我们