More options on protecting against recent IE vulnerabilities on a domain

The VML vulnerability continues to haunt us. According to SANS the exploit is "spreading," although SANS is not giving any details on what the relative spread is, or what intelligence it is basing that claim on. There is also now an unofficial patch from a group calling themselves the "Zeroday Emergency Response Team". The patch is truly just that: it creates a copy of the vulnerable DLL and performs a binary patch on it. It then unregisters the vulnerable original, and registers the patched version. Not having verified the actual machine code that constitutes the patched code I cannot say how exactly that works. Suffice to say that I would be extremely wary about deploying an unofficial patch. In the end, it is a risk-management decision though. The same considerations I discussed in regards to the WMF vulnerability in January applies here too. If you have an absolute need to view VML pages between now and October 10, when the official security update is currently scheduled to come out, and you feel that you cannot live with the risk of getting attacked, then the unofficial patch may be your only option. Personally, I worry about putting unverified and untrusted binaries on my system, and about the likelihood that they are going to be any higher quality than the ones Microsoft released. Consequently, I will not use the unofficial patch, nor can I think of anyone I would recommend it to.

This leads us to look for another option for protecting against the attack. On Tuesday I showed one method. That method works by setting an access control list (ACL) on the associated DLL to prevent read-access to it. This works, but can cause problems with Windows 2000. On Windows 2000 the Automatic Updates mechanism will attempt to open the DLL and get an access denied error. This error will cause it to try to replace the file with the most recent version, which is the one announced in Microsoft Security Bulletin MS04-028. However, that replacement will fail as well, causing the cycle to repeat all over again.

There are several other techniques to resolve the problem though, and perhaps the most robust is to unregister the vulnerable DLL entirely. This is done using the regsvr32.exe tool, but requires us to run commands on all the systems we want to protect. If we have an Enterprise Management System (EMS) this should be a snap, but if we do not, we need to develop a way to achieve the same result. A natural method to run commands are either logon scripts or startup scripts. Logon scripts are batch files that run when a user logs on to the system. A startup script is essentially like a logon script, but rather than runnig in the user's security context when the user logs on it runs in the context of the LocalSystem account when the machine boots. This makes it a much better option than a logon script for what we are trying to achieve because all the work-arounds for the problem represent administrative actions that users running with least privilege, as all users should, cannot take.

If we are going to use a startup script to protect against the VML vulnerability (CVE-2006-4868) we may as well make it block the DirectAnimation Path issue (I will refer to it as the "DAXCTL vulnerability" from now on; officially it is CVE-2006-4777) that came out last week while we are at it. This vulnerability is similar in scope to the VML vulnerability, but has a different mitigation strategy. The DAXCTL vulnerability is mitigated using a kill bit instead of by unregistering the DLL. The kill bit is a flag on an ActiveX control that causes IE to refuse to invoke the control. That requires modifying the registry on the system, which you can do several ways, including with a custom group policy template. However, if you are using a startup script already it is easy to add this functionality to the script.

The VML mitigation is simple. All we do is run the command Microsoft gives in the advisory:

regsvr32 -u "%ProgramFiles%\Common Files\Microsoft Shared\VGX\vgx.dll"

We will modify this just slightly. by replacing "%ProgramFiles%\Common Files" with "%CommonProgramFiles%" we can make the script work on non-English systems as well. We are also going to add the -s switch to run the command silently.

To mitigate the DAXCTL vulnerability we need a registry script that sets the kill bit. We can then call that script using the regedit.exe command to import it into the registry. However, that means we need to put it somewhere where the clients can read it while starting up. This does turn out to be a bit tricky. The current working directory while processing startup scripts is %systemroot%, and the assumption is that we cannot deploy files to every host, which means we cannot rely on the registry script being in the current working directory.

The natural place to put the registry script is in the sysvol folder on the domain controllers, but that means we need an absolute path to that folder. The first thing we need for that is the name of the domain. While there is an environment variable called %USERDNSDOMAIN% that we could use to obtain this value, the environment block where it is specified is not available when startup scripts run. In addition, if we put the script in the Group Policy Object (GPO) directory we would also have to modify the script to include the GPO GUID, which is unique for each script. One way to resolve all this is to use the %PATH% variable is available to us and contains this value:

\\<dns domain name>\sysvol\<dns domain name>\policies\<GPO GUID>\machine

