Hang

KARL, ASLR, KASLR

2019-08-27

前段时间在零星地看一些关于 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#

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

RNG#

不管是 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#

在 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就更有价值了。

链接#