不多说了,都是理由,还是不够自律。2023.10.24
漏洞编号 | 相关漏洞 | 漏洞描述 | 漏洞影响 | CVSS3.1基础得分 | 受影响的版本 | 受影响的仓库 | 修复链接 | 参考链接 |
OpenHarmony-SA-2023-0203 | CVE-2023-22436 | 内核子系统中check_permission_for_set_tokenid函数中存在UAF漏洞。 | 本地攻击者利用该漏洞攻击可以权限提升,获得root权限。 | 7.8 | OpenHarmony-v3.1-Release 到 OpenHarmony-v3.1.5-Release | kernel_linux_5.10 | 3.1.x | 研究员上报 |
patch
+5 -6 drivers/accesstokenid/access_tokenid.c
@@ -8,7 +8,6 @@
#define pr_fmt(fmt) "access_token_id: " fmt
- #include <linux/cred.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
@@ -24,7 +23,7 @@ int access_tokenid_get_tokenid(struct file *file, void __user *uarg)
static bool check_permission_for_set_tokenid(struct file *file)
{
- const struct cred *cred = get_task_cred(current);
+ kuid_t uid = current_uid();
struct inode *inode = file->f_inode;
if (inode == NULL) {
@@ -32,8 +31,8 @@ static bool check_permission_for_set_tokenid(struct file *file)
return false;
}
- if (uid_eq(cred->uid, GLOBAL_ROOT_UID) ||
- uid_eq(cred->uid, inode->i_uid)) {
+ if (uid_eq(uid, GLOBAL_ROOT_UID) ||
+ uid_eq(uid, inode->i_uid)) {
return true;
}
@@ -58,7 +57,7 @@ static bool check_permission_for_ftokenid(struct file *file)
{
int i;
struct group_info *group_info;
- const struct cred *cred = get_task_cred(current);
+ kuid_t uid = current_uid();
struct inode *inode = file->f_inode;
if (inode == NULL) {
@@ -66,7 +65,7 @@ static bool check_permission_for_ftokenid(struct file *file)
return false;
}
- if (uid_eq(cred->uid, GLOBAL_ROOT_UID))
+ if (uid_eq(uid, GLOBAL_ROOT_UID))
return true;
group_info = get_current_groups();
分析
accesstokenid
驱动提供读写tokenid/ftokenid
的能力,两者类似,这里仅分析前者。
[1]
处通过get_task_cred()
获取当前进程的cred对象,此时cred对象的reference count会加1。但是函数check_permission_for_set_tokenid
在后续的过程中,使用完cred对象后并没有将reference count减1(即调用put_cred
)。
那么,如果攻击者一直调用函数check_permission_for_set_tokenid()
,那么cred对象的reference count会一直递增1,最后溢出为0,触发释放导致UAF。
static bool check_permission_for_set_tokenid(struct file *file)
{
const struct cred *cred = get_task_cred(current); // [1]
struct inode *inode = file->f_inode;
if (inode == NULL) {
pr_err("%s: file inode is null\n", __func__);
return false;
}
if (uid_eq(cred->uid, GLOBAL_ROOT_UID) ||
uid_eq(cred->uid, inode->i_uid)) {
return true;
}
return false;
}
所有进程都有各自的cred对象(task结构体中),用来描述进程的uid等身份信息,且所有cred对象都存储于名为cred_jar
的slab池中。如下所示,一开始attack process
的cred指针指向cred_jar
中的第二个cred。
------------- -------------
cred -------- cred(uid==x)
------------- | -------------
attack process -----------------> cred(uid==xxx)
-------------
cred(uid==y)
-------------
cred(uid==z)
-------------
cred_jar
接着触发漏洞,让cred对象的reference count溢出为0,触发释放,然而引用并没有销毁,指针仍然指向它。
reference count溢出为0时不会自动释放cred对象,可以通过open & close一个文件来进行释放。open会+1,close会-1并释放。
------------- -------------
cred -------- cred(uid==x)
------------- | -------------
attack process ----------------->
-------------
cred(uid==y)
-------------
cred(uid==z)
-------------
cred_jar
最后借助某一个privileged process
进行堆喷,覆盖刚才释放的cred对象,UAF,本地提权到root。
------------- -------------
cred -------- cred(uid==x)
------------- | -------------
attack process -----------------> cred(uid==0)
| -------------
cred(uid==y)
------------- | -------------
cred -------- cred(uid==z)
------------- -------------
privileged process
参考文献
- https://gitee.com/openharmony/security/blob/master/zh/security-disclosure/2023/2023-02.md
- https://gitee.com/openharmony/kernel_linux_5.10/commit/4048f12d993e0a89e9dda401224f12a99b701052