# HG changeset patch # User Sean Farley # Date 1501544657 25200 # Node ID 60ee7af2a2ba806fc6f8b112f951097b1b3fb807 # Parent 475af2f89636121debef26c9253f1b45a231b375 subrepo: add tests for svn rogue ssh urls (SEC) 'ssh://' has an exploit that will pass the url blindly to the ssh command, allowing a malicious person to have a subrepo with '-oProxyCommand' which could run arbitrary code on a user's machine. In addition, at least on Windows, a pipe '|' is able to execute arbitrary commands. When this happens, let's throw a big abort into the user's face so that they can inspect what's going on. diff -r 475af2f89636 -r 60ee7af2a2ba mercurial/subrepo.py --- a/mercurial/subrepo.py Mon Jul 31 16:04:44 2017 -0700 +++ b/mercurial/subrepo.py Mon Jul 31 16:44:17 2017 -0700 @@ -1281,6 +1281,10 @@ # The revision must be specified at the end of the URL to properly # update to a directory which has since been deleted and recreated. args.append('%s@%s' % (state[0], state[1])) + + # SEC: check that the ssh url is safe + util.checksafessh(state[0]) + status, err = self._svncommand(args, failok=True) _sanitize(self.ui, self.wvfs, '.svn') if not re.search('Checked out revision [0-9]+.', status): diff -r 475af2f89636 -r 60ee7af2a2ba mercurial/util.py --- a/mercurial/util.py Mon Jul 31 16:04:44 2017 -0700 +++ b/mercurial/util.py Mon Jul 31 16:44:17 2017 -0700 @@ -2905,7 +2905,8 @@ Raises an error.Abort when the url is unsafe. """ path = urlreq.unquote(path) - if path.startswith('ssh://-') or '|' in path: + if (path.startswith('ssh://-') or path.startswith('svn+ssh://-') + or '|' in path): raise error.Abort(_('potentially unsafe url: %r') % (path,)) diff -r 475af2f89636 -r 60ee7af2a2ba tests/test-subrepo-svn.t --- a/tests/test-subrepo-svn.t Mon Jul 31 16:04:44 2017 -0700 +++ b/tests/test-subrepo-svn.t Mon Jul 31 16:44:17 2017 -0700 @@ -639,3 +639,67 @@ $ hg update -q -C '.^1' $ cd ../.. + +SEC: test for ssh exploit + + $ hg init ssh-vuln + $ cd ssh-vuln + $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub + $ svn co --quiet "$SVNREPOURL"/src s + $ hg add .hgsub + $ hg ci -m1 + $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub + $ hg ci -m2 + $ cd .. + $ hg clone ssh-vuln ssh-vuln-clone + updating to branch default + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s") + [255] + +also check that a percent encoded '-' (%2D) doesn't work + + $ cd ssh-vuln + $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub + $ hg ci -m3 + $ cd .. + $ rm -r ssh-vuln-clone + $ hg clone ssh-vuln ssh-vuln-clone + updating to branch default + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepository "s") + [255] + +also check for a pipe + + $ cd ssh-vuln + $ echo "s = [svn]svn+ssh://fakehost|sh%20nested" > .hgsub + $ hg ci -m3 + $ cd .. + $ rm -r ssh-vuln-clone + $ hg clone ssh-vuln ssh-vuln-clone + updating to branch default + abort: potentially unsafe url: 'svn+ssh://fakehost|sh nested' (in subrepository "s") + [255] + +also check that a percent encoded '|' (%7C) doesn't work + + $ cd ssh-vuln + $ echo "s = [svn]svn+ssh://fakehost%7Csh%20nested" > .hgsub + $ hg ci -m3 + $ cd .. + $ rm -r ssh-vuln-clone + $ hg clone ssh-vuln ssh-vuln-clone + updating to branch default + abort: potentially unsafe url: 'svn+ssh://fakehost|sh nested' (in subrepository "s") + [255] + +also check that hiding the attack in the username doesn't work: + + $ cd ssh-vuln + $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub + $ hg ci -m3 + $ cd .. + $ rm -r ssh-vuln-clone + $ hg clone ssh-vuln ssh-vuln-clone + updating to branch default + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepository "s") + [255]