type
status
date
slug
summary
tags
category
icon
password
最后编辑时间
Jul 31, 2023 08:54 AM
漏洞编号
No.
同步状态
状态
Author
前言
用友NC是一款企业级ERP软件。作为一种信息化管理工具,用友NC提供了一系列业务管理模块,包括财务会计、采购管理、销售管理、物料管理、生产计划和人力资源管理等,帮助企业实现数字化转型和高效管理。
用友NC6.5中的
nc/bs/framework/server/InvokerServlet.class
存在未授权任意类调用漏洞,该漏洞通过构造特殊的数据包调用NC自有类可导致反序列化或命令执行。其中可利用BeanShell接口执行任意命令,漏洞编号CNVD-2021-30167。安装
windows10 x64 jdk1.7.0_21
下载nc6.5,解压后启动安装脚本
path\NC6.5\yonyou_nc\setup.bat
1、全选安装产品

2、安装完成后自动启动
path\yonyou\home\bin\sysConfig.bat
1)配置服务器信息,点击读取,修改需要自定义的参数后保存

2)这里不添加任何数据源(数据库)和授权,直接
部署EJB
即可
3)运行
path\yonyou\home\startServer.bat
无报错即可
准备
导出目录下所有jar包,加载到idea中,配置idea远程调试参数

在启动yonyounc时添加jdwp参数

调试
- servlet-mapping

- poc

moduleName
根据poc在
bsh/servlet/BshServlet.class$doGet()
方法下断点,跟进到 nc/bs/framework/server/InvokerServlet.class$doAction()
方法关键代码
pathInfo

传参结果,
moduleName
为 ali
, serviceName
为 /bsh.servlet.BshServlet

跟进
this.getServiceObject()
方法
在
BusinessAppServer.getInstance().getContainer()
中,根据模块名在nameModulesMap
中获取对应的容器,228个模块名可利用
初始化方法中将
/home/modules
目录下的所有子目录放入HashMap

ServiceName兵分两路
getServiceObject
之后兵分两路,主要分为两个方法触发漏洞Service
obj
为 BshServlet@12592
接着启动线程监控器跟踪指定线程
obj对象
BshServlet
继承了HttpServlet
类,重写了doGet()
方法
跟进service方法:
javax/servlet/http/HttpServlet.class$service()
serlvetMappings如下


经过
org/apache/catalina/connector/RequestFacade.class$getMethod()
方法之后调用当前对象BshServlet
的doGet
方法处理
注意,如果是POST方法,同样是传递到doGet中执行

bsh/servlet/BshServlet.class$doGet()
evalScript
方法跟进到
Interpreter.class$eval()
方法
最后调用不同参数类型的eval方法

该方法创建一些新的BeanShell解释器实例,并将输入输出流、命令空间、调用堆栈等传入,逐行读取BeanShell脚本
并调用
BSHPrimaryExpression.class$eval()
方法执行命令,最后返回执行的结果var4

BSHPrimaryExpression.class$eval()
方法解析出解释器对象要执行的脚本参数,反射调用exec
在
bsh/Name.class$invokeMethod
中调用本地方法bsh/Name.class$invokeLocalMethod
,var6获取var2的类型为String,调用getCommand
方法
var7获取到执行的脚本位置
/bsh/commands/exec.bsh
,通过 BshClassManager.getClassManager().getResourceAsStream()
读取到该脚本内容到InputStream
,之后调用bsh/NameSpace.class$loadScriptedCommand
方法加载脚本
exec.bsh
用Runtime.getRuntime().exec
执行传入的参数
后续简单看一下调用栈
返回到
invokeLoaclMethod
,var10为null,进入var9.invoke

跟进
bsh/BshMethod.class$invoke()


invokeImpl()

这段代码定义了一个BeanShell解释器中的类
BSHMethodInvocation
,用于处理方法调用的执行。在
invokeImpl
方法中,首先获取方法返回值和参数类型,然后判断参数个数是否匹配。如果匹配,则创建一个新的命名空间对象,并设置方法参数的值。在设置方法参数的值时,如果参数类型不为空,则调用Types.getAssignableForm
方法将参数转化为可赋值的形式,并调用setTypedVariable
方法设置参数的值;否则,直接调用setLocalVariable
方法设置参数的值。然后,将新的命名空间对象压入调用堆栈中,并调用方法体的
eval
方法计算方法体的值。在计算方法体的值时,如果方法体返回一个ReturnControl
对象,则将其转化为返回值,并执行相应的控制流程(如continue和break)。如果方法返回值的类型不为空,将返回值转化为可赋值的形式,并返回该值。如果返回值的类型为空,则返回Primitive.VOID
对象。接着执行代码块

最后调用
BSHPrimaryInvocation.class$eval()
执行 exec.bsh
中的命令doAction
以
/servlet/~ic/nc.bs.framework.mx.monitor.MonitorServlet
这个payload为例同样在
InvokerServlet.class$doAction()
中通过getServiceObject
获取到obj为MonitorServlet
对象
之后进入
adaptor.doAction
,该方法中提供了反序列化输入流的操作readObject

Reference
- 作者:3R1C
- 链接:https://notion-3r1c.vercel.app/article/128
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。