项目的主要目的是帮助企业保护自己较为敏感的ELF文件。

KAP 意在对 Linux ELF 在构建之后到上线运行的过程中对应用进行保护,可以通过加密的方式来保护 ELF 泄漏后无法被执行,还可以保护 ELF 在运行过程中不会被调试,也能监控被保护的 ELF 被篡改。且解密校验后运行是采用无文件的方式来运行,保证了解密后的 ELF 不落盘,且可以自定义运行前基线检查。相比于 Linux 默认方式,KAP 对完整性和保密性上进行了大幅度提升,并且还有一个未完全完成的Linux驱动来从内核态来保护 ELF。

1

目标

我 https://github.com/AlkenePan 和 Will https://github.com/EBWi11 参加了2019 GoHack,编写的项目KAP https://github.com/AlkenePan/KAP 很荣幸获得了第一名,这里和大家聊聊关于 KAP 和这次比赛。

640_wx_fmt_png

由于时间紧任务重,一开始又没有太多沟通。我们后来选了一个相对实现难度简单的小项目。项目的主要目的是帮助企业保护自己较为敏感的ELF文件。我们的目标很明确,要在不信任的 Linux 服务器上完成对可执行程序的保护,我们觉得要能对抗以下风险:

  1. ⽣产环境可执⾏行行⽂文件窃取

  2. ⾮可靠⽣生产环境

  3. 可执⾏⽂件被调试

  4. 过⾼的执⾏权限

  5. 可执⾏程序被篡改

2设计

我们首先简要回顾程序的一生: Code -> Compile -> Exec(Load) 。当然,现代化的程序员早已全副武装,每个步骤都有自动或是半自动的工具辅助完成:代码编辑器 -> CI/CD -> 进程管理器。我们盯准在构建之后的产出物到 Linux 加载可执行程序的环节,进行以下设计:

  1. 要对构建出的 ELF 进行粉碎式加密并重新打包,不可执行,手工不可恢复。
  2. 要自己完成 Linux 装载动作,解密后的可执行程序不可落盘。
  3. 通过 APPID 分发公私钥,研发人员在构建后不能手工执行程序。
  4. 生产服务器必须与 Server 通信才能完成装载,不正确的执行装载会触发告警,通信还会做一些校验如特定的基线检查。

640_wx_fmt_png 1

3实现

为了在规定时间内完成比赛,我们选择使用 Irsi 作为 API 的框架,Gorm 读写 SQLie,使用 Go 提供的 RSA 库完成加解密,当然如果需要无论是 API 的性能还是存储方案都能够更好的去修改或是替换。加密可执行文件的部分本想对 ELF 文件头进行加密,但是这样依旧会被手动补充,我们选择了更粗暴的方案:对整个 ELF 文件的区间块进行加密,尽可能的不引起产出物过于膨胀。APPID 和 源文件 Hash 会被添加到头部。并且 Will 给加密后的文件起了 Magic Number: `ELF -> 31F`。正常的ELF:640_wx_fmt_png 2粉碎加密:640_wx_fmt_png 3最后对比一下:640_wx_fmt_png 4
加载部分,为了实现可执行程序不落盘,我们在解密可执行文件之后,会创建内存文件:memfd,我们执行使用 `Syscall(MFD_CREATE, …)`,并将解密后的文件写入。一切准备就绪,使用 `syscall.StartProcess` 启动进程。640_wx_fmt_png 5整个过程不会对程序的性能或是之后对进程的操作造成影响,同时无论是内鬼还是黑客拷贝这个加密后的程序或是打包整个执行环境都无法启动。为了达到其他的安全特性,我们增加了执行程序的权限控制(也可以在注册APPID时填写,默认nobody)。640_wx_fmt_png 6Will 在这里使用了特别的 Ptrace 技巧防止执行中的程序被调试:通过提前进行不影响进程的Ptrace来保护进程不会被其他进程进行Ptrace。640_wx_fmt_png 7此外我们增加了额外的 DNS 校验,尽可能的确定在我们期望的环境中执行。并且还增加了通过fsnotify来监控我们的加密后的二进制文件是否被write/remove。640_wx_fmt_png 8然后还抓紧时间用很久都不用的bootstrap画了个前端,用来查看我们的一些基本信息:APP INFO:640_wx_fmt_png 9ALERT INFO:640_wx_fmt_png 10这里我们可以看到我们保护的ELF是否正常启动,是否有异常操作等等。

4进阶

看似一切都符合我们的期待,此时比赛还剩下大约12小时时间,功能已经都实现,然而这时 Will 发现 memfd 依旧可能会被读取,我们的一番操作可能只是提高了攻击者的门槛。为了保护在内存中的可执行程序,Will 决定增加一个 Agent 保护 /proc/ 文件树,并自制内核模块利用 fsnotify 确保可执行程序在装载后不会被探测或是破坏。主要思路是通过一个内核驱动来Hook fsnotify,如果发现有进程读去我们需要保护的目录,则从Kernel来kill掉他。640_wx_fmt_png 11这里坑还是很多的,小BUG不断,虽然勉强达到了我们预期的功能,但是很遗憾的是最后路演阶段出了BUG没有演示成功。这里是效果图:640_wx_fmt_png 12经过一宿奋战后,具备更强保护能力的 KAF 诞生了,我们顺利完成了比赛的其他要求,包括写一个说明 slide 和准备演示环境,并在路演之后,意外的获得了一等奖,抱着一等奖牌的 Will 暂时忘记了困倦。比赛结束,我们结束了我们第一次 hackathon。KAF 的项目地址:KAF:  https://github.com/AlkenePan/KAP ,目前 KAF 还处在原型阶段,欢迎与我们一起完善这个项目,也欢迎其他项目借鉴我们。最后非常感谢Go中国社区举办的这次Hackathon!看到了许许多多充满想法的Gopher的精彩的项目,我们收获良多。

也许你还会喜欢:

重磅活动预告Gopher Meetup 北京站即将开启。来自探探、美团、阿里巴巴、蚂蚁金服的大咖讲师讲带来 Go 开发领域的一线实践经验分享,尽在11月30日,IFC国际财源中心!报名请戳:阅读原文

640_wx_fmt_jpeg

Go中国

国内最具规模和生命力的 Go 开发者社区

欢迎投稿,请联系:

situzhihui@163.com