自学内容网 自学内容网

InterPVD部分漏洞成因分析

本文是对 On the Effectiveness of Function-Level Vulnerability Detectors for Inter-Procedural Vulnerabilities 一文中提出的 InterPVD 漏洞数据集的一部分进行漏洞成因分析。

CVE-2014-9658

本漏洞来自于 FreeType 项目,漏洞在于 98 行(下文代码中的第 53 行)可能产生数组越界。代码中的 p += 2 是为了跳过字体版本号的字符串,但如果已经进入到了最内层的子表,那么此时 p 指针就越界了。

[2字节版本号][2字节子表数量]
[
  [2字节版本号][2字节长度][2字节覆盖]
  [
    [2字节数量]
    ...
  ]
]

上面是子表结构。本漏洞的修复的方法是将第 102 行(下文代码的第 57 行) length <= 6 修改为 length <= 6 + 8,确保存在子表的情况下再进行下轮循环,这样下轮循环中的 p += 2 就确保有效。

FT_LOCAL_DEF(FT_Error)
tt_face_load_kern(TT_Face face, FT_Stream stream)
{
    FT_Error error;
    FT_ULong table_size;
    FT_Byte *p;
    FT_Byte *p_limit;
    FT_UInt nn, num_tables;
    FT_UInt32 avail = 0, ordered = 0;

    /* the kern table is optional; exit silently if it is missing */
    error = face->goto_table(face, TTAG_kern, stream, &table_size);
    if (error)
        goto Exit;

    if (table_size < 4) /* the case of a malformed table */
    {
        FT_ERROR(("tt_face_load_kern:"
                  " kerning table is too small - ignored\n"));
        error = FT_THROW(Table_Missing);
        goto Exit;
    }

    if (FT_FRAME_EXTRACT(table_size, face->kern_table))
    {
        FT_ERROR(("tt_face_load_kern:"
                  " could not extract kerning table\n"));
        goto Exit;
    }

    face->kern_table_size = table_size;

    p = face->kern_table;
    p_limit = p + table_size;

    p += 2; /* skip version */
    num_tables = FT_NEXT_USHORT(p);

    if (num_tables > 32) /* we only support up to 32 sub-tables */
        num_tables = 32;

    for (nn = 0; nn < num_tables; nn++)
    {
        FT_UInt num_pairs, length, coverage;
        FT_Byte *p_next;
        FT_UInt32 mask = (FT_UInt32)1UL << nn;

        if (p + 6 > p_limit)
            break;

        p_next = p;
        // 本行p += 2会出现错误
        p += 2; /* skip version */
        length = FT_NEXT_USHORT(p);
        coverage = FT_NEXT_USHORT(p);
        // 本行修改:length <= 6 -> length <= 6 + 8
        if (length <= 6 + 8)
            break;

        p_next += length;

        if (p_next > p_limit) /* handle broken table */
            p_next = p_limit;

        /* only use horizontal kerning tables */
        if ((coverage & ~8) != 0x0001 ||
            p + 8 > p_limit)
            goto NextTable;

        num_pairs = FT_NEXT_USHORT(p);
        p += 6;

        if ((p_next - p) < 6 * (int)num_pairs) /* handle broken count */
            num_pairs = (FT_UInt)((p_next - p) / 6);

        avail |= mask;

        /*
         *  Now check whether the pairs in this table are ordered.
         *  We then can use binary search.
         */
        if (num_pairs > 0)
        {
            FT_ULong count;
            FT_ULong old_pair;

            old_pair = FT_NEXT_ULONG(p);
            p += 2;

            for (count = num_pairs - 1; count > 0; count--)
            {
                FT_UInt32 cur_pair;

                cur_pair = FT_NEXT_ULONG(p);
                if (cur_pair <= old_pair)
                    break;

                p += 2;
                old_pair = cur_pair;
            }

            if (count == 0)
                ordered |= mask;
        }

    NextTable:
        p = p_next;
    }

    face->num_kern_tables = nn;
    face->kern_avail_bits = avail;
    face->kern_order_bits = ordered;

Exit:
    return error;
}

CVE-2014-9659

本漏洞来自于 FreeType 项目,是一个跨函数的漏洞,触发处位于 cf2_interpT2CharString 函数中,具体的漏洞触发点在 cf2_stack_getReal 函数,特别是在 189 行的 cf2_intToFixed(stack->buffer[idx].u.i); 语句。

本漏洞涉及多个函数调用,具体路径为 cf2_interpT2CharStringcf2_doStemscf2_stack_getReal,跨越了三个函数层次。

本漏洞的三处修改,都集中在 switch 语句的提前退出。由于原始函数 cf2_interpT2CharString 中缺少 break(新增的第 21 、第 45 行和第 71 行的 break)导致在不合法的情况之下仍然执行了 cf2_doStems 函数导致了错误。在 cf2_doStems 函数中调用了 cf2_stack_getReal 函数最终在此函数中发生了错误。

/src/cff/cf2hints.c 文件中的修改如下。本文件修改与本漏洞无关。

FT_LOCAL_DEF(void)
cf2_hintmap_build(CF2_HintMap hintmap,
                  CF2_ArrStack hStemHintArray,
                  CF2_ArrStack vStemHintArray,
                  CF2_HintMask hintMask,
                  CF2_Fixed hintOrigin,
                  FT_Bool initialMap)
{
    ...
    /* use the hStem hints only, which are first in the mask */
    bitCount = cf2_arrstack_size(hStemHintArray);
    // 新增如下退出代码7-8行
    /* Defense-in-depth.  Should never return here. */
    if (bitCount > hintMask->bitCount)
        return;
    ...
}

src/cff/cf2intrp.c 中修改如下:

FT_LOCAL_DEF(void)
cf2_interpT2CharString(CF2_Font font,
                       CF2_Buffer buf,
                       CF2_OutlineCallbacks callbacks,
                       const FT_Vector *translation,
                       FT_Bool doingSeac,
                       CF2_Fixed curX,
                       CF2_Fixed curY,
                       CF2_Fixed *width)
{
...
    // 593行
    case cf2_cmdHSTEM:
    FT_TRACE4((op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n"));

    /* never add hints after the mask is computed */
    if (cf2_hintmask_isValid(&hintMask))
    {
        FT_TRACE4(("cf2_interpT2CharString:"
                   " invalid horizontal hint mask\n"));
        break; // 新增本行,跳出switch语句,不要执行下面的代码
    }

    cf2_doStems(font,
                opStack,
                &hStemHintArray,
                width,
                &haveWidth,
                0);

    if (font->decoder->width_only)
        goto exit;

    break;

    case cf2_cmdVSTEMHM:
    case cf2_cmdVSTEM:
        FT_TRACE4((op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n"));

        /* never add hints after the mask is computed */
        if (cf2_hintmask_isValid(&hintMask))
        {
            FT_TRACE4(("cf2_interpT2CharString:"
                    " invalid vertical hint mask\n"));
            break; // 新增本行,跳出switch语句,不要执行下面的代码
        }

        cf2_doStems(font,
                    opStack,
                    &vStemHintArray,
                    width,
                    &haveWidth,
                    0);

        if (font->decoder->width_only)
            goto exit;

        break;

        ...
            // 1147行
            /* `cf2_hintmask_read' (which also traces the mask bytes) */
            FT_TRACE4((op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask"));
        // 修改整个判定逻辑,将合法条件上移
        /* never add hints after the mask is computed */
        if (cf2_stack_count(opStack) > 1 && // 原本是cf2_stack_count( opStack ) != 0
            cf2_hintmask_isValid(&hintMask))
        {

            FT_TRACE4(("cf2_interpT2CharString: invalid hint mask\n"));
            break; // 新增break
        }
        ...
}

CVE-2014-9660

本漏洞来自于 FreeType 项目,是一个单一函数漏洞,位于 _bdf_parse_glyphs 函数。本漏洞主要问题是在处理 ENDFONT 标志的时候首先判断是否存在 _BDF_GLYPH_BITS 标志,以判定数据是否损坏,否则应当及时退出。原始代码未进行这个处理,导致在接下来的 p->flags &= ~_BDF_START; 代码中出现了错误。

src/bdf/bdflib.c 文件中修改如下:

/* Actually parse the glyph info and bitmaps. */
static FT_Error
_bdf_parse_glyphs(char *line,
                  unsigned long linelen,
                  unsigned long lineno,
                  void *call_data,
                  void *client_data)
{
    ...
    /* Check for the ENDFONT field. */
    if (_bdf_strncmp(line, "ENDFONT", 7) == 0)
    {
        // 新增接下来的7行,处理数据损坏的情况
        if (p->flags & _BDF_GLYPH_BITS)
        {
            /* Missing ENDCHAR field. */
            FT_ERROR(("_bdf_parse_glyphs: " ERRMSG1, lineno, "ENDCHAR"));
            error = FT_THROW(Corrupted_Font_Glyphs);
            goto Exit;
        }

        /* Sort the glyphs by encoding. */
        ft_qsort((char *)font->glyphs,
                 font->glyphs_used,
                 sizeof(bdf_glyph_t),
                 by_encoding);

        p->flags &= ~_BDF_START;

        goto Exit;
    }
    ...
}

CVE-2013-0853

本漏洞位于 FFmpeg,是一个跨函数漏洞,具体位于 wavpack_decode_frame 函数中。该漏洞由于在处理 WavPack 数据时,未正确初始化 s->frame.nb_samples 变量,导致在调用 av_samples_get_buffer_size 函数时,可能会发生数组越界访问。其内部调用有非常多层,但漏洞出现在调用层。这种情况通常是由于一个 off-by-one 错误引起的。

libavcodec/wavpack.c 文件中的修复如下:

static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
                                int *got_frame_ptr, AVPacket *avpkt)
{
    ...
    /* get output buffer */
    // 新增本行
    s->frame.nb_samples = s->samples + 1;
    if ((ret = avctx->get_buffer(avctx, &s->frame)) < 0)
    {
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
        return ret;
    }
    // 新增本行
    s->frame.nb_samples = s->samples;
    ...
}

CVE-2012-2745

本漏洞是位于 linux 项目的多函数漏洞。本漏洞产生原因是因为没有对 p->replacement_session_keyring 变量赋初值或清空,导致第 162 行的 void __put_cred(struct cred *cred) 函数中的 kdebug("__put_cred(%p{%d,%d})", cred, atomic_read(&cred->usage), read_cred_subscribers(cred)); 会出现出错误。

kernel/cred.c 文件中的修复如下:

int copy_creds(struct task_struct *p, unsigned long clone_flags)
{
#ifdef CONFIG_KEYS
struct thread_group_cred *tgcred;
#endif
struct cred *new;
int ret;
// 新增本行,添加初始值
p->replacement_session_keyring = NULL;
    ...
}

CVE-2012-3364

本漏洞是位于 Linux 项目的单函数漏洞,主要问题在于没有限制 nfca_poll->nfcid1_lennfcb_poll->sensb_res_lennfcf_poll->sensf_res_lennfca_poll->rats_res_len 四个变量的大小(长度),导致在第 337 行中的 memcpy(nfca_poll->rats_res,data, nfca_poll->rats_res_len); 中出现错误。

net/nfc/nci/ntf.c 文件中的修改全部集中在对上述几个变量的赋值上,要求它们小于最大要求长度。

static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
struct rf_tech_specific_params_nfca_poll *nfca_poll,
     __u8 *data)
{
nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
data += 2;
// 修改:对nfca_poll->nfcid1_len进行限制
nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE);

pr_debug("sens_res 0x%x, nfcid1_len %d\n",
 nfca_poll->sens_res, nfca_poll->nfcid1_len);

memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len);
data += nfca_poll->nfcid1_len;

nfca_poll->sel_res_len = *data++;

if (nfca_poll->sel_res_len != 0)
nfca_poll->sel_res = *data++;

pr_debug("sel_res_len %d, sel_res 0x%x\n",
 nfca_poll->sel_res_len,
 nfca_poll->sel_res);

return data;
}

static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev,
struct rf_tech_specific_params_nfcb_poll *nfcb_poll,
     __u8 *data)
{
    // 修改:对nfcb_poll->sensb_res_len进行限制
nfcb_poll->sensb_res_len = min_t(__u8, *data++, NFC_SENSB_RES_MAXSIZE);

pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len);

memcpy(nfcb_poll->sensb_res, data, nfcb_poll->sensb_res_len);
data += nfcb_poll->sensb_res_len;

return data;
}

static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev,
struct rf_tech_specific_params_nfcf_poll *nfcf_poll,
     __u8 *data)
{
nfcf_poll->bit_rate = *data++;
   // 修改:对nfcf_poll->sensf_res_len进行限制
nfcf_poll->sensf_res_len = min_t(__u8, *data++, NFC_SENSF_RES_MAXSIZE);

pr_debug("bit_rate %d, sensf_res_len %d\n",
 nfcf_poll->bit_rate, nfcf_poll->sensf_res_len);

memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len);
data += nfcf_poll->sensf_res_len;

return data;
}

static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
{
struct activation_params_nfca_poll_iso_dep *nfca_poll;
struct activation_params_nfcb_poll_iso_dep *nfcb_poll;

switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
         // 修改:限制nfca_poll->rats_res_len的长度
nfca_poll->rats_res_len = min_t(__u8, *data++, 20);
pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len);
if (nfca_poll->rats_res_len > 0) {
            // 可能报错处
memcpy(nfca_poll->rats_res,
       data, nfca_poll->rats_res_len);
}
break;

case NCI_NFC_B_PASSIVE_POLL_MODE:
nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep;
         // 修改:限制nfcb_poll->attrib_res_len的大小
nfcb_poll->attrib_res_len = min_t(__u8, *data++, 50);
pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len);
if (nfcb_poll->attrib_res_len > 0) {
memcpy(nfcb_poll->attrib_res,
       data, nfcb_poll->attrib_res_len);
}
break;

default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
       ntf->activation_rf_tech_and_mode);
return NCI_STATUS_RF_PROTOCOL_ERROR;
}

return NCI_STATUS_OK;
}

CVE-2013-2234

本漏洞是位于 Linux 项目的跨函数漏洞,主要错误在于未对变量 hdr->sadb_msg_reserved 进行初始化。该漏洞成因是在在 key_notify_sa_flushkey_notify_policy_flush 函数中未初始化 sadb_msg_reserved 字段。这导致在广播消息处理时,未初始化的内存区域可能包含敏感信息,从而被本地用户读取。本漏洞会造成第 198 行的 *skb2 = skb_clone(skb, allocation); 语句的错误。

本漏洞的修改位于 net/key/af_key.c 文件:

// 1710行
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
// 新增初始化
hdr->sadb_msg_reserved = 0;

pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
...
// 2700行
static int key_notify_policy_flush(const struct km_event *c)
{
struct sk_buff *skb_out;
struct sadb_msg *hdr;

skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
if (!skb_out)
return -ENOBUFS;
hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
hdr->sadb_msg_type = SADB_X_SPDFLUSH;
hdr->sadb_msg_seq = c->seq;
hdr->sadb_msg_pid = c->portid;
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
     // 新增本行赋值语句
hdr->sadb_msg_reserved = 0;
pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;

}

CVE-2013-2237

本漏洞是 Linux 项目的跨函数漏洞,具体位于 key_notify_policy_flush 函数中。漏洞的成因是在处理广播消息时未初始化 sadb_msg_satype 字段,导致本地用户可以通过读取广播消息获取敏感信息。

本漏洞的修改位于 net/key/af_key.c 文件。

static int key_notify_policy_flush(const struct km_event *c)
{
struct sk_buff *skb_out;
struct sadb_msg *hdr;

skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
if (!skb_out)
return -ENOBUFS;
hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
hdr->sadb_msg_type = SADB_X_SPDFLUSH;
hdr->sadb_msg_seq = c->seq;
hdr->sadb_msg_pid = c->portid;
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_errno = (uint8_t) 0;
    // 新增本行赋值语句
hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;

}

