HOWTO: Use IIsWebFile to Securely Run CGI in /cgi-bin from the root directory

One of the least leveraged features of IIS is the IIsWebFile, which can work absolute wonders within the proper configuration, as I will shortly show...

Question:

Hi

I have a CGI script (in perl) in a cgi-bin directory. I want to be able to run that script from the root of the site without the user seeing the ugly URL.

I don't want to make the root directory executable.

For example:

Instead of: https://www.mysite.com/cgi-bin/script.cgi
I want:     https://www.mysite.com/

I tried putting a Server.Transfer in index.asp but that didn't seem to like calling non-asp.

I am using a 3rd party host so don't have a lot of control although they would likely change settings if I asked.

Any pointers would be greatly appreciated.

Thanks in advance,

Answer:

IIS supports this behavior intrinsically without needing external plugins nor ASP pages. All you need to do is set up a IIsWebFile with the proper configuration. I will first give the necessary steps in sequence, then explain what is happening with each step, and finally how it all works together at runtime.

The Non-Obvious Steps...

 cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/DefaultDoc Document.foobar
Create a zero byte file at /Document.foobar
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs CREATE W3SVC/1/ROOT/Document.foobar IIsWebFile
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/Document.foobar/AccessFlags 512
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/Document.foobar/ScriptMaps "*,C:\perl\bin\perl.exe %SYSTEMDRIVE%\Inetpub\Scripts\Script.cgi,1"

Now, make a request to https://www.mysite.com/ and notice that script.cgi gets executed and its response sent back. Notice that:

  • The file "script.cgi" does not even need to be in /cgi-bin
  • No configuration of the root directory needs to change other than DefaultDoc (expected, since you want special behavior for the file named "/" [i.e. default document]). In particular, root directory does not need to allow script nor executable, yet requests to the root's default document can execute a script...
  • The extension of the bogus Default Document does not need to be scriptmapped.

In other words, it does exactly what you are asking for and nothing else, no security issues, and runs circles around .htaccess

The Details

Here is the step-by-step explanation of what each above step accomplishes:

  1. In order to catch requests to the Default Document, you must set the DefaultDoc property to some named resource. In this case, I set it to be a resource named "Document.foobar".

  2. Since the Default Document routing in IIS will check for the existence of the named Default Document, I have to create a zero-byte file of that name to get this going.

  3. This is the magical step where I create an IIsWebFile that corresponds to the URL of /Document.foobar . This is used for all subsequent behaviors.

  4. I set the AccessFlags applicable to the URL /Document.foobar to 512, which only enables Script execution. Neither Read nor Execute permissions are needed to execute a script - this is why I set 512 instead of 517 = 1(Read) + 4(Execute) + 512(Script)

  5. I set the ScriptMap applicable to the URL /Document.foobar to be a wildcard, so it is applicable to everything sent to the URL, and for each such request have IIS execute a commandline of

    C:\perl\bin\perl.exe %SYSTEMDRIVE%\Inetpub\Scripts\Script.cgi

    Now, the trailing "1" in the configuration is important because it makes the mapping into a Script Engine based scriptmapping (so code execution will not need the Execute permission). If you set it to "0", then the prior AccessFlags setting needs to be 4 instead of 512.

Basically, what I am doing is setting an arbitrary URL resource as the Default Document and then using IIsWebFile to modify the execution behavior associated with exactly that URL resource and nothing else.

Finally, at Runtime, this is what happens:

  1. When you make a request to https://www.mysite.com/ , IIS eventually routes the request handling to the default document handler.
  2. Since you set DefaultDoc to be "Document.foobar", the handler looks for the file on disk, notices it is there, and proceeds to execute the URL "/Document.foobar".
  3. To execute that URL, IIS gathers the metadata applicable to the URL. This is basically a merge between the inherited values from w3svc/1/root and the additional override metadata provided by our IIsWebFile for the URL "/Document.foobar", which modify the AccessFlags and ScriptMaps
  4. The AccessFlags modification is why the root does NOT need script/executable permissions - because we already set those permissions on the specific URL of "/Document.foobar" that is being executed as the default document. You can view setting script/executable permissions at the virtual directory level as setting it at the parent URL and "inheriting" it to any child URL resource - we are simply doing the reverse here to get more control.
  5. The ScriptMaps modification controls the execution of that single resource, so setting it to directly run PERL with a script name will cause that perl script's output to appear as the response to the original request to the default document.

Voila! Enjoy.

//David