长亭百川云 - 文章详情

是谁在LINUX内核中开了这个大洞?

格蠹老雷

25

2024-08-08

最近一段时间,一个诡异的内核问题困扰着格蠹的内核开发团队。在测试幽兰的系统镜像时,有一个随机的内核oops。

一旦这个oops发生,那么便产生一系列连锁的不良反应:比如声音无法播放,reboot失败等等。

内存越界

7月30日那天,我一边准备关于Windows 719大蓝屏的讲义,一边看这个oops,并在直播中提到了这个诡异的oops。

这个oops与719大蓝屏类似,也是非法访问内存,也就是通常所说的越界。内存是软件的舞台,内存问题就是人类社会的住房问题,千头万绪,盘根错节。

有趣的是,越界访问的内存地址非常奇怪,不是一般的0指针,也不是明显的小指针(小于4096的地址),而是一个很长的地址:

003a72656c646e69

根据多年的经验,我一眼看出这个地址中包含很多可读的ASCII字符,使用windbg的.formats命令转换,果然如此:

0:000> .formats 203a72656c646e69
Evaluate expression:
  Hex:     203a7265`6c646e69
  Decimal: 2322294337798696553
  Octal:   0200723446255431067151
  Binary:  00100000 00111010 01110010 01100101 01101100 01100100 01101110 01101001
  Chars:    :reldni
  Time:    Wed Jan 23 00:02:59.869 8960 (UTC + 8:00)
  Float:   low 1.10463e+027 high 1.57927e-019
  Double:  1.9725e-153

也就是错误地址刚好对应的是" :reldni"这8个字符。把字节序调整一下,就是"indler: "。注意冒号后面还有一个空格(在栈上找到,有时体现在寄存器里)。

对于这个indler: ,如果加上一个h那么就很像软件世界里非常常用的一个单词:“handler: ”

这个oops是随机的,根据oop提供的函数地址,发生崩溃的内核函数名叫:sysfs_file_ops,源代码如下:

static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
  struct kobject *kobj = kn->parent->priv;


  if (kn->flags & KERNFS_LOCKDEP)
    lockdep_assert_held(kn);
  return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}

但是大家都知道,对于这样的内存溢出问题,这肯定不是第一现场。这只是一个受害者。

全城搜索

对于内存越界这样的问题,因为溢出时常常无声无息,不知过了多久,才有现象,所以很难定位。

为了找到产生溢出的元凶,小伙伴们想了很多办法,比如根据使用UEFI启动没有oops,而使用u-boot启动有oops的现象,找u-boot的问题,修改了几版u-boot,看似有所缓解,其实是假象,因为本来就是个随机的问题。换了u-boot后,没看到oops,但过了几天又发生了。

上法器

对于越界这样的内存顽症,直接的方法就是上内存检查工具,比如ASAN(Address Sanitizer)。

Linux内核具有ASAN支持,称为KASAN,但是默认不启用,因为会影响性能。

为了抓到越界元凶,我一开始就建议启用KASAN,但是小伙伴启用KASAN编译出内核后,启动失败,上挥码枪调试,发现卡死在u-boot阶段。

第一次尝试KASAN失败后,小伙伴又想了一些其它排查手段,包括增加内核消息打印等,但都没有找到真正的凶手。

前天晚上,我亲自测试新版内核镜像,再次遇到开篇说的oops,很是恼火。

再上法器

昨天,我再次强推KASAN方法,坚定方向,一定要把这个方向走通。

大约一个小时后,好消息接连而至。

第一个好消息是小伙伴通过修改u-boot,成功把启用了KASAN的内核运行起来了。

第二个好消息是KASAN成功找到了一个内存越界写。

KASAN报告

听到这个消息,我立刻停下手上的其它工作。让小伙伴发我内核消息,凝神静气,仔细阅读。

内核时间戳25秒时,KASAN报告初始化完毕。

[   25.146063] kasan: KernelAddressSanitizer initialized (generic)

33秒时,KASAN抓到越界写。

[   33.918201] ==================================================================
[   33.918234] BUG: KASAN: slab-out-of-bounds in __memcpy_fromio+0x8c/0x100
[   33.918259] Write of size 8 at addr ffffff8101838afc by task systemd/1


[   33.918283] CPU: 5 PID: 1 Comm: systemd Not tainted 6.1.43-rockchip-rk3588-taiyi #1.0.8
[   33.918300] Hardware name: YourLand CodeBook (DT)
[   33.918312] Call trace:
[   33.918324]  dump_backtrace+0xd0/0x130
[   33.918338]  show_stack+0x20/0x30
[   33.918350]  dump_stack_lvl+0xac/0xe0
[   33.918368]  print_report+0x164/0x464
[   33.918384]  kasan_report+0xc8/0x1a0
[   33.918400]  __asan_store8+0x80/0xa4

上面的调用栈省略了很多,因为涉及到敏感的安全问题(下文介绍)。

调用栈下面,有关于这次非法方法的详细细节。

[   33.919049] The buggy address belongs to the object at ffffff8101838000
                which belongs to the cache kmalloc-4k of size 4096
[   33.919061] The buggy address is located 2812 bytes inside of
                4096-byte region [ffffff8101838000, ffffff8101839000)


[   33.919082] The buggy address belongs to the physical page:
[   33.919094] page:00000000663e5886 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x101838
[   33.919109] head:00000000663e5886 order:3 compound_mapcount:0 compound_pincount:0
[   33.919123] flags: 0x8000000000010200(slab|head|zone=2)
[   33.919146] raw: 8000000000010200 0000000000000000 dead000000000122 ffffff8100002a80
[   33.919159] raw: 0000000000000000 0000000000040004 00000001ffffffff 0000000000000000
[   33.919172] page dumped because: kasan: bad access detected


[   33.919192] Memory state around the buggy address:
[   33.919205]  ffffff8101838a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   33.919218]  ffffff8101838a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   33.919234] >ffffff8101838b00: 01 fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   33.919246]                    ^
[   33.919255]  ffffff8101838b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   33.919270]  ffffff8101838c00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   33.919282] ==================================================================

安全问题

为什么上面故意省略了一些函数名呢?因为昨天夜里我仔细看了元凶代码后,觉得事情非常蹊跷。

为了方便描述,姑且把这个漏洞称为indler漏洞,根据就是最先发现它端倪的那个越界访问地址对应的是indler: 。

经过我们初步分析,indler漏洞的代码至少在2013年就已经存在,而且至今仍在内核主代码树。

这意味着,从2013年到今天的几乎所有Linux内核都有这个漏洞在。跨度长度十几年。

让我感觉不安的第二个特征是,这个漏洞可以在用户空间通过Linux的虚文件机制触发。这意味着黑客可以使用用户空间的某个应用做跳板,转而轻松攻击内核。

第三个关键特征是,这个漏洞可能导致的溢出可以非常大,可以长达数千字节。

查看这个漏洞代码的来源,它来自Google。这更让我觉得这个漏洞来头很大。

综合以上特征,黑客可以使用indler漏洞,实施多种攻击,包括向内核空间注入代码,实现远程代码执行(RCE)。至少,黑客可以通过这个漏洞,做DOS攻击,促发溢出,让内核崩溃,停止工作。

想到这个漏洞代码存在于从终端到云的数以亿计的计算机系统上,一旦被黑客利用,后果不堪设想。

因为此,我们故意隐藏了indler漏洞的具体函数名。正在与专业的安全团队合作,商讨下一步的计划。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2