一直想用Python的上层覆盖机制HOOK关键函数来实现一个RASP,最近有时间收集了这方面的一些资料。
0x00 引子
RASP (Runtime application self-protection),运行时应用自我保护。很早之前提出的一个防御概念,将防御注入到应用程序中,不需要修改原有应用的业务代码即可实现对原有应用的防护。
详细概念参考绿盟的这篇讲解:
http://blog.nsfocus.net/rasp-tech/
Python HOOK 函数劫持打算通过 Python 动态分析中用到的命名空间覆盖的机制,对危险函数及类操作进行中转重写。其实相当于
相应的技术参考下面篇技术:
逢魔安全实验室的研究
聂大佬的研究
pyekaboo-自动化的hook生成工具
0x01 从函数hook到类hook
函数 hook
模块函数 hook 可以直接通过覆盖劫持的方式进行,这种情况适用于面向过程以函数为主的模块。
示例如下,这里为了环境方便,直接在 umarfarook882 的一个 web demo 上进行修改测试(其实他在视频里已经展示了自己针对不同语言实现的RASP,但是 github 上只放出了测试环境,并未提供RASP代码)。
首先是一个命令执行的测试页面,现在我们希望不修改代码,直接在应用中内嵌防御模块。
命令执行的实现代码如下:
1 2 3 4 5 6 7 8 9 10
def post (self) : print ("POST call" ) print ("POST " , self.request.uri,"\nBODY" , self.request.body) ping = self.get_argument ('ping' ) result = os.popen (ping) res = result.read () a = '' for line in res.splitlines () : a += str (line) self.render ("ping.html" ,msg=a)
通过 hook 关键 os 函数 popen,可以实现一个针对命令执行的防御,详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
首先,在应用的同目录下,新建 os.py 文件进行覆盖 然后,中转到正常的 os 模块并在 popen 函数调用时增加 import sysimport ioclass _InstallFcnHook (object) : def __init__ (self, fcn, debug=False) : self.debug = debug self._fcn = fcn def _pre_hook (self, *args, **kwargs ) : return (args, kwargs) def _post_hook (self, retval , *args, **kwargs) : if 'whoami' in args: a = io.open('log' ,'w+' ) a.write(u"'whoami' is dangerous" ) a.close() a = io.open('log' ,'r' ) retval = a return retval def __call__ (self, *args, **kwargs) : _hook_args = args _hook_kwargs = kwargs (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs) retval = self._fcn(*_hook_args, **_hook_kwargs) retval = self._post_hook(retval, *_hook_args, **_hook_kwargs) return retval def _load_and_regisqter_as (input_modname, output_modnames=[], look_path=[]) : import imp mod = None fd = None try : fd, pathname, description = imp.find_module(input_modname, look_path) for output_name in output_modnames: mod = imp.load_module(output_name, fd, pathname, description) finally : if fd is not None : fd.close() return mod if __name__ != "__main__" : _load_and_regisqter_as(__name__, [__name__, "orig_" + __name__], sys.path[::-1 ]) popen = _InstallFcnHook(popen)
其中 _InstallFcnHook 类实现了对应模块函数的劫持,_load_and_regisqter_as 函数则相当于是一个重定向的加载,加载完成后,对相应的函数进行覆盖。
而RASP就可以在覆盖的过程中,对整个应用调用前和调用后的参数进行获取,进而进行判断和阻断。这里需要注意的是,由于原来的代码使用 popen 来做命令执行的复现,因此这里劫持时需要返回一个 file 对象,又由于 os 模块自带 open 的机制和 io.open 不同,所以需要我们使用 io.open 重新生成一个 file 对象,在命中过滤规则时重写原有的 file 对象,也就是下面这段代码。
1 2 3 4 5 6 7 8 9 10
def _post_hook(self, retval, *args, **kwargs): #print (retval.read() ) if 'whoami' in args: a = io.open ('log' ,'w+' ) a .write (u"'whoami' is dangerous" ) #file = open ('log' ,O_RDONLY) a .close () a = io.open ('log' ,'r' ) retval = a return retval
我们先判断传递进来的命令是否命中规则(作为demo,这里仅以 whoami 为例),如果命中规则,就更改返回对象。这里选择了在命令执行后阻断结果回显,还有许多命令显然需要在执行前进行阻断,因此这一类操作就需要在 _pre_hook 中编写相应的防御代码。
类 hook
(由于最新的一篇论文阅读笔记发布,该部分实验未完待续~~)
FROM : phantom0301 | Author:phantom0301
评论