5

KARL, ASLR, KASLR

 2 years ago
source link: https://yanhang.me/post/2019-08-27-karl-aslr/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

KARL, ASLR, KASLR

2019-08-27 约 1654 字 预计阅读 4 分钟

前段时间在零星地看一些关于 unikerel 的文章。里面提到的一些安全相关的技术非常有趣,就又开始转向 kernel 安全这部分。本文主要探讨的是 KARL, KASLR 以及 ASLR 相关的技术。

安全相关的实现技术很多,但想了想,大体的分类可以归为以下几类:

  • RWX 权限检查
  • 随机化: 尽量不用确定性的内存地址
  • 暴露最少的信息: 日志,输出里尽量不暴露相关信息

KARL, KASLR 以及 ASLR 都属于第二类。尽量让应用程序以及内核在内存中的地址随机化,这样攻击者就难以猜出相关的地址。

简单来讲, ASLR(Address-space layout randomization) 主要是指内存中用户层的地址随机化,KASLR 是指内存中内核的地址随机化, 而 KARL(kernel adress randomized link)是指内核本身的地址随机化。ASLR出现的时间更早一些,更成熟一些。

注意有一个区别, 三者中都有一个 L,但意义不一样。 ASLR/KASLR 中的 L 是指 layout,是说内存中的布局。你不可能要求大量的应用程序自己去考虑随机化,而是在应用程序无感知的情况下,由内核来选择一个随机的位置来运行应用程序。而 KARL 中的 L 是指 link ,是在链接阶段就做好了地址的随机化处理,这样就不需要再内存中在做了。

ASLR 理解起来相对更为简单,本身不需要再重复介绍。

不管是 KARL / ASLR / KASLR,其核心都是一个随机的概念。明显它依赖于随机数的生成算法。当然随机程度越高越安全,但是又要考内核的性能。这个平衡很难掌握,选择哪个 RNG(random number generation) 也经过了很多讨论。

一开始用的是 get_random_int(),一个性能比较好但安全性不够好的方法,后来有人提出用 get_random_bytes(),安全性大大提高,但是性能下降也比较多。再后来 Linux 自己对get_random_int()做了一些改进, 增加了一些噪音进去,在不影响性能的情况下改进了随机性。这也是目前在用的方法。

但是,这仍然是一个不够完美的选择。get_random_int内部用了half_md4_transform,一个更弱化版本的MD4算法,现在 MD4/MD5 的碰撞都很容易了。后来有人换成 SHA1做 benchmark,性能下降 50%左右,对内核的人来说也是基本上不可接受。所以目前只能是这样了。再说,现在SHA1的碰撞也已经被发现了。

在 KARL 之前, kernel 里面的 boostrap代码,汇编语言 runtime 代码(locore.S),以及其他 c 代码之间的位置都是固定的。一般都是 locore.S 在前,然后是各种 c 文件。引入 KARL 之后,locore 被分成两部分,一部分是最开始的 boostrap, 然后是汇编语言 runtime 以及 其他的 c 文件,不过后者的放置是随机的了。这样每一个新的内核都是独一无二的,代码以及数据之间的偏移量也是不固定的。当然 boostrap 的代码还是固定的。但是内核可以在启动之后清理掉相关信息。

这样就有个问题,虽然内核以及做了 KARL,但你不能编译好一次之后重复去用,不然还是不安全。最好的方式是在每次启动时重新 link,相当于生成一个新的内核。通过各种优化手段,这个过程可以做到耗时很短。

KASLR

KASLR 与 ASLR 类似,但是是针对于 kernel 的。与 ASLR 不同,KASLR 有几个问题

  • 如果用户在暴力破解找 kernel 的位置,会直接导致机器 crash

  • 与电脑的休眠机制不兼容。

  • 有很多其他的模块需要或者依赖于知道 kernel 的位置,这些都需要谨慎处理。

第三条设计的面比较广, 一个例子是 SIDT 指令,它不是特权指令,并且能够拿到IDT(interrupt descriptro table)的地址,进而能够推断出内核的地址。所以需要解除 IDT 的位置与内核位置的关联,将其放置其他地方,并且设置为只读。

另外一个例子是 INET_DIAG socket API, 它用了内核地址作为 handler。 虽然对上层应用透明,但是仍然是个安全隐患。可以通过数据混淆解决这个问题。

当然,除了这些已知的状况,还应该通过各种方式来限制其他模块使用内核地址,并且尽量不泄露给用户,比如:

  • 开启kptr_restrict 系统调用防止内核地址泄露到用户空间
  • 使用dmesg_restrict 防止 dmesg 泄露内核地址
  • /var/log/messages 应该设置为只有 root 用户能访问.

对于一般的用户来说,比如个人电脑, KASLR 的意义不是很大。但是对于现在的容器化环境(包括container 以及浏览器的 sandbox )来说,有很多安全场景需要考虑(从容器侵入宿主机内核) , KASLR就更有价值了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK