gh-99889: Fix directory traversal security flaw in uu.decode() (#104096)

* Fix directory traversal security flaw in uu.decode()
* also check absolute paths and os.altsep
* Add a regression test.

---------

Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google]
This commit is contained in:
Sam Carroll 2023-05-09 12:01:58 -04:00 committed by GitHub
parent afe7703744
commit 0aeda29793
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 1 deletions

View file

@ -147,6 +147,34 @@ class UUTest(unittest.TestCase):
uu.encode(inp, out, filename) uu.encode(inp, out, filename)
self.assertIn(safefilename, out.getvalue()) self.assertIn(safefilename, out.getvalue())
def test_no_directory_traversal(self):
relative_bad = b"""\
begin 644 ../../../../../../../../tmp/test1
$86)C"@``
`
end
"""
with self.assertRaisesRegex(uu.Error, 'directory'):
uu.decode(io.BytesIO(relative_bad))
if os.altsep:
relative_bad_bs = relative_bad.replace(b'/', b'\\')
with self.assertRaisesRegex(uu.Error, 'directory'):
uu.decode(io.BytesIO(relative_bad_bs))
absolute_bad = b"""\
begin 644 /tmp/test2
$86)C"@``
`
end
"""
with self.assertRaisesRegex(uu.Error, 'directory'):
uu.decode(io.BytesIO(absolute_bad))
if os.altsep:
absolute_bad_bs = absolute_bad.replace(b'/', b'\\')
with self.assertRaisesRegex(uu.Error, 'directory'):
uu.decode(io.BytesIO(absolute_bad_bs))
class UUStdIOTest(unittest.TestCase): class UUStdIOTest(unittest.TestCase):
def setUp(self): def setUp(self):

9
Lib/uu.py Executable file → Normal file
View file

@ -133,7 +133,14 @@ def decode(in_file, out_file=None, mode=None, quiet=False):
# If the filename isn't ASCII, what's up with that?!? # If the filename isn't ASCII, what's up with that?!?
out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii") out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
if os.path.exists(out_file): if os.path.exists(out_file):
raise Error('Cannot overwrite existing file: %s' % out_file) raise Error(f'Cannot overwrite existing file: {out_file}')
if (out_file.startswith(os.sep) or
f'..{os.sep}' in out_file or (
os.altsep and
(out_file.startswith(os.altsep) or
f'..{os.altsep}' in out_file))
):
raise Error(f'Refusing to write to {out_file} due to directory traversal')
if mode is None: if mode is None:
mode = int(hdrfields[1], 8) mode = int(hdrfields[1], 8)
# #

View file

@ -0,0 +1,2 @@
Fixed a security in flaw in :func:`uu.decode` that could allow for
directory traversal based on the input if no ``out_file`` was specified.