作者简介
Eric,携程资深开发工程师,关注应用安全、渗透测试方面的技术和相关开源产品的二次开发。
一、前言
携程信息安全部门目前在研究百度OpenRASP技术,让它在携程落地进行服务漏洞扫描防护。做安全漏洞测试的同学都知道,用类似黑盒测试工具测试服务漏洞的时候,在测试服务接口的请求上做参数注入的修改,会污染测试服务的数据源,OpenRASP技术也不例外。
本文主要讲述我们IAST漏洞扫描系统中OpenRASP在携程快速部署及如何防止流量重放对数据污染的一系列实践经验。让业务部门无感知地发现他们的服务在测试环境中暴露的漏洞。
这里先简单介绍下什么是IAST,DAST和RASP。
IAST:Interactive Application Security Testing,交互式应用安全测试。近实时检测、误报率极低、可定位到代码行数、展示污点调用过程等等,非常适用于敏捷开发和DevOps理念。可以在软件的开发和测试阶段无缝集成现有开发流程,让开发人员和测试人员在执行功能测试的同时,无感知的完成安全测试,解决了现有应用安全测试技术面临的挑战。我们该套IAST产品中也会尽力体现污点调用过程,偏向代码层。
DAST:Dynamic Application Security Testing,动态应用程序安全测试。在测试或运行阶段分析应用程序的动态运行状态。它模拟黑客行为对应用程序进行动态攻击,分析应用程序的反应,从而确定该Web应用是否易受攻击。这种技术主要采用渗透测试,发现应用系统的潜在风险。
RASP:Runtime Application Security Testing,运行时应用安全测试。很多企业在上线前进行漏洞检测,都要求解决高中危漏洞,在业务紧急上线的情况下,低危漏洞往往可以选择性地忽略,DevOps因为强调速度,这种情况会更多。但是作为一个安全人员或者项目经理,忽略低危漏洞真的放心吗?攻击者可能不会通过这些低危漏洞来直接攻击业务,但是往往会成为攻击链中的一环,获取某些敏感信息等,那RASP的作用就是,在运维阶段继续针对性的保护那些被忽略的低危漏洞。
二、挑战
IAST/RASP的原理在这里就不介绍了,其主要优点就是检测精准。技术是好技术,但要在企业中大规模部署,缺点也很明显:
1)因为在从IAST agent(OpenRASP)回传的流量会再次通过黑盒扫描工具DAST(修改请求参数)重放流量,这会给待测试的服务造成数据污染。对于测试人员,这会影响到服务的测试流程,进而影响测试人员在服务里接入IAST Agent的意愿。目前业界也没有很好的方案来解决IAST黑盒测试产生的脏数据,如新思seeker只是通过其他机制减少脏数据的产生。
2)要实现大规模部署,携程集团有几千个应用系统、上万台服务器的适配、测试、部署、维护…推广和维护工作太重。虽然在c0debreak大佬团队的努力下,rasp agent对服务器的性能影响已控制到3%左右。但在旅游交易系统中,我们也不敢轻易上到生产环境“玩火”,只能将目标锁定在测试环境上。通过PAAS发布系统,在docker容器的镜像中直接打上IAST agent包来部署推广。
3)目前服务落地的数据一般是放到:关系数据库系统,缓存(redis和memcache),ElasticSearch,消息队列(kafka,qmq,hermes)中,如何做到流量重放产生的脏数据不落地。关键是落地组件数量多,如何做到一次性一劳永逸地解决掉。
三、找到问题
通常认知是,我们了解到数据落地的组件(DB,redis,ElasticSearch等)都是通过网络来传送数据的,在这些组件的java实现中,我们很自然地就想到他们是使用java中套接字Socket来发送数据的,所以研究方法就是在Socket上做文章。
如何证实我们的猜测呢?那就是在这些落地组件读写数据时,通过java方法调用链是否能找到Socket的读写方法。
我们用到字节码操作工具byte buddy,实现java方法调用链展示,主要跟踪了以下数据落地组件发送数据的调用链。
另一种方法就是本地调试看这些落地组件是否运行了Socket对象的输出流写数据的方法。
四、IAST部署架构及数据污染的处理方案
IAST/DAST部署架构
在携程实践的IAST(agent被动检测+分布式扫描器主动扫描)分为下面4个部分:
1)IAST agent
集成到测试环境应用docker容器的agent,hook tomcat底层调用,用来检测应用中的漏洞,同时会把所有访问到应用docker的http流量复制回传到用于收集流量的kafka消息队列。
2)IAST服务管理端
管理IAST agent和漏洞详情展示统计分析的控制台。
3)流量回传的kafka消息队列
用于收集待扫描的流量,除了从IAST agent回传的流量,还有来自主动爬虫、chrome插件以及提测平台调用api发送过来的流量。
4)分布式扫描器
消费kafka里的流量并且按照url去重,调用扫描器进行漏洞扫描。
这样一套架构的好处在于:
这套扫描系统在少量应用灰度期间就发现了内部存在已久未被发现的通用型漏洞,对于内部安全检测能力的补齐提供了很好的帮助。
IAST流量重放产生数据污染的处理方案
利用JDK Instrumentation API我们可以提供一个Agent代理用来监测和协助运行在JVM上的程序,可以在程序启动前修改类的定义。简单来说就是在运行的应用中织入一个我们的程序。而在这个程序中我们就拥有了获取当前应用的上下文,在应用运行中实时分析数据流以及调用栈的能力。
插桩技术是在保证目标程序原有逻辑完整的情况下,在特定的位置插入代码段,从而收集程序运行时的动态上下文信息。在Java中插桩通过Instrument以及字节码操作工具(如:ASM,Javassist,Byte Buddy等)实现。Instrumentation会使用类ClassFileTransformer的transform方法对jvm中的未加载的类进行重写。已经加载过的类可以使用retransform去进行重写。
IAST agent技术其实主要就是对编程语言的底层函数进行插桩hook,毕竟再怎么编码转换以及调用,最后肯定会去执行最底层的某个方法然后对系统进行调用。由此可以反推出其hook点。在第三小节,我们了解到数据落地组件是在网络层发送数据包的,我们就在Socket输入输出流上的读写方法进行字节码操作插桩hook拦截处理。
下图展示的是iast agent启动加载处理脏数据的流程,关键点是通过字节码操作工具在程序里使用SocketOutputStream输出流对象调用方法write之前,加入代码判断是否是安全重放的流量,进行拦截发送的脏数据,并重定向到特定提示页面。
重放拦截关键代码如下:
五、总结
字节码操作技术能解决很多其他场景的问题,如IntelliJ Idea 的代码调试Debug,应用热部署等。本文主要讲述的就是在Socket输入输出流的方法上,进行字节码修改插桩来防止脏数据落地。
对于在java中使用BIO流(Block Input/Output,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序)进行通信的组件(关系型数据库MySql DB,redis)来插装hook SocketOutputSteam的write写数据方法,隔离避免脏数据的产生,不影响服务的测试流程。这种字节码操作技术能拦截所有使用Socket流来发送接收的数据。