I am a bit distrustful of relative paths though specified in environment variables though, especially as I do not get instant feedback on whether they worked or not, so I do not like this idea. Alternatively, we can specify the full path to the registry script in our startup script. but that means that we have to modify the script each time we move it to another domain or each time we specify it in a different GPO.

The approach I like best is to just write the script on the fly. Since it is such a simple script this is easy. That way our startup script is totally portable regardless of the name of the domain we put it on. 

Putting it all together we end up with a script that looks like this:

=======START OF SCRIPT=========

@echo off

REM Print some debugging info
date /t >> %systemdrive%\ProtectIE.out
time /t >> %systemdrive%\ProtectIE.out

REM Start writing the header to our registry script
echo Windows Registry Editor Version 5.00 > DAXCTL.reg
echo. >> DAXCTL.reg
echo [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{D7A7D7C3-D47F-11D0-89D3-00A0C90833E6}] >> DAXCTL.reg

if "%1" == "" (
 echo ERROR: You must specify either "enable" or "disable" on the command line for this script to have any effect >> %systemdrive%\ProtectIE.out
 goto cleanup
)


if /i disable == %1 (
 echo Disabling VML and DAX  >> %systemdrive%\ProtectIE.out
 
 REM In case the ACL-workaround has been applied, remove the Everyone ACE
 cacls "%CommonProgramFiles%\Microsoft Shared\VGX\vgx.dll" /e /r Everyone >> %systemdrive%\ProtectIE.out
 
 REM This unregisters the vgx control that enables VML
 regsvr32.exe /s /u "%CommonProgramFiles%\Microsoft Shared\VGX\vgx.dll" >> %systemdrive%\ProtectIE.out

 REM Finish creating our reg file. This one sets the kill bit
 echo "Compatibility Flags"=dword:00000400 >> DAXCTL.reg

 REM Call the reg file
 regedit /s DAXCTL.reg >> %systemdrive%\ProtectIE.out

 goto cleanup

) else (
 if /i enable == %1 (
  echo Enabling VML and DAX  >> %systemdrive%\ProtectIE.out
 
  REM In case the ACL-workaround has been applied, remove the Everyone ACE
  cacls "%CommonProgramFiles%\Microsoft Shared\VGX\vgx.dll" /e /r Everyone >> %systemdrive%\ProtectIE.out
  
  REM This registers the vgx control that enables VML
  regsvr32.exe /s "%CommonProgramFiles%\Microsoft Shared\VGX\vgx.dll" >> %systemdrive%\ProtectIE.out
 
  REM Finish creating our reg file. This one sets the kill bit
  echo "Compatibility Flags"=dword:00000000 >> DAXCTL.reg

  REM Call the reg file
  regedit /s DAXCTL.reg >> %systemdrive%\ProtectIE.out

  goto cleanup
 ) else (
  echo ERROR: You must specify either "enable" or "disable" on the command line for this script to have any effect >> %systemdrive%\ProtectIE.out
  goto cleanup
   )

  )

:cleanup
REM Delete the registry script
DEL DAXCTL.reg

echo Finished processing >> %systemdrive%\ProtectIE.out
echo. >> %systemdrive%\ProtectIE.out
echo ======================================= >> %systemdrive%\ProtectIE.out

=======END OF SCRIPT=========

The script itself should be pretty self-explanatory at this point. I added some debugging information to it that seemed interesting. You can remove that (most of the "echo" and >> commands) if you do not want it.

One thing that is worth explaining though is that this same script can be used both to mitigate the problems by disabling the related functionality and to turn the functionality back on again once you have deployed the security updates. The script takes a command line argument of either "enable" or "disable" that tells it which of the actions to take. "Disable" disables the vulnerable functionality and turns on the protection, and "enable" enables the functionality and therefore turns off the protection. Note also that you can test the script by running it interactively, although you get no output other than the debugging file if you do so. Of course, you can also call the script from an EMS if you have one of those.

