Go命令的PATH安全性

今日Golang发布一个安全版本(Go 1.15.7 和Go 1.14.14),解决了涉及在不受信任目录中进行PATH查找的问题CVE-2021-3115,相关的问题会导致执行go get命令时远程执行。关于该问题Golang官方博客进行了介绍,我们来学习下。

Go命令的PATH安全性

go命令和远程执行

golang语言的go命令设计目标是用来执行大量的操作,包括go build,go doc,go get,go install,以及go list,他们可以从互联网上下载但是不会运行任意代码。但是还有几个命令,比如go run,go test以及go generate可以直接运行任意代码,包括可复制的版本和安全性。因此,当go get可以被诱骗执行任意代码时,这是一个安全漏洞。

如果go get不能运行任意代码,那么其调用的所有程序,例如编译器和版本控制系统,也都在安全范围内。例如,过去曾经遇到过这样的问题,即在版本控制系统中巧妙地使用晦涩的编译器功能或远程执行漏洞,导致Go中的远程执行。

Go命令和PATH路径

所有操作系统都有一个可执行路径PATH的概念(在类Unix系统上是用$PATH定义,Windows上为%PATH%)。它是一些可执行文件保存目录的列表。在shell提示符下输入命令时,shell依次在列出的每个目录中查找具有该名称的可执行文件,然后运行找到的第一个命令,如果编译列表找不到该名称,就会报错 "找不到命令"。

-bash: XXX: command not found

Bourne shell手册说明中:

shell参数$PATH定义包含命令的目录的搜索路径。每个备用目录名称都用冒号(:)分隔。预设路径为:/bin:/usr/bin。如果命令名称中包含/,则不使用搜索路径。否则,将在PATH中的各个目录中搜索可执行文件。

默认值:当前目录(.)在/bin和之前列出/usr/bin。MS-DOS和Windows中是硬编码了这个行为:在windows下会首先自动搜索当前目录(.),然后再搜索%PATH%列出的目录。

PATH设置的系统目录之前先搜索当前目录,这样如果cd进入目录并运行ls,则可能会从当前目录中执行恶意副本,而不是系统程序。而且,如果可以欺骗系统管理员以ls身份登录时,其他用户主目录root中运行,那么也可以运行所需的任何代码。由于这个问题和其他类似的问题,基本上所有现代*nix发行版都将默认PATH设置中排除当前目录(即.)。但是,在Windows中,默认是首先搜索当前目录(.)。例如,当输入go version命令时,默认配置的*nix上,shell都从PATH中的系统目录运行go可执行文件。但是,在Windows上键入输入该命令时,cmd.exe会首先检查当前目录(.),如果存在.go.exe(或.go.bat等等),cmd.exe就会先运行这个可执行文件(.go.exe),而不是PATH中路径设定的系统go编译器。

对于Golang,PATH搜索由exec.LookPath自动处理exec.Command。为了很好地适应主机系统,Goland的exec.LookPath在*nix上和Windows各自通过调用底层的系统的规则来实现。下面的命令:

out, err := exec.Command("go", "version").CombinedOutput()

行为与在操作系统shell中直接执行go version命令是一样的。

在Windows上,如果当前目录存在.go.exe,就会优先运行。

漏洞解析

当go get下载并构建一个包含包 import "C",它会运行cgo程序来处理golang中的有关C代码。cgo命令在包含软件包源的目录中运行。一旦cgo已经产生其转到输出文件,go命令会调用所生成的文件转交给主机C编译器(gcc或clang编译器)来构建引入到包中的C源码。

为了调用C编译器,go命令需要通过系统PATH来查找。但是如果在程序包源目录中运行C编译器时,它将从go调用命令的原始目录中查找:

cmd := exec.Command("gcc", "file.c")

cmd.Dir = "badpkg"

cmd.Run()

因此,即使Windows系统上存在badpkggcc.exe,也找不到它。exec.Command进行PATH搜寻时候不包括badpkg目录。

go命令使用类似的代码来调用cgo,但是不会有路径查找,因为cgo总是来自于GOROOT:

cmd := exec.Command(GOROOT+"/pkg/tool/"+GOOS_GOARCH+"/cgo", "file.go")

cmd.Dir = "badpkg"

cmd.Run()

这第一段代码看起来更加安全,没有运行存在的问题的cgo.exe可能。

但是,实际上cgo本身还需要在它创建的某些临时文件上调用宿主的C编译器,这样,它还要执行以下代码:

cmd := exec.Command("gcc", "tmpfile.c")

cmd.Run()

由于cgo运行时,当前目录就是badpkg,而不是go运行命令的目录,因此如果badpkggcc.exe存在,就会被调用执行,而不会再去PATH中查找系统的gcc。

因此,攻击者可以创建一个使用cgo并包含的恶意程序gcc.exe的打包,当用户通过go get以下载并build的时候,Windows用户会就会优先于系统路径中的gcc先执行这个恶意的gcc.exe,从而导致的远程执行漏洞。

*nix系统由于从根本上就不包括当前目录,所以不会受到影响。并且由于golang模块解包时不会对其写入的文件设置执行位,所以默认是无法执行的。

但是,如果系统设置时候,手动PATH中增加"."路径,并且使用GOPATH模式的用户也会受到影响。

漏洞修复

上面说了,可能的风险就是下载并执行恶意的gcc.exe。那么如何解决呢?

一个可能的解决方法:由于cgo在不受信任的源目录中而不是在go调用命令的目录中C编译器,这会导致问题。那么解决我们通过更改go命令,将cgo完整路径传递给主机C编译器,让cgo无需在不受信任的目录中进行PATH查找。

由于问题出在PATH查找时候的查找当前路径(.),无论在Windows上自动发生还是由于在*nix系统上显式的设置当前路径(.)的PATH。用户可能希望在当前目录中查找以找到在控制台或shell程序窗口中键入的命令,但是一般不会希望查找键入的命令的子命令。那么解决方法就是更改cgo 的PATH查找过程中不包括当前目录(.)。

golang的新版本中对这两方面都予以了修复。go命令会把完整的主机C编译器路径传递给cgo。最重要的是,go和cgo转到命令现在使用的一个变种os/exec,如果将以前使用的当前目录(.)的可执行文件,会报错误。这些go程序包go/build命令和其他工具go/import使用相同的策略。出于谨慎考虑,还对诸如goimports和gopls命令以及golang.org/x/tools/go/analysis和golang.org/x/tools/go/packages库进行了类似的修复,这些库将命令作为子进程调用。

Go命令的PATH安全性

更新到新的Go版本后,可以gopls使用以下方法更新到最新版本:

GO111MODULE=on

go get golang.org/x/tools/gopls@v0.6.4

使用以下方法更新到最新版的goimports工具或其他工具:

GO111MODULE=on

go get golang.org/x/tools/cmd/goimports@v0.1.0

在golang.org/x/tools/go/packages通过在以下过程中添加对依赖关系的显式升级来更新依赖于的程序:

GO111MODULE=on

go get golang.org/cmd/thecmd golang.org/x/tools@v0.1.0

对于使用的程序,go/build 就可以使用更新的Go版本重新编译它们。

同样,仅当是Windows用户或*nix用户且PATH中手动设置了.并且在不信任的源目录中运行这些程序(可能包含恶意程序)时,才需要更新这些程序。

如果代码中使用了exec.LookPath或exec.Command,则需要关注在不受信任内容的目录中运行程序。可以使用os/execas替换:

import "os/exec"

import exec "golang.org/x/sys/execabs"

并重新编译。

展开阅读全文

页面更新:2024-03-21

标签:命令   编译器   路径   漏洞   安全性   恶意   类似   名称   主机   版本   代码   文件   目录   程序   用户   系统   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top