上一篇(第一篇)介绍了很多关于Linux内核漏洞CVE-2017-11176一些背景知识,本篇再介绍一点基础知识,不然后面就完全看不懂了,之后再想办法触发利用它。
从上一篇了解到,这个漏洞属于use-after-free(以下简称UAF), 接下来首先看看什么是UAF,它是怎样产生的,有什么危害。这个概念虽然很基础,但如果不是彻底了解,后面也没办法玩了。
比如我们部门中有一个叫江疏影(长发飘飘,笑容甜美,写的代码凭颜值会直接进主线)的女程序员写了这样一段代码:
struct str_info {
int age;
void (*func)(void);
}__attribute__ ((packed));
static void walk(void)
{
// cut...
}
static struct str_info *actress;
static int __init my_init(void)
{
[0] actress = kmalloc(sizeof(struct str_info),GFP_KERNEL);
[1] actress->age = 0x18; (记住先)
[2] actress->func = walk;
(假设函数地址为:0xffff810057575757)
[3] actress->func();
[4] kfree(actress);
[5] actress->func();
return 0;
}
江疏影同学在[0]处申请了一块内存,内存大小,用口可以算出是 12字节,
在[1][2]进行初始化,在[3]处调用了walk(),在[4]处释放内存,在[5]处由于心情舒坦又调用了一次walk(),在[5]处调用函数就属于UAF.
下面详细分析此函数在内存中的情况:
现在的Linux版本,为了解决页内碎片的问题,通常采用slub分配器,这个概念就不详细描述了。
比如江疏影同学在[0]处调用了kmalloc,想要申请12个字节,slub分配器就从kmalloc-16(object都为16个字节)的kmem_cache中给她分配一个object,返回给她这个object的首地址。
slub分配器大致是这样管理内存的,首先对于kmalloc-16这样的kmem_cache,内核会分配2的N次方个连续的page, 然后第一个page的freelist指针指向第一个object的地址, linux内核中有个struct page结构:
struct page {
...
void *freelist; /* slub/slob first free object */
...
};
page->freelist指向第一个object的首地址,比如我在内核中抓出kmalloc-16 kmem_cache连续5个free object的内存信息:
[a]20 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
[b]30 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
[c]40 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
[d]50 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
[e]60 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
page->freelist = 0xFFFF81001FFFD010(为[a]object在内存中的首地址),而[a]object的内存内容的前八个字节为[b]object的首地址,[b]object内存内容的前8个字节为[c]object的首地址,依次类推....
比如江疏影申请了12个字节之后,那个对应kmalloc-16 这个kmem-cache对应的第一个page的firstlist指针将等于[b]object的首地址 0xFFFF81001FFFD020,之前是0xFFFF81001FFFD010
代码中[0]中的actress会等于0xFFFF81001FFFD010(指向[a]object),那么执行完[1][2]之后,现在[a]object中的内容将由:
[a]20 D0 FF 1F 00 81 FF FF
00 00 00 00 00 00 00 00
变为:
[a]18 00 00 00 57 57 57 57
00 81 FF FF 00 00 00 00
0x18为江疏影的age变量
0xffff810057575757为walk的函数地址
page->firstlist = 0xFFFF81001FFFD020 ([b]object的首地址,从[a]object的前八个字节中获得) 。
当执行代码[4]调用kfree(actress) 之后,page中的firstlist指针和[a]object在内存中的情况是这样的:
[a]18 00 00 00 57 57 57 57
00 81 FF FF 00 00 00 00
变成:
[a]20 D0 FF 1F 00 81 FF FF
00 81 FF FF 00 00 00 00
其中 20 D0 FF 1F 00 81 FF FF
从上一次的page->firstlist中获得。
page->firstlist = 0xFFFF81001FFFD010
(从kfree(actress)中的actress地址中获得)
所以现在再看下江疏影的代码[4]处kfree之后,再执行[5]actress->func()
现在执行的函数首地址就变成了 0xFFFF8100FFFF8100,至于0xFFFF8100FFFF8100
处是什么代码,只有天知道了:) ,所以这样UAF之后是非常危险的,一不小心系统就发生oops或者panic. 还有就是在调用kfree(actress)之后如果系统中的其他函数申请走了刚刚释放的内存填充了其他数据,那actress->func()的函数地址就变成其他的了,所以这个时候就只能靠祷告,祈求上天,actress->func()执行的代码都是nop,或者是一个人畜无害的函数。