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_interpT2CharString
→ cf2_doStems
→ cf2_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_len
、nfcb_poll->sensb_res_len
、nfcf_poll->sensf_res_len
、nfca_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_flush
和 key_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,
¶m_list->extra_response_list);
return 0;
}
CVE-2015-8631
本漏洞是位于 krb5 项目的单一函数漏洞,主要错误是多处内存泄露。具体的问题在 init_2_svc
函数的最后一行释放资源的时候没有及时释放 client_name
和 service_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)!