【漏洞分析】Go语言任意代码执行漏洞 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漏洞的成因,可以看到编译是软件构建过程中一个重要环节,保证软件可信,不被来自外界的病毒、恶意代码破坏,不仅要从程序代码本身着手,还应从编译,生成,分发等各方面努力,保证整个软件供应链体系的安全可信。

Spread the word. Share this post!

Meet The Author

Leave Comment