To create a GPO that runs this script, take the following steps:

  1. Copy the script above (everything between the begin and end tags) and paste it into a new text document. Save the document as "ProtectIE.cmd". Alternatively, just download and expand the ProtectIE_vX.zip file attached to this post.
  2. Copy the ProtectIE.cmd file to \\<your domain>\sysvol\<your domain>\scripts. where you replace "<your domain>" with the full DNS name of your domain.
  3. Open the GPMC (if you do not have the Group Policy Management Console, you need to get it. Strictly speaking you can manage GPOs without it, but you really don't want to)
  4. Right-click the domain or OU where you want to link the GPO - you may as well do it at the domain level - and select "Create and Link a GPO Here..." Name your new GPO "ProtectIE"
  5. Right-click the GPO ProtectIE and select "Edit..."
  6. Expand "Computer Configuration:Windows Settings" and click on "Scripts (Startup/Shutdown).
  7. Double-click "Startup" in the right-hand pane
  8. Click "Add..."
  9. Browse to \\<your domain>\sysvol\<your domain>\scripts and select "ProtectIE.cmd". Click "Open"
  10. In the "Script Parameters:" box type "Disable" without the quotes. Click "OK."
  11. Click "OK" again.
  12. Close the GPO editor and go back to the GPMC
  13. In the "Security Filtering" pane remove "Authenticated Users" and click Add...
  14. In the text box called "Enter the object name..." type "Domain Computers" or some other relevant group that you want to apply the policy to. Click OK.

Now you need to reboot all the computers before they get the policy applied to them. One of the great comments I got on Tuesday's post was a pointer from Joe to http://www.specopssoft.com/products/specopsgpupdate/default.asp. Special Operations Software has a tool that enables you to propagate group policy to all the systems on your domain, as well as remotely trigger a restart if you want. That presents its own challenges though. First, if you restart the whole domain at the same time it is likely that your domain controllers (DC) are not ready to serve group policy when your clients are ready to receive it, not to mention how loudly the DCs will complain about every client trying to authenticate at the same time. Used judiciously this could be a valuable tool though. I also would not be overly concerned with servers. These vulnerabilities are primarily a problem when people surf untrusted web sites. If people are using your servers (not including Terminal Servers, naturally) to surf untrusted web sites you have a lot of other problems.

The security updates to fix these issues are currently scheduled for October 10. Once you have those applied you can go back to these instructions and change the parameter "Disable" in step 10 to "Enable" to turn all the functionality back on again.

Attachment: ProtectIE_v1.0.zip
Published Fri, Sep 22 2006 9:49 AM by jesper
Filed under:

Comments

# Shadow said on 23 September, 2006 03:57 AM
The unregister dll command is useless, but it seems some see a different problem than I. Mines a total crash, useless could well be an overstatement since the most recent crash is much better apparent coding by the oponent, as usual, unless such unregistering of the dll and the other advice for the other recent 0-day is the cause of such intensified crash not seen since originally had to shove the data down microsofts throats in order to get the page http://www.microsoft.com/technet/security/Bulletin/MS04-028.mspx written up way back when. Then wasn't their a long delay before XP, if not then XP sp2, was included in the microsft security bull. Perhaps if I played with what your seeing because I visit no such websites you claim as the only cause.
# Torgeir Bakken (MS MVP) said on 25 September, 2006 05:12 AM
Hi Jesper, To access a registry file from a bat/cmd based startup file, you can use the %0\..\ trick (as long as the reg file is placed in the same folder as the bat/cmd). In a bat file, %0 contains the path (inclusive the file name) to the bat file itself. So this should work (using .. to get rid of the bat file name from the path string): regedit /s %0\..\DAXCTL.reg
# jesper said on 25 September, 2006 09:10 AM

Torgeir, I tried that, but it does not seem to work for me. The %0 resolves to the full path name of the script, so let's say that is

\\domain.local\sysvol\domain.local\policies\<someguid>\machine\scripts\foo.bat

When you append \..\ to it you get something entirely wrong:

\\domain.local\sysvol\domain.local\policies\<someguid>\machine\scripts\foo.bat\..\bar.reg

The regedit tool will parse that command and try to add foo.bat to the registry, not the bar.reg file that I want.

# Steve said on 26 September, 2006 11:58 AM
Jesper, It is interesting that neither of these are vulnerabilities in IE7. It does tell me that there are more than graphical changes between the two browsers even before the IE7/Vista combination. I wonder what the support lifecycle policy will be for a free browser? Will it be as long as the 12/24 month timeframe? Logically you would want Microsoft to spend their efforts in hardening one code base rather than two. It just would be nice if a Microsoft person reading this blog would start to think about how long IE6 will be supported so that corporate developers need to know when they need to support IE7 as an option and need to remove IE6 as it will no longer be patched. P.S. Today was the first day that I noticed that CVS.com allowed IE7 browsers to view their site. Besides that, and a warning from Google calendar, I have only seen minor graphical errors in public WebPages.
# Earl said on 26 September, 2006 02:55 PM
Inorder for this batch file to run in a logon script, wouldn't the person have to administrative rights to register or unregister a dll.
# jesper said on 26 September, 2006 03:13 PM

Earl, yes, the user running this would have to have administrative rights. That is why I recommend running the script as a startup script instead of a logon script.

Steve, I think the support lifecycle for IE 7 would be the same as for IE 6? It was supported with the usual n-1 support policy where they supported it for five years. I have no evidence to support or refute that expectation, but that seems logical.

# Torgeir Bakken (MS MVP) said on 27 September, 2006 04:43 AM
Hi Jesper, It might be an issue then using %0\.. in startup scripts then, or maybe with UNC paths. Parsing %0 instead should always work I think: set LaunchPath=%~d0%~p0 echo %LaunchPath% From running "for /?": %~dI - expands %I to a drive letter only %~pI - expands %I to a path only
# Romeo said on 27 September, 2006 08:10 AM
I've written an adm so that the kill bit for the Daxctle.ocx can be set using the computer policy. As the registry is modified in part not suggested to be touched by policies you have todo the following to see the policy in the editor: "View"->"Filtering..." an then uncheck the last checkbox ("Only show policy settings that can be fully managed") ---------------------------------------------- CLASS MACHINE CATEGORY "Microsoft\Advisory\Workaround" POLICY "925444" KEYNAME "SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{D7A7D7C3-D47F-11D0-89D3-00A0C90833E6}" EXPLAIN !!help VALUENAME "Compatibility Flags" VALUEON NUMERIC 1024 VALUEOFF DELETE END POLICY END CATEGORY [strings] help="Sets the kill bit on the Daxctle.ocx suggested by microsoft as a workaround in their advisory. see:\nhttp://www.microsoft.com/technet/security/advisory/925444.mspx\nfor details.\n\nCaution:\nTo revert the workaround once a patch is avaiable don't delete the policy, but just set it to disable" -----------------------------------------------------
# jesper said on 27 September, 2006 08:45 AM

Torgeir, that's the trick. I had forgotten about the ~d and ~p parameters on the argv[0] variable. Not sure how I could have forgotten those, but I did. Pitiful really...

Romeo, that script rocks! I was initially going down this route, but since it can't be enforced I stopped working on it. Very nice though. The nice part about using ADM templates is you can delete the value, which is more of a true reversal than setting it to null.

# Jonathan Starr said on 27 September, 2006 10:46 AM
I have had an issue with the workaround, people using Sage Line 50 Manufacturing/Financial Controller may find that it corrupts its company file at startup after applying the update (approx 30% of my machines had problems). This is easily fixed by re-installing the last hotfix, Sage had no idea why this happens and the hotfix doesn't touch/adjust permissions for the VML DLL or the DAXCTL killbit...... I have now modified the script to only do the killbit, and to reregister the DLL, and the official VML patch has been installed with zero issues. Thanks for the script!
# Romeo said on 27 September, 2006 11:44 AM
1. Sorry for the lousy layout of the script. will post it again with tags hope that works in this blog: 2. what do you mean about "can't be enforced". when connected to the domain at least every 2 hours the computer policy is applied. Your setup is enforced with a reboot and must also have contact to the domain to get the script from the policy. When forcing a reboot the adm gets applied as well as your script. Or am I missing something? 3. when putting a minus before a key in a reg file it gets removed. so put
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{D7A7D7C3-D47F-11D0-89D3-00A0C90833E6}]
in your enable part.

CLASS MACHINE

CATEGORY "Microsoft\Advisory\Workaround"

  POLICY "925444" 

  KEYNAME "SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{D7A7D7C3-D47F-11D0-89D3-00A0C90833E6}"

  		EXPLAIN !!help

  		VALUENAME "Compatibility Flags"

      VALUEON NUMERIC 1024

      VALUEOFF DELETE

  END POLICY

END CATEGORY



[strings]

help="Sets the kill bit on the Daxctle.ocx suggested by microsoft as a workaround in their advisory. see:\nhttp://www.microsoft.com/technet/security/advisory/925444.mspx\nfor details.\n\nCaution:\nTo revert the workaround once a patch is avaible don't delete the policy, but just set it to disable"
# jesper said on 27 September, 2006 05:11 PM

Not being able to enforce the policies was the wrong way to say it. The difference is that settings outside of the policies nodes are "preferences." They are tattooed into the registry and cannot be easily undone. They can be enforced strictly speaking.