Vulnerabilities > CVE-2017-0290 - Improper Restriction of Operations within the Bounds of a Memory Buffer vulnerability in Microsoft products

047910
CVSS 9.3 - CRITICAL
Attack vector
NETWORK
Attack complexity
MEDIUM
Privileges required
NONE
Confidentiality impact
COMPLETE
Integrity impact
COMPLETE
Availability impact
COMPLETE
network
microsoft
CWE-119
critical
nessus
exploit available

Summary

The Microsoft Malware Protection Engine running on Microsoft Forefront and Microsoft Defender on Microsoft Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8.1, Windows Server 2012 Gold and R2, Windows RT 8.1, Windows 10 Gold, 1511, 1607, and 1703, and Windows Server 2016 does not properly scan a specially crafted file leading to memory corruption, aka "Microsoft Malware Protection Engine Remote Code Execution Vulnerability."

Common Attack Pattern Enumeration and Classification (CAPEC)

  • Buffer Overflow via Environment Variables
    This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.
  • Overflow Buffers
    Buffer Overflow attacks target improper or missing bounds checking on buffer operations, typically triggered by input injected by an attacker. As a consequence, an attacker is able to write past the boundaries of allocated buffer regions in memory, causing a program crash or potentially redirection of execution as per the attackers' choice.
  • Client-side Injection-induced Buffer Overflow
    This type of attack exploits a buffer overflow vulnerability in targeted client software through injection of malicious content from a custom-built hostile service.
  • Filter Failure through Buffer Overflow
    In this attack, the idea is to cause an active filter to fail by causing an oversized transaction. An attacker may try to feed overly long input strings to the program in an attempt to overwhelm the filter (by causing a buffer overflow) and hoping that the filter does not fail securely (i.e. the user input is let into the system unfiltered).
  • MIME Conversion
    An attacker exploits a weakness in the MIME conversion routine to cause a buffer overflow and gain control over the mail server machine. The MIME system is designed to allow various different information formats to be interpreted and sent via e-mail. Attack points exist when data are converted to MIME compatible format and back.

Exploit-Db

descriptionMicrosoft Windows 8 / 8.1 / 10 / Windows Server / SCEP, Microsoft Security Essentials - 'MsMpEng' Remotely Exploitable Type Confusion. CVE-2017-0290. Remote ...
fileexploits/windows/remote/41975.txt
idEDB-ID:41975
last seen2017-05-09
modified2017-05-09
platformwindows
port
published2017-05-09
reporterExploit-DB
sourcehttps://www.exploit-db.com/download/41975/
titleMicrosoft Windows 8 / 8.1 / 10 / Windows Server / SCEP, Microsoft Security Essentials - 'MsMpEng' Remotely Exploitable Type Confusion
typeremote

Nessus

NASL familyWindows
NASL idSMB_KB4022344.NASL
descriptionThe version of Microsoft Malware Protection Engine (MMPE) installed on the remote Windows host is prior to 1.1.13704.0. It is, therefore, affected by a remote code execution vulnerability in the NScript component in mpengine.dll due to a type confusion error. An unauthenticated, remote attacker can exploit this, via a specially crafted file, to execute arbitrary code in the security context of the LocalSystem account. Nessus has checked if a vulnerable version of MMPE is being used by any of the following applications : - Microsoft Forefront Endpoint Protection 2010 - Microsoft Endpoint Protection - Microsoft Forefront Security for SharePoint - Microsoft System Center Endpoint Protection - Microsoft Security Essentials - Windows Defender for Windows 7, Windows 8.1, Windows RT 8.1, Windows 10, Windows 10 1511, Windows 10 1607, Windows 10 1703, and Windows Server 2016 - Windows Intune Endpoint Protection
last seen2020-06-01
modified2020-06-02
plugin id100051
published2017-05-09
reporterThis script is Copyright (C) 2017-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
sourcehttps://www.tenable.com/plugins/nessus/100051
titleMS Security Advisory 4022344: Security Update for Microsoft Malware Protection Engine
code
#
# (C) Tenable Network Security, Inc.
#

include("compat.inc");

