一個(gè)隱藏在Go語言標(biāo)準(zhǔn)庫中的目錄穿越漏洞 CVE-2022-29804
前言
這是半年前我在 Go 語言中發(fā)現(xiàn)的一個(gè)目錄穿越漏洞(雖然被人搶先發(fā)現(xiàn)了)。
Go 語言支持非常方便的交叉編譯,但是在不同平臺(tái)下,操作系統(tǒng)對(duì)某些功能的實(shí)現(xiàn)有所差異。這些差異可能會(huì)導(dǎo)致一些安全問題。
一天,我看完了番劇后,閑著無聊審計(jì)了一下我用來做內(nèi)網(wǎng)共享的小工具——“Go HTTP File Server”。
這是一個(gè)跨平臺(tái)的文件服務(wù)器,它可以快速搭建一個(gè)簡單的HTTP服務(wù)器來共享文件。
它的默認(rèn)共享路徑為當(dāng)前路徑(./)

代碼審計(jì)
非常安全的代碼?
這個(gè)工具默認(rèn)是沒有鑒權(quán)的 ?所以我們直接看文件瀏覽的部分
乍一看上去,這段代碼好像沒有什么問題。它使用了 Go 標(biāo)準(zhǔn)庫中的 filepath.Clean
(去除 ..) 和 filepath.Join
(合并路徑) 函數(shù),來防止目錄穿越。
標(biāo)準(zhǔn)庫中的漏洞
我剛好還有些空余時(shí)間,所以我又開始檢查 Go 標(biāo)準(zhǔn)庫中的函數(shù)實(shí)現(xiàn)。
filepath.Clean
調(diào)試了一遍后,我發(fā)現(xiàn) filepath.Clean
對(duì)路徑處理非常完美。這個(gè)函數(shù)可以將路徑中的冗余部分去除,同時(shí)可以處理不同操作系統(tǒng)下的路徑分隔符問題.
filepath.Join
但是 filepath.Join 函數(shù)就不太一樣了,這個(gè)函數(shù)在 Plan9、Unix 和 Windows 三個(gè)操作系統(tǒng)類型下有著不同的實(shí)現(xiàn)。
在 Unix 系統(tǒng)下,filepath.Join 非常簡單,它會(huì)在Clean之后直接拼接路徑,沒有任何問題。
在 Windows 系統(tǒng)下,filepath.Join 函數(shù)的實(shí)現(xiàn)要復(fù)雜得多,因?yàn)樾枰幚砺窂椒指舴?UNC 路徑等特殊情況。
到這里就變得有趣了一些 filepath.Join 的輸入不完全是用戶控制的 ?Clean函數(shù)會(huì)把用戶輸入和固定路徑一起處理
這個(gè)工具剛好出現(xiàn)了一個(gè)非常特殊的情況
文件服務(wù)器本來想要限制訪問當(dāng)前目錄下的文件filepath.Join("./",'已經(jīng)處理后的用戶輸入')
如果輸入的路徑是./ abc/1.txt
Clean處理后會(huì)變成 abc/1.txt
Clean去除了開頭的設(shè)定的./
這個(gè)處理在linux系統(tǒng)下沒有問題
但是在windows 系統(tǒng)下 如果我們構(gòu)造路徑組./ c:/1.txt
Clean處理后會(huì)變成 c:/1.txt
顯然從Clean處理后把當(dāng)前目錄下的路徑變?yōu)榱薱盤根目錄
在這里,filepath.Clean 函數(shù)的處理并沒有避免目錄穿越問題,反而造成了一個(gè)安全漏洞。
最終在http server 上復(fù)現(xiàn)成功


小插曲
提交給go 官方之后才發(fā)現(xiàn)這洞3個(gè)月前就被修復(fù)了. 我電腦上的go版本一直沒更新 23333

漏洞issue
https://github.com/golang/go/issues/52476
漏洞影響&利用條件
使用
filepath.Clean/filepath.Join
處理路徑左側(cè)被拼接路徑為
./
右側(cè)路徑可完全控制
Go編譯Windows二進(jìn)制文件使用 ?Go 1.18 <1.18.3 ?Go 1.17 <1.17.11 (不在維護(hù)的版本應(yīng)該不會(huì)修復(fù))
目標(biāo)二進(jìn)制部署在windows 操作系統(tǒng)
標(biāo)準(zhǔn)庫的漏洞會(huì)影響編譯分發(fā)出的二進(jìn)制文件
解決方法
更新go到最新版本 重新編譯發(fā)布二進(jìn)制文件