Page 1 of 1

eval() security

Posted: Thu Feb 04, 2016 9:21 am
by Kami
Hey guys,

I've been exploring the possibilities to execute a python function with a servercommand and I came across eval().

Example of what I'm trying to do:

Code: Select all

execute_python test_module test_function <args>


which would then call:

Syntax: Select all

def test_function(args):


in the test_module.

But while looking for examples I often read that its dangerous to use due to security reasons (for example if someone would pass a string to it, that does bad stuff like deleting folders etc).

Would it be possible for someone to exploit said servercommand (or find a way to go around it) and do harm to a server with it?

I also found getattr, which seems to work only with Classes though (please correct me if I'm wrong!)

Posted: Thu Feb 04, 2016 10:23 am
by Ayuto
Where do you want to use eval() in this example? To me it looks like you just want to create sub-commands.

Posted: Thu Feb 04, 2016 12:07 pm
by iPlayer
I believe Kami wants to eval Python test_function's name with args.

Posted: Thu Feb 04, 2016 12:14 pm
by satoon101
getattr works on modules, too. I would highly recommend that over eval.

*Edit: I should clarify that getattr works on any/every object not just classes and modules. Just, in this specific case, getattr(<module>, <attr>) is what you could use. Ayuto is correct though that you would probably be better served using sub-commands which is something we already have available but are working on a much better system.

Posted: Thu Feb 04, 2016 1:10 pm
by Kami
Thank you for your answers! I'm actually trying to replace servercommands I previously made (lets say a servercommand for freezing a player) and calling the function to that directly to avoid having registered like thousand server commands.

It kinda works now, but with restrictions. I have this test command:

Syntax: Select all

from commands.server import ServerCommand
import test2

@ServerCommand('test')
def _test_command(command):
func_name = command[1]
function = getattr(test2.test2, func_name)
function()


and can call the test function in the test2.py:

Syntax: Select all

def test():
print('this is a test')


Now I was wondering, if you have to predefine which module you want to get the function from or if that can be variable too. (I know it's propably a bad comparison but kinda like es_doblock)

What I've also been wondering, does this even make sense or doesn't it matter how many servercommands you register?

Posted: Thu Feb 04, 2016 2:04 pm
by satoon101
It doesn't really matter how many server commands you register, but sub-commands can definitely make your life easier. Again, this is something we are currently working on, so it will be much easier to implement in the future.

Posted: Thu Feb 04, 2016 2:27 pm
by Kami
Oh okay, so that means it's not really necessary to avoid servercommands which makes my initial idea pointless :D

Thank you very much for helping me out!

Posted: Thu Feb 04, 2016 2:35 pm
by necavi
in python, eval() can basically _never_ be secure. I'd recommend creating a dictionary of the commands you want to be run and using that for your subcommands.

Posted: Thu Feb 04, 2016 5:39 pm
by Ayuto
I still don't really understand where you want to use eval here, but well... :D

Like Satoon mentioned we are working on a new sub-command system, which allows you to map server commands to Python functions. These changes are currently only available in the NewPerms branch, but I would like to give you an example of how your example would look like.

Syntax: Select all

from commands.typed import TypedServerCommand
from commands.typed import filter_str

# Your example without arguments
@TypedServerCommand(['execute_python', 'test_module', 'test_function'])
def test_function():
print('test_function')

# Your example with arguments
@TypedServerCommand(['execute_python', 'test_module', 'test_function2'])
def test_function2(a, b):
print('test_function2', (a, b))

# You can also specify types. If no type was specified, it will be handled as
# a string.
@TypedServerCommand(['execute_python', 'test_module', 'test_function3'])
def test_function3(a:int, b:float):
print('test_function3', (a, b))

# Optional arguments are also supported (can be combined with a type as well)!
@TypedServerCommand(['execute_python', 'test_module', 'test_function4'])
def test_function4(a:int, b:float, c='test'):
print('test_function4', (a, b, c))

# Or variadic arguments (can be combined with a type as well)!
@TypedServerCommand(['execute_python', 'test_module', 'test_function5'])
def test_function5(a:int, b:float, *args):
print('test_function5', (a, b, args))

# Another cool thing is that you can create your own functions/classes and use
# them as a type (filter_str is a built-in function).
# Could be called from console like this:
# execute_python test_module test_function6 t+ct
@TypedServerCommand(['execute_python', 'test_module', 'test_function6'])
def test_function6(players:filter_str):
print('test_function6')
for player in players:
print(player.name)

And you always get a descriptive error message if you did something wrong! :)