雷石|对用友NC“任意文件读取”漏洞的分析

雷石安全实验室

共 7479字,需浏览 15分钟

 · 2024-04-10

8697e71a1a5d0f1c35834bb9ddfe1f6f.webp



     最近看到⼀些漏洞资讯越来越离谱,⼀些错误的信息竟可以互相转发却⽆⼈察觉,今天就分析⼀下漏洞。


⽤友NC-word.docx任意⽂件读取?你确定吗?


     起始时看到漏洞⽂章,所有公众号都在发该接⼝的存在任意⽂件读取,但清⼀⾊给出的poc都是读取web.xml

6f9fcc1d0a6a2907cc1c5b400d21ee77.webp


     看到漏洞正好有环境当然要复现下,但复现过程中发现了不对劲,对其展开了⼀番研究。


漏洞复现

     ⾸先看⽹上给的POC:

                  
                    http://127.0.0.1/portal/docctr/open/word.docx?disp=/WEB-INF/web.xml
                  
                

     看起来似乎没有没问题,试了⼀下确实"读"到了⽂件,但试了不能穿越⽬录。

ab11fab48c5d04848b1cb4aced26a8d4.webp


     那么再试试该其他⽂件,先查看下该⽬录下有什么⽂件。

cc28d078a4f7a4a7d70a75190134c268.webp


     试⼀下index.jsp,显示了html代码,jsp解析了,这样⽂件"读取"有点奇怪啊。

ccee922b73f7da4e05e56029e853067c.webp


漏洞分析:

     从代码下⼿,发现了⼤问题。

     关键代码如下:

                  
                    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⽂件读取-删除

     这⾥先贴下代码吧。

393676f7036d296765b56755e2797175.webp


     从代码引⽤的包就可看到引⽤了⽂件流的包。

     ⾸先是接收参数filePath,然后定义realPath变量,并拼接组合,然后读取⽂件。

     在46⾄62⾏,是读取⽂件流并返回给浏览器的实现过程。但在64⾏的判断中会删除服务器中的⽂件。


由于会删除文件,建议在测试环境复现


复现漏洞 ‍‍

 POC

                  
                    http://172.16.1.200:8088/servlet/~uapweb/nc.lfw.billtemplate.servlet.PrintTemplateFileS
                  
                  
                    ervlet?filePath=2222.txt
                  
                

     

     访问不存在⽂件会报错,并泄漏绝对路径。

409477cd68d9f7fd8b548deafac8f5a4.webp


     ⾸先在服务器该⽬录放置⼀个测试⽂件“flag.txt”并再次访问该⽂件。

23dfdc9f9cd5a508674ea1ba7aa7d6af.webp


     读取该⽂件,成功返回⽂件内容。

d32dd9d83e2c336a177f74964d791ef2.webp


     此时服务器⽬录下⽂件已被删除。

24295b5580156029d8b26f0e025f3f6c.webp


结语

     不知何时圈⼦从浮躁到现在的抽象,SQL注⼊不叫“注入”叫“任意文件上传”(通过sql注⼊写webshell)或叫“RCE”( 写webshell再调⽤命令执⾏函数)、权限绕过不叫“绕过”叫任意⽂件读取 ,漏洞评级也有了“核弹级”。


本文作者:xueqi


雷石安全实验室


商务咨询:

0571-87031601

商务邮箱:

mtn@motanni.com

浏览 8
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报