if (description)
{
  script_id(100051);
  script_version("1.10");
  script_cvs_date("Date: 2019/11/13");

  script_cve_id("CVE-2017-0290");
  script_bugtraq_id(98330);
  script_xref(name:"MSKB", value:"4022344");

  script_name(english:"MS Security Advisory 4022344: Security Update for Microsoft Malware Protection Engine");
  script_summary(english:"Checks engine version.");

  script_set_attribute(attribute:"synopsis", value:
"The remote host has an antimalware application installed that is
affected by a remote code execution vulnerability.");
  script_set_attribute(attribute:"description", value:
"The version of Microsoft Malware Protection Engine (MMPE) installed on
the remote Windows host is prior to 1.1.13704.0. It is, therefore,
affected by a remote code execution vulnerability in the NScript
component in mpengine.dll due to a type confusion error. An
unauthenticated, remote attacker can exploit this, via a specially
crafted file, to execute arbitrary code in the security context of the
LocalSystem account.

Nessus has checked if a vulnerable version of MMPE is being used by
any of the following applications :

  - Microsoft Forefront Endpoint Protection 2010
  - Microsoft Endpoint Protection
  - Microsoft Forefront Security for SharePoint
  - Microsoft System Center Endpoint Protection
  - Microsoft Security Essentials
  - Windows Defender for Windows 7, Windows 8.1, Windows RT
    8.1, Windows 10, Windows 10 1511, Windows 10 1607,
    Windows 10 1703, and Windows Server 2016
  - Windows Intune Endpoint Protection");
  script_set_attribute(attribute:"see_also", value:"https://docs.microsoft.com/en-us/security-updates/SecurityAdvisories/2017/4022344");
  script_set_attribute(attribute:"solution", value:
"Enable automatic updates to update the scan engine for the relevant
antimalware applications. Refer to KB4022344 for information on how to
verify MMPE has been updated.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:M/Au:N/C:C/I:C/A:C");
  script_set_cvss_temporal_vector("CVSS2#E:H/RL:OF/RC:C");
  script_set_cvss3_base_vector("CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H");
  script_set_cvss3_temporal_vector("CVSS:3.0/E:H/RL:O/RC:C");
  script_set_attribute(attribute:"cvss_score_source", value:"CVE-2017-0290");

  script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
  script_set_attribute(attribute:"exploit_available", value:"true");
  script_set_attribute(attribute:"exploited_by_malware", value:"true");

  script_set_attribute(attribute:"vuln_publication_date", value:"2017/05/09");
  script_set_attribute(attribute:"patch_publication_date", value:"2017/05/09");
  script_set_attribute(attribute:"plugin_publication_date", value:"2017/05/09");

  script_set_attribute(attribute:"plugin_type", value:"local");
  script_set_attribute(attribute:"cpe", value:"cpe:/o:microsoft:windows");
  script_set_attribute(attribute:"cpe", value:"cpe:/a:microsoft:malware_protection_engine");
  script_end_attributes();

  script_category(ACT_GATHER_INFO);
  script_family(english:"Windows");

  script_copyright(english:"This script is Copyright (C) 2017-2019 and is owned by Tenable, Inc. or an Affiliate thereof.");

  script_dependencies("smb_hotfixes.nasl", "fcs_installed.nasl");
  script_require_keys("SMB/Registry/Enumerated");
  script_require_ports(139, 445);

  exit(0);
}

include("audit.inc");
include("global_settings.inc");
include("misc_func.inc");
include("smb_func.inc");

get_kb_item_or_exit("SMB/Registry/Enumerated");

# indicates if any antimalware products were found. this is used
# to determine whether or not the plugin should check if defender is affected
antimalware_installed = FALSE;

# Connect to the appropriate share.
port    =  kb_smb_transport();
login   =  kb_smb_login();
pass    =  kb_smb_password();
domain  =  kb_smb_domain();

if(! smb_session_init()) audit(AUDIT_FN_FAIL, 'smb_session_init');

rc = NetUseAdd(login:login, password:pass, domain:domain, share:"IPC$");
if (rc != 1)
{
  NetUseDel();
  audit(AUDIT_SHARE_FAIL, "IPC$");
}

# Connect to remote registry.
hklm = RegConnectRegistry(hkey:HKEY_LOCAL_MACHINE);
if (isnull(hklm))
{
  NetUseDel();
  audit(AUDIT_REG_FAIL);
}

# Figure out where it is installed.
path = NULL;
info = '';
info2 = '';
engine_version = NULL;

fixed_engine_version = "1.1.13704.0";
last_vulnerable = "1.1.13701.0";

# Forefront Client Security (either both or neither of these will be in the KB)
engine_version = get_kb_item("Antivirus/Forefront_Client_Security/engine_version");
fcs_path = get_kb_item("Antivirus/Forefront_Client_Security/path");
if (!isnull(engine_version))
{
  antimalware_installed = TRUE;

  if (ver_compare(ver:engine_version, fix:last_vulnerable) <= 0)
  {
    info +=
      '\n  Product           : Microsoft Forefront Client Security'+
      '\n  Path              : ' + fcs_path +
      '\n  Installed version : ' + engine_version +
      '\n  Fixed version     : ' + fixed_engine_version + '\n';
  }
  else info2 += 'Microsoft Forefront Client Security with MMPE version '+ engine_version + ". ";
}

# Microsoft Security Essentials
# Forefront Endpoint Protection
# System Center Endpoint Protection
engine_version = NULL;

NetUseDel(close:FALSE);
rc = NetUseAdd(login:login, password:pass, domain:domain, share:"IPC$");
if (rc != 1)
{
  NetUseDel();
  audit(AUDIT_SHARE_FAIL, "IPC$");
}

# Connect to remote registry again.
hklm = RegConnectRegistry(hkey:HKEY_LOCAL_MACHINE);
if (isnull(hklm))
{
  NetUseDel();
  audit(AUDIT_REG_FAIL);
}

key = "SOFTWARE\Microsoft\Microsoft Antimalware\Signature Updates";
key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
if (!isnull(key_h))
{
  value = RegQueryValue(handle:key_h, item:"EngineVersion");
  if (!isnull(value)) engine_version = value[1];

  RegCloseKey(handle:key_h);
}

path = NULL;
key = "SOFTWARE\Microsoft\Microsoft Antimalware";
key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
if (!isnull(key_h))
{
  value = RegQueryValue(handle:key_h, item:"InstallLocation");
  if (!isnull(value)) path = value[1];

  RegCloseKey(handle:key_h);
}

if(!isnull(path))
{
  found = 0;
  # Check if the main exe exists.
  share = ereg_replace(pattern:"^([A-Za-z]):.*", replace:"\1$", string:path);
  exe =  ereg_replace(pattern:"^[A-Za-z]:(.*)", replace:"\1\MsMpEng.exe", string:path);
  NetUseDel(close:FALSE);
  rc = NetUseAdd(login:login, password:pass, domain:domain, share:share);
  if (rc != 1)
  {
    NetUseDel();
    audit(AUDIT_SHARE_FAIL, share);
  }
  fh = CreateFile(
    file:exe,
    desired_access:GENERIC_READ,
    file_attributes:FILE_ATTRIBUTE_NORMAL,
    share_mode:FILE_SHARE_READ,
    create_disposition:OPEN_EXISTING
  );
  if (!isnull(fh))
  {
    antimalware_installed = TRUE;
    found = 1;
    CloseFile(handle:fh);
  }

  if (found && !isnull(engine_version))
  {
    if (ver_compare(ver:engine_version, fix:last_vulnerable) <= 0)
    {
      info +=
       '\n  Product           : Microsoft Security Essentials / Forefront Endpoint Protection / System Center Endpoint Protection'+
       '\n  Path              : ' + share[0] + ':' + exe +
       '\n  Installed version : ' + engine_version +
       '\n  Fixed version     : ' + fixed_engine_version + '\n';
    }
    else info2 += 'Microsoft Security Essentials / Forefront Endpoint Protection / System Center Endpoint Protection with MMPE version ' + engine_version + ". ";
  }
}

# Microsoft Windows Defender
# defender is apparently disabled when other antimalware products are installed,
# so it will only be checked if the plugin hasn't detected other products are present
if (!antimalware_installed)
{
  defender_enabled = TRUE;
  engine_version = NULL;

  # Check if Windows Defender is disabled via group policy
  key = "SOFTWARE\Policies\Microsoft\Windows Defender";
  key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
  if (!isnull(key_h))
  {
    value = RegQueryValue(handle:key_h, item:"DisableAntiSpyware");
    if (!isnull(value))
    {
      if (value[1] > 0)
      {
        defender_enabled = FALSE;
      }
    }
    RegCloseKey(handle:key_h);
  }
  key = "SOFTWARE\Microsoft\Windows Defender";
  key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
  if (!isnull(key_h))
  {
    value = RegQueryValue(handle:key_h, item:"DisableAntiSpyware");
    if (!isnull(value))
    {
      if (value[1] > 0)
      {
        defender_enabled = FALSE;
      }
    }
    RegCloseKey(handle:key_h);
  }
  if (defender_enabled)
  {
    key = "SOFTWARE\Microsoft\Windows Defender\Signature Updates";
    key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
    if (!isnull(key_h))
    {
      value = RegQueryValue(handle:key_h, item:"EngineVersion");
      if (!isnull(value)) engine_version = value[1];

      RegCloseKey(handle:key_h);
    }

    path = NULL;
    key = "SOFTWARE\Microsoft\Windows Defender\Signature Updates";
    key_h = RegOpenKey(handle:hklm, key:key, mode:MAXIMUM_ALLOWED);
    if (!isnull(key_h))
    {
      value = RegQueryValue(handle:key_h, item:"SignatureLocation");
      if (!isnull(value)) path = value[1];

      RegCloseKey(handle:key_h);
    }

    if(!isnull(path))
    {
      found = 0;
      defender_dll = NULL;
      # Check the version of the main exe.
      share = ereg_replace(pattern:"^([A-Za-z]):.*", replace:"\1$", string:path);
      # this is the path smb_kb4022344.nasl checks
      dll1 =  ereg_replace(pattern:"^[A-Za-z]:(.+Windows Defender\\Definition Updates).+", replace:"\1\Default\MpEngine.dll", string:path);
      # this path works for Windows Defender on Windows 8
      dll2 =  ereg_replace(pattern:"^[A-Za-z]:(.+)$", replace:"\1\MpEngine.dll", string:path);
      NetUseDel(close:FALSE);
      rc = NetUseAdd(login:login, password:pass, domain:domain, share:share);
      if (rc != 1)
      {
        NetUseDel();
        audit(AUDIT_SHARE_FAIL, share);
      }
      fh = CreateFile(
        file:dll1,
        desired_access:GENERIC_READ,
        file_attributes:FILE_ATTRIBUTE_NORMAL,
        share_mode:FILE_SHARE_READ,
        create_disposition:OPEN_EXISTING
      );
      if (!isnull(fh))
      {
        found =1 ;
        defender_dll = share[0] + ':' + dll1;
        CloseFile(handle:fh);
      }

      if (found == 0)
      {
        fh = CreateFile(
          file:dll2,
          desired_access:GENERIC_READ,
          file_attributes:FILE_ATTRIBUTE_NORMAL,
          share_mode:FILE_SHARE_READ,
          create_disposition:OPEN_EXISTING
        );
        if (!isnull(fh))
        {
          found =1 ;
          defender_dll = share[0] + ':' + dll2;
          CloseFile(handle:fh);
        }
      }

      if (found && !isnull(engine_version))
      {
        if (ver_compare(ver:engine_version, fix:last_vulnerable) <= 0)
        {
          info +=
           '\n  Product           : Microsoft Windows Defender'+
           '\n  Path              : ' + defender_dll +
           '\n  Installed version : ' + engine_version +
           '\n  Fixed version     : ' + fixed_engine_version + '\n';
        }
        else info2 += 'Microsoft Windows Defender with MMPE version ' + engine_version + ". ";
      }
    }
  }
}

RegCloseKey(handle:hklm);
NetUseDel();

if (info)
{
  report = '\n' +
    "Nessus found following vulnerable product(s) installed :" +'\n'+
    info;
  security_report_v4(severity:SECURITY_HOLE, port:port, extra:report);

  exit(0);
}
else if(info2) exit(0,"The following instance(s) of MMPE are installed and not vulnerable : "+ info2);
else exit(0, "Nessus could not find evidence of affected Microsoft antimalware products installed.");

Seebug

  • bulletinFamilyexploit
    descriptionNatalie Silvanovich and Tavis Ormandy of Google Project Zero found a pretty nasty bug in Microsoft Malware Protection Engine, allowing an attacker to execute arbitrary code as LocalSystem on any Windows computer running any Microsoft anti-malware product such as Security Essentials or Windows Defender by simply having that computer access a malicious file. Attack vectors were abundant, from emailing the file or sending it via any other channel like Skype or Messenger, to having it hosted on a malicious web site or uploading it to an IIS web server. Unlike many other stories of the past week, mine is not about how Natalie and Tavis found this bug, how they reported it to Microsoft or how the fact that they found and reported it was made known to the public. Rather, it is about the bug itself, its root cause, and - of course - about writing a micropatch for it. But first: why would we want to write a micropatch for a vulnerability that would quickly get automatically fixed on all Windows computers anyway? As you may know, Microsoft was super fast in fixing this bug and made an update available literally over the weekend. Furthermore, the Malware Protection Engine is implemented as a dynamic-load library mpengine.dll, and Microsoft designed their anti-malware products smartly enough to not require a computer restart - the old DLL is simply unloaded, and the new one loaded. So why write a micropatch? Well, not every computer gets updated automatically: while automatic application of updates is configured by default, admins can change that if they want to control what gets applied when. And enterprise admins like to have such control, allowing them to test new code before deploying it to computers throughout their organization. Just imagine the updated mpengine.dll having a flaw that prevented users from accessing legitimate files. Another reason for writing this micropatch was to learn, as we haven't patched a security product before - and one can expect to stumble upon something new here (and stumble I did, as you will see). The final reason was to teach, to share some knowledge with those of you who want to analyze vulnerabilities yourselves and learn how to write micropatches. #### Reproducing CVE-2017-0290 The first step in analyzing a vulnerability is to reproduce its exploitation. The Project Zero report provides a downloadable proof-of-concept file, which has a .zip extension, but is really an HTML-lookalike file that comprises a tiny exploit bit and a lot of random HTML content that makes sure the engine processes the file. Reproducing on 64-bit Windows 8.1 was trivial - just downloading and saving the file was enough to make the Windows Defender service crash, instantly turning from this: ![](https://images.seebug.org/1494914984070) to this: ![](https://images.seebug.org/1494915017327) After the crash, the Application Event Log contained an Error event about this crash, revealing the crashing module being mpengine.dll, and the crash location being at offset 0x21745a. (You will find a different crash address in Google's report because they were working on a 32-bit computer.) ![](https://images.seebug.org/1494915111577) Note that I was using mpengine.dll version 1.1.13701.0, which is the last vulnerable version before the fixed 1.1.13704.0. It is always good to do your analysis on the last vulnerable version in order to minimize the difference with the fixed version - you will thank yourself when diffing these versions. #### Analyzing CVE-2017-0290 With the bug successfully reproduced, the path was clear towards analysis. Here, the Google report was a great start, as Natalie and Tavis have clearly gained substantial understanding of what goes on in the crash case. The most important detail for me was that it was a type confusion error, specifically with some function expecting a string object but getting a number object (which resulted in calling a string vtable function where there really was no vtable). This was important because when a bug is a type confusion error, a typical fix is to add the missing check for the correct type. And such a fix is usually easy to recognize when observing the difference between vulnerable and fixed code. Which brings us to IDA. The image below shows the function that crashed - the exact access violation location was the mov rax, [rcx] instruction (see the red box) at address 0x75A31745A, which is at offset 0x21745a from mpengine.dll's default base address. ![](https://images.seebug.org/1494915171176) When a vendor patch is available, diffing the vulnerable and the patched version usually provides useful information and allows you to understand the bug, and the patch, better. Diffing can be a time-consuming operation though first for the computer and then for you, and with large binaries (mpengine.dll is 12MB) you can get a lot of matched functions, and finding where the code logic is different - as opposed to where the code is different - can be somewhat frustrating. So I went on to diff the two versions of mpengine.dll, the vulnerable 1.1.13701.0 and the patched 1.1.13704.0. There were 38440 matched functions, which, in scientific terms, is an awful lot. What I could do with these results was compare the above crashing function between the two versions. If I was lucky, the patch would be there and I could go home early. ![](https://images.seebug.org/1494915222213) Nope. Both functions are logically identical, which means that the flaw (and the patch) is somewhere higher on the call stack. At this point one could diff all functions that call this function, but there are about 50 of them - and if all of those turned out to be identical as well, such approach could turn into an exponential mission impossible. (Not to mention that IDA may not see all callers.) Now about the call stack: you will notice that I haven't used a debugger up to this point, and the reason is that Windows Defender is a protected service and as such tries very hard to protect itself from tampering. You cannot attach a debugger to a protected process, even if you're a local administrator. And it's not easy to un-protect a protected service either: while its protected status is defined by the LaunchProtected registry value (in our case under HKLM\SYSTEM\CurrentControlSet\Services\WinDefend), you cannot change that value for Windows Defender while Windows Defender is running as it prevents you from "attacking" it. Fortunately, we have a way to stop Windows Defender - by crashing it with the PoC. So what I did was crash Windows Defender, rename its LaunchProtected registry value, restarted the computer (the protected status of services is read only at system startup), then configured Windows Error Reporting to generate dump files for crashing processes. (I only created the LocalDumps key and the DumpFolder value containing "C:\dumps" in it.) After crashing Windows Defender again, I got its mini dump file in C:\dumps, and it contained a full call stack for the access violation. I was only interested in locations from mpengine.dll: ``` mpengine!FreeSigFiles+0x11ea9a mpengine!FreeSigFiles+0x12046f mpengine!FreeSigFiles+0x111e81 mpengine!FreeSigFiles+0x111d9e mpengine!FreeSigFiles+0x125eaa mpengine!FreeSigFiles+0x3de1d mpengine!FreeSigFiles+0x3dbf5 mpengine!FreeSigFiles+0x125eaa mpengine!FreeSigFiles+0x117ade mpengine!FreeSigFiles+0x120146 mpengine!FreeSigFiles+0x113d76 mpengine!FreeSigFiles+0xcce7f mpengine+0x54a99 mpengine+0x865e1 mpengine+0x50f3f mpengine+0x50d1f mpengine+0x8c208 mpengine+0x8bf47 mpengine!FreeSigFiles+0x174a3 mpengine+0x13b7d mpengine!FreeSigFiles+0x1535a mpengine!_rsignal+0x243 mpengine!_rsignal+0xe7 ``` The top one we already know - it's the access violation location in the crashing function that we diffed just moments ago. So I proceeded with the second address, FreeSigFiles+0x12046f, and located it in IDA. It was, as expected, after a call to the crashing function. I then took the address of the function containing that address, and viewed the diff with its patched version. ![](https://images.seebug.org/1494915274761) Now we're talking! This looks like a typical added check that exits a function if something is not right. (The patched version is on the left.) The upper red block is added code that takes rdi (the object) and passes it to a call to some function, and if the result of that function is 4, the execution continues as before, otherwise the return value (al) is set to 0 in the lower red block, and the function exits. The function that gets called from the upper red block seems to determine the type of the object and returns its type code. Reviewing other calls to this function I found a very obvious implementation of JavaScript's typeof operator, which confirmed that type code for string is actually 4. This is clearly the patch I was looking for. It was simple, it did exactly what I was expecting it to do, and it was in the code path of our crash. #### Micropatching CVE-2017-0290 My goal at this point was to create a micropatch that would inject the same patch logic into the vulnerable version of mpengine.dll. In a perfect world, I could use literally the same code that I found in the patched version, and inject it in the same place - but in this world a compiler likes to use different registers and different implementation of the same logic in two subsequent builds. So I had to re-implement the patch logic from the original patch. Let's look at the vulnerable function in IDA. ![](https://images.seebug.org/1494915325915) The above image shows the vulnerable function and a good location for injecting our code. The location is selected so that it allows us to jump from our patchlet code directly to the function epilogue (the lowest block of code). Here is the patch code for 64-bit mpengine.dll version 1.1.13701.0: ``` ;0patch for CVE-2017-0290 in 64-bit mpengine.dll version 1.1.13701.0 MODULE_PATH "C:\Analysis\CVE-2017-0290\mpengine.dll_64bit_1.1.13701.0\mpengine.dll" PATCH_ID 271 PATCH_FORMAT_VER 2 VULN_ID 2436 PLATFORM win64 patchlet_start PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x218E10 ; We'll need the GetTypeOf function and the location of function epilogue PIT mpengine.dll!0x218940,mpengine.dll!0x218E9A ; Note that GetTypeOf taints the following registers: ; rdx - always ; rcx - only in case of an exception ; rax - expected, this is the return value code_start push rcx ; We need to preserve rcx, as it's still used after our patchlet code ; while GetTypeOf taints rdx, we don't need to preserve it mov rcx, r9 ; r9 points to the object call PIT_0x218940 ; GetTypeOf object pop rcx ; restore rcx cmp eax, 4 ; is the object of type string? jz OK ; It is? Very well, continue... xor al, al ; It isn't? Exit this function without doing anything, return 0 call PIT_ExploitBlocked ; Show "Exploit attempt blocked" jmp PIT_0x218E9A ; Jump to function epilogue OK: code_end patchlet_end ``` What this single-patchlet patch, inserted at the shown point in code, does is - just as the original patch - call GetTypeOf on the object (whose address is in register r9) and see if its type code is 4 (string). If it is, it continues execution of original code where it was injected . Otherwise, it sets the return code (register al) to 0 and jumps to function epilogue. Note that in order to avoid any negative side effects, I had to (1) review the GetTypeOf function to see which registers it may taint and whether that could impact the code after our injected patch (it taints rdx and rcx, but rdx holds nothing valuable at our injection point), and then (2) store rcx on the stack before calling GetTypeOf function because rcx holds some value that is still being used after our injected patch. I also wrote the same patch for the last vulnerable 32-bit version of mpengine.dll. If you have 0patch Agent installed, patches ZP-271 and ZP-272 should already be downloaded to your computer, waiting for any occurrence of the vulnerable mpengine.dll getting loaded. #### The Irony of Protected Services To restore the original system configuration, I turned Windows Defender back to a protected service, and... shoot, the patch stopped getting applied. It quickly became clear that we can't inject our loader into the protected Windows Defender because only binaries signed by Microsoft are allowed to get loaded. (It's a bit more complex than that but close enough.) This is Windows Defender protecting itself against local malware - even with admin privileges - trying to compromise it. The irony is that a Windows anti-malware protection prevents our security product from fixing a vulnerability in Windows Defender, while the exploit for the same vulnerability can freely execute arbitrary code in Windows Defender. (Hmm, perhaps we should leverage this exploit to get our code running inside Windows Defender and thereby fix it.) So while we're exploring options for extending our reach towards patching protected services, patches ZP-271 and ZP-272 for Malware Protection Engine will only get applied on Windows 7 and Windows Vista, which don't have protected services. #### Experimenting with Micropatches for CVE-2017-0290 If you want to experiment with these micropatches, you'll need two things: * A 32-bit or 64-bit Windows 7 computer running Windows Defender. While you could also do the testing on newer Windows versions, you would have to un-protect the Windows Defender service in order to proceed. * Vulnerable mpengine.dll. If your Windows Defender doesn't happen to have this exact version (unlikely, due to automatic updates), you can get it here: * [32-bit mpengine.dll](https://0patch.com/poc/CVE-2017-0290/mpengine.dll_1.1.13701.0-32b/mpengine.dll) version 1.1.13701.0 for 32-bit Windows * [64-bit mpengine.dll](https://0patch.com/poc/CVE-2017-0290/mpengine.dll_1.1.13701.0-64b/mpengine.dll) version 1.1.13701.0 for 64-bit Windows First of all, stop the Windows Defender service via elevated Services console. Then browse to C:\ProgramData\Microsoft\Windows Defender (folder permissions don't originally allow you to open it so Windows will ask you for elevation, after which it will add your account to the folder ACL). Open folder Definition Updates, and notice one or more subfolders with GUID-like names starting with curly braces. Open each of these folders to find the one containing mpengine.dll. Rename the existing mpengine.dll into something else, then save the vulnerable mpengine.dll there. Start the Windows Defender service. Download the [proof-of-concept](https://bugs.chromium.org/p/project-zero/issues/attachment?aid=283405) file and store it in some empty temporary folder. Launch the Windows Defender console via the Control Panel, and Custom-Scan the above folder. Notice that Windows Defender service crashes. Now install 0patch Agent on the computer. If you don't already have it, [download a free copy](https://dist.0patch.com/download/latestagent) and register it with your [free 0patch account](https://dist.0patch.com/User/Register). Finally, restart the Windows Defender service and re-scan the temporary folder. This time, you'll see an "Exploit Attempt Blocked" popup instead of Windows Defender crashing. If you want to build our patches yourself, you can download their [source code](https://0patch.com/poc/CVE-2017-0290/ZP-271-272.zip) and build them using [0patch Agent for Developers](https://dist.0patch.com/download/latestagentdev). While this vulnerability has already been automatically fixed on most computers, it turned out to be an interesting learning experience to micropatch it. I hope this post will help future micropatchers jump-start their research. While I was writing this post, the world got pierced by the WannaCry ransomware worm exploiting a known vulnerability that had an official patch available for Windows operating systems which Microsoft supported at the time. Many hospitals and other critical infrastructure components were taken offline, partly also because they were stuck with unsupported OSs such as Windows XP. They have very rational and complex reasons for being on such outdated systems in 2017, and undoubtedly they will have similar reasons next year and the year after. One of our goals with 0patch is to provide protection for such end-of-support systems while users scramble to update them (and have the same problem almost immediately afterwards). Defending against modern attackers will require rapid response, and this exercise with CVE-2017-0290 - although likely of low value to users - was an example of building up skills and speed. The world will need a lot of 3rd-party patch developers though, so all existing and prospective security researchers are warmly welcome to join us.
    idSSV:93110
    last seen2017-11-19
    modified2017-05-16
    published2017-05-16
    reporterRoot
    titleMicrosoft Malware Protection Engine RCE (CVE-2017-0290)
  • bulletinFamilyexploit
    descriptionMsMpEng is the Malware Protection service that is enabled by default on Windows 8, 8.1, 10, Windows Server 2012, and so on. Additionally, Microsoft Security Essentials, System Centre Endpoint Protection and various other Microsoft security products share the same core engine. MsMpEng runs as NT AUTHORITY\SYSTEM without sandboxing, and is remotely accessible without authentication via various Windows services, including Exchange, IIS, and so on. On workstations, attackers can access mpengine by sending emails to users (reading the email or opening attachments is not necessary), visiting links in a web browser, instant messaging and so on. This level of accessibility is possible because MsMpEng uses a filesystem minifilter to intercept and inspect all system filesystem activity, so writing controlled contents to anywhere on disk (e.g. caches, temporary internet files, downloads (even unconfirmed downloads), attachments, etc) is enough to access functionality in mpengine. MIME types and file extensions are not relevant to this vulnerability, as MsMpEng uses it's own content identification system. Vulnerabilities in MsMpEng are among the most severe possible in Windows, due to the privilege, accessibility, and ubiquity of the service. The core component of MsMpEng responsible for scanning and analysis is called mpengine. Mpengine is a vast and complex attack surface, comprising of handlers for dozens of esoteric archive formats, executable packers and cryptors, full system emulators and interpreters for various architectures and languages, and so on. All of this code is accessible to remote attackers. NScript is the component of mpengine that evaluates any filesystem or network activity that looks like JavaScript. To be clear, this is an unsandboxed and highly privileged JavaScript interpreter that is used to evaluate untrusted code, by default on all modern Windows systems. This is as surprising as it sounds. We have written a tool to access NScript via a command shell for testing, allowing us to explore and evaluate it: $ mpscript main(): Please wait, initializing engine... main(): Ready, type javascript (history available, use arrow keys) > 6 * 9 JavaScriptLog(): 54 > document.location.hostname JavaScriptLog(): www.myserver.com > "abcd" + String.fromCharCode(0x3f) JavaScriptLog(): abcd? > /[y]e+(s|S)/.exec("yes")[0] // C++ regex engine running unsandboxed as SYSTEM on attacker controlled REGEX? JavaScriptLog(): yes > for (i in document) log(i) JavaScriptLog(): appendChild JavaScriptLog(): attributes JavaScriptLog(): childNodes JavaScriptLog(): createElement JavaScriptLog(): createTextNode JavaScriptLog(): getElementById JavaScriptLog(): getElementsByTagName JavaScriptLog(): write JavaScriptLog(): writeln JavaScriptLog(): referrer JavaScriptLog(): cookie JavaScriptLog(): location JavaScriptLog(): undefined > window.ScriptEngineBuildVersion JavaScriptLog(): [object Function] > window.ScriptEngineBuildVersion() JavaScriptLog(): 8831 We have discovered that the function JsDelegateObject_Error::toString() reads the "message" property from the this object, but fails to validate the type of the property before passing it to JsRuntimeState::triggerShortStrEvent(). In pseudocode, the code does something like this: prophash = JsObject::genPropHash("message", 0); RuntimeState::getThisPtr(&thisptr) if (JsObject::get(thisptr, prophash, &message)) { JsRuntimeState::triggerShortStrEvent("error_tostring", message); } The method assumes that message is a string, but it can be of any type, so this type confusion allows an attacker to pass arbitrary other objects. JsRuntimeState::triggerShortStrEvent() calls JsString::numBytes() on the passed object, which will invoke a method from the object's vtable. int __fastcall JsString::numBytes(JsString this) { if ( this == 0x12 ) return 0; if ( (this & 0x12) == 0x12 ) return this >> 5; return this->vtbl->GetLength(this); } Nscript supports "short" strings, with length and values contained in the handle and "long" strings with out-of-line memory. If the string is "long" (or appears to be due to type confusion), a vtable call is made to retrieve the length. Integer handles are represented as four-byte values with the final bit set to one by the engine. The integer itself is left shifted by one bit, and the final bit set to create the handle. Handles to most objects, including strings are represented as the value of the pointer to the object with no modification. Therefore, this type confusion allows an integer to be specified and treated as pointer (though the bits need to shifted to get the correct value in the handle, and only odd pointer values are possible). To reproduce this vulnerability, download the attached testcase. The debugging session below was captured after visiting a website that did this: <a href="testcase.txt" download id=link> <script> document.getElementById("link").click(); </script> ``` 3: kd> !process PROCESS 8805fd28 SessionId: 0 Cid: 0afc Peb: 7ffdf000 ParentCid: 01c8 DirBase: bded14e0 ObjectTable: bfb99640 HandleCount: 433. Image: MsMpEng.exe 3: kd> !token -n _EPROCESS 8805fd28, _TOKEN 00000000 TS Session ID: 0 User: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM) 3: kd> .lastevent Last event: Access violation - code c0000005 (first chance) debugger time: Fri May 5 18:22:14.740 2017 (UTC - 7:00) 3: kd> r eax=00000010 ebx=1156c968 ecx=41414141 edx=115730f8 esi=68bd9100 edi=41414141 eip=68b1f5f2 esp=0208e12c ebp=0208e134 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010293 mpengine!FreeSigFiles+0xec822: 001b:68b1f5f2 8b07 mov eax,dword ptr [edi] ds:0023:41414141=???????? 3: kd> lmv mmpengine start end module name 68790000 6917a000 mpengine (export symbols) mpengine.dll Loaded symbol image file: mpengine.dll Image path: c:\ProgramData\Microsoft\Microsoft Antimalware\Definition Updates\{1C2B7358-645B-41D0-9E79-5FA3E5C4EB51}\mpengine.dll Image name: mpengine.dll Timestamp: Thu Apr 06 16:05:37 2017 (58E6C9C1) CheckSum: 00A1330D ImageSize: 009EA000 Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4 3: kd> u mpengine!FreeSigFiles+0xec822: 001b:68b1f5f2 8b07 mov eax,dword ptr [edi] 001b:68b1f5f4 56 push esi 001b:68b1f5f5 8b7008 mov esi,dword ptr [eax+8] 001b:68b1f5f8 8bce mov ecx,esi 001b:68b1f5fa ff15c0450e69 call dword ptr [mpengine!MpContainerWrite+0x35f3a0 (690e45c0)] 001b:68b1f600 8bcf mov ecx,edi 001b:68b1f602 ffd6 call esi <--- Jump to attacker controlled address 001b:68b1f604 5e pop esi ``` Before executing JavaScript, mpengine uses a number of heuristics to decide if evaluation is necessary. One such heuristic estimates file entropy before deciding whether to evaluate any javascript, but we've found that appending some complex comments is enough to trigger this. The attached proof of concept demonstrates this, but please be aware that downloading it will immediately crash MsMpEng in it's default configuration and possibly destabilize your system. Extra care should be taken sharing this report with other Windows users via Exchange, or web services based on IIS, and so on. As mpengine will unpack arbitrarily deeply nested archives and supports many obscure and esoteric archive formats (such as Amiga ZOO and MagicISO UIF), there is no practical way to identify an exploit at the network level, and administrators should patch as soon as is practically possible. We have verified that on Windows 10, adding a blanket exception for C:\ is enough to prevent automatic scanning of filesystem activity (you can still initiate manual scans, but it seems prudent to do so on trusted files only, making the action pointless). This vulnerability was discovered by Natalie Silvanovich and Tavis Ormandy of Google Project Zero. This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public.
    idSSV:93085
    last seen2017-11-19
    modified2017-05-09
    published2017-05-09
    reporterAnonymous
    titleMsMpEng: Remotely Exploitable Type Confusion(CVE-2017-0290)

The Hacker News