CVE-2013-2850

本漏洞是 Linux 项目的单一函数漏洞,漏洞主要出现在 iscsi_add_notunderstood_response 函数中,具体在处理未理解的键值对时。该漏洞的成因在于使用 strncpy 函数将键复制到 extra_response->key 时,没有正确地处理目标缓冲区的大小,可能会导致缓冲区溢出。这种情况下,未正确处理的输入数据可能会覆盖内存中的其他数据,导致潜在的安全问题。

本漏洞修复位于 drivers/target/iscsi/iscsi_target_parameters.c 文件。

static int iscsi_add_notunderstood_response(
char *key,
char *value,
struct iscsi_param_list *param_list)
{
struct iscsi_extra_response *extra_response;

if (strlen(value) > VALUE_MAXLEN) {
pr_err("Value for notunderstood key \"%s\" exceeds %d,"
" protocol error.\n", key, VALUE_MAXLEN);
return -1;
}

extra_response = kzalloc(sizeof(struct iscsi_extra_response), GFP_KERNEL);
if (!extra_response) {
pr_err("Unable to allocate memory for"
" struct iscsi_extra_response.\n");
return -1;
}
INIT_LIST_HEAD(&extra_response->er_list);
     // 将strncpy修改为strlcpy函数
strlcpy(extra_response->key, key, sizeof(extra_response->key));
strlcpy(extra_response->value, NOTUNDERSTOOD,
sizeof(extra_response->value));

list_add_tail(&extra_response->er_list,
&param_list->extra_response_list);
return 0;
}

CVE-2015-8631

本漏洞是位于 krb5 项目的单一函数漏洞,主要错误是多处内存泄露。具体的问题在 init_2_svc 函数的最后一行释放资源的时候没有及时释放 client_nameservice_name 资源。

本漏洞的补丁位于 src/kadmin/server/server_stubs.c 文件的倒数第二行,新增两行处理内存泄露处理。

generic_ret *init_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
{
    static generic_ret         ret;
    // 给下面两个变量增加赋初值操作
    gss_buffer_desc            client_name = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc            service_name = GSS_C_EMPTY_BUFFER;
    kadm5_server_handle_t      handle;
    OM_uint32                  minor_stat;
    const char                 *errmsg = NULL;
    size_t clen, slen;
    char *cdots, *sdots;

    xdr_free(xdr_generic_ret, &ret);

    if ((ret.code = new_server_handle(*arg, rqstp, &handle)))
        goto exit_func;
    if (! (ret.code = check_handle((void *)handle))) {
        ret.api_version = handle->api_version;
    }

    free_server_handle(handle);

    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
        ret.code = KADM5_FAILURE;
        goto exit_func;
    }

    if (ret.code != 0)
        errmsg = krb5_get_error_message(NULL, ret.code);

    clen = client_name.length;
    trunc_name(&clen, &cdots);
    slen = service_name.length;
    trunc_name(&slen, &sdots);
    /* okay to cast lengths to int because trunc_name limits max value */
    krb5_klog_syslog(LOG_NOTICE, _("Request: kadm5_init, %.*s%s, %s, "
                                   "client=%.*s%s, service=%.*s%s, addr=%s, "
                                   "vers=%d, flavor=%d"),
                     (int)clen, (char *)client_name.value, cdots,
                     errmsg ? errmsg : _("success"),
                     (int)clen, (char *)client_name.value, cdots,
                     (int)slen, (char *)service_name.value, sdots,
                     client_addr(rqstp->rq_xprt),
                     ret.api_version & ~(KADM5_API_VERSION_MASK),
                     rqstp->rq_cred.oa_flavor);
    if (errmsg != NULL)
        krb5_free_error_message(NULL, errmsg);

exit_func:
    // 新增下面的两行,释放内存
    gss_release_buffer(&minor_stat, &client_name);
    gss_release_buffer(&minor_stat, &service_name);
    return(&ret);
}

原文地址:https://blog.csdn.net/m0_52048145/article/details/143646359

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!