一个隐藏在Go语言标准库中的目录穿越漏洞 CVE-2022-29804
前言
这是半年前我在 Go 语言中发现的一个目录穿越漏洞(虽然被人抢先发现了)。
Go 语言支持非常方便的交叉编译,但是在不同平台下,操作系统对某些功能的实现有所差异。这些差异可能会导致一些安全问题。
一天,我看完了番剧后,闲着无聊审计了一下我用来做内网共享的小工具——“Go HTTP File Server”。
这是一个跨平台的文件服务器,它可以快速搭建一个简单的HTTP服务器来共享文件。
它的默认共享路径为当前路径(./)

代码审计
非常安全的代码?
这个工具默认是没有鉴权的 所以我们直接看文件浏览的部分
乍一看上去,这段代码好像没有什么问题。它使用了 Go 标准库中的 filepath.Clean
(去除 ..) 和 filepath.Join
(合并路径) 函数,来防止目录穿越。
标准库中的漏洞
我刚好还有些空余时间,所以我又开始检查 Go 标准库中的函数实现。
filepath.Clean
调试了一遍后,我发现 filepath.Clean
对路径处理非常完美。这个函数可以将路径中的冗余部分去除,同时可以处理不同操作系统下的路径分隔符问题.
filepath.Join
但是 filepath.Join 函数就不太一样了,这个函数在 Plan9、Unix 和 Windows 三个操作系统类型下有着不同的实现。
在 Unix 系统下,filepath.Join 非常简单,它会在Clean之后直接拼接路径,没有任何问题。
在 Windows 系统下,filepath.Join 函数的实现要复杂得多,因为需要处理路径分隔符和 UNC 路径等特殊情况。
到这里就变得有趣了一些 filepath.Join 的输入不完全是用户控制的 Clean函数会把用户输入和固定路径一起处理
这个工具刚好出现了一个非常特殊的情况
文件服务器本来想要限制访问当前目录下的文件filepath.Join("./",'已经处理后的用户输入')
如果输入的路径是./ abc/1.txt
Clean处理后会变成 abc/1.txt
Clean去除了开头的设定的./
这个处理在linux系统下没有问题
但是在windows 系统下 如果我们构造路径组./ c:/1.txt
Clean处理后会变成 c:/1.txt
显然从Clean处理后把当前目录下的路径变为了c盘根目录
在这里,filepath.Clean 函数的处理并没有避免目录穿越问题,反而造成了一个安全漏洞。
最终在http server 上复现成功


小插曲
提交给go 官方之后才发现这洞3个月前就被修复了. 我电脑上的go版本一直没更新 23333

漏洞issue
https://github.com/golang/go/issues/52476
漏洞影响&利用条件
使用
filepath.Clean/filepath.Join
处理路径左侧被拼接路径为
./
右侧路径可完全控制
Go编译Windows二进制文件使用 Go 1.18 <1.18.3 Go 1.17 <1.17.11 (不在维护的版本应该不会修复)
目标二进制部署在windows 操作系统
标准库的漏洞会影响编译分发出的二进制文件
解决方法
更新go到最新版本 重新编译发布二进制文件