可以滥用literal_eval(f'{}')从外部源执行代码吗?

时间:2018-08-22 16:44:56

标签: python python-3.x

我只是很好奇这是否是一种风险,我想不出一种用户可能会导致这种情况发生的方法……但似乎令人担忧。

>>> import ast
>>> help(ast.literal_eval)
Help on function literal_eval in module ast:

literal_eval(node_or_string)
    Safely evaluate an expression node or a string containing a Python
    expression.  The string or node provided may only consist of the following
    Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
    and None.

ast.literal_eval(f'{os.remove("dontdelete.txt")-hello-world}')将执行文件删除。有可能将其用于应用程序吗?

  

“安全评估表达式节点”

是平均值?

1 个答案:

答案 0 :(得分:1)

以这种方式定义literal_eval的原因是,它是ast模块中的专用功能,而不是旨在用于其他各种目的的魔术功能,因此您应该期待如果使用的话,至少要摘录ast文档。

literal_eval可以采用表示已解析表达式的ast.Node,也可以采用字符串(当赋予ast,parse时产生这样的节点)。

然后,只要表达式仅使用“以下Python文字结构:字符串,字节,数字,元组,列表,字典,集合,布尔值和None”,它将对表达式求值;否则,将引发错误。


因此,如果将字符串'os.remove("dontdelete.txt")-hello-world'传递给它,那么当您解析它时,将得到一个BinOp表达式,它不是literal_eval的表达式节点之一接受,因此它立即以ValueError拒绝它。


如果将其传递给字符串"""f'{os.remove("dontdelete.txt")-hello-world}'""",则在解析该字符串时,会得到一个JoinedStr表达式节点,其中包含一堆literal_eval也不接受的节点(如果您不了解f字符串的工作原理,则值得进行ast.dump来确切地了解那些节点是什么),因此将再次使用ValueError拒绝它。


但是,如果您将其传递给字符串f'{os.remove("dontdelete.txt")-hello-world}',该怎么办?

好吧,该字符串在甚至传递给literal_eval之前就已经由解释器求值。

在大多数情况下,这甚至会在调用literal_eval之前引发一个异常,因此literal_eval完全无关紧要。例如:

  • 如果未导入os,则会得到NameError
  • 如果文件不存在,您将得到一个FileNotFoundError
  • 如果helloworld不存在,您将获得NameError
  • 如果hello存在但属于任何普通类型的值,因为remove返回None,那么您将获得TypeError来尝试减去任何类型的hello来自None

在某些情况下,当然会删除文件。但是由于literal_eval从未被调用,因此在这里几乎没有意义。您可以使用print而不是literal_eval做同样的事情,或者什么都不做。

但是如果导入了os并且文件存在,并且hello是某种类型的实例,并且其中的__rsub__接受None,并且{{1 }}是某种类型的实例,可以将其传递给返回的任何world的{​​{1}}吗?然后__sub__将返回hello.__rsub__返回的任何内容。如果是字符串或literal_eval,它甚至会求值。也许那将是其他一些危险的字符串?但是在那种情况下,sub会在那个危险的字符串上引起ast.Node,因此不会造成额外的伤害。