Retme的未来道具研究所

世界線の収束には、逆らえない

一个iovec最大长度是被规定为MAX_RW_COUNT,这个检查由vfs中的rw_copy_check_uvector去做



但是在aio中,命令IOCB_CMD_PWRITE会让buffer被aio_setup_single_vector组装成一个iovec


io_submit -> aio_run_iocb -> aio_setup_single_vector



static ssize_t aio_setup_single_vector(int rw, struct kiocb *kiocb)
{
    if (unlikely(!access_ok(!rw, kiocb->ki_buf, kiocb->ki_nbytes)))
        return -EFAULT;

    kiocb->ki_iovec = &kiocb->ki_inline_vec;
    kiocb->ki_iovec->iov_base = kiocb->ki_buf;
    kiocb->ki_iovec->iov_len = kiocb->ki_nbytes;
    kiocb->ki_nr_segs = 1;
    return 0;
}

kiocb是用户态参数iocb的内核拷贝,所以kiocb->ki_nbytes是用户可控的值,没有校验iov_len是否大于MAX_RW_COUNT(MAX_RW_COUNT=0x7FFFF000)



所以在64位机器上可以设置iov_len为0xffffffff,且绕过access_ok的检查.



static ssize_t do_sock_write(struct msghdr *msg, struct kiocb *iocb,
            struct file *file, const struct iovec *iov,
            unsigned long nr_segs)

static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
                size_t total_len = 0xffffffff)

in pppol2tp_sendmsg
    skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) +
               uhlen + session->hdr_len +
               sizeof(ppph) + total_len,
               0, GFP_KERNEL);

struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force,
                 gfp_t priority)
{
    if (force || atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
        struct sk_buff *skb = alloc_skb(size, priority);
        if (skb) {
            skb_set_owner_w(skb, sk);
            return skb;
        }
    }
    return NULL;
}

那么在alloc_skb中,把size_t len 转成了int len



skbuff会变得很小,skbuff后面的内存会被写入。


不过对硬件内存要求很高,一般安卓机肯定卡爆


https://code.google.com/p/google-security-research/issues/detail?id=735&can=1&start=500



然而Android上面并不能访问key retention

https://github.com/torvalds/linux/blob/3a50597de8635cd05133bd12c95681c82fe7b878/security/keys/process_keys.c

long join_session_keyring(const char *name)
{
    const struct cred *old;
    struct cred *new;
    struct key *keyring;
    long ret, serial;

    new = prepare_creds();
    if (!new)
        return -ENOMEM;
    old = current_cred();

    /* if no name is provided, install an anonymous keyring */
    if (!name) {
        ret = install_session_keyring_to_cred(new, NULL);
        if (ret < 0)
            goto error;

        serial = new->session_keyring->serial;
        ret = commit_creds(new);
        if (ret == 0)
            ret = serial;
        goto okay;
    }

    /* allow the user to join or create a named keyring */
    mutex_lock(&key_session_mutex);

    /* look for an existing keyring of this name */
    keyring = find_keyring_by_name(name, false);  //这里 key->usage +1
    if (PTR_ERR(keyring) == -ENOKEY) {
        /* not found - try and create a new one */
        keyring = keyring_alloc(name, old->uid, old->gid, old,
                    KEY_ALLOC_IN_QUOTA, NULL);
        if (IS_ERR(keyring)) {
            ret = PTR_ERR(keyring);
            goto error2;
        }
    } else if (IS_ERR(keyring)) {
        ret = PTR_ERR(keyring);
        goto error2;
    } else if (keyring == new->session_keyring) {  //走到这个路径,说明请求的 keyname == 当前cred中的 key name 
        ret = 0;                //直接返回,绕过key_put
        goto error2;
    }

    /* we've got a keyring - now to install it */
    ret = install_session_keyring_to_cred(new, keyring);
    if (ret < 0)
        goto error2;

    commit_creds(new);
    mutex_unlock(&key_session_mutex);

    ret = keyring->serial;
    key_put(keyring);
okay:
    return ret;

error2:
    mutex_unlock(&key_session_mutex);
error:
    abort_creds(new);
    return ret;
}
ref:

https://gist.github.com/PerceptionPointTeam/18b1e86d1c0f8531ff8f


这是一份可以从华为mate 7中获取指纹数据的TrustZone攻击利用代码,现在已经完全公开并且包含漏洞细节的文档。

https://github.com/retme7/mate7_TZ_exploit




图片来自  P站:51658095 | 画师:またろ