Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Use eval() safely?

4 views
Skip to first unread message

W. Martin Borgert

unread,
Feb 21, 2010, 4:25:11 PM2/21/10
to pytho...@python.org
Hi,

I know that this issue has been discussed before, but most of
the time using only one argument to eval().

Is it possible to use the following code, e.g. run as part of a
web application, to break in and if so, how?

import math

def myeval(untrustedinput):
return eval(untrustedinput, {"__builtins__": None},
{ "abs": abs, "sin": math.sin })

Is it possible to define functions or import modules from the
untrusted input string?

Which Python built-ins and math functions would I have to add to
the functions dictionary to make it unsafe?

TIA! (Please cc me, thanks.)

Steven D'Aprano

unread,
Feb 21, 2010, 6:33:45 PM2/21/10
to

You've got the right idea, but the task is difficult.

Please read this thread:

http://tav.espians.com/a-challenge-to-break-python-security.html

--
Steven

Gregory Ewing

unread,
Feb 22, 2010, 12:45:40 AM2/22/10
to
W. Martin Borgert wrote:

> def myeval(untrustedinput):
> return eval(untrustedinput, {"__builtins__": None},
> { "abs": abs, "sin": math.sin })
>
> Is it possible to define functions or import modules from the
> untrusted input string?

This is NOT safe as it stands. It still isn't safe even if
you put nothing in the globals dict at all.

A couple of ways someone can do nasty things to you:

# Wipe out any file writable by the calling process
eval("[c for c in (0).__class__.__bases__[0].__subclasses__() if c.__name__ ==
'file'][0]('/my/precious/file', 'w')")

# Use up large amounts of memory and CPU time
eval("100000**100000")

--
Greg

Steven D'Aprano

unread,
Feb 22, 2010, 1:07:05 AM2/22/10
to
On Mon, 22 Feb 2010 18:45:40 +1300, Gregory Ewing wrote:

> W. Martin Borgert wrote:
>
>> def myeval(untrustedinput):
>> return eval(untrustedinput, {"__builtins__": None},
>> { "abs": abs, "sin": math.sin })
>>
>> Is it possible to define functions or import modules from the untrusted
>> input string?
>
> This is NOT safe as it stands. It still isn't safe even if you put
> nothing in the globals dict at all.

It's *especially* not safe if you put nothing in the globals dict,
because Python kindly rectifies that by putting the builtins into it:

>>> eval("__builtins__.keys()", {}, {})
['IndexError', 'all', 'help', 'vars', ... 'OverflowError']


