我下面所讲的沙盒是指在Windows上构造一个和外界绝缘的空间来禁止用户行为的一种技术手段。在沙盒中的进程不能通过标准的Windows APIs读写文件,也不能通过socket等通信方式于其他进程进行信息交流,导致沙盒外面的信息进入不了,沙盒内的信息出不去。
我既不从事软件安全,也不从事什么黑客技术的研究,所以我在很长一段时间里不知道何为Sandbox,更别提如何跳出它。直到两三周之前,我想从Chrome中fprintf一些信息,却发现没有任何结果,所以就我开始踏上跳出沙盒之旅。
跳出沙盒阶段1 —— 阅读Chrome source code
我在github上找到了Chrome的source code,看到它对Nt读写函数进行了Hook,除此之外没有发现其他限制行为了,我暗想Chrome的安全做的太差了吧(但后来证实我想错了,难道Chrome有未开源的代码?)。我没有细读Chrome Hook的代码,我想通过测试代码更快的知道它的工作原理。
跳出沙盒阶段2 —— 试验
1)如果Chrome的Hook只是修改了输入地址表,那么这种技术有一个无法克服的缺陷。因为该Hook只是替换了函数地址,而Hook程序段是在全局变量构造的时候被执行的,这在程序的生命周期看来已经是后面的事情了,我们可以在全局变量被执行之前植入DLL来记住被Hook的原始地址,也就是记住读写文件的NT函数的地址,下次用这些地址进行读写。但是我的测试代码并没有通过,Chrome显然没有使用这个技术。
2)如果它是修改了函数入口的指令,那么我可以载入一个新的ntdll_new.dll,利用这个DLL直接跳转到Kernel,这样就可以逃离Chrome的魔爪了。通过观察测试代码的call stack,发现这种方法真的能逃过Chrome的沙盒直接进入Kernel。这时候Chrome进程空间里有两个DLL(ntdll.dll和ntdll_new.dll),测试用例可以通过ntdll_new.dll进入Kernel。但是一旦在Chrome上进程这个实验,虽然ntdll_new.dll的NT读写函数可以进入kernel,并且和chrome进入kernel的参数一模一样,但是不知道为什么被Windows判定为没有权限,Windows是如何做到的呢?这种方法看起来成功了一半,至少它能进入Kernel,但是很遗憾被Windows拦截了。
跳出沙盒阶段3 —— 出其不意
Chrome的能力再强也仅仅能够在用户空间横行霸道,一旦到了系统空间,大家谁也管不着谁,如果有合法的函数能够进入系统空间就能很容易打破沙盒。但是这种方案的难度在于要写一个伪装的系统模块让Windows载入。虽然Windows不会加载没有数字签名的系统模块,但是Windows也没有彻底堵上这条路,我们未签名的模块可以通过特殊的启动项被Windows载入。接着我们要做的就是寻找从用户空间向系统空间的搬运工了。这样的搬运工候选APIs很多,只要有能力将一段私有数据由用户空间传递给系统空间的API都能胜任。我选择的是gdi32::escape, 这个是图形库常用的函数,chrome没有对它进行限制,也很难对它进行Hook,否则chrome的渲染将无法进行。
跳出沙盒阶段4 —— 结果
我们要用到的模块有两个,一个是用户空间的DLL,另一个是系统空间的SYS文件。首先,在用户空间我们运行一个Server,让它在系统空间注册一个windows event。其次我们向Chrome注入该DLL,让它也在系统空间注册一个event,并且关联一个回调函数。运行过程为:Server发送一个消息给系统,系统通过事件回调函数告诉Chrome有消息到来,Chrome再去系统拿消息,通过Server在系统注册的event通知Server命令已经被拿到。
补充
在这期间,我也研究过Chrome的插件,它的确有能力进行文件读写等操作,但是插件是一个独立的进程,不是Chrome的核心进程,所以影响不到沙盒,换句话说沙盒进程和插件进程无法通信。此文涉及的所有研究仅仅是解决一个困扰我的问题,出发点和目的地都很单纯,不会对Chrome的安全造成影响。
【逃出Chrome沙盒——反Hook技术】
推荐阅读
- 个人日记|K8s中Pod生命周期和重启策略
- 学习分享|【C语言函数基础】
- C++|C++浇水装置问题
- 数据结构|C++技巧(用class类实现链表)
- C++|从零开始学C++之基本知识
- 步履拾级杂记|VS2019的各种使用问题及解决方法
- leetcode题解|leetcode#106. 从中序与后序遍历序列构造二叉树
- 动态规划|暴力递归经典问题
- 麦克算法|4指针与队列
- 遇见蓝桥遇见你|小唐开始刷蓝桥(一)2020年第十一届C/C++ B组第二场蓝桥杯省赛真题