From 087561fd4945a93b8c9b35e0ffc3a9fe4de1a5a8 Mon Sep 17 00:00:00 2001 From: samcarroll42 Date: Tue, 2 May 2023 13:49:34 -0400 Subject: [PATCH 1/5] pythongh-99889: Fix directory traversal security flaw in uu.decode() --- Lib/uu.py | 2 ++ 1 file changed, 2 insertions(+) mode change 100755 => 100644 Lib/uu.py diff --git a/Lib/uu.py b/Lib/uu.py old mode 100755 new mode 100644 index 6f8805d8c5d0c6..9d87b8e6320a6d --- a/Lib/uu.py +++ b/Lib/uu.py @@ -134,6 +134,8 @@ def decode(in_file, out_file=None, mode=None, quiet=False): 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) + if '../' in out_file: + raise Error('Writing to %s would result in directory traversal' % out_file) if mode is None: mode = int(hdrfields[1], 8) # From 2d2b53a59e618a2da228a8d16c789956aaa47248 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 17:56:32 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst 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 00000000000000..43e424ecb96baa --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst @@ -0,0 +1 @@ +Fixed a security in flaw in the uu.decode() function that could allow for directory traversal. From 5791bd6cd32c13e5a6e33b854190b836f9e2cb36 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 9 May 2023 08:18:38 -0700 Subject: [PATCH 3/5] also check absolute paths and os.altsep --- Lib/uu.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/uu.py b/Lib/uu.py index 9d87b8e6320a6d..26bb59ae073ec5 100644 --- a/Lib/uu.py +++ b/Lib/uu.py @@ -133,9 +133,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) - if '../' in out_file: - raise Error('Writing to %s would result in directory traversal' % 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) # From ee098389db7e4d6a25397ea6e27f8d2057486010 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 9 May 2023 08:19:54 -0700 Subject: [PATCH 4/5] ReSTify the news entry. --- .../Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 43e424ecb96baa..b7002e81b6b677 100644 --- 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 @@ -1 +1,2 @@ -Fixed a security in flaw in the uu.decode() function that could allow for directory traversal. +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. From 6bc681e6bbbd50c422d82748815893a895c1af18 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Tue, 9 May 2023 15:35:12 +0000 Subject: [PATCH 5/5] Add a regression test. --- Lib/test/test_uu.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index 0493aae4fc67be..a189d6bc4b05d3 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -147,6 +147,34 @@ def test_newlines_escaped(self): 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):