СколÑко бÑло Ñломано копий пÑи обÑÑждении вопÑоÑа «Ðозможно ли ÑделаÑÑ
Ðогда мне понадобилоÑÑ Ð½Ð°Ð¹Ñи ÑазвÑÑнÑÑÑй оÑÐ²ÐµÑ Ð½Ð° ÑÑÐ¾Ñ Ð²Ð¾Ð¿ÑоÑ, Ñ Ð½Ð°ÑкнÑлÑÑ Ð½Ð° один поÑÑ. ÐÐµÐ½Ñ Ð¿ÑиÑÑно Ñдивила глÑбина иÑÑледованиÑ, Ñак ÑÑо Ñ ÑеÑил, ÑÑо ÑÑо ÑÑÐ¾Ð¸Ñ Ð¿ÐµÑевеÑÑи.
Ð Python еÑÑÑ Ð²ÑÑÑÐ¾ÐµÐ½Ð½Ð°Ñ ÑÑнкÑиÑ
ÐÑо оÑÐµÐ½Ñ Ð¼Ð¾ÑнаÑ, но в Ñо же вÑÐµÐ¼Ñ Ð¸ оÑÐµÐ½Ñ Ð¾Ð¿Ð°ÑÐ½Ð°Ñ Ð¸Ð½ÑÑÑÑкÑиÑ, оÑобенно еÑли ÑÑÑоки, коÑоÑÑе Ð²Ñ Ð¿ÐµÑедаÑÑе в
ÐекоÑоÑÑе ÑÑвеÑждаÑÑ, ÑÑо возможно ÑделаÑÑ
Ðднако Ð¼Ñ Ð²ÑÑ ÐµÑÑ Ð¼Ð¾Ð¶ÐµÐ¼ импоÑÑиÑоваÑÑ Ð¼Ð¾Ð´Ñли и обÑаÑаÑÑÑÑ Ðº ним, иÑполÑзÑÑ Ð²ÑÑÑоеннÑÑ ÑÑнкÑиÑ
СледÑÑÑей попÑÑкой обÑÑно ÑÑановиÑÑÑ ÑеÑение запÑеÑиÑÑ Ð´Ð¾ÑÑÑп к
ÐекоÑоÑÑе говоÑÑÑ, ÑÑо «да» и ÑовеÑÑаÑÑ Ð¾ÑибкÑ. ÐÐ»Ñ Ð¿ÑимеÑа, Ð²Ð¾Ñ ÑÑÐ¾Ñ Ð½ÐµÐ±Ð¾Ð»ÑÑой кÑÑок кода вÑзовеÑ
ÐÑак, давайÑе ÑазбеÑÑмÑÑ, ÑÑо же здеÑÑ Ð¿ÑоиÑÑ Ð¾Ð´Ð¸Ñ. ÐаÑнÑм Ñ ÑÑого:
Ðак многие могли догадаÑÑÑÑ, ÑÑо пÑоÑÑо один из ÑпоÑобов обÑаÑиÑÑÑÑ Ðº
ТепеÑÑ Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем ÑпиÑок вÑÐµÑ ÐºÐ»Ð°ÑÑов, коÑоÑÑе наÑледÑÑÑ
ÐÑли замениÑÑ Ð´Ð»Ñ ÑдобоÑиÑаемоÑÑи ÑÑо вÑÑажение на
Ðалее в коде нам надо бÑÐ´ÐµÑ Ð´Ð²Ð°Ð¶Ð´Ñ Ð¸ÑкаÑÑ ÐºÐ»Ð°ÑÑ, Ñак ÑÑо Ñоздадим ÑÑнкÑиÑ:
ЧÑÐ¾Ð±Ñ Ð²ÑзваÑÑ ÑÑнкÑиÑ, надо как-Ñо ÐµÑ Ð½Ð°Ð·Ð²Ð°ÑÑ, но, Ñак как Ð¼Ñ Ð±Ñдем вÑполнÑÑÑ ÑÑÐ¾Ñ ÐºÐ¾Ð´ внÑÑÑи
Ðднако, еÑÑÑ Ð¸ ÑÑеÑий ваÑианÑ: паÑамеÑÑÑ Ð¿Ð¾ ÑмолÑаниÑ. ÐÑи обÑÑвлении лÑмбдÑ, как и пÑи обÑÑвлении лÑбой обÑÑной ÑÑнкÑии, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ задаÑÑ Ð¿Ð°ÑамеÑÑÑ Ð¿Ð¾ ÑмолÑаниÑ, Ñак ÑÑо еÑли Ð¼Ñ Ð¿Ð¾Ð¼ÐµÑÑим веÑÑ ÐºÐ¾Ð´ внÑÑÑи еÑÑ Ð¾Ð´Ð½Ð¾Ð¹ лÑмбдÑ, и зададим ей наÑÑ, как паÑамеÑÑ Ð¿Ð¾ ÑмолÑаниÑ, â Ð¼Ñ Ð´Ð¾Ð±ÑÑмÑÑ Ð¶ÐµÐ»Ð°ÐµÐ¼Ð¾Ð³Ð¾:
ÐÑак, Ð¼Ñ Ð¸Ð¼ÐµÐµÐ¼ ÑÑнкÑиÑ, коÑоÑÐ°Ñ ÑÐ¼ÐµÐµÑ Ð¸ÑкаÑÑ ÐºÐ»Ð°ÑÑÑ, и можем обÑаÑаÑÑÑÑ Ðº ней по имени. ЧÑо далÑÑе? ÐÑ Ñоздадим обÑÐµÐºÑ ÐºÐ»Ð°ÑÑа
Ðз вÑÐµÑ Ð¸Ð½Ð¸ÑиализÑÑÑÐ¸Ñ Ð¿Ð°ÑамеÑÑов Ð½Ð°Ñ Ð¸Ð½ÑеÑеÑÑÐµÑ ÑолÑко «KABOOM». ÐÑо и еÑÑÑ Ð¿Ð¾ÑледоваÑелÑноÑÑÑ Ð±Ð°Ð¹Ñ-кодов, коÑоÑÑÑ Ð±ÑÐ´ÐµÑ Ð¸ÑполÑзоваÑÑ Ð½Ð°Ñ Ð¾Ð±ÑекÑ, и, как Ð²Ñ Ñже могли догадаÑÑÑÑ, ÑÑа поÑледоваÑелÑноÑÑÑ Ð½Ðµ ÑвлÑеÑÑÑ Â«Ñ Ð¾ÑоÑей». Ðа Ñамом деле лÑбого байÑ-кода из Ð½ÐµÑ Ñ Ð²Ð°Ñило бÑ, Ñак как вÑÑ ÑÑо â бинаÑнÑе опеÑаÑоÑÑ, коÑоÑÑе бÑдÑÑ Ð²ÑÐ·Ð²Ð°Ð½Ñ Ð¿Ñи пÑÑÑом ÑÑеке, ÑÑо пÑиведÑÑ Ðº
ÐÑак, Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑÐµÐºÑ ÐºÐ»Ð°ÑÑа
ÐÑ Ð¸ ÑепеÑÑ, когда Ñ Ð½Ð°Ñ ÐµÑÑÑ ÑÑнкÑиÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÐµÑ Ð²ÑполниÑÑ. ÐонкÑеÑно ÑÑа ÑÑнкÑÐ¸Ñ Ð¿Ð¾Ð¿ÑÑаеÑÑÑ Ð²ÑполниÑÑ Ð½Ð°Ñ Ð½ÐµÐºÐ¾ÑÑекÑно ÑоÑÑавленнÑй байÑ-код и пÑиведÑÑ Ðº кÑÐ°Ñ Ñ Ð¸Ð½ÑеÑпÑеÑаÑоÑа.
ÐÐ¾Ñ Ð²ÐµÑÑ ÐºÐ¾Ð´ еÑÑ Ñаз:
ÐÑак, надеÑÑÑ ÑепеÑÑ Ð½Ð¸ Ñ ÐºÐ¾Ð³Ð¾ не оÑÑалоÑÑ Ñомнений в Ñом, ÑÑо
РпÑимеÑе вÑÑе Ð¼Ñ Ð¸ÑполÑзовали ÑпиÑок вÑÐµÑ Ð¿Ð¾Ð´ÐºÐ»Ð°ÑÑов клаÑÑа
ÐÐ¾Ñ ÐµÑÑ Ð¾Ð´Ð¸Ð½ пÑÐ¸Ð¼ÐµÑ Ñого, ÑÑо можно ÑделаÑÑ:
ÐодÑÐ»Ñ lib/site.py ÑодеÑÐ¶Ð¸Ñ ÐºÐ»Ð°ÑÑ
Ðод вÑÑе Ð½Ð°Ñ Ð¾Ð´Ð¸Ñ ÑÑÐ¾Ñ ÐºÐ»Ð°ÑÑ, инÑÑанÑииÑÑÐµÑ ÐµÐ³Ð¾ и вÑзÑваеÑ, Ñем завеÑÑÐ°ÐµÑ ÑабоÑÑ Ð¸Ð½ÑеÑпÑеÑаÑоÑа.
СейÑÐ°Ñ Ð¼Ñ Ð·Ð°Ð¿ÑÑкали
Ð ÑлÑÑае иÑполÑзованиÑ
ÐÑоблема вÑÐµÑ Ð¿Ð¾Ð´Ð¾Ð±Ð½ÑÑ Ð¿Ð¾Ð¿ÑÑок ÑделаÑÑ
Ðогда Ñ Ð¿Ñоводил иÑÑледование ÑÑой ÑемÑ, Ñ Ð½Ð°ÑкнÑлÑÑ Ð½Ð° заÑиÑеннÑй Ñежим вÑполнениÑ
ÐкÑаÑÑе, он ÑабоÑÐ°ÐµÑ ÑледÑÑÑим обÑазом: еÑли
РвÑÑ-Ñаки, можно ли ÑделаÑÑ
Ð ÑÑеде на Reddit Ñ Ð½Ð°ÑÑл коÑоÑкий ÑниппеÑ, позволÑÑÑий нам в eval полÑÑиÑÑ Â«Ð¾ÑигиналÑнÑе» __builtins__:
eval
безопаÑнÑм?» â невозможно ÑоÑÑиÑаÑÑ. ÐÑегда наÑ
одиÑÑÑ ÐºÑо-Ñо, кÑо ÑÑвеÑждаеÑ, ÑÑо наÑÑл ÑпоÑоб огÑадиÑÑÑÑ Ð¾Ñ Ð²ÑеÑ
возможнÑÑ
поÑледÑÑвий вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑÑой ÑÑнкÑии.Ðогда мне понадобилоÑÑ Ð½Ð°Ð¹Ñи ÑазвÑÑнÑÑÑй оÑÐ²ÐµÑ Ð½Ð° ÑÑÐ¾Ñ Ð²Ð¾Ð¿ÑоÑ, Ñ Ð½Ð°ÑкнÑлÑÑ Ð½Ð° один поÑÑ. ÐÐµÐ½Ñ Ð¿ÑиÑÑно Ñдивила глÑбина иÑÑледованиÑ, Ñак ÑÑо Ñ ÑеÑил, ÑÑо ÑÑо ÑÑÐ¾Ð¸Ñ Ð¿ÐµÑевеÑÑи.
ÐоÑоÑко о пÑоблеме
Ð Python еÑÑÑ Ð²ÑÑÑÐ¾ÐµÐ½Ð½Ð°Ñ ÑÑнкÑиÑ
eval()
, коÑоÑÐ°Ñ Ð²ÑполнÑÐµÑ ÑÑÑÐ¾ÐºÑ Ñ ÐºÐ¾Ð´Ð¾Ð¼ и возвÑаÑÐ°ÐµÑ ÑезÑлÑÑÐ°Ñ Ð²ÑполнениÑ:assert eval("2 + 3 * len('hello')") == 17
ÐÑо оÑÐµÐ½Ñ Ð¼Ð¾ÑнаÑ, но в Ñо же вÑÐµÐ¼Ñ Ð¸ оÑÐµÐ½Ñ Ð¾Ð¿Ð°ÑÐ½Ð°Ñ Ð¸Ð½ÑÑÑÑкÑиÑ, оÑобенно еÑли ÑÑÑоки, коÑоÑÑе Ð²Ñ Ð¿ÐµÑедаÑÑе в
eval
, полÑÑÐµÐ½Ñ Ð½Ðµ из довеÑенного иÑÑоÑника. ЧÑо бÑдеÑ, еÑли ÑÑÑокой, коÑоÑÑÑ Ð¼Ñ ÑеÑим ÑкоÑмиÑÑ eval
'Ñ, окажеÑÑÑ os.system('rm -rf /')
? ÐнÑеÑпÑеÑаÑÐ¾Ñ ÑеÑÑно запÑÑÑÐ¸Ñ Ð¿ÑоÑеÑÑ ÑÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²ÑеÑ
даннÑÑ
Ñ ÐºÐ¾Ð¼Ð¿ÑÑÑеÑа, и Ñ
оÑоÑо еÑÑ, еÑли он бÑÐ´ÐµÑ Ð²ÑполнÑÑÑÑÑ Ð¾Ñ Ð¸Ð¼ÐµÐ½Ð¸ наименее пÑивилегиÑованного полÑзоваÑÐµÐ»Ñ (в поÑледÑÑÑиÑ
пÑимеÑаÑ
Ñ Ð±ÑÐ´Ñ Ð¸ÑполÑзоваÑÑ clear
(cls
, еÑли Ð²Ñ Ð¸ÑполÑзÑеÑе Windows) вмеÑÑо rm -rf /
, ÑÑÐ¾Ð±Ñ Ð½Ð¸ÐºÑо из ÑиÑаÑелей ÑлÑÑайно не вÑÑÑÑелил Ñебе в ногÑ).Ðакие еÑÑÑ ÑеÑениÑ?
ÐекоÑоÑÑе ÑÑвеÑждаÑÑ, ÑÑо возможно ÑделаÑÑ
eval
безопаÑнÑм, еÑли запÑÑкаÑÑ ÐµÐ³Ð¾ без доÑÑÑпа к Ñимволам из globals. РкаÑеÑÑве вÑоÑого (опÑионалÑного) аÑгÑменÑа eval()
пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑловаÑÑ, коÑоÑÑй бÑÐ´ÐµÑ Ð¸ÑполÑзован вмеÑÑо глобалÑного пÑоÑÑÑанÑÑва имÑн (вÑе клаÑÑÑ, меÑодÑ, пеÑеменнÑе и пÑ., обÑÑвленнÑе на «веÑÑ
нем» ÑÑовне, доÑÑÑпнÑе из лÑбой ÑоÑки кода) кодом, коÑоÑÑй бÑÐ´ÐµÑ Ð²Ñполнен eval
'ом. ÐÑли eval
вÑзÑваеÑÑÑ Ð±ÐµÐ· ÑÑого аÑгÑменÑа, он иÑполÑзÑÐµÑ ÑекÑÑее глобалÑное пÑоÑÑÑанÑÑво имÑн, в коÑоÑое мог бÑÑÑ Ð¸Ð¼Ð¿Ð¾ÑÑиÑован модÑÐ»Ñ os
. ÐÑли же пеÑедаÑÑ Ð¿ÑÑÑой ÑловаÑÑ, глобалÑное пÑоÑÑÑанÑÑво имÑн Ð´Ð»Ñ eval
'а бÑÐ´ÐµÑ Ð¿ÑÑÑÑм. ÐÐ¾Ñ Ñакой код Ñже не ÑÐ¼Ð¾Ð¶ÐµÑ Ð²ÑполниÑÑÑÑ Ð¸ возбÑÐ´Ð¸Ñ Ð¸ÑклÑÑение NameError: name 'os' is not defined
:eval("os.system('clear')", {})
Ðднако Ð¼Ñ Ð²ÑÑ ÐµÑÑ Ð¼Ð¾Ð¶ÐµÐ¼ импоÑÑиÑоваÑÑ Ð¼Ð¾Ð´Ñли и обÑаÑаÑÑÑÑ Ðº ним, иÑполÑзÑÑ Ð²ÑÑÑоеннÑÑ ÑÑнкÑиÑ
__import__
. Так, код ниже оÑÑабоÑÐ°ÐµÑ Ð±ÐµÐ· оÑибок:eval("__import__('os').system('clear')", {})
СледÑÑÑей попÑÑкой обÑÑно ÑÑановиÑÑÑ ÑеÑение запÑеÑиÑÑ Ð´Ð¾ÑÑÑп к
__builtins__
изнÑÑÑи eval
'a, Ñак как имена, подобнÑе __import__, доÑÑÑÐ¿Ð½Ñ Ð½Ð°Ð¼ поÑомÑ, ÑÑо они наÑ
одÑÑÑÑ Ð² глобалÑной пеÑеменной __builtins__
. ÐÑли Ð¼Ñ Ñвно пеÑедадим вмеÑÑо Ð½ÐµÑ Ð¿ÑÑÑой ÑловаÑÑ, код ниже Ñже не ÑÐ¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð²Ñполнен:eval("__import__('os').system('clear')", {'__builtins__':{}}) # NameError: name '__import__' is not defined
ÐÑ Ð° ÑепеÑÑ-Ñо Ð¼Ñ Ð² безопаÑноÑÑи?
ÐекоÑоÑÑе говоÑÑÑ, ÑÑо «да» и ÑовеÑÑаÑÑ Ð¾ÑибкÑ. ÐÐ»Ñ Ð¿ÑимеÑа, Ð²Ð¾Ñ ÑÑÐ¾Ñ Ð½ÐµÐ±Ð¾Ð»ÑÑой кÑÑок кода вÑзовеÑ
segfault
, еÑли Ð²Ñ Ð·Ð°Ð¿ÑÑÑиÑе его в CPython:s = """
(lambda fc=(
lambda n: [
c for c in
().__class__.__bases__[0].__subclasses__()
if c.__name__ == n
][0]
):
fc("function")(
fc("code")(
0,0,0,0,"KABOOM",(),(),(),"","",0,""
),{}
)()
)()
"""
eval(s, {'__builtins__':{}})
ÐÑак, давайÑе ÑазбеÑÑмÑÑ, ÑÑо же здеÑÑ Ð¿ÑоиÑÑ Ð¾Ð´Ð¸Ñ. ÐаÑнÑм Ñ ÑÑого:
().__class__.__bases__[0]
Ðак многие могли догадаÑÑÑÑ, ÑÑо пÑоÑÑо один из ÑпоÑобов обÑаÑиÑÑÑÑ Ðº
object
. ÐÑ Ð½Ðµ можем пÑоÑÑо напиÑаÑÑ object
, Ñак как __builtins__
пÑÑÑÑ, но Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑоздаÑÑ Ð¿ÑÑÑой коÑÑеж (ÑÑÑпл), пеÑвÑм базовÑм клаÑÑом коÑоÑого ÑвлÑеÑÑÑ object
и, пÑойдÑÑÑ Ð¿Ð¾ его ÑвойÑÑвам, полÑÑиÑÑ Ð´Ð¾ÑÑÑп к клаÑÑÑ object
.ТепеÑÑ Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем ÑпиÑок вÑÐµÑ ÐºÐ»Ð°ÑÑов, коÑоÑÑе наÑледÑÑÑ
object
или, инÑми Ñловами, ÑпиÑок вÑеÑ
клаÑÑов, обÑÑвленнÑÑ
в пÑогÑамме на даннÑй моменÑ:().__class__.__bases__[0].__subclasses__()
ÐÑли замениÑÑ Ð´Ð»Ñ ÑдобоÑиÑаемоÑÑи ÑÑо вÑÑажение на
ALL_CLASSES
, неÑÑÑдно бÑÐ´ÐµÑ Ð·Ð°Ð¼ÐµÑиÑÑ, ÑÑо вÑÑажение ниже наÑ
Ð¾Ð´Ð¸Ñ ÐºÐ»Ð°ÑÑ Ð¿Ð¾ его имени:[c for c in ALL_CLASSES if c.__name__ == n][0]
Ðалее в коде нам надо бÑÐ´ÐµÑ Ð´Ð²Ð°Ð¶Ð´Ñ Ð¸ÑкаÑÑ ÐºÐ»Ð°ÑÑ, Ñак ÑÑо Ñоздадим ÑÑнкÑиÑ:
lambda n: [c for c in ALL_CLASSES if c.__name__ == n][0]
ЧÑÐ¾Ð±Ñ Ð²ÑзваÑÑ ÑÑнкÑиÑ, надо как-Ñо ÐµÑ Ð½Ð°Ð·Ð²Ð°ÑÑ, но, Ñак как Ð¼Ñ Ð±Ñдем вÑполнÑÑÑ ÑÑÐ¾Ñ ÐºÐ¾Ð´ внÑÑÑи
eval
'a, Ð¼Ñ Ð½Ðµ можем ни обÑÑвиÑÑ ÑÑнкÑÐ¸Ñ (иÑполÑзÑÑ def
), ни иÑполÑзоваÑÑ Ð¾Ð¿ÐµÑаÑÐ¾Ñ Ð¿ÑиÑвоениÑ, ÑÑÐ¾Ð±Ñ Ð¿ÑивÑзаÑÑ Ð½Ð°ÑÑ Ð»ÑÐ¼Ð±Ð´Ñ Ðº какой-нибÑÐ´Ñ Ð¿ÐµÑеменной.Ðднако, еÑÑÑ Ð¸ ÑÑеÑий ваÑианÑ: паÑамеÑÑÑ Ð¿Ð¾ ÑмолÑаниÑ. ÐÑи обÑÑвлении лÑмбдÑ, как и пÑи обÑÑвлении лÑбой обÑÑной ÑÑнкÑии, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ задаÑÑ Ð¿Ð°ÑамеÑÑÑ Ð¿Ð¾ ÑмолÑаниÑ, Ñак ÑÑо еÑли Ð¼Ñ Ð¿Ð¾Ð¼ÐµÑÑим веÑÑ ÐºÐ¾Ð´ внÑÑÑи еÑÑ Ð¾Ð´Ð½Ð¾Ð¹ лÑмбдÑ, и зададим ей наÑÑ, как паÑамеÑÑ Ð¿Ð¾ ÑмолÑаниÑ, â Ð¼Ñ Ð´Ð¾Ð±ÑÑмÑÑ Ð¶ÐµÐ»Ð°ÐµÐ¼Ð¾Ð³Ð¾:
(lambda fc=(
lambda n: [
c for c in ALL_CLASSES if c.__name__ == n
][0]
):
# ÑепеÑÑ Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ обÑаÑаÑÑÑÑ Ðº наÑей лÑмбде ÑеÑез fc
)()
ÐÑак, Ð¼Ñ Ð¸Ð¼ÐµÐµÐ¼ ÑÑнкÑиÑ, коÑоÑÐ°Ñ ÑÐ¼ÐµÐµÑ Ð¸ÑкаÑÑ ÐºÐ»Ð°ÑÑÑ, и можем обÑаÑаÑÑÑÑ Ðº ней по имени. ЧÑо далÑÑе? ÐÑ Ñоздадим обÑÐµÐºÑ ÐºÐ»Ð°ÑÑа
code
(внÑÑÑенний клаÑÑ, его ÑкземплÑÑом, напÑимеÑ, ÑвлÑеÑÑÑ ÑвойÑÑво func_code
обÑекÑа ÑÑнкÑии):fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,"")
Ðз вÑÐµÑ Ð¸Ð½Ð¸ÑиализÑÑÑÐ¸Ñ Ð¿Ð°ÑамеÑÑов Ð½Ð°Ñ Ð¸Ð½ÑеÑеÑÑÐµÑ ÑолÑко «KABOOM». ÐÑо и еÑÑÑ Ð¿Ð¾ÑледоваÑелÑноÑÑÑ Ð±Ð°Ð¹Ñ-кодов, коÑоÑÑÑ Ð±ÑÐ´ÐµÑ Ð¸ÑполÑзоваÑÑ Ð½Ð°Ñ Ð¾Ð±ÑекÑ, и, как Ð²Ñ Ñже могли догадаÑÑÑÑ, ÑÑа поÑледоваÑелÑноÑÑÑ Ð½Ðµ ÑвлÑеÑÑÑ Â«Ñ Ð¾ÑоÑей». Ðа Ñамом деле лÑбого байÑ-кода из Ð½ÐµÑ Ñ Ð²Ð°Ñило бÑ, Ñак как вÑÑ ÑÑо â бинаÑнÑе опеÑаÑоÑÑ, коÑоÑÑе бÑдÑÑ Ð²ÑÐ·Ð²Ð°Ð½Ñ Ð¿Ñи пÑÑÑом ÑÑеке, ÑÑо пÑиведÑÑ Ðº
segfault
'Ñ CPython. "KABOOM" пÑоÑÑо вÑглÑÐ´Ð¸Ñ Ð·Ð°Ð±Ð°Ð²Ð½ÐµÐµ, ÑпаÑибо lvh за ÑÑÐ¾Ñ Ð¿ÑимеÑ.ÐÑак, Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑÐµÐºÑ ÐºÐ»Ð°ÑÑа
code
, но напÑÑмÑÑ Ð²ÑполниÑÑ ÐµÐ³Ð¾ Ð¼Ñ Ð½Ðµ можем. Тогда Ñоздадим ÑÑнкÑиÑ, кодом коÑоÑой и бÑÐ´ÐµÑ Ð½Ð°Ñ Ð¾Ð±ÑекÑ:fc("function")(CODE_OBJECT, {})
ÐÑ Ð¸ ÑепеÑÑ, когда Ñ Ð½Ð°Ñ ÐµÑÑÑ ÑÑнкÑиÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÐµÑ Ð²ÑполниÑÑ. ÐонкÑеÑно ÑÑа ÑÑнкÑÐ¸Ñ Ð¿Ð¾Ð¿ÑÑаеÑÑÑ Ð²ÑполниÑÑ Ð½Ð°Ñ Ð½ÐµÐºÐ¾ÑÑекÑно ÑоÑÑавленнÑй байÑ-код и пÑиведÑÑ Ðº кÑÐ°Ñ Ñ Ð¸Ð½ÑеÑпÑеÑаÑоÑа.
ÐÐ¾Ñ Ð²ÐµÑÑ ÐºÐ¾Ð´ еÑÑ Ñаз:
(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]):
fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})()
)()
ÐаклÑÑение
ÐÑак, надеÑÑÑ ÑепеÑÑ Ð½Ð¸ Ñ ÐºÐ¾Ð³Ð¾ не оÑÑалоÑÑ Ñомнений в Ñом, ÑÑо
eval
ÐÐ ÐÐÐÐÐÐСÐÐ, даже еÑли ÑбÑаÑÑ Ð´Ð¾ÑÑÑп к глобалÑнÑм и вÑÑÑоеннÑм пеÑеменнÑм.РпÑимеÑе вÑÑе Ð¼Ñ Ð¸ÑполÑзовали ÑпиÑок вÑÐµÑ Ð¿Ð¾Ð´ÐºÐ»Ð°ÑÑов клаÑÑа
object
, ÑÑÐ¾Ð±Ñ ÑоздаÑÑ Ð¾Ð±ÑекÑÑ ÐºÐ»Ð°ÑÑов code
и function
. ТоÑно Ñаким же обÑазом можно полÑÑиÑÑ (и инÑÑанÑиÑоваÑÑ) лÑбой клаÑÑ, ÑÑÑеÑÑвÑÑÑий в пÑогÑамме на Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð²Ñзова eval()
.ÐÐ¾Ñ ÐµÑÑ Ð¾Ð´Ð¸Ð½ пÑÐ¸Ð¼ÐµÑ Ñого, ÑÑо можно ÑделаÑÑ:
s = """
[
c for c in
().__class__.__bases__[0].__subclasses__()
if c.__name__ == "Quitter"
][0](0)()
"""
eval(s, {'__builtins__':{}})
ÐодÑÐ»Ñ lib/site.py ÑодеÑÐ¶Ð¸Ñ ÐºÐ»Ð°ÑÑ
Quitter
, коÑоÑÑй вÑзÑваеÑÑÑ Ð¸Ð½ÑеÑпÑеÑаÑоÑом, когда Ð²Ñ Ð½Ð°Ð±Ð¸ÑаеÑе quit()
.Ðод вÑÑе Ð½Ð°Ñ Ð¾Ð´Ð¸Ñ ÑÑÐ¾Ñ ÐºÐ»Ð°ÑÑ, инÑÑанÑииÑÑÐµÑ ÐµÐ³Ð¾ и вÑзÑваеÑ, Ñем завеÑÑÐ°ÐµÑ ÑабоÑÑ Ð¸Ð½ÑеÑпÑеÑаÑоÑа.
СейÑÐ°Ñ Ð¼Ñ Ð·Ð°Ð¿ÑÑкали
eval
в пÑÑÑом окÑÑжении, иÑÑ
Ð¾Ð´Ñ Ð¸Ð· Ñого, ÑÑо ÑказаннÑй в ÑÑаÑÑе код â ÑÑо веÑÑ ÐºÐ¾Ð´ наÑей пÑогÑаммÑ. Ð ÑлÑÑае иÑполÑзованиÑ
eval
'а в ÑеалÑном пÑиложении злоÑмÑÑленник Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп ко вÑем клаÑÑам, коÑоÑÑе Ð²Ñ Ð¸ÑполÑзÑеÑе, Ñак ÑÑо его возможноÑÑи не бÑдÑÑ Ð¾Ð³ÑаниÑÐµÐ½Ñ Ð¿ÑакÑиÑеÑки ниÑем.ÐÑоблема вÑÐµÑ Ð¿Ð¾Ð´Ð¾Ð±Ð½ÑÑ Ð¿Ð¾Ð¿ÑÑок ÑделаÑÑ
eval
безопаÑнÑм в Ñом, ÑÑо они вÑе оÑÐ½Ð¾Ð²Ð°Ð½Ñ Ð½Ð° идее «ÑÑÑнÑÑ
ÑпиÑков», идее о Ñом, ÑÑо надо ÑбÑаÑÑ Ð´Ð¾ÑÑÑп ко вÑем веÑам, коÑоÑÑе, как нам кажеÑÑÑ, могÑÑ Ð±ÑÑÑ Ð¾Ð¿Ð°ÑÐ½Ñ Ð¿Ñи иÑполÑзовании в eval
'е. С Ñакой ÑÑÑаÑегией пÑакÑиÑеÑки Ð½ÐµÑ ÑанÑов на победÑ, Ð²ÐµÐ´Ñ ÐµÑли окажеÑÑÑ Ð½ÐµÐ·Ð°Ð¿ÑеÑÑннÑм Ñ
оÑÑ ÑÑо-Ñо, ÑиÑÑема бÑÐ´ÐµÑ ÑÑзвима.Ðогда Ñ Ð¿Ñоводил иÑÑледование ÑÑой ÑемÑ, Ñ Ð½Ð°ÑкнÑлÑÑ Ð½Ð° заÑиÑеннÑй Ñежим вÑполнениÑ
eval
'а в Python, коÑоÑÑй ÑвлÑеÑÑÑ ÐµÑÑ Ð¾Ð´Ð½Ð¾Ð¹ попÑÑкой побоÑоÑÑ ÑÑÑ Ð¿ÑоблемÑ:>>> eval("(lambda:0).func_code", {'__builtins__':{}})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
RuntimeError: function attributes not accessible in restricted mode
ÐкÑаÑÑе, он ÑабоÑÐ°ÐµÑ ÑледÑÑÑим обÑазом: еÑли
__builtins__
внÑÑÑи eval
оÑлиÑаÑÑÑÑ Ð¾Ñ Â«Ð¾ÑиÑиалÑнÑÑ
» â eval
пеÑеÑ
Ð¾Ð´Ð¸Ñ Ð² заÑиÑеннÑй Ñежим, в коÑоÑом закÑÑÑ Ð´Ð¾ÑÑÑп к некоÑоÑÑм опаÑнÑм ÑвойÑÑвам, Ñаким как func_code
Ñ ÑÑнкÑий. Ðолее подÑобное опиÑание ÑÑого Ñежима можно найÑи ÑÑÑ, но, как Ð¼Ñ Ñже видели вÑÑе, он Ñоже не ÑвлÑеÑÑÑ Â«ÑеÑебÑÑной пÑлей».РвÑÑ-Ñаки, можно ли ÑделаÑÑ
eval
безопаÑнÑм? Сложно ÑказаÑÑ. Ðак мне кажеÑÑÑ, злоÑмÑÑÐ»ÐµÐ½Ð½Ð¸ÐºÑ Ð½Ðµ ÑдаÑÑÑÑ Ð½Ð°Ð²ÑедиÑÑ Ð±ÐµÐ· доÑÑÑпа к обÑекÑам Ñ Ð´Ð²ÑÐ¼Ñ Ð½Ð¸Ð¶Ð½Ð¸Ð¼Ð¸ подÑÑÑкиваниÑми, обÑамлÑÑÑими имÑ, Ñак ÑÑо возможно, еÑли иÑклÑÑиÑÑ Ð¸Ð· обÑабоÑки вÑе ÑÑÑоки Ñ Ð´Ð²ÑÐ¼Ñ Ð½Ð¸Ð¶Ð½Ð¸Ð¼Ð¸ подÑÑÑкиваниÑми, Ñо Ð¼Ñ Ð±Ñдем в безопаÑноÑÑи. Ðозможно...P.S.
Ð ÑÑеде на Reddit Ñ Ð½Ð°ÑÑл коÑоÑкий ÑниппеÑ, позволÑÑÑий нам в eval полÑÑиÑÑ Â«Ð¾ÑигиналÑнÑе» __builtins__:
[
c for c in ().__class__.__base__.__subclasses__()
if c.__name__ == 'catch_warnings'
][0]()._module.__builtins__