>>> eval("globals()", {}, {})
{'__builtins__': {...}}
>>>
>>> eval("globals()", {'__builtins__': None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'globals' is not defined

So {'__builtins__': None} is safer than {}. Still not safe, exactly, but
safer. Or at least you make the Black Hats work harder before they own
your server :)


--
Steven

Jonathan Gardner

unread,
Feb 22, 2010, 2:45:10 PM2/22/10
to W. Martin Borgert, pytho...@python.org
On Sun, Feb 21, 2010 at 1:25 PM, W. Martin Borgert <deb...@debian.org> wrote:
>
> I know that this issue has been discussed before, but most of
> the time using only one argument to eval().
>
> Is it possible to use the following code, e.g. run as part of a
> web application, to break in and if so, how?
>
> import math
>
> def myeval(untrustedinput):
>    return eval(untrustedinput, {"__builtins__": None},
>                { "abs": abs, "sin": math.sin })
>
> Is it possible to define functions or import modules from the
> untrusted input string?
>
> Which Python built-ins and math functions would I have to add to
> the functions dictionary to make it unsafe?
>

Why would you ever run untrusted code on any machine in any language,
let alone Python?

If you're writing a web app, make it so that you only run trusted
code. That is, code installed by the admin, or approved by the admin.

--
Jonathan Gardner
jgar...@jonathangardner.net

Steven D'Aprano

unread,
Feb 23, 2010, 2:08:30 AM2/23/10
to
On Mon, 22 Feb 2010 11:45:10 -0800, Jonathan Gardner wrote:

> Why would you ever run untrusted code on any machine in any language,
> let alone Python?

Because sometimes you have to run untrusted code, so you want to run it
in a sandbox so it can't eat your machine.

E.g. viewing PDF files.

Or you might be building an app that allows the user to enter code and
execute it:

http://tryruby.org/

> If you're writing a web app, make it so that you only run trusted code.
> That is, code installed by the admin, or approved by the admin.

But do you trust the admin? Do you think your admin has audited the
entire tool chain of every application, library and operating system
module in your system?

--
Steven

Dieter Maurer

unread,
Feb 24, 2010, 4:11:25 AM2/24/10
to pytho...@python.org
Steven D'Aprano <ste...@REMOVE.THIS.cybersource.com.au> writes on 22 Feb 2010 06:07:05 GMT:
> ...

> It's *especially* not safe if you put nothing in the globals dict,
> because Python kindly rectifies that by putting the builtins into it:
>
> >>> eval("__builtins__.keys()", {}, {})
> ['IndexError', 'all', 'help', 'vars', ... 'OverflowError']
>
>
> >>> eval("globals()", {}, {})
> {'__builtins__': {...}}
> >>>
> >>> eval("globals()", {'__builtins__': None}, {})
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<string>", line 1, in <module>
> NameError: name 'globals' is not defined
>
> So {'__builtins__': None} is safer than {}. Still not safe, exactly, but
> safer. Or at least you make the Black Hats work harder before they own
> your server :)

Using functionality introduced with the class/type homogenization,
it is quite easy to get access to the "file" type (even when "__builtins__"
is disabled). Having "file", arbitrary files can be read, written, destroyed...


Dieter

Steven D'Aprano

unread,
Feb 24, 2010, 8:06:16 PM2/24/10
to
On Wed, 24 Feb 2010 10:11:25 +0100, Dieter Maurer wrote:

> Using functionality introduced with the class/type homogenization, it is
> quite easy to get access to the "file" type (even when "__builtins__" is
> disabled). Having "file", arbitrary files can be read, written,
> destroyed...

Not that I don't believe you (I do!) but could you demonstrate for the
record?

--
Steven

Gregory Ewing

unread,
Feb 25, 2010, 1:00:26 AM2/25/10
to
Steven D'Aprano wrote:

> Not that I don't believe you (I do!) but could you demonstrate for the
> record?

I posted a demonstration of this earlier in this thread. The
key thing is the __subclasses__() method of a class. You can
start with any object, work your way up the base class chain
to object, and then use __subclasses__() to get to any builtin
class in the system, including file.

There was a sandboxing scheme put forward a while back which
involves vetting the code and disallowing the use of any
double-underscore attribute names. With a suitably censored
set of builtin functions, this prevents the use of the
__subclasses__ hack, as well as some other potential lines
of attack. As far as I know, nobody managed to break it at
the time, but it probably hasn't been tested much in
the real world, if at all, so I probably wouldn't recommend
using it for anything critical.

--
Greg

W. Martin Borgert

unread,
Feb 28, 2010, 5:52:27 PM2/28/10
to pytho...@python.org
Gregory Ewing wrote:
> I posted a demonstration of this earlier in this thread.

As you wrote, your example does not work when using eval() like
in my original post with second and third parameter to eval():

>>> import math
>>> eval("[c for c in (0).__class__.__bases__[0].__subclasses__() if c.__name__ == 'file'][0]('/myfile', 'w')",
{ "__builtins__": None }, { "abs": abs, "sin": math.sin })


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>

IOError: file() constructor not accessible in restricted mode

(Same result with Python 2.4, 2.5, and 2.6.)

While I believe, that eval() is not save, I have yet to see an
example for exploiting it. Leaving out the second and third
parameter just proves, that one shouldn't omit them :~)

Thanks in advance for any black hat example!

P.S. Please Cc me, thanks.

0 new messages