一、前言

        上一篇分析了该论文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文件也能看出。

对比分析:

  1. 修改了lastcomm数组的大小:

    • 原始代码中,lastcomm数组的大小为8,即8个字符。
    • 修改后的代码中,lastcomm数组的大小由8改为sizeof(me->comm),即当前进程的命令名称me->comm的大小。
  2. 使用了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);

对比分析:

  1. 修改了事件类型的判断:
    • 原始代码中,使用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

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