SySeVR中NVD数据集漏洞分析
上一篇分析了该论文,因为篇幅原因没有放在一篇中叙述。论文开源的NVD数据集中包含源代码和代码的diff文件,我们在预处理NVD数据集时便可以利用该补丁文件提取漏洞对应位置,以下是diff文件的详细介绍。diff`是一个用于比较两个文件或目录之间差异的命令行工具。它生成的文件通常称为`diff文件`或`补丁文件`,这些文件包含了源文件和目标文件之间的改变信息。`diff`文件通常以`.diff`或`
一、前言
上一篇分析了该论文SARD数据集的源码漏洞,因为篇幅原因没有放在一篇中叙述。论文开源的NVD数据集中包含源代码和代码的diff文件,我们在预处理NVD数据集时便可以利用该补丁文件提取漏洞对应位置,以下是diff文件的详细介绍。
diff`是一个用于比较两个文件或目录之间差异的命令行工具。它生成的文件通常称为`diff文件`或`补丁文件`,这些文件包含了源文件和目标文件之间的改变信息。`diff`文件通常以`.diff`或`.patch`作为文件扩展名。
diff`文件的主要用途是记录源文件与目标文件之间的更改,以便可以应用这些更改到另一个版本的文件中。这样,可以在不重新编写整个文件的情况下,将更新或修复应用到原始文件中,特别在软件开发和版本控制中非常有用。
`diff`文件的格式通常采用类似于以下的格式:
```
diff --git a/path/to/source/file b/path/to/target/file
index <source_file_checksum> <target_file_checksum>
--- a/path/to/source/file
+++ b/path/to/target/file
@@ -<start_line>,<num_lines> +<start_line>,<num_lines> @@
<changed_lines>
```
其中的重要部分解释如下:
- `diff --git a/source/file b/target/file`: 表示要比较的源文件和目标文件路径。
- `index <source_file_checksum> <target_file_checksum>`:表示源文件和目标文件的校验和,用于标识文件内容。
- `--- a/source/file`:表示源文件的路径。
- `+++ b/target/file`:表示目标文件的路径。
- `@@ -<start_line>,<num_lines> +<start_line>,<num_lines> @@`:表示接下来的更改块。`-<start_line>,<num_lines>`表示源文件中被删除的行号及行数,`+<start_line>,<num_lines>`表示目标文件中被添加的行号及行数。
- `<changed_lines>`:表示源文件和目标文件之间的具体更改内容,可以是添加、删除或修改的行。简言之‘-’为删减的行,‘+’为修改增加的行。
通过`diff`文件,用户可以查看源文件与目标文件之间的差异,以及对源文件应用这些差异从而得到目标文件的变更。在软件开发中,开发人员可以使用`diff`文件来查看代码的更改,或者将补丁应用到版本控制系统中,以记录和管理代码的更新和修复。
二、漏洞代码分析
数据集代码中包含了patched已修复的版本和vuln漏洞的版本。
2.1 CVE_2004_1151_PATCHED_sys32_ni_syscall.c
int CVE_2004_1151_PATCHED_sys32_ni_syscall(int call)
{
struct task_struct *me = current;
static char lastcomm[sizeof(me->comm)];
if (strncmp(lastcomm, me->comm, sizeof(lastcomm))) {
printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call,
me->comm);
strncpy(lastcomm, me->comm, sizeof(lastcomm));
}
return -ENOSYS;
}
diff文件:
diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c
index
--- a/arch/x86_64/ia32/sys_ia32.c
+++ b/arch/x86_64/ia32/sys_ia32.c
@@ -525,11 +525,12 @@ sys32_waitpid(compat_pid_t pid, unsigned
int sys32_ni_syscall(int call)
{
struct task_struct *me = current;
- static char lastcomm[8];
- if (strcmp(lastcomm, me->comm)) {
- printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call,
- current->comm);
- strcpy(lastcomm, me->comm);
+ static char lastcomm[sizeof(me->comm)];
+
+ if (strncmp(lastcomm, me->comm, sizeof(lastcomm))) {
+ printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call,
+ me->comm);
+ strncpy(lastcomm, me->comm, sizeof(lastcomm));
}
return -ENOSYS;
}
该源代码是修复后的版本,我们对比diff文件也能看出。
对比分析:
-
修改了
lastcomm
数组的大小:- 原始代码中,
lastcomm
数组的大小为8,即8个字符。 - 修改后的代码中,
lastcomm
数组的大小由8改为sizeof(me->comm)
,即当前进程的命令名称me->comm
的大小。
- 原始代码中,
-
使用了
strncmp()
和strncpy()
函数代替strcmp()
和strcpy()
函数:- 原始代码中,使用
strcmp()
函数来比较lastcomm
数组和当前进程的命令名称me->comm
是否相等,使用strcpy()
函数来将me->comm
复制到lastcomm
数组中。 - 修改后的代码中,使用
strncmp()
函数来比较lastcomm
数组和当前进程的命令名称me->comm
的前sizeof(lastcomm)
个字符是否相等,使用strncpy()
函数来将me->comm
的前sizeof(lastcomm)
个字符复制到lastcomm
数组中。
- 原始代码中,使用
目的:
这些修改的目的是为了避免潜在的缓冲区溢出问题。原始代码中使用了strcpy()
函数将me->comm
的完整内容复制到lastcomm
数组,而不考虑数组的大小。如果me->comm
的长度大于8个字符,就会导致缓冲区溢出。为了避免这个问题,修改后的代码使用strncpy()
函数,限制复制的长度为sizeof(lastcomm)
,保证不会超过数组的大小。同时,将strcmp()
函数替换为strncmp()
函数,以限制比较的长度为sizeof(lastcomm)
,避免比较超出数组的范围。
2.2 CVE_2005_0146_VULN_nsTextEditorMouseListener__MouseClick.c
nsresult
CVE_2005_0146_VULN_nsTextEditorMouseListener::MouseClick(nsIDOMEvent* aMouseEvent)
{
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
if (!mouseEvent) {
//non-ui event passed in. bad things.
return NS_OK;
}
nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor);
if (!editor) { return NS_OK; }
// If we got a mouse down inside the editing area, we should force the
// IME to commit before we change the cursor position
nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
if (imeEditor)
imeEditor->ForceCompositionEnd();
PRUint16 button = (PRUint16)-1;
mouseEvent->GetButton(&button);
// middle-mouse click (paste);
if (button == 1)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch =
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && prefBranch)
{
PRBool doMiddleMousePaste = PR_FALSE;;
rv = prefBranch->GetBoolPref("middlemouse.paste", &doMiddleMousePaste);
if (NS_SUCCEEDED(rv) && doMiddleMousePaste)
{
// Set the selection to the point under the mouse cursor:
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aMouseEvent));
if (!nsuiEvent)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode> parent;
if (NS_FAILED(nsuiEvent->GetRangeParent(getter_AddRefs(parent))))
return NS_ERROR_NULL_POINTER;
PRInt32 offset = 0;
if (NS_FAILED(nsuiEvent->GetRangeOffset(&offset)))
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsISelection> selection;
if (NS_SUCCEEDED(editor->GetSelection(getter_AddRefs(selection))))
(void)selection->Collapse(parent, offset);
// If the ctrl key is pressed, we'll do paste as quotation.
// Would've used the alt key, but the kde wmgr treats alt-middle specially.
PRBool ctrlKey = PR_FALSE;
mouseEvent->GetCtrlKey(&ctrlKey);
nsCOMPtr<nsIEditorMailSupport> mailEditor;
if (ctrlKey)
mailEditor = do_QueryInterface(mEditor);
PRInt32 clipboard;
#if defined(XP_OS2) || defined(XP_WIN32)
clipboard = nsIClipboard::kGlobalClipboard;
#else
clipboard = nsIClipboard::kSelectionClipboard;
#endif
if (mailEditor)
mailEditor->PasteAsQuotation(clipboard);
else
editor->Paste(clipboard);
// Prevent the event from bubbling up to be possibly handled
// again by the containing window:
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(mouseEvent));
if (nsevent) {
nsevent->PreventBubble();
}
mouseEvent->PreventDefault();
// We processed the event, whether drop/paste succeeded or not
return NS_OK;
}
}
}
return NS_OK;
}
diff文件:
--- editor/libeditor/text/nsEditorEventListeners.cpp 27 Feb 2004 17:17:28 -0000 1.222
+++ editor/libeditor/text/nsEditorEventListeners.cpp 24 Oct 2004 06:25:03 -0000
@@ -276,20 +276,23 @@ nsTextEditorMouseListener::HandleEvent(n
return NS_OK;
}
nsresult
nsTextEditorMouseListener::MouseClick(nsIDOMEvent* aMouseEvent)
{
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
- if (!mouseEvent) {
- //non-ui event passed in. bad things.
+ nsCOMPtr<nsIDOMNSEvent> nsEvent ( do_QueryInterface(aMouseEvent) );
+ PRBool isTrusted = PR_FALSE;
+ if (!mouseEvent || !nsEvent ||
+ NS_FAILED(nsEvent->GetIsTrusted(&isTrusted)) || !isTrusted) {
+ //non-ui, or non-trusted evet passed in. bad things.
return NS_OK;
}
nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor);
if (!editor) { return NS_OK; }
// If we got a mouse down inside the editing area, we should force the
// IME to commit before we change the cursor position
nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
对比分析:
- 修改了事件类型的判断:
- 原始代码中,使用
do_QueryInterface()
将aMouseEvent
强制转换为nsIDOMMouseEvent
类型,然后检查是否成功。如果转换失败,即mouseEvent
为空指针,则认为传入的事件类型不是UI事件,因此返回NS_OK
。 - 修改后的代码中,除了检查
mouseEvent
是否为空指针,还额外使用do_QueryInterface()
将aMouseEvent
强制转换为nsIDOMNSEvent
类型,并通过nsEvent->GetIsTrusted()
来检查事件是否为受信任的事件。如果其中任何一步失败(nsEvent
为空指针、GetIsTrusted()
调用失败或事件不是受信任的),则认为传入的事件不是UI事件,返回NS_OK
。
- 原始代码中,使用
目的:
这些修改的目的是增加了对事件的信任检查。在修改后的代码中,除了检查事件是否为UI事件外,还额外检查了事件是否为受信任的事件。对于非UI事件或者不受信任的事件,函数会直接返回NS_OK
,这样就避免了处理非预期的、不受信任的事件类型,防止出现潜在的安全问题或错误行为。
漏洞原因:
漏洞的根本原因在于,在执行粘贴操作之前,并没有对中间鼠标按钮点击事件进行足够的验证。具体来说,如果按下了Ctrl键,就会执行粘贴操作,并根据当前mEditor
的类型,执行不同的粘贴操作(Paste()
或PasteAsQuotation()
)。然而,当按下Ctrl键时,没有对mEditor
的类型进行充分验证,这可能导致在不适当的编辑器类型上执行粘贴操作,从而引发安全问题或错误行为。
潜在风险:
攻击者可能利用这个漏洞来在不受控制的编辑器上执行粘贴操作,从而导致恶意内容被插入到编辑器中,或者触发编辑器的其他不受欢迎的行为。例如,攻击者可以在用户不知情的情况下执行粘贴操作,将恶意代码粘贴到文档中,从而导致代码注入漏洞或XSS(跨站脚本)攻击等安全问题。
2.3 CVE_2005_2492_PATCHED_raw_probe_proto_opt.c
static void CVE_2005_2492_VULN_rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
{
struct iovec *iov;
u8 __user *type = NULL;
u8 __user *code = NULL;
int probed = 0;
int i;
if (!msg->msg_iov)
return;
for (i = 0; i < msg->msg_iovlen; i++) {
iov = &msg->msg_iov[i];
if (!iov)
continue;
switch (fl->proto) {
case IPPROTO_ICMPV6:
/* check if one-byte field is readable or not. */
if (iov->iov_base && iov->iov_len < 1)
break;
if (!type) {
type = iov->iov_base;
/* check if code field is readable or not. */
if (iov->iov_len > 1)
code = type + 1;
} else if (!code)
code = iov->iov_base;
if (type && code) {
get_user(fl->fl_icmp_type, type);
__get_user(fl->fl_icmp_code, code);
probed = 1;
}
break;
default:
probed = 1;
break;
}
if (probed)
break;
}
}
diff文件:
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -355,7 +355,7 @@
if (type && code) {
get_user(fl->fl_icmp_type, type);
- __get_user(fl->fl_icmp_code, code);
+ get_user(fl->fl_icmp_code, code);
probed = 1;
}
break;
对比分析:
在原始代码中,使用了get_user()
宏读取type
指针指向的用户空间数据到内核空间中的fl->fl_icmp_type
变量。而在code
指针的读取中,使用了__get_user()
宏来执行类似的操作,将用户空间数据读取到fl->fl_icmp_code
变量。
而在修改后的代码中,将__get_user()
宏替换为了get_user()
宏,实际上是对代码进行了修正,修复了之前提到的潜在漏洞。
潜在风险:
在原始代码中,__get_user()
宏并不会执行对用户空间数据的检查,可能导致内核访问不可读的用户空间数据,从而引发内核错误或崩溃。
将__get_user()
宏替换为get_user()
宏是一个正确的修复方式。get_user()
宏会执行对用户空间数据的可读性检查,并在读取不可读数据时返回错误。这样可以确保在读取用户空间数据时不会发生访问错误,增加代码的安全性和稳定性。
2.4 1
更多推荐
所有评论(0)