eval() security

Please post any questions about developing your plugin here. Please use the search function before posting!
User avatar
Kami
Global Moderator
Posts: 264
Joined: Wed Aug 15, 2012 1:24 am
Location: Germany

eval() security

Postby Kami » Thu Feb 04, 2016 9:21 am

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!)
User avatar
Ayuto
Project Leader
Posts: 2195
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Thu Feb 04, 2016 10:23 am

Where do you want to use eval() in this example? To me it looks like you just want to create sub-commands.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Postby iPlayer » Thu Feb 04, 2016 12:07 pm

I believe Kami wants to eval Python test_function's name with args.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
satoon101
Project Leader
Posts: 2698
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Thu Feb 04, 2016 12:14 pm

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.
Image
User avatar
Kami
Global Moderator
Posts: 264
Joined: Wed Aug 15, 2012 1:24 am
Location: Germany

Postby Kami » Thu Feb 04, 2016 1:10 pm

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?
User avatar
satoon101
Project Leader
Posts: 2698
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Thu Feb 04, 2016 2:04 pm

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.
Image
User avatar
Kami
Global Moderator
Posts: 264
Joined: Wed Aug 15, 2012 1:24 am
Location: Germany

Postby Kami » Thu Feb 04, 2016 2:27 pm

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!
necavi
Developer
Posts: 129
Joined: Wed Jan 30, 2013 9:51 pm

Postby necavi » Thu Feb 04, 2016 2:35 pm

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.
User avatar
Ayuto
Project Leader
Posts: 2195
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Thu Feb 04, 2016 5:39 pm

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! :)

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 5 guests