Loading

Nexus Repository Manager 3 未授权目录穿越漏洞(CVE-2024-4956)

https://github.com/vulhub/vulhub/blob/master/nexus/CVE-2024-4956/README.zh-cn.md
https://ares-x.com/2020/04/20/IDEA远程调试Docker中程序的方法/
https://t.zsxq.com/MDrfR
漏洞类型:未授权任意文件读取(路径穿越)
POC

GET /%2F%2F%2F%2F%2F%2F%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd

两个问题

  1. 为什么要进行url编码,不能直接传入../../../../../../../etc/passwd吗?
  2. 为什要在路径穿越利用中,需要构造这么多url编码后的/,即%2F?

调试环境

docker ps 
docekr exec -it container_id bash
pwd
docker cp container_id:/opt/sonatype ./sonatype

补丁分析

参考1ue师傅,照猫画虎。

临时修补措施

https://support.sonatype.com/hc/en-us/articles/29412417068819-Mitigations-for-CVE-2024-4956-Nexus-Repository-3-Vulnerability
Pasted image 20240526122911.png
删除了jetty的资源配置,不再使用jetty去挂载public目录下的资源文件。
在Jetty的配置文件中,

<Set name="resourceBase"><Property name="karaf.base"/>/public</Set>`

这一行的作用是将karaf.base目录下的public子目录设置为资源基目录,即从这个目录中提供静态资源。

  1. 移除资源配置:Jetty不再知道public目录的存在,也就无法从这个目录中提供任何静态资源。
  2. 取消挂载public目录下的资源文件不再与任何URL路径关联。客户端请求这些资源时,Jetty将无法找到并提供它们。

简单来说,删除这行配置意味着Jetty不再负责处理和提供public目录中的静态资源文件,可能需要其他方式来管理和提供这些资源(例如,使用不同的Web服务器或将静态资源打包到应用程序中)。

补丁包进行对比

https://github.com/sonatype/nexus-public
Pasted image 20240526114507.png
在IDEA里面ctrl+D即可进行二进制对比。
Pasted image 20240526114817.png
筛选*.java,可以看出有3个java文件改动。
分别是
WebResourceServicelmpl.java
PublicFilesWebResourceBundle.java
RaptureWebResourceBundle.java
根据师傅们的提示,漏洞代码位于WebResourceServicelmpl.java,看名字是关于web资源的业务逻辑的实现类。
这里1ue师傅说是jetty相关的漏洞,这里意思应该是漏洞与servlet相关,而nexus中托管servlet的容器是jetty。

Jetty是一个纯粹的基于Java的网页服务器和Java Servlet容器。

其中删除了如下代码,直接在此处断点,找调用链。

// 3) third, look into WAR embedded resources  
if (resource == null) {  
  URL url;  
  try {  
    url = servletContext.getResource(path);  
    if (url != null && !isDirectory(url)) {  
      resource = new UrlWebResource(url, path, mimeSupport.guessMimeTypeFromPath(path));  
      log.trace("Found servlet-context resource: {}", resource);  
    }  
  }  
  catch (MalformedURLException e) {  
    throw new RuntimeException(e);  
  }  
}
private boolean isDirectory(final URL url) {  
  if ("file".equals(url.getProtocol())) {  
    File file = new File(url.getFile());  
    return file.isDirectory();  
  }  
  return false;  
}

idea搜索WebResourceServicelmpl定位代码,进行远程调试
路由
image.png

关键代码调用

WebResourceServlet#doGet
        WebResource resource = webResources.getResource(path); // 获取对应路径的资源
WebResourceServiceImpl#getResource
        url = servletContext.getResource(path);
WebAppContext#getResource
        Resource resource = WebAppContext.this.getResource(path);
            resource = super.getResource(uriInContext);
                Resource resource = _baseResource.addPath(path);	//_baseResource是jetty.xml中配置的public目录
PathResource#addPath	
        if (URIUtil.canonicalPath(subPath) == null) 	//绕过
        return "/".equals(subPath) ? this : new PathResource(this, subPath);
PathResource#PathResource
        this.uri = URIUtil.addPath(parent.uri, childPath);
URIUtil#addPath
        encodePath(buf, path, offset);

POC分析

  1. 直接传入../../../../../../../etc/passwd会怎么样?

image.png
400,1ue师傅说这是正常的,这里暂时存疑。

  1. 传入..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd呢?

image.png
jetty会进行一层解码,但是依旧400。

  1. 双重url编码,传入..%252F..%252F..%252F..%252F..%252F..%252F..%252Fetc%252Fpasswd

依旧不行。

P牛:
image.png

为什么paylaod是这样的?
%2F%2F%2F%2F%2F%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd

  1. 首先这个payload可以通过jeety传递到后端,通过编码避免斜线后第一个字符出现..或者../造成400.
  2. 利用多级///绕过canonicalPath的检查。

URIUtil.canonicalPath 函数通常用于规范化 URI 路径。规范化路径的过程包括移除冗余的路径元素(如 . 和 ..),以及处理重复的斜杠 /。

与spring中的cleanPath()函数一样,在处理路径的时候将空的字符串也认为是一个目录。
也就是

canonicalPath("//../etc/passwd")
输出
/etc/passwd

/我是空字符,但是cleanPath()和canonicalPath()认为我是空目录/../etc/passwd
这里的../实际上是跨了一层空目录,通过了canonicalPath的检查。
但系统认为//不是一个目录,会通过../跳到上级目录。
image

至此就导致了路径穿越+任意文件读取。
在后面拼接目录的时候尽管存在多级//(canonicalPath眼中的空目录),但系统选择无视,而且file协议是支持的。注:file://中的//也不是必要的
image.png
image.png
image.png

补丁绕过?

  1. 临时修补措施

image.png
去除掉jetty中public的配置,猜想在这一步由于获取不到_baseResource后直接抛出异常了。

  1. 删除了入口点,提供了别的接口访问。
posted @ 2024-05-26 22:19  _rainyday  阅读(273)  评论(0编辑  收藏  举报