雷石|对用友NC“任意文件读取”漏洞的分析
最近看到⼀些漏洞资讯越来越离谱,⼀些错误的信息竟可以互相转发却⽆⼈察觉,今天就分析⼀下漏洞。
⽤友NC-word.docx任意⽂件读取?你确定吗?
起始时看到漏洞⽂章,所有公众号都在发该接⼝的存在任意⽂件读取,但清⼀⾊给出的poc都是读取web.xml
看到漏洞正好有环境当然要复现下,但复现过程中发现了不对劲,对其展开了⼀番研究。
漏洞复现 :
⾸先看⽹上给的POC:
http://127.0.0.1/portal/docctr/open/word.docx?disp=/WEB-INF/web.xml
看起来似乎没有没问题,试了⼀下确实"读"到了⽂件,但试了不能穿越⽬录。
那么再试试该其他⽂件,先查看下该⽬录下有什么⽂件。
试⼀下index.jsp,显示了html代码,jsp解析了,这样⽂件"读取"有点奇怪啊。
漏洞分析:
从代码下⼿,发现了⼤问题。
关键代码如下:
package
nc.uap.lfw.file.action
;
import
java.io.
IOException;
import
javax.servlet.
RequestDispatcher;
import
javax.servlet.
ServletException;
import
javax.servlet.
ServletRequest;
import
javax.servlet.
ServletResponse;
import
javax.servlet.http.
HttpServlet;
import
javax.servlet.http.
HttpServletRequest;
import
javax.servlet.http.
HttpServletResponse;
import
org.apache.commons.lang.
StringUtils;
public class DocServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
String url = req.getParameter("disp");
if (StringUtils.isEmpty(url)) //判断url是否为空
return;
if (url.toLowerCase().startsWith("/portal")) //字符转⼩写,并字符串检查开头
url = url.substring(7); //第7个字符开始提取⼦字符串
if (StringUtils.isEmpty(url))
return;
if (url.toLowerCase().startsWith("/docctr/open/office"))
return;
byte[] byte1 = url.getBytes("ISO-8859-1");
url = new String(byte1, "UTF-8");
RequestDispatcher dispatcher =
req.getSession().getServletContext().getRequestDispatcher(url);
dispatcher.forward((ServletRequest)req, (ServletResponse)resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req, resp);
}
}
⾸先⼤概看⼀眼代码,并没有⽤于⽂件操作或IO流的包和函数。
阅读如上代码,从接收参数disp后,先是⼏次判读是否为空和字符串开头,条件为真直接结束,否则往下执 ⾏。
⽽关键代码是
req.getSession().getServletContext().getRequestDispatcher(url);
getRequestDispatcher() 是请求转发,前后页⾯共享⼀个 request ; 这个是在服务端运⾏的,对浏览器来说 是透明的 .
很明显这绝不是⼀个⽂件读取的漏洞,⽽是请求转发造成的漏洞,访问⼀个URL会将请求转发到另⼀个URL, 访问jsp⽂件能够解析执⾏也证明了这点。
但请求转发造成的漏洞也不能够称呼"重定向漏洞", 请求重定向(redirect):客户端发送⼀个请求,服务器返响应,响应⾥⾯记录客户端需再次发起请求的地址,此 时客户端会再次请求发送给这个新的地址。重定向是客户端的⾏为,客户端需要访问两次服务器,发送的是两 次请求且是不同的Request请求和Response响应,请求过程数据不共享。
在第⼆次请求时浏览器url地址会变 化 请求转发(forward):指的是浏览器向服务器发送⼀个请求,服务器会帮客户端将请求转发到⽬标地址,再将 响应结果返回给客户端,重定向是服务器实现的,客户端不关⼼服务器如何⼯作,只接受响应,在交互过程中 使⽤同⼀个Request请求对象和Response响应对象,数据都是共享的,浏览器URL地址不会发⽣变化 那为什么能访问WEB-INF⽬录下配置⽂件呢?正常情况访问 查阅资料了解到:WEB-INF⽬录,应⽤服务器把它指为禁访⽬录,在浏览器 中⽆法访问, 但是可以让servlet进 ⾏访问。
从以上可以判断,漏洞原理是通过请求转发绕过了对web-inf⽬录的访问限制,应该称为“绕过”,而绝非“文件读取”。相似的漏洞如“Apache Shiro身份绕过(CVE-2022-40664)”漏洞是通过EequestDispatcher转发或包含时Shiro中的身份验证绕过⽽产⽣的漏洞上⾯看了反⾯例⼦,再来看⼀个正经的⽂件读取漏洞。
⽤友U8C-PrintTemplateFileServlet⽂件读取-删除
这⾥先贴下代码吧。
从代码引⽤的包就可看到引⽤了⽂件流的包。
⾸先是接收参数filePath,然后定义realPath变量,并拼接组合,然后读取⽂件。
在46⾄62⾏,是读取⽂件流并返回给浏览器的实现过程。但在64⾏的判断中会删除服务器中的⽂件。
由于会删除文件,建议在测试环境复现
复现漏洞
POC
http://172.16.1.200:8088/servlet/~uapweb/nc.lfw.billtemplate.servlet.PrintTemplateFileS
ervlet?filePath=2222.txt
访问不存在⽂件会报错,并泄漏绝对路径。
⾸先在服务器该⽬录放置⼀个测试⽂件“flag.txt”并再次访问该⽂件。
读取该⽂件,成功返回⽂件内容。
此时服务器⽬录下⽂件已被删除。
结语
不知何时圈⼦从浮躁到现在的抽象,SQL注⼊不叫“注入”叫“任意文件上传”(通过sql注⼊写webshell)或叫“RCE”( 写webshell再调⽤命令执⾏函数)、权限绕过不叫“绕过”叫任意⽂件读取 ,漏洞评级也有了“核弹级”。
本文作者:xueqi
雷石安全实验室
商务咨询:
0571-87031601
商务邮箱:
mtn@motanni.com