作者:綠盟科技
來源:http://blog.nsfocus.net/cve-2018-6574/

前不久,Go官方修復了CVE-2018-6574這個漏洞,這個漏洞又是涉及軟件編譯環節,和2015年Xcode被污染類似,攻擊者可以通過在軟件編譯環節插入惡意數據從而執行任意代碼,雖然原理并不復雜,但有很好的警示意義。

什么是Go語言?

Go 是一個Google推出的開源編程語言,它能讓構造簡單、可靠且高效的軟件變得更容易,且有著更高的開發效率和運行性能,因此受到了許多開發者的歡迎。

Go 程序源碼 以 *.go 結尾,通過 go build 編譯成native代碼。

以最簡單的hello world程序為例:hello.go

//  hello.go
package main

import "fmt"
func main() {
    fmt.Println("Hello, World!")
}

通過go build編譯出可執行程序,再運行 ./hello 。也可以直接通過 go run ./hello.go 直接在/tmp目錄下編譯生成可執行文件,并運行。

同時Go語言允許與C語言的互操作,即在Go語言中直接使用C代碼,因為本身Go在語法等方面和C就很像,其設計者以及其設計目標都與C語言有著千絲萬縷的聯系。

例如下面的代碼,我們可直接在Go源碼文件中嵌入了C代碼,并用注釋包裹起來

package main

/*
#include
#include
void my_print(char *str){
    printf("%s\n",str);
}
*/
import "C"
import "unsafe"
import "fmt"

func main() {
    fmt.Println("Hello, World!")

    s:="hello cgo"
    cs:=C.CString(s)
    C.my_print(cs)
    C.free(unsafe.Pointer(cs))
}

我們依然可以直接通過go build或go run來編譯和執行,但實際編譯過程中,go調用了名為cgo的工具,cgo會識別和讀取Go源文件中的C元素,并將其提取后交給C編譯器編譯,最后與Go源碼編譯后的目標文件鏈接成一個可執行程序。

CVE-2018-6574 漏洞分析

參看CVE公告,這個漏洞是由于在源碼編譯時,未禁止 “-fplugin=”這類的參數導致在使用gcc/clang編譯時產生代碼執行。

上面已經說了在Go源碼文件中可以嵌入了C代碼,同時cgo會識別和讀取Go源文件中的C元素,并將其提取后交給C編譯器編譯。

cgo調用gcc或者clang編譯提取出的C代碼。

gcc/clang這類的C編譯器自然都有CFLAGS,LDFLAGS等編譯開關讓開發者在編譯時指定設置。

cgo作為一個gcc的封裝,自然也支持這類的編譯開關選項。而gcc編譯時,可以通過“-fplugin”指定額外的插件,gcc在編譯時會加載這個插件。

因此,除了在Go源碼文件中可以嵌入了C代碼之外,還可以指定通過“#cgo CFLAGS”指定gcc編譯時的惡意插件。

cgo在解析到CFLAGS關鍵字時,會將后面的編譯選項傳遞給gcc。

“-fplugin”指定額外的插件,可為任意的動態庫,因此就獲得了代碼的執行權限。

package main

/*
#cgo CFLAGS: -fplugin=./foo.so
#include
#include
void print(char *str){
    printf("%s\n",str);
}
*/
import "C"
import "unsafe"
import "fmt"

func main() {
    fmt.Println("Hello, World!")

    s:="hello cgo"
    cs:=C.CString(s)
    C.print(cs)
    C.free(unsafe.Pointer(cs))
}

對于本地自己編寫的程序或許不會有問題,因為畢竟不會自己去加載執行惡意代碼。 但當需要獲取遠程代碼執行“go get”時,就可能出現問題。“go get”先從遠程地址下載源碼,再執行go build并安裝。

例如,我們經常會從github上獲得Go的項目或第三方包,這些第三方包如果未經檢查,就完全可能借助這個漏洞執行任意代碼。

我們通過本地反彈shell來演示一下這個漏洞執行的流程,如下圖。

CVE-2018-6574 防御修復

Go官方在最新版本中,加強了對編譯和鏈接環節的檢查過濾。

只允許指定的編譯鏈接選項代入gcc執行,其他未經允許的都會被禁止。

總結

本文分析了CVE-2018-6574漏洞的成因,可以看到編譯是軟件構建過程中一個重要環節,保證軟件可信,不被來自外界的病毒、惡意代碼破壞,不僅要從程序代碼本身著手,還應從編譯,生成,分發等各方面努力,保證整個軟件供應鏈體系的安全可信。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/585/