前言:
最近分析了 CVE-2017-10661 这个漏洞,不过没有找到什么可以利用的点。把分析过程拿出来分享下。
首先看看官方的修补代码:
上面是官方的修补代码, 可以很直观的看到,修补代码主要做了几件事
首先在结构体里面添加了一个锁的成员变量,
然后在代码中上了锁
从这两个结论,首先就可以基本推论出来,这个漏洞应该是多线程并行,导致出来的问题。
接着继续分析修补代码,我们发现上锁的代码主要在 一下代码
这样看不太方便,感觉有点混乱,我把这个函数从源码中找出来了
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
{
+ spin_lock(&ctx->cancel_lock);
if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) &&
(flags & TFD_TIMER_CANCEL_ON_SET)) {
if (!ctx->might_cancel) {
ctx->might_cancel = true;
spin_lock(&cancel_lock);
list_add_rcu(&ctx->clist, &cancel_list);
spin_unlock(&cancel_lock);
}
} else if (ctx->might_cancel) {
timerfd_remove_cancel(ctx);
}
+ spin_unlock(&ctx->cancel_lock);
}
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
{
if (ctx->might_cancel) {
ctx->might_cancel = false;
spin_lock(&cancel_lock);
list_del_rcu(&ctx->clist);
spin_unlock(&cancel_lock);
}
}
结合两段代码,我们可以看到 主要是给中间的这一段代码上了锁。
那么主要问题就是出现在这一段代码当中了。
这两条分支
上面是链表添加结点
下面是移除
两条路线分支主要是 ctx->might_cancel 这个成员变量判断的
然后这个变量,我们发现,他是会在分支的运行过程里面修改的 蓝色的标记代码就是修改的地点
那么我们根据上面得到的信息,可以得到一个假设 如果两条线程同时运行这一段代码
线程1 删除了结点 并且 ctx->might_cancel = false;
那么他下次再运行的到这里的时候
} else if (ctx->might_cancel) { //ctx->might_cancel == false
timerfd_remove_cancel(ctx);
}
这个条件就不会成立,那么就不会运行 timerfd_remove_cancel 函数
以上是正常情况,现在我们假设一中特殊情况
线程1 第二次执行到 else if (ctx->might_cancel) 这里的时候,CPU进行切换,切换到线程2执行
线程2 走上面分支
if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) &&
(flags & TFD_TIMER_CANCEL_ON_SET)) {
if (!ctx->might_cancel) {
ctx->might_cancel = true;
设置了 ctx->might_cancel = true; 然后CPU继续执行线程1
现在 ctx->might_cancel 被线程2修改成了 true 又会执行 timerfd_remove_cancel
} else if (ctx->might_cancel) {
timerfd_remove_cancel(ctx);
}
那么现在 timerfd_remove_cancel(ctx); 删除了两次结点 这就是漏洞的产生原理。
利用:
如果猜测是对的,那么我们创建两条线程, 一条循环执行删除操作,一个执行添加操作,就会触发到漏洞。
测试过后,成功触发了漏洞。
注:看了下代码本身流程,这里删除结点,只是想 节点的 上下两个点的指向修改了下。
崩溃的主要原因是因为,entry->prev = LIST_POISON2; 这里执行到第二次的时候 尝试在LIST_POISON2 写入内容,导致了崩溃。
测试机器:
nexus5 5.0 hammerhead-lrx21o-factory-56a09d43 版本
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)