翻译:running_wen
预估稿费:140RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
git-shell在git远程会话之上引入了ssh隧道,是一种受限的shell。其背后的基本思想就是,在ssh会话中限制能够执行的命令,使其仅仅能执行git需要的相应命令。git需要执行的命令如下 :
git-receive-pack
Receives repository updates from the client.
git-upload-pack
Pushes repository updates to the client.
git-upload-archive
Pushes a repository archive to the client.
除了上面这几个自带命令,管理员也可以通过shell脚本文件或者其他可执行文件来提供自定义命令。由于这些自定义的都是完全自定义的,因此此处主要讨论自带的命令。
如果你熟悉git,那你可能知道大部分服务器都会将git协议封装在SSH、HTTP/S[3]协议中。这是因为,git协议是基于简单文本的协议[4],在数据传输时并不提供任何认证或者保护机制。通常的做法是利用SSH协议实现repository写权限的控制,因为SSH协议本身提供多种认证机制、可靠的加密、低的协议额外开销。
使用SSH的缺点是,期初SSH是为了远程用户提供Shell访问。而通常情况下,git用户是不具备shell访问能力的。为了限制连接,使其仅仅能够访问repository,我们需要将原始shell(典型的就是bash shell,或者类似的)替换为更受限的shell。大型主机厂商通常都自己实现上面git命令的功能。但是也能使用git开发者提供的shell(该shell限制为仅仅允许与调用执行白名单中的命令)。
搭建过程很简单。比较建议在服务器上服务器上创建一个特定用户,并 使用git-shell命令作为
该用户的login shell[5]。另一种方式是,使用SSH force命令,是你能够对每一个客户端进行限制(依赖于登录过程中使用的key),后面还会介绍其他 方式。
如果在本地repository中设置好了可远程访问的repository,git push命令本质上会执行下面的命令:
ssh git@remoteserver “git-receive-pack ‘/myrepository.git'”
008957d650a081a34bcbacdcdb5a94bddb506adfe8e0 refs/heads/develop report-status delete-refs side-band-64k quiet ofs-delta agent=git/2.1.4
003fbe8910f121957e3326c4fdd328ab9aabd05abdb5 refs/heads/master
00000000
如果两个repository具有同样的提交,如果执行的命令不在白名单中(不是上面列举的自带命令,也不在home目录下的git-shell-commands目录下),那么会报错提示命令无法识别。由于不是交互式shell,典型的命令注入攻击此处并不适用。相反,命令行仅仅以空格分隔开(引号包含的是整体),并被execve执行。
上面的情况,有让我更多的考虑赋值协议处理的二进制文件本身。git本身提供help命令,能针对特定命令打开帮助页面(man page),如init命令:
$ git help init
GIT-INIT(1) Git Manual GIT-INIT(1)
NAME
git-init - Create an empty Git repository or reinitialize an existing one
[...]
其他的一些命令也可以通过-help参数来显示该命令对应的帮助页面,如下所示:
$ git init --help
GIT-INIT(1) Git Manual GIT-INIT(1)
NAME
git-init - Create an empty Git repository or reinitialize an existing one
[...]
同样,这也适用于git-receive-pack、git-upload-archive命令。在服务器上运行git-receive-pack -help命令,如下所示:
$ ssh git@remoteserver "git-receive-pack '--help'"
GIT-RECEIVE-PACK(1) Git Manual GIT-RECEIVE-PACK(1)
NAME
git-receive-pack - Receive what is pushed into the repository
[...]
但是怎样才能绕过能够执行命令的限制呢?在大多数系统上,如果使用man命令打开帮助页面,man specification被解析、渲染,并以ANSI输出,通过管道传递给pager(通常是less命令)。这样,我们就可以滚动与搜索帮助页面,而与terminal终端的大力与容量无关。
除了作为一个简单的pager,less命令还具有额外的交互性特性。它允许你打开其他的文件并读取,输出当前输出到log文件,在当前shell下执行系统命令。要想能够利用这些特性,需要在交互式模式下运行less命令。在pty可用情况下,交互式模式可用。通常SSH连接到服务器,pty就启用了,但直接运行命令是pty不可用。幸运的是,我们可以强制ssh 客户端分配一个pty(只要服务器端没有禁用它,通常服务端也不会禁用它)。运行实例如下:
$ ssh -t git@remoteserver "git-receive-pack '--help'"
GIT-RECEIVE-PACK(1) Git Manual GIT-RECEIVE-PACK(1)
NAME
git-receive-pack - Receive what is pushed into the repository
Manual page git-receive-pack(1) line 1 (press h for help or q to quit)
现在我们可以使用less命令的交互特性。上面建议的建立方式有一个限制,那就是由于shell执行任然是在当前git-shell环境下,之前git-shell对ssh中能执行的命令限制同样适用于此时能执行的命令。不管怎样,我们限制可以读文件、列举目录(利用tab补全),并将当前显示结果输出到文件(如果能够控制文件部分内容,则作用更大)
如你所料,还有另一种适用git-shell的方式,虽然不是很常见。该方法适用于限制特定用户能访问repository,或者不允许改变git用户的shell的情况。
此时,我们将login shell提供给用户,并在.ssh/authorized_keys文件中指定git-shell命令来限定用户。限定用户的代码如下:
command="git-shell -c "$SSH_ORIGINAL_COMMAND"" ssh-rsa AAAAB3NzaC1yc2EA[...]
除了能执行less命令,上面的配置其他行为与配置为log shell的相同。
另外,你也可以给force命令提供额外参数来限制ssh的特性。最常见的标记就是no-pty[6],这能防止客户端请求一个pty,这样就不能在交互式模式下运行less命令。
时间线
2017-04-25 Reported to the git-security mailing list
2017-05-01 Assigned CVE-2017-8386
2017-05-10 Release of the fixed versions v2.4.12, v2.5.6, v2.6.7, v2.7.5, v2.8.5, v2.9.4, v2.10.3, v2.11.2, v2.12.3 and v2.13.0
参考
[1] https://about.gitlab.com/2017/04/05/gitlab-9-dot-0-dot-4-security-release/
[2] https://about.gitlab.com/2017/05/08/gitlab-9-dot-1-dot-3-security-release/
[3] https://git-scm.com/book/no-nb/v1/Git-on-the-Server-The-Protocols
[4] https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt
[5] https://git-scm.com/book/en/v2/Git-on-the-Server-Setting-Up-the-Server