背景
让我们再次回顾下安全从业人员为了努力做好软件安全,在运营阶段做了什么事情。
我们从上层推动网络安全意识教育、商业行为准则要求以及人力资源政策与流程,通过培训和宣导持续提升全员网络安全意识及网络安全能力。
我们建立软件安全需求定义、确保需求在软件安全设计阶段得到风险评估,通过安全功能满足特殊安全保护需求。开发人员将其文档化去实现。
我们用seasponge、owasp-threat-dragon、PTA画了不少DFD图来做架构设计安全分析。研发人员去迭代,修改模型。
我们使用代码缺陷率、漏洞数、修复率去关注安全开发的数据,例会季度去评比。
我们用静态分析+review评估软件交付件,排查是否满足安全编码规范。开发人员修复安全特性方面的缺陷。
我们不断用DAST发现安全漏洞,以更高的覆盖率、更低的误报率、漏报率而自豪,运营人员反馈得到工单去及时迭代修复。
而现在又兴起了供应链安全的热点。
-
XcodeGhost事件后要求开发人员使用正版开发软件,保证“砍柴刀”的安全性。
-
xshell、CCleaner事件提醒需确保桌面办公环境的安全性,升级到最新的版本,并且保持良好习惯使用一些防病毒的产品。
-
event-stream包事件要求负责人开发者去检查项目所引用的组件依赖。使用safecode、cobot、blackduck更去检查各种开源组件的license和后门状况。
-
“Reflections on Trusting Trust ”甚至要求去检查编译器!
Google的实践是联合业界使用Grafeas API管理联通软件供应链安全项目。
我在早期接触安全时曾经获取曾经对某cms的官方release包修改了jar包里的DispatcherServlet的class文件加入特殊指令,现在想来也是为了供应链“不安全”贡献了力量,当时思路还是太局限了。基于重点代码的会对性能有巨大影响,此外预期产生的安全威胁主要是Tampering篡改和Elevation of Privileges权限提升,意图还没有深入到Information Disclosure。在真实复杂的攻击场景中,需要持续“打游击”,供应链攻击更需要具备“Spoofing”伪装,去隐藏、快速扩散,必要时Denial of Services拒绝服务和具备Repudiation抵赖去主动清理痕迹、消除残留的意识。
那么站在防御的角度,我们如何去做这样的检测?如何保证软件供应链生产端和消费端的一致性?如何识别繁杂的二进制和开源组件?如何及时跟进迭代开源片段的引入?值得思考。
笔者曾经参与过英国安全认证中心(UK CSEC)的评估工作。目前见到的比较好的实践是:通过IPD全流程设置严格的版本管理,对第三方软件包进行结构化标识来做追溯管理和缺陷管理,将安全融入产品开发流程中,各人的手头工作都重视安全,由pmo组织进行业务赋能。使用第三方软件、开源组件时必须通过法务、技术评审、使用公司实体库的组件并提供技服。验证时提供编译镜像保证代码和二进制附件清单和最终发布产品完全一致,且均符合软件安全基线、无内外部公开漏洞。此外要求供应商遵循一致的安全机制,采购时即重视签署安全协议、引导供应商及时提供补丁和方案。
但是关于供应链安全的讨论,除了各种外部报道的手段替换组件被执行恶意操作之外,笔者还想到我们是否遗漏了一类关键点--对持续集成人员的要求、对代码检视人员的要求?对编译阶段的检查?如何确保编译指令的安全性?攻击者如果是针对编译时,而不是编译后进行突破?引申出来的实际问题是如何保证CI|CD系统的内部安全性。这里通过写文章来演示如何对这些系统进行攻击,抛砖引玉同大家探讨解决方案。
介绍
这里主要探讨编译阶段,使用cve、弱口令口令直接攻击jenkins、GitLab CI、Spinnaker等不再讨论范围之内。
编译阶段的需要关注的事情首推JDWP漏洞,在 JPDA 体系中,作为前端(front-end)的调试者(debugger)进程和后端(back-end)的被调试程序(debuggee)进程之间的交互数据的格式就是由 JDWP 来描述的,它详细完整地定义了请求命令、回应数据和错误代码,保证了前端和后端的JVMTI 和 JDI 的通信通畅。通过建立这个隧道,恶意调试脚本直连后门进行通信。
编译时,可以指定命令。
“-Xdebug -Xrunjdwp:transport=dt_socket,address=port,server=y,suspend=y”
当然也可以细看适配官方文档:
https://docs.oracle.com/javase/6/docs/technotes/guides/jpda/conninv.html#Transports
使用客户端模式:
“-Xdebug -Xrunjdwp:transport=dt_socket,address=ipv4\ipv6:port,server=n,suspend=y“以TCP协议连接指定的恶意JDPA服务器。
利用工具参考https://github.com/IOActive/jdwp-shellifier。
maven
maven主要服务于基于Java平台的项目构建,依赖管理和项目信息管理。在Maven的特性中子项目是可以继承父项目中的依赖的,比如说有一个父项目maven-parent,该父项目拥有一个子项目A,如果在父项目中依赖了junit,那么在子项目A中即便是没有引入junit,在子项目中仍然能够使用junit,因为子项目天然继承了父项目中的junit依赖。在pom.xml文件方面是可以很方便的隐藏恶意脚本、第三方组件的。
使用maven的平台首先想到的攻击点肯定是测试用例,在执行mvn test命令时,会运行测试用例的源码代码。
另外Maven定义了三套生命周期:clean、default、site,每个生命周期都包含了一些阶段(phase),其中最关键的default阶段的核心事件是compile,原理是org.codehaus.plexus.compiler.javac.JavaxToolsCompiler.compileInProcess调用了javac,javac是java语言的编译器,位于jdk的bin目录,可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。核心入口位置是/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/tools.jar!/com/sun/tools/javac/Main.class。javac参数详细解释请参考:
https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html
所以针对javac命令的直接编译,可以使用@<文件名> 从文件读取选项和文件名,@可以跟随绝对路径,从本地文件获取参数的配置信息。这里的攻击点是可以判断本地文件是否存在,当然只能根据回显错误获取空格前后的一行,而且受限于文件权限设置。但是作为探针检测机器安装的软件、版本足够了。
另一种方式是作为资源耗尽的方式读取大文件影响系统可用性。
我们还注意到其支持
-proc:{none,only} 控制是否执行注释处理和/或编译。
-processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
-processorpath <路径> 指定查找注释处理程序的位置
简单说就是在编译注解类型的源代码时,指定自定义的class文件。以演示项目https://github.com/nanolikeyou/attackjavac为例,
我们在samples项目指定一个注解,希望在编译阶段,由processor module下的程序进行处理。AttackProcessor继承于AbstractProcessor,重写process。
填入我们的攻击向量,执行mvn clean compile -e -X查看一次编译过程。
Congratulations!
从console上我们可以看到,javac的命令行参数添加了-processorpath ,执行了我们的自定义的代码获取到环境变量的敏感信息。也就是说,实现了不运行代码,编译时触发攻击。
注意在我们的目标是攻击代码的编译过程,目标会是持续集成平台或者是代码扫描工具(当然持续集成一般直接提供了run shell的功能.....概念验证的代码里执行测试用例会反弹shell,普通编译会打印敏感信息)。
云编译平台的安全性各个不同,一般会为了效率和隔离在docker里运行,也会受到CVE-2019-5736runc这样的漏洞影响。笔者测试发现目前商业ci平台、运行的用户权限不同,对外网络的连通性也不同,并不符合最小权限原则,这方面需要纳入企业的安全视野。
travis-ci
编译阶段反弹shell,用户是travis。
阿里云CodePipeline,jenkins用户不能反弹shell。
华为云cloudbuild,root不支持反弹shell。
tencent hub
支持以root权限反弹shell。
此外在软件部署阶段有这个案例,值得一看:https://blog.travis-ci.com/2018-08-29-addressing-reported-mitm-rce
https://dev.acquia.com/blog/a-travis-cigithub-security-vulnerability/14/11/2017/18871。
Javadoc:
持续平台有可能提供应用的javadoc自动化生成帮助文档。通过修改plugin repository的url节点可以实现替换maven-javadoc-plugin的功能。
此外对于javadoc,oracle官方文档指出了引入JavaScript的不安全性。
恶意代码可以通过如下的配置注入xss。
通过mvn javadoc:javadoc生成的html就有xss风险。
此外还有CVE-2013-1571,参见https://www.us-cert.gov/ncas/alerts/TA13-169A。修复方案是添加applyJavadocSecurityFix标签。
Javadoc的另外一个安全风险是maven-javadoc-plugin支持自定义doclet,也就是说在直接解析注释、注解时,容许引用第三方的jar包来执行mojo方法。
写法参考https://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/doclet/overview.html,如示例代码所示,
生成注解文档时也会生成main.xml,内容包含执行这个start函数里的代码结果(其实就是命令执行)。
如果在pom.xml里的build进行配置,那么执行mvn javadoc:javadoc触发,如果在reporting节点配置,执行mvn site命令同样触发。
仓库安全
Maven的一个项目级功能是pom.xml里的repositories命令支持指定仓库,攻击者可以替换jar包实现破坏完整性产生篡改威胁。自带的中央仓库使用的Id为central。如果其他的仓库声明也是用该Id 就会覆盖中央仓库的配置。这样在执行mvn compile命令时,会使用偷梁换柱的插件版本,里面存在恶意代码。触发安全风险。
架设自己的托管服务器,替换了maven-javadoc-plugin,版本是3.11.2,修改恶意插件。
pom文件配置了本地仓库不存在的高版本,则会去我们搭建的nexus服务器下载jar包。
上述漏洞已经反馈相关厂商,并得到修复。
仓库里jar文件是个大问题。使用Dns污染settings.xml也是一个好办法,
cdn缓存攻击也是一个思路,关键就是后期如何进行维护、恶意组件里的脚本如何五花八门去实现了。
Gradle、ant:
对于上述的javadoc,gradle可以使用
options.addBooleanOption("-allow-script-in-comments",true)
ant使用<jvmargvalue="${jvm.arg}"/>进行配置。当然gradle、ant本身也支持shell命令编写。
第一节完结,欲知后事如何,且听下回分解。
网友评论