From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Tue, 9 May 2023 16:46:47 +0000 (-0700) Subject: [3.10] gh-99889: Fix directory traversal security flaw in uu.decode() (GH-104096... X-Git-Tag: v3.10.12~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cfa4295cd14ce00fe5263c7083aa5a73b515828d;p=thirdparty%2FPython%2Fcpython.git [3.10] gh-99889: Fix directory traversal security flaw in uu.decode() (GH-104096) (#104330) gh-99889: Fix directory traversal security flaw in uu.decode() (GH-104096) * Fix directory traversal security flaw in uu.decode() * also check absolute paths and os.altsep * Add a regression test. --------- (cherry picked from commit 0aeda297931820436a50b78f4f7f0597274b5df4) [Google] Co-authored-by: Sam Carroll <70000253+samcarroll42@users.noreply.github.com> --- diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index 753b31eef0d3..8cc1c074850b 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -145,6 +145,34 @@ class UUTest(unittest.TestCase): uu.encode(inp, out, filename) 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): def setUp(self): diff --git a/Lib/uu.py b/Lib/uu.py old mode 100755 new mode 100644 index 9f1f37f1a641..9fe252a639ea --- a/Lib/uu.py +++ b/Lib/uu.py @@ -130,7 +130,14 @@ def decode(in_file, out_file=None, mode=None, quiet=False): # If the filename isn't ASCII, what's up with that?!? out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii") 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: mode = int(hdrfields[1], 8) # diff --git a/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst b/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst new file mode 100644 index 000000000000..b7002e81b6b6 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst @@ -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.