向Python SimpleXMLRPC服务器注入任意代码
在Python的文档中,关于SimpleXMLRPC Server,提到了一些警告:
警告:开启allow_dotted_names选项会让不法分子有机会访问你模块中的全局变量,并可能让他们在你的机器上执行任意代码。这个选项只应该在安全的、封闭的网络中使用。
现在我有一个服务器,代码如下:
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler
server = SimpleXMLRPCServer(("localhost", 8000),
requestHandler=RequestHandler)
server.register_introspection_functions()
server.register_function(pow)
def adder_function(x,y):
return x + y
server.register_function(adder_function, 'add')
class MyFuncs:
def mul(self, x, y):
return x * y
server.register_instance(MyFuncs(), allow_dotted_names=True)
server.serve_forever()
请解释一下,这个漏洞是如何被利用来在服务器上注入任意代码的?如果我上面的代码没有漏洞,那请给一个可以被利用的例子,以及相应的客户端代码。
1 个回答
MyFuncs().mul
不仅仅是一个可以调用的函数,它和所有Python函数一样,都是一个一等对象,拥有自己的属性。
除了很多以 __xxx__
开头的魔法方法(这些你无法访问,因为SimpleXMLRPCServer会阻止访问任何以 _
开头的东西),还有一些内部方法成员,比如 im_class
(指向类对象)、im_self
(指向 MyFuncs()
的实例)和 im_func
(指向 mul
的函数定义)。这个函数对象本身有很多可访问的属性,最重要的是 func_globals
,它可以访问包含文件的变量作用域字典。
因此,通过调用 mul.im_func.func_globals.get
,攻击者可以读取你在脚本中设置的任意全局变量,或者使用 update()
方法来修改这些变量。在上面的例子中,这并不可被利用,因为你的全局变量中没有敏感信息。但这并不是你想要一直依赖的事情。
完全“执行任意代码”的可能性不大,但你可以想象一个可写的全局变量 codeToExecute
,它可能会在后面被 eval
执行,或者有人通过 register_instance
注册了整个模块,这样它导入的所有模块都可以被访问(典型的例子是 os
和 os.system
)。
在Python 3中,这种特定的攻击方式已经无法实现,因为函数/方法的内部属性被重命名为双下划线版本,这样就被阻止了。但总的来说,默认开放并允许外部访问实例的任何属性,单凭名称来看似乎是个坏主意——没有保证未来不会出现其他非下划线名称,或者不会有属性被添加到可访问的内置类型(如元组、字典)中,这些属性可能会以某种方式被利用。
如果你真的需要嵌套属性访问,似乎更安全的做法是设计一个版本的SimpleXMLRPCServer,要求使用类似 @rpc_accessible
的装饰器来定义哪些内容应该是可见的。