1. 项目概述一次针对用友畅捷通T的实战漏洞验证最近在梳理一些历史高危漏洞的资产影响面时用友畅捷通T的密码重置漏洞QVD-2023-20016再次进入了我的视野。这个漏洞的利用方式直接、危害极大攻击者无需任何前置条件即可直接修改管理员密码从而完全控制目标系统。网上虽然有不少关于漏洞原理的简单描述但大多停留在“存在漏洞”的层面缺乏从资产发现到批量验证的完整实战视角。恰好手头有一个内部授权的测试项目我便以此为契机结合Nuclei这款强大的漏洞扫描器完整地走了一遍从环境理解、POC构造到批量验证的流程。这篇文章我就把这个过程中的核心思路、踩过的坑以及最终成型的批量验证方案分享出来希望能给负责安全运营、渗透测试或红队评估的同行们提供一个可复现的参考模板。简单来说这个漏洞的核心在于用友畅捷通T软件中一个名为/tplus/ajaxpro/RecoverPassword.aspx的接口存在逻辑缺陷。该接口本意是用于管理员忘记密码时通过手机验证码重置但其验证逻辑可以被绕过导致攻击者能够直接指定新的管理员密码。这意味着任何一个暴露在公网且存在该漏洞的T系统都可能面临被“一键接管”的风险。我们的目标就是高效、准确地在海量资产中定位并验证存在此漏洞的目标。2. 漏洞原理深度解析与影响范围评估2.1 漏洞接口与触发逻辑拆解要理解这个漏洞我们首先得看看正常的密码找回流程应该是什么样。在一个设计良好的系统中RecoverPassword这类接口通常会严格校验用户的身份。例如需要用户输入已绑定的手机号系统向该手机发送验证码用户回填正确的验证码后才能进行密码重置。整个链条中“验证码”是证明“操作者是手机号主人”的关键凭证。然而在存在漏洞的用友畅捷通T版本中/tplus/ajaxpro/RecoverPassword.aspx接口的实现出现了严重的逻辑错误。通过对漏洞细节和公开POC的分析其问题主要出在服务端对请求参数的校验环节。攻击者构造的HTTP POST请求中可以直接提交目标用户的账号如admin和新密码而服务端在处理时要么完全跳过了对验证码的校验步骤要么校验逻辑存在缺陷例如验证码是否为空或是否为某个默认值导致攻击者无需提供任何有效的验证信息密码修改操作就能被执行。从代码层面推测可能存在类似如下的伪代码逻辑// 存在缺陷的伪代码示例 String username request.getParameter(“user”); String newPassword request.getParameter(“newpwd”); String verifyCode request.getParameter(“code”); // 问题点校验逻辑不严谨或缺失 if (verifyCode ! null verifyCode.equals(getStoredCode(username))) { // 正常校验 updatePassword(username, newPassword); } else { // 漏洞点可能else分支缺失或verifyCode为空时误入成功分支 // 或者根本没有对verifyCode进行有效性判断直接执行了updatePassword updatePassword(username, newPassword); // 危险操作 }当然实际代码比这复杂但核心就是身份验证环节的缺失或绕过。这使得一个本该是受严格保护的敏感操作变成了一个无需认证的公开功能。2.2 受影响版本与资产特征识别根据漏洞情报QVD-2023-20016该漏洞影响用友畅捷通T的多个版本。通常这类漏洞影响的是某个时间点之前发布的所有版本直到官方发布补丁修复。对于防守方而言首要任务是确定自身资产是否在受影响范围内。如何快速识别一个用友畅捷通T系统网页特征访问目标IP或域名的根路径其登录页面通常具有明显的用友软件风格标题或页脚可能包含“用友”、“畅捷通”、“T”等字样。路径特征尝试访问/tplus目录。如果存在很可能是T的典型路径。进一步访问/tplus/ajaxpro/RecoverPassword.aspx即使页面不返回内容或返回错误该路径的存在本身就是一个强指示。指纹识别使用常见的Web指纹识别工具如Wappalyzer、WhatWeb或扫描器内置的指纹规则进行识别。用友系列产品的HTTP响应头、Cookie、特定静态文件如图片、JS库往往带有独特标识。在批量扫描中我们主要依赖路径特征和HTTP状态码/响应体特征来进行初筛。一个典型的探测请求是GET /tplus/ajaxpro/RecoverPassword.aspx如果返回状态码是200、403可能存在但禁止访问或特定的404自定义错误页面而非标准的“Not Found”都可以将其列为可疑目标进入下一步的漏洞验证阶段。注意直接访问漏洞路径可能会在目标系统的访问日志中留下明显记录。在授权测试中这是可接受的但在未授权的情况下这种行为具有攻击性。所有批量验证操作必须在获得明确授权的资产范围内进行。3. 工具选型为何是Nuclei面对成千上万的潜在目标手动一个个去测试是不现实的。我们需要一款自动化漏洞验证工具。市面上可选的有Pocsuite、Xray、Goby以及Nuclei。我最终选择Nuclei作为本次批量验证的核心引擎主要基于以下几点考量模板化优势Nuclei使用YAML格式的模板文件来定义漏洞检测逻辑。一个模板即可完成从指纹识别到漏洞验证的全过程。这对于标准化漏洞验证流程、团队协作和知识沉淀极其有利。写好一个模板全团队都能复用。高性能与并发控制Nuclei基于Go语言开发天生具有高并发处理能力。它可以非常高效地对大量目标进行扫描并且可以灵活调节并发线程、请求速率避免对目标造成过大压力或被封禁IP。丰富的社区生态Nuclei拥有一个活跃的社区官方和社区贡献者维护着数千个漏洞检测模板CVE、CNVD、QVD等。即使没有现成的模板参考类似模板进行开发也事半功倍。与资产发现流程无缝集成Nuclei可以非常方便地与其他工具链集成。例如我们可以先用httpx或naabu进行资产发现和端口扫描将存活的Web服务输出为列表然后直接交给Nuclei加载模板进行漏洞扫描。这条流水线非常顺畅。当然Nuclei也不是万能的。它的模板主要针对HTTP/HTTPS协议对于非Web服务或需要复杂交互的漏洞如某些反序列化漏洞支持起来比较麻烦。但针对本次的用友T Web漏洞它无疑是“对症下药”的利器。4. Nuclei模板开发实战编写QVD-2023-20016检测模板Nuclei模板的编写是本次批量验证的核心技术环节。一个好的模板不仅要能准确触发漏洞还要能清晰地判断漏洞是否存在并尽量减少误报和漏报。4.1 模板结构规划一个完整的Nuclei模板通常包含以下几个部分id: 漏洞唯一标识我习惯用QVD-2023-20016这样的编号。info: 包含漏洞名称、作者、严重等级、描述、参考链接等信息。requests: 定义发送的HTTP请求这是模板的主体。matchers: 定义如何根据响应结果来判断漏洞是否存在。我们的目标是发送一个精心构造的POST请求到/tplus/ajaxpro/RecoverPassword.aspx如果请求成功且响应内容表明密码被修改则判定漏洞存在。4.2 请求Requests构造细节首先我们需要分析漏洞触发的HTTP请求包格式。通过公开的POC和自行搭建环境测试我总结了以下关键点方法POST路径/tplus/ajaxpro/RecoverPassword.aspxContent-Type通常为application/x-www-form-urlencoded或application/json需要根据实际目标进行调整测试。经过测试目标系统接受表单格式。请求体需要包含关键参数。根据分析参数可能包括用户IDUserID、新密码NewPassword等。注意参数名的大小写可能有严格要求。Payload设计为了验证漏洞我们需要一个“无害”的测试方法。直接修改真实管理员密码是破坏性的。幸运的是经过测试发现该接口在修改密码时如果提供的UserID在系统中不存在仍然会返回“成功”的逻辑但实际不会影响任何账户。这为我们提供了安全的验证方式。因此我们可以使用一个随机生成的、不存在的UserID如test_vuln_加上时间戳和任意的新密码如VulnTest2024作为Payload。基于以上分析我编写的requests部分如下requests: - method: POST path: - {{BaseURL}}/tplus/ajaxpro/RecoverPassword.aspx headers: Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 body: UserIDnon_existent_user_{{randstr}}NewPasswordVulnTest2024ConfirmPasswordVulnTest2024 matchers-condition: and matchers: - type: status status: - 200 - type: word words: - “success”:true - “密码修改成功” - “操作成功” condition: or关键点解析{{BaseURL}}这是Nuclei的变量会自动替换为目标URL。{{randstr}}生成一个随机字符串确保每次请求的UserID都是唯一的、不存在的避免意外碰撞或干扰。双密码字段有些系统需要输入新密码和确认密码因此我同时添加了NewPassword和ConfirmPassword两个参数值相同以提高兼容性。Matchers组合我设置了两个匹配条件matchers-condition: and。首先HTTP状态码必须是200type: status。其次响应体中必须包含表明操作成功的字样type: word。我用了condition: or并列出了几种可能的成功关键词如JSON返回的“success”:true或中文提示“密码修改成功”、“操作成功”。这样可以适配不同版本或不同配置的系统返回信息。4.3 指纹识别HTTP与条件请求为了提高扫描效率我们不应该对每一个目标都盲目发送POST请求。可以先通过一个简单的GET请求来识别目标是否为用友畅捷通T系统。这就是Nuclei模板中http链式请求的用处。我在requests的开头增加了一个前置的raw请求也可以单独写一个http块用于指纹识别requests: - method: GET path: - {{BaseURL}}/tplus/login.aspx matchers: - type: word words: - “畅捷通T” - “/tplus/” - “UFIDA” condition: or extractors: - type: regex name: version part: body regex: - “T\ ([\d\.])” stop-at-first-match: true这个请求会先访问T的登录页。如果匹配到“畅捷通T”、“/tplus/”或用友的商标“UFIDA”等关键词则判定该目标可能是T系统并尝试通过正则表达式提取版本号。stop-at-first-match: true确保只有当前置指纹匹配成功时才会继续执行后面的POST漏洞验证请求。这通过Nuclei的raw请求模板或多阶段请求可以很方便地实现能极大减少无效请求提升扫描速度。5. 批量验证实战流程与操作记录有了量身定制的Nuclei模板批量验证就变成了一条高效的流水线。以下是我在实际项目中的操作步骤。5.1 环境准备与目标收集安装Nuclei从官方GitHub Release页面下载最新版二进制文件或通过go install命令安装。确保版本在v2.9.0以上。# 示例下载并解压Linux版本 wget https://github.com/projectdiscovery/nuclei/releases/download/v2.9.15/nuclei_2.9.15_linux_amd64.zip unzip nuclei_2.9.15_linux_amd64.zip sudo mv nuclei /usr/local/bin/准备目标列表将需要扫描的IP或域名整理成一个文本文件每行一个例如targets.txt。这些目标应来自于前期的资产发现阶段如naabu端口扫描发现80/443/8080端口再经httpx验证为HTTP服务。192.168.1.100 tplus.example.com 10.0.10.50:80805.2 执行批量扫描使用以下命令启动扫描nuclei -l targets.txt -t qvd-2023-20016.yaml -o results.txt -stats -si 5 -rate-limit 50参数详解-l targets.txt指定包含目标列表的文件。-t qvd-2023-20016.yaml指定我们编写好的漏洞检测模板文件。-o results.txt将扫描结果输出到results.txt文件。-stats实时显示扫描统计信息已扫描目标数、发现漏洞数等方便监控进度。-si 5每5秒更新一次统计信息。-rate-limit 50限制每秒最多发送50个请求。这是非常关键的参数不加限制地狂轰滥炸会对目标系统造成拒绝服务DoS攻击在授权测试中也是不专业且危险的行为。根据目标网络环境和自身带宽合理设置此值通常建议在10-150之间。5.3 扫描结果分析与验证扫描结束后打开results.txt文件。Nuclei的输出格式非常清晰会包含以下信息漏洞模板ID[QVD-2023-20016]漏洞名称和严重等级[critical]目标URL匹配到的关键词即我们matchers中定义的成功标识重要人工复核自动化工具可能存在误报。对于扫描结果中报出的每一个目标我强烈建议进行手动验证。打开浏览器访问目标系统的登录页面http://目标/tplus/login.aspx确认其确实是用友畅捷通T。使用Burp Suite或curl命令手动构造一个与模板中相同的POST请求但将UserID改为一个已知存在的测试账号如果允许或者观察返回信息。验证响应是否确实包含成功信息。有时系统可能返回一个通用的错误页面也是200状态码但内容不符这就需要调整模板中的matchers关键词。6. 常见问题、排查技巧与优化建议在实际操作中我遇到了不少问题也总结了一些优化技巧。6.1 常见问题速查表问题现象可能原因排查与解决方案扫描速度极慢1. 目标网络延迟高。2.-rate-limit设置过低。3. 模板中未使用指纹前置条件对所有目标都发送了POST请求。1. 使用ping或tcping测试网络质量。2. 适当提高-rate-limit值并观察系统负载。3. 确保模板包含有效的HTTP指纹识别阶段过滤非相关目标。误报率高1.matchers关键词过于宽泛其他无关页面也返回了类似内容。2. 目标系统返回了自定义的200错误页。1. 精炼关键词使用更独特的字符串组合。可以尝试匹配JSON结构如\success\:true。2. 增加negative匹配器排除包含“错误”、“失败”、“404”等字样的响应。漏报1. 目标系统路径不是默认的/tplus。2. 请求参数名或格式不正确。3. 目标系统使用了HTTPS且证书有问题或存在WAF拦截。1. 结合资产测绘信息尝试常见路径变体如/yonyou、/yy等。2. 抓取正常密码重置流程的流量包分析准确的参数名和格式。3. 在Nuclei命令中添加-retries 3重试或使用-proxy选项设置代理以绕过某些网络策略。检查WAF返回的特征码。Nuclei报告“No results found”但目标疑似存在1. 模板语法错误。2. 目标需要特定的Cookie或Header才能访问漏洞路径。1. 使用nuclei -validate命令校验模板语法。2. 在模板的headers部分添加必要的Cookie如先访问登录页获取Session或自定义Header。这可能需要更复杂的多步骤交互模板。6.2 实操心得与进阶优化“无害化”验证原则在编写漏洞验证POC时首要原则是“不造成实际损害”。本次使用的“修改不存在的用户密码”的方法是一个典范。在编写其他漏洞模板时也应优先考虑读取而非写入、查询而非删除的操作。例如SQL注入模板优先使用sleep()或md5()函数进行延时或布尔盲注验证而非drop table。速率限制与道德考量-rate-limit不是可选项是必选项。尤其是在对生产环境进行授权测试时必须将并发请求数控制在目标系统可承受的范围内。可以先用一个很小的目标集如5-10个进行试扫描观察目标系统的响应时间和错误率再确定全量扫描的速率。模板的版本兼容性一个漏洞可能影响多个版本但不同版本的接口响应可能略有差异。我们的模板中使用了condition: or来匹配多种成功关键词就是为了提高兼容性。在可能的情况下可以尝试提取响应中的版本信息通过extractors并与漏洞影响版本进行对比让报告更精确。结果集成与报告Nuclei的原始输出-o是文本格式。对于大型项目可以结合-json输出选项将结果保存为结构化JSON然后导入到Elasticsearch、Jira或自定义的漏洞管理平台进行跟踪和处理。也可以使用nuclei -json -o results.json输出方便后续用脚本解析。7. 防御视角如何发现与修复此类漏洞作为攻击方我们研究漏洞利用作为防守方我们更应关注如何免疫。从这次漏洞分析中我们可以提炼出一些通用的防御思路。输入校验与身份鉴权这是最根本的。任何执行敏感操作如修改密码、支付、删除数据的接口都必须进行严格的身份验证和授权检查。RecoverPassword接口必须强制验证“验证码”、“旧密码”、“安全问题答案”等二次凭证且这些凭证必须在服务端进行强校验不能依赖于客户端传递或可预测的值。最小权限原则即使接口存在也应确保其功能最小化。例如密码重置接口不应允许直接设置任意密码而应强制跳转到设置强密码的页面或生成临时密码通过安全通道发送给用户。日志审计与异常监控对所有敏感接口的访问请求进行完整日志记录包括请求IP、时间、用户标识、操作类型和关键参数。建立监控告警规则对短时间内同一IP或用户账号的频繁密码重置请求进行告警。定期漏洞扫描与补丁管理企业应建立内部的漏洞扫描机制使用Nuclei等工具搭配自研或订阅的POC模板库定期对自身资产进行安全检测。同时密切关注用友等主要供应商的安全公告及时测试并安装官方发布的安全补丁。网络层面控制对于类似/tplus/ajaxpro/RecoverPassword.aspx这样的高危管理接口可以考虑通过网络防火墙或WAFWeb应用防火墙策略限制其访问来源IP仅允许管理员所在的特定网络段访问。通过这次从漏洞分析到批量验证的完整实践我深刻体会到一个高效的漏洞运营流程离不开对漏洞原理的透彻理解、对自动化工具的熟练运用以及贯穿始终的安全与合规意识。这份Nuclei模板和实战经验希望能成为你武器库中的一件实用装备。在安全的世界里知己知彼方能百战不殆。