独自快乐|怎样Hack Linux的内核符号?
Linux内核是不是坚不可摧?答案是NO!尽管内核中存在诸多限制 , 但你只需要稍微花点心思 , 也可以想办法突破它们 。 下面我们将通过一个例子来展示这趟有趣的旅程 。
首先简单介绍一下项目的背景 。 客户提供了一批嵌入式智能设备给我们 , 希望能够检测并且修复其中的安全漏洞 。 我们能够从设备中接触到二进制形式的固件 , 但却接触不到固件的源码 。 对于二进制固件的漏扫和加固是一个行业难题 。 此外为了减少人工成本 , 客户还希望我们提供一个自动化的漏扫和加固解决方案 , 这无疑成为了一件不可能完成的任务 。
所谓固件 , 其实就是一个嵌入式操作系统 , 常见的有定制化的Linux和安卓系统 。 本质上它们都具有相似的结构:Bootloader、Kernel、根文件系统等 。 根文件系统中又包含了众多用户态程序、脚本、配置等 。 对于Kernel的CVE漏洞自动化扫描和修复是我们当前工作的主要内容 。 而自动化漏扫技术又可单独成文 , 本文将主要介绍自动化漏洞修复所用到的内核符号Hack技术 。
所谓内核漏洞 , 其实就是Linux内核中存在的缺陷函数 。 所谓漏洞利用 , 就是在用户态通过一系列精巧的传参和调用 , 最终触发内核缺陷的过程 。 这里存在两种修复方式:
1)在触发缺陷的必由调用路径上设卡 , 做参数或调用关系过滤 。 比如c函数是缺陷函数 , 该漏洞触发的调用关系是Func a-> Func b-> Func c , 那么可以在a或b函数上做传参检查 , 一旦参数非法则立刻退出 。 这种方式的优点是修复过程简单 , 尤其当c函数调用非常深的时候 , 可以在表层易于打桩的函数中做传参检查;缺点是需要开发者深入理解漏洞的利用原理 , 同时不同漏洞的利用方式各不相同 , 修复方式也各异 。
2)用与c函数功能相同 , 并且已经打好补丁的c‘函数替换掉c函数 。 修补时只需要保证每次对c函数的调用都会无条件进入到c’即可 。 这种方式的优点是修复方法统一 , 便于自动化 , 可不必深究不同漏洞的利用原理 。
图示展示了方案二技术架构图:中间是Hook框架 , 提供缺陷函数拦截、函数跳板(Trampoline)、修复函数注册、内核代码区修改等基础功能;右侧是包含具体CVE漏洞修复业务的模块 。这里有很多核心问题需要解决 , 其中之一是修复函数使用未导出内核符号问题 。
我们都知道Linux是宏内核架构(Monolithic Kernel) 。 为了实现内核功能的动态扩展 , Linux又引入了内核模块 。 内核模块将不可避免的使用内核函数 。 正常情况下 , Linux内核代码会将一些基础功能性函数导出 。 如控制台输出函数printk等 。 所有被导出的函数都会通过export_symbols族的宏修饰 。 最后这些符号会被内核编译到特殊的段中 。 而针对我们漏洞修复的场景 , 内核缺陷函数可能存在于内核的任何地方 , 因此如果仅仅使用内核导出的少量符号 , 很多缺陷函数或其依赖函数将无法被解析到 。
于是我们把目光放到了内核的Kallsyms功能上 。 这个功能是内核为了方便调试而引入的 。 当内核发生错误时会输出一系列Stacktrace , 后者其实是一系列函数地址 。 有了Kallsyms , 在输出Stacktrace的时候内核可以把地址解析成函数名输出 , 告诉开发人员错误发生在哪个函数的哪个位置:
由于内核错误可能发生在任何地方 , 因此Kallsyms单独保存了一份函数符号和函数地址的对应关系 , 其中的符号数量远远多于export_symbols宏导出的符号量 。 即使内核开启了地址随机化(Address Space Layout Randomization)功能 , Kallsyms也能在运行时解析到符号正确地址 。 如果在内核模块中想使用未导出的符号 , 可以使用Kallsyms提供的kallsyms_lookup_name函数将符号名解析到函数地址 , 再以函数指针的形式调用即可 , 如:
- 董明珠|当年“骗”董明珠26个亿的魏银仓,带着钱逃往美国后,结果怎样了?
- 郑强|当初那个认为科学有国界,要取消英语高考的郑强,现再怎样了
- 虚拟偶像密集进入大众视线 折射出怎样的文化心态?
- 半月谈|虚拟偶像密集进入大众视线 折射出怎样的文化心态?
- 直播吧|他能给拜仁带来很多快乐,京多安:我会想念萨内
- 快乐的郁适戚|上港4-0取得2连胜:阿瑙托维奇双响,洛佩斯2球,上港取得2连胜
- 细雨如绽|瑞典是个怎样的国家
- 历史先秦颂|他经历了怎样的过程,从万人敬仰的王子一下子变成落魄的阶下囚
- 明星八卦|不顾父母反对,被“背叛”也要嫁给黄海波的她,现在怎样了?
- 结婚|1米“小矮人”和1.7米女子一见钟情,只领证不办婚礼,后来怎样?