Golang 1.24 已经进入冻结期了,目前很多特性我们在Go 1.24 Release Notes 能够看到,接下来一段时间我会学习Golang1.24将会添加的新功能以及修改点。如果您也想学习Go最新的进展,请关注我,
这篇文章,我们将学习一下os.Root
这个新增的Standard library。
Proposal
目录遍历漏洞是一类常见的漏洞,攻击者会诱骗程序打开其不希望的文件。这些攻击通常采用提供相对路径名的形式,例如"../../../etc/passwd"
,这会导致在预期位置之外进行访问。 CVE-2024-3400是最近的一个真实的目录遍历示例,导致远程代码执行漏洞被主动利用。
在其他语言以及操作系统中,已经都有类似的实现, 比如:
- Python 的 chroot:通过 os.chroot() 将根目录限制为特定目录。
- Linux 的文件系统命名空间:通过 mount 和 chroot 限制进程的视图。
我们可以写一个demo 测试一下。
首先构造一个 “机密”文件
1
|
echo 'password123' >> /tmp/password
|
然后写一个Golang函数, 原义是打开当前目录的文件
1
2
3
4
5
6
7
8
9
10
11
12
|
func main() {
fileName := os.Args[1]
//readFile
localFilePath := "."
filePath := fmt.Sprintf("%s/%s", localFilePath, fileName)
content, err := os.ReadFile(filePath)
if err != nil {
fmt.Printf("Error reading file %s: %s\n", fileName, err)
return
}
fmt.Printf("File %s opened successfully. file content %s\n", fileName, content)
}
|
但是因为传入了不可靠的参数,代码能够访问到 权限范围之外的地方。
1
2
3
4
5
6
|
➜ os_root git:(main) ✗ pwd
/Users/hxzhouh/workspace/github/me/blog-example/go/go1.24/os_root
➜ os_root git:(main) ✗ ./main ../../../../../../../../../tmp/password
./../../../../../../../../../tmp/password
File ../../../../../../../../../tmp/password opened successfully. file content password123
|
在 Go1.24 中,新增了 os.Root
的类型,其提供了在特定目录中执行文件系统操作的能力。整个体系是围绕着这个新类型进行的。 对应的核心函数是 os.OpenRoot
,函数打开一个目录并返回一个 os.Root
。
os.Root
上的方法仅允许在目录内操作,不允许指向目录外部位置的路径,包括遵循目录外符号链接的路径。(防御住了前面提案背景提到的攻击范围)
我们用Go1.24 的方式修改代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
func main() {
fileName := os.Args[1]
root, err := os.OpenRoot(".")
if err != nil {
panic(err)
}
file, err := root.Open(fileName)
if err != nil {
fmt.Println(fmt.Sprintf("Error opening file %s: %s\n", fileName, err.Error()))
return
}
content := make([]byte, 1024)
c, err := file.Read(content)
if err != nil {
panic(err)
}
content = content[:c]
fmt.Printf("File %s opened successfully. file content %s\n", fileName, content)
}
|
再次运行
1
2
3
4
5
|
➜ os_root git:(main) ✗ gotip version
go version devel go1.24-fafd447 Wed Dec 11 15:57:34 2024 -0800 darwin/arm64
➜ os_root git:(main) ✗ gotip build -o go1.24 main.go
➜ os_root git:(main) ✗ ./go1.24 ../../../../../../../../../tmp/password
Error opening file ../../../../../../../../../tmp/password: openat ../../../../../../../../../tmp/password: path escapes from parent
|
如果文件夹脱离了parent
层级,就会报错。
更多的APi接口请参考官方文档 。1.24正式发布后,估计很多第三方库都会第一时间适配把,毕竟其他语言基本上都有这种功能了。