From 45c726fd4daa63236a8f3653530f297dc87b160a Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 27 Oct 2023 11:21:18 +0200 Subject: [PATCH 1/3] Don't allow __ or builtins in env dictionarys for ImageMath.eval --- src/PIL/ImageMath.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 7ca512e7568..cf108e2586f 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -237,6 +237,10 @@ def eval(expression, _dict={}, **kw): args.update(_dict) args.update(kw) for k, v in args.items(): + if '__' in k or hasattr(__builtins__, k): + msg = f"'{k}' not allowed" + raise ValueError(msg) + if hasattr(v, "im"): args[k] = _Operand(v) From 0ca3c33c59927e1c7e0c14dbc1eea1dfb2431a80 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 Oct 2023 15:58:52 +1100 Subject: [PATCH 2/3] Allow ops --- Tests/test_imagemath.py | 5 +++++ src/PIL/ImageMath.py | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 22de86c7cab..9a0326ece3b 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -64,6 +64,11 @@ def test_prevent_exec(expression): ImageMath.eval(expression) +def test_prevent_double_underscores(): + with pytest.raises(ValueError): + ImageMath.eval("1", {"__": None}) + + def test_logical(): assert pixel(ImageMath.eval("not A", images)) == 0 assert pixel(ImageMath.eval("A and B", images)) == "L 2" diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index cf108e2586f..fd7d78d4583 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -234,13 +234,14 @@ def eval(expression, _dict={}, **kw): # build execution namespace args = ops.copy() - args.update(_dict) - args.update(kw) - for k, v in args.items(): - if '__' in k or hasattr(__builtins__, k): + for k in list(_dict.keys()) + list(kw.keys()): + if "__" in k or hasattr(__builtins__, k): msg = f"'{k}' not allowed" raise ValueError(msg) + args.update(_dict) + args.update(kw) + for k, v in args.items(): if hasattr(v, "im"): args[k] = _Operand(v) From 557ba59d13de919d04b3fd4cdef8634f7d4b3348 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 30 Dec 2023 09:30:12 +1100 Subject: [PATCH 3/3] Include further builtins --- Tests/test_imagemath.py | 5 +++++ docs/releasenotes/10.2.0.rst | 9 ++++++--- src/PIL/ImageMath.py | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 9a0326ece3b..9281de6f66a 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -69,6 +69,11 @@ def test_prevent_double_underscores(): ImageMath.eval("1", {"__": None}) +def test_prevent_builtins(): + with pytest.raises(ValueError): + ImageMath.eval("(lambda: exec('exit()'))()", {"exec": None}) + + def test_logical(): assert pixel(ImageMath.eval("not A", images)) == 0 assert pixel(ImageMath.eval("A and B", images)) == "L 2" diff --git a/docs/releasenotes/10.2.0.rst b/docs/releasenotes/10.2.0.rst index 9883f10baf3..ade152fcd59 100644 --- a/docs/releasenotes/10.2.0.rst +++ b/docs/releasenotes/10.2.0.rst @@ -62,10 +62,13 @@ output only the quantization and Huffman tables for the image. Security ======== -TODO -^^^^ +Restricted environment keys for ImageMath.eval +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +:cve:`2023-50447`: If an attacker has control over the keys passed to the +``environment`` argument of :py:meth:`PIL.ImageMath.eval`, they may be able to execute +arbitrary code. To prevent this, keys matching the names of builtins and keys +containing double underscores will now raise a :py:exc:`ValueError`. Other Changes ============= diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index fd7d78d4583..b77f4bce567 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -235,7 +235,7 @@ def eval(expression, _dict={}, **kw): # build execution namespace args = ops.copy() for k in list(_dict.keys()) + list(kw.keys()): - if "__" in k or hasattr(__builtins__, k): + if "__" in k or hasattr(builtins, k): msg = f"'{k}' not allowed" raise ValueError(msg)