CVE-2024-4956 Nexus Repository 3 任意文件读取浅析
1686806683614784 发表于 四川 漏洞分析 1421浏览 · 2024-05-23 10:26

环境搭建

使用官方的docker镜像
docker pull sonatype/nexus3:3.68.0-java8
使用命令 docker inspect + id 查看镜像的启动命令,可以通过 INSTALL4J_ADD_VM_PARAMS 来添加启动参数

修改参数,添加调试端口

docker run -d -p 8081:8081 -p 5005:5005 --name nexus_3.68.0 -e INSTALL4J_ADD_VM_PARAMS="-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m -Djava.util.prefs.userRoot=/nexus-data/javaprefs -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" sonatype/nexus3:3.68.0-java8

分析

根据官方的临时修复方案,删除(basedir)/etc/jetty/jetty.xml
<Set name="resourceBase"><Property name="karaf.base"/>/public</Set>这一行
整个xml

<New id="NexusHandler" class="org.sonatype.nexus.bootstrap.jetty.InstrumentedHandler">
    <Arg>
      <New id="NexusWebAppContext" class="org.eclipse.jetty.webapp.WebAppContext">
        <Set name="descriptor"><Property name="jetty.etc"/>/nexus-web.xml</Set>
        <Set name="resourceBase"><Property name="karaf.base"/>/public</Set>
        <Set name="contextPath"><Property name="nexus-context-path"/></Set>
        <Set name="throwUnavailableOnStartupException">true</Set>
        <Set name="configurationClasses">
          <Array type="java.lang.String">
            <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
          </Array>
        </Set>
      </New>
    </Arg>
  </New>

public 下的资源可以直接进行访问,比如此时访问
http://127.0.0.1:8081/robots.txt

org.sonatype.nexus.internal.webresources.WebResourceServlet会对请求进行处理,这里获取pathinfo并传入org.sonatype.nexus.internal.webresources.WebResourceServiceImpl#getResource


getResource方法又会调用org.eclipse.jetty.webapp.WebAppContext#getResource

这里继续调用org.eclipse.jetty.server.handler.ContextHandler#getResource,然后就到了漏洞最关键的部分,这里的baseResource就是之前在jetty.xml里定义的web下的/public目录,所以临时修复也在这,如果删除baseResource的定义,这里返回null,也就不会走到后续流程里,避免了漏洞。

所以,我们继续跟进addPath方法,这里会调用URIUtil.canonicalPathcanonicalPath是关键

构造路径穿越

我们以 /../../../../../../etc/passwd 为例,来看看处理的逻辑,但是在请求中发现请求/../../../../../../etc/passwd返回400

canonicalPath 规范化路径

在断点过程中,发现在jetty层会主动调用一次URIUtil.canonicalPath方法,来看看逻辑
看第一部分,刚开始,把slash属性设置为true,然后遍历,如果遇到/,就把slash设置为true,然后判断/ 后面的内容,如果是.,则跳出循环,此时i为1,往下走,如果是其他值,就把slash设置为false。

我们看到跳出循环,接着看关键的部分,这里会将路径中的第一个 '.' 之前的部分添加到 canonical 中。这里就是/,然后处理canonical,把dots设置为1,用来标记.的数量,++i,此时i为2

然后判断下一位,如果依然为.,此时dots设置为2,接着continue,下一位是/,所以调用doDotsSlash

此时,判断canonical的长度只有1,所以doDotsSlash方法返回true,然后就返回null了,所以会出现400

不过,当//..//..//..//..//..//..//etc/passwd,再进判断canonical// ,等于2,不会返回true,并且还会去掉一个/,最终返回
/../../../../../../etc/passwd,所以,回到addPath方法,new PathResource,把/public目录和路径进行拼接


最终在UrlWebResource里读取文件

发请求的时候记得编码。

0 条评论
某人
表情
可输入 255