CatCoding

Ruby 的 open 函数导致命令执行

2018-02-12

说明

首先看看 open 函数的文档说明:
https://apidock.com/ruby/v1_9_3_392/Kernel/open/class:

If path starts with a pipe character, a subprocess is created, connected to the caller by a pair of pipes. The returned IO object may be used to write to the standard input and read from the standard output of this subprocess. If the command following the “|” is a single minus sign, Ruby forks, and this subprocess is connected to the parent. In the subprocess, the open call returns nil. If the command is not “-”, the subprocess runs the command. If a block is associated with an open(“|-”) call, that block will be run twice—once in the parent and once in the child. The block parameter will be an IO object in the parent and nil in the child. The parent’s IO object will be connected to the child’s stdin and stdout. The subprocess will be terminated at the end of the block.

其中说明了如果以 | 开头则会 fork 出一个进程,| 后面的内容则会当成一条命令执行,比如:

cmd = open("|date")
print cmd.gets
cmd.close
=> 2018212 日 星期一 213745CST

漏洞

正因为这样,这个 open 函数真的是很容易出错,最近的这个 PR:

https://github.com/ruby/ruby/pull/1777

之前我们的项目里也出现过类似的情况,直接相当于一个 webshell,任意执行命令。这样的 command injection 当然也很好检测,brakeman 之类的就可以。所以 Rails 项目还是时不时地扫描一下比较好。

Ruby 里面有几个 Open,这里有比较明晰的解释,Kernel.open 这个函数就是一个 wrapper,根据不同的情况做对应的处理。趟多了坑之后,才会觉得这样的特性其实是增加了程序员的负担,比如这个|特性可能有的人就没注意到,即使是看过文档也可能看到了老版本的文档,从而不知道这个边边角角。

当然同样的 system 这样的命令执行函数也是类似的情况,比如railsgoat 里的这个 command injection。原则是对于任何用户输入的参数,都需要做不安全的假设,做好检查。

https://github.com/OWASP/railsgoat这个项目里有各种 Rails 漏洞,值得看看。

公号同步更新,欢迎关注👻