HOWTO: Run Console Applications from IIS6 on Windows Server 2003, Part 2


I finally have enough blog entries about various portions of IIS6 request processing that I can stitch together this meta-blog-entry explaining how it all works together and then apply it towards an issue. You probably want to keep a link to this loaded entry. Anyhow, here goes…


Question:


We currently has IIS 6 installed on a Windows 2003 server, and we are attempting to run some executables through the browser. We have “Scripts and Executables” selected for the web application, and we are currently allowing “All Unknown CGI Extensions” and “All Unknown ISAPI Extensions” under Web Service Extensions during testing.


With this setup we are able to run some executables successfully through a browser, like:


Ping
Hostname
netstat
query session


However other executables are not working:


Net
Dirquota
Filescrn
Fsutil quota


We are getting no value for most of these, except “Fsutil quota” which is returning:


The FSUTIL utility requires that you have administrative privileges


I have confirmed that the browser is using an ID that has administrative privileges by returning the userid it is using. This administrative ID does not have any problem running Fsutil quota on the server directly.


What privileges am I missing? Does something else need to be set up, or is it possible the executables not working are not CGI or ISAPI?


Thanks for any help.


Answer:


Ok, this blog entry is unique in that it is the first time that I am aggregating information from all my prior posts and applying it to resolve a problem. I will NOT explain everything in detail – only the new stuff… for everything else, if I pulled the information from a blog entry, it will be linked in this blog entry somewhere appropriate.


Putting it all together


When it comes to request execution on IIS6, this is how I think about things:



  1. HTTP.SYS parses HTTP requests in kernel mode and sends a digested form to user mode w3wp.exe. Interesting details about this process can be found in this blog entry.
  2. IIS6 in user mode w3wp.exe receives this request and determines the URL namespace (and hence all applicable metadata from configuration that affect the execution of this request – which includes the authentication required, the ScriptMaps applicable for the request, etc)
  3. Then, the authentication process runs to determine the user token used to execute a request.

    One of the factors that affect a user token’s abilities is the way the user logon was performed (i.e. logon type). In other words, Interactive logon from Remote Desktop/Local Console is different than the Network logon performed by IIS6. This blog entry describes the differences in more detail.

    Furthermore, IIS6 can perform different logon types depending on authentication protocol as well as by configuration, though the defaults pretty much work and should not be fiddled with.

    So, logon difference alone may affect how FSUTIL.EXE behaves.
  4. In addition to authentication producing a user token, IIS has to determine the correct handler to execute the requested resource (i.e. should the resource be handled as a static file and sent back as-is? or should it go to a CGI/ISAPI to process? or should it produce a courtesy redirect? Etc). This blog entry and this blog entry describes that decision process.
  5. After determining the specific type of handler to execute a request, IIS uses the user token to execute the handler for the request. This blog entry describes how IIS selects the user token to execute the handler.
  6. Additional user code can be run by the handler, which have arbitrary behavior when it comes to user token, privileges, and generated response
  7. The browser client can have automatic behavior which affects the number and sequence of requests made to IIS, as described in this blog entry.

Whew. If you got to here and read through all the associated blog entries, you have just gone through a LOT of details about how IIS6 functions to process requests as well as how it interacts with user identity. Time to put this info to good use and apply it. 🙂


The Problem Restated…


Ok, the following details and conclusions all come from the aforementioned blog entries. If you get lost, I suggest you read ’em and read ’em again until you understand it. 🙂


The first thing that needs to be clear is HOW you are “running executables through the browser”. Since IIS only recognizes static files, CGI/ISAPI, or Scripts (ok, I am simplifying DAV away since it is not involved in this picture), there are two basic ways to run executables:


As a CGI/ISAPI

If you want IIS to directly run the executable as a CGI or ISAPI, then you need to configure “Scripts and Executables” execute permission as well as Web Service Extension for the binary. i.e.



  1. /cgi-bin has “Scripts and Executable” execute permission enabled.
  2. <full-path-to-FSUTIL.EXE>\FSUTIL.EXE is enabled as a Web Service Extension.
  3. You make a request to http://localhost/cgi-bin/FSUTIL.EXE
  4. IIS is going to execute FSUTIL.EXE as a CGI, so it checks it against Web Service Extension. It is allowed.
  5. Thus, IIS will execute FSUTIL.EXE as a CGI EXE using the user token obtained through whatever authentication protocol is negotiated between the browser and server.
  6. The EXE is executed using either CreateProcess() or CreateProcessAsUser() Win32 API call, depending on IIS configuration for CGI execution. If it is CreateProcess(), then FSUTIL.EXE runs with the Process Identity; if it is CreateProcessAsUser(), then FSUTIL.EXE runs with the IIS impersonated identity.
  7. IIS parses the text output from FSUTIL.EXE according to CGI specification and then sends a response back to the client.

    If you use the nph- prefix, IIS won’t parse the text and just send it back as is. Of course, if you happen to send invalid HTTP response using NPH, the client can complain or behave weirdly…

Via a Scriptmapped CGI/ISAPI

If you want to run executables on IIS from a script (i.e. an ASP, ASP.Net, or PHP page is considered a script resource executed by ASP.DLL, ASPNET_ISAPI.DLL, or PHP-CGI.EXE / PHPISAPI.DLL Script Engine, respectively), then you need to configure “Scripts” execute permission as well as Web Service Extension for the appropriate Script Engine. i.e.


MyScript.asp contains the following content which executes FSUTIL.EXE:

<%
set objShell = Server.CreateObject( “WScript.Shell” )
objShell.Run( “FSUTIL.EXE” )
%>


  1. /cgi-bin has “Scripts” execute permission enabled.
  2. %systemroot%\System32\inetsrv\ASP.DLL is enabled as a Web Service Extension.
  3. /cgi-bin has a ScriptMaps property which associates .asp extension to %systemroot%\System32\inetsrv\ASP.DLL as a Script Engine.
  4. You make a request to http://localhost/cgi-bin/MyScript.asp
  5. IIS identifies ASP.DLL as the ISAPI Script Engine to process the /cgi-bin/MyScript.asp resource and checks it against Web Service Extension. Since it is allowed, it executes ASP.DLL using the user token obtained through whatever authentication protocol is negotiated between the browser and server.

    Note: even though the ASP page runs FSUTIL.EXE, FSUTIL.EXE does NOT need to be in Web Service Extension because IIS never runs nor knows about FSUTIL.EXE. IIS only knows it is running ASP.DLL so that is what needs to be enabled as a Web Service Extension.
  6. ASP.DLL will keep the impersonated identity from IIS and parse/execute the script code in MyScript.asp using Windows Scripting Host. objShell.Run() translates into a CreateProcess() Win32 API call, and FSUTIL.EXE runs using the Process Identity (this is how CreateProcess is documented to work!)
  7. FSUTIL output is unknown to ASP (and IIS) unless you capture the output of objShell.Run() somehow and then Response.Write() it so that IIS knows about it.

Ok, back to the Original Problem


At this point, a couple differences should jump out at you:



  1. When you ran FSUTIL QUOTA directly, it was executed by a user token with Interactive logon type. When you ran it from IIS, the user token has Network logon type.
  2. Depending on how you ran the executables, FSUTIL.EXE was either called via CreateProcess() or CreateProcessAsUser(). The critical difference between them is that CreateProcess() implicitly uses the Process Identity to create the new FSUTIL.EXE process, while CreateProcessAsUser() uses the impersonated identity obtained by IIS through authentication.

    The Application Pool Identity of the Application Pool servicing the URL namespace that executed the request determines the Process Identity

    The authentication protocol negotiated between the browser and server determines the impersonated identity, in a manner consistent with this blog entry.

I do not know if FSUTIL.EXE has special code that cares about the above details. But I do know that CMD.EXE does… so YMMV.


What about the Executables which appear to return Nothing


As for the question of whether the executables “not working” are not CGI/ISAPI… After executing a request handler, IIS expects a valid HTTP response to come back so that it can send it back to the requesting client.


CGI is nothing more than an executable which outputs text which conforms to the CGI specification (this blog entry describes an interest interaction between CGI and HTTP). If the output conforms to specification, IIS sends it back as-is; otherwise, IIS returns a 502 response.


Since IIS6 in WS03SP1 is lax about CGI conformance and no longer requires the status: and content-type headers, if the executable happens to output a CRLF before printing data, that data gets interpreted as response entity by IIS and sent back to the client as-is. The following are some illustrative examples with CRLF clearly marked as \r\n.


NET.EXE Output:
The syntax of this command is:\r\n
\r\n
\r\n
NET [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |\r\n
HELPMSG | LOCALGROUP | NAME | PAUSE | PRINT | SEND | SESSION |\r\n
SHARE | START | STATISTICS | STOP | TIME | USE | USER | VIEW ]\r\n

PING.EXE Output:
\r\n
Active Connections\r\n
\r\n
Proto Local Address Foreign Address State\r\n
TCP TEST-MACHINE:80 0.0.0.0:80 ESTABLISHED\r\n

As you can see, NET.EXE fails because its CGI output starts off with what looks like an invalid header named “The syntax of this command is:” (it has spaces in it, and no header value) prior to the double CRLF, so it fails to parse correctly and IIS returns a 502 “Bad Gateway” response. Meanwhile, PING.EXE succeeds because it starts off with a CRLF that tells IIS to use a 200 OK response header ahead of the PING output.


Now, if you prefix the resources with NPH- (i.e. nph-netstat.exe or nph-ping.exe), then IIS will NOT process the CGI output and just send it as-is back to the client. That is what NPH means – Non-Parsed Header. Of course, this avoids the 502 response from IIS, but if the output is not proper HTTP, it will now confuse the client, which can have its own arbitrary behavior…


Basically, these console commands are not designed to produce output that conforms to CGI specification, so the fact that they work when invoked as CGI by IIS is purely random, mostly depending on whether the output begins with something that looks like broken HTTP headers or not.


Conclusion


I congratulate you on getting to the end of this marathon of a blog post. I finally got a juicy topic to explain and a lot of blog entries to logically stitch together. 🙂


Hopefully, this clears up common confusions surrounding:



  • IIS request execution logic
  • User identity used to execute a request
  • Executing an EXE via CreateProcess() is different than via CreateProcessAsUser(), regardless if you made IIS directly execute it as a CGI or indirectly through a Script
  • Executing an ISAPI uses the impersonated identity by default, but can be the Process Identity, depending on the ISAPI
  • How some EXEs magically work as “CGIs” but others fail
  • How some programs, like CMD.EXE, simply fail depending on whether invoked via CreateProcess() or CreateProcessAsUser()… and maybe FSUTIL.EXE is in the same category

//David

Comments (47)

  1. jvierra says:

    That was teh best short explanation of how it all works that I have ever seen.  I think, for the first time,  I understand all of to pieces.

    You should save this and post it somewhere obvious.

  2. David.Wang says:

    jvierra – thanks for the suggestion. I am planning to do that with this post because it lays down the foundation for understanding how request processing works on IIS6 – it is rather straight forward once you see it all – and should help in understanding how to configure and troubleshoot it, too.

    I will probably make additions to it as time goes on (there are still tons of interesting details missing), as new posts, etc…

    //David

  3. jvierra says:

    IIS has always been somewhat poorly documented with MS seaming to go for web site and business apps and not providing in-depth technical information about how IIS works under the covers.

    I have been using IIS since it first debuted.  I have found that, once you get to know it it is really quite simple.

    Except when I have to do something extra.  esterday I needed to set up a scond SSL site on a WS2003 SP1 server and relaized that there is no way to specify a header value.

     Looking at teh IIS manager page I thought that ther must be a way to do this.  I though – maybe MetaDat Editor???

    I also had just discovered the following webcast.  Who knew?

    TechNet Webcast: Using Host Headers with SSL-Enabled Web Sites in IIS 6

    https://msevents.microsoft.com/cui/EventDetail.aspx?culture=en-US&EventID=1032280959&EventCategory=5.0 (Level 200)

  4. Maurits says:

    the leading-newline problem could be worked around by using a batch file.  Consider:

    :: net.bat ::

    @echo off

    rem issue a blank line to terminate the headers

    echo.

    rem run "net.exe" with the arguments supplied to net-iis.bat

    net.exe %*

    rem Don’t leave off the ".exe" above or you’ll infinitely recurse!

  5. jvierra says:

    Well, as long as we are going to use scripts how would using this work:

    set objShell = Server.CreateObject( "WScript.Shell" )

    objShell.Exec( "FSUTIL.EXE" )

    while not input = "someline"

       input = objShell.Readln()

    wend

    Do what you need with reformatting the lines and sending them to the browser.

    This would avoid some issues but is still not the easiest way to set up.

    I would try and build everyting in script embedded in the ASP web page.  All of the commands listed can have script couterparts.  You would need to run the web application at an elevated privilege or only allow admins to use it.  All things like quotas and such are available from either AD or from WMI.

    I would also consider using NET 2.0 as it has access to the system management objects and can be made to work in a web application.

    The only issue I have found with this is that the web server cannot access other hosts under any circumstances without being allowed to delegate.

  6. David.Wang says:

    jvierra – I agree. IIS has long been a platform that played second-viola to all the other Microsoft Servers. See:

    http://blogs.msdn.com/david.wang/archive/2006/04/13/IIS7_Product_or_Platform.aspx

    Yes, with Windows Server 2003 SP1, you can use SSL with Host headers, with the obvious caveats:

    – For a given IP:Port which varies by Host header, the SAME SSL Server Certificate must be returned (remember, we still need the Host header to determine the right website’s Server Certificate to use for SSL, but we need to negotiate SSL to first decrypt and obtain the Host header)

    – Since Certificate Authorities cannot give out wildcard domain SSL Server Certificates, you cannot use this to host *.com domains. However, subdomains should be easy (subA.domain.com and subB.domain.com).

    //David

  7. David.Wang says:

    maurits – Details, details… 🙂

    Since .bat is not recognized by IIS as an executable extension, it is really the same as ASP from an IIS-perspective (i.e. it is a ScriptMapped scenario).

    In particular, .bat extension has to be ScriptMapped to CMD.EXE to function, and CMD.EXE has extremely tight security checks and is a huge additional dependency that is not present in the simple "run NET.EXE as a CGI by IIS" use.

    However, the nice thing is that the .bat file is executed under CreateProcessAsUser().

    //David

  8. David.Wang says:

    jvierra – The approach of executing FSUTIL.EXE with objShell.Exec() would execute FSUTIL.EXE using CreateProcess(), which uses the process identity and not impersonated identity. This means that if you:

    1. configure the Application Pool to be Network Service

    2. enable authentication

    3. an Administrator logs in to run this ASP page

    It STILL gets run as "Network Service" instead of Administrator – probably not what you expected.

    Meanwhile, if you use ADSI/WMI from Script, they would most likely use the impersonated identity (unless the code internally does RevertToSelf()).

    I am not certain that .NET 2.0 makes it any easier/better because System.Management is just a managed code layer sitting on top of WMI, which is all native code.

    RE: delegation – that has NOTHING to do with the web server nor technology and everything to do with security basics. Just because I authenticated to the web server to execute an ASPX page does NOT mean the web server can take and use my identity to contact my bank electronically and withdraw my money from my bank account as ME.

    Also read this blog entry:

    http://blogs.msdn.com/david.wang/archive/2005/07/06/SSO_ISAPI_Considerations_2.aspx

    //David

  9. jvierra says:

    David –

    According to the Webcast wildcard certificates CAN be used with SP1.  I am just getting ready to try this on a test server.  Somtime next week I hope.

    Part of the main theme of the webcast is "using wildcard certs" for cross site security.

    Is it possible the the Webcast is wrong.  The demo appears to work.

  10. jvierra says:

    David – My point with using WMI/ADSI is that it is easier to manage the code ubder ASP and can execute in the users security context for MOST things that admins want to see.  

    Two problems come up when trying this.

    1.  Non-admin users can’t execute most WMI and some ADSI requests.

    2.  WMI queries against remote machines will only work if the IIS machine is "Trusted for Delegation" in AD.

    Of course if you run the web site under administrative credentials some of these things can be circumvented but that is never a good choise in my mind.

  11. jvierra says:

    ADDENDUM to previous comment:

    For clarity I want to add that this will only work WITHIN the domain and using  "Integrated Security".

    David – your blog on SSO/XAPI was excellent and should be helpful in settling issues of "how" and "why" authentication works and doesn’t work.

  12. David Wang says:

    This is a frequently asked question about IIS6 extensibility – how to access the request entity body…

  13. PL says:

    Not sure why this blog is so classic ASP centered, this and most things today is best solved with ASP.NET.

    For example to launch processes on the server and retrieve the result you simply do this:

    1. Make sure the ASPNET user has enough priviliges, meaning if you want to copy files or anything you need to set that up on the server.

    2. Some programs require desktop access, to enable this for ASP.NET worker processes double click the WWW publishing service and under Logon check the box that says "allow interaction with desktop". If the process doesnt launch at all this is the thing you need to do.

    3. Use a page like this:

    <%@ Page LANGUAGE="C#" CODEPAGE="65001"%>

    <%@ Import Namespace="System.Diagnostics" %>

    <%@ Import Namespace="System.IO" %>

    <script language="C#" runat="server">

    protected void RunCommand(object sender, EventArgs e)

    {

    string sCommandTool = @"E:datenbatchtest.cmd";

    string sCommandArguments = "";

    string sOutput = "";

    try

    {

    using( Process p = new Process() )

    {

    p.StartInfo.UseShellExecute = false;

    p.StartInfo.RedirectStandardOutput = true;

    p.StartInfo.FileName = sCommandTool;

    p.StartInfo.Arguments = sCommandArguments;

    p.Start();

    sOutput = p.StandardOutput.ReadToEnd();

    p.WaitForExit();

    }

    litrOutput.Text = sOutput;

    }

    catch (Exception ex)

    {

    litrOutput.Text = "Error: " + ex.Message;

    }

    }

    </script>

    <html>

    <body>

    <form runat="server">

    <asp:Button id="btnRun" runat="server" OnClick="RunCommand" Text="Run !"/>

    <p>

    <pre><asp:Literal id="litrOutput" runat="server" EnableViewState="False"/></pre>

    </form>

    </body>

    </html>

  14. David.Wang says:

    PL – I prefer ASP because it is simple, cleanly illustrates the issue, and allows me to explain what is going on without all the fluff. I am interested in explaining the underlying concepts and issues which stay valid through time, and not whatever is the latest and hotest technology.

    ASP.Net can only obscure the issue because it involve the Managed Code layer as a system… when we ultimately only care about the Win32 function calls generated by that system

    FYI:
    1. What you just wrote in ASP.Net is much shorter in ASP.
    2. Since the issue has nothing to do with ASP or ASP.Net but rather whether the code ultimately calls CreateProcess() or CreateProcessAsUser(), giving an ASP.Net-based solution does not seem fiiting.
    3. My explanation can be equally applied to PHP, Perl, etc – while an ASP.Net-based solution without explaining the fundamentals only caters to the ASP.Net user.

    //David

  15. PL says:

    Oh my good, you just wrote two pages about it but you claim it was shorter ?

    I can make it shorter if you want me to:

    <%

    System.Diagnostics.Process.Start("whatever");

    %>

    I think you better buy a book about .NET, can’t belive you work at MS.

  16. David.Wang says:

    PL – yo, chill… we’re both on the same side, just have different approaches, hear me out.

    I like to explain the details of what is going on so that anyone can apply knowledge to make their own decisions – critical thinking and problem solving – i.e. teach a man to fish, he will never go hungry

    You mention another popular approach – pattern matching – just give me a working solution to the problem. Fast and to the point.

    I am not saying that either is good/bad; everyone learns and teaches differently – so please be tolerent.

    RE: Book about .NET

    Me, I never need to buy a book about .NET; I already studied its core – MSIL / CLR – as well as the C# Specification. The .Net Framework is all applied stuff on top of the core. I don’t need to buy a book – I just need to reflect on the DLL for its APIs and I am ready to go.

    With your approach, one has to buy a book about .NET and every new Namespace/API because that is how you obtain new information when you do not know how the core really works.

    Yes, my approach takes way more time (and pages) than yours, but that does not make either less valid. The objectives are different.

    //David

  17. James Vierra says:

    David, PL,

    Yes – youcan run a copy of an exe by altering the nature of IIS maknig it less secure and, yes, there might be a reason for doing this but I would question whether it is the best and safest approach.

    CGI runs EXEs (Console Apps) just fine but on most servers it is disabled by default for a reason.  If not set up carefully it can create a security issue.

    COM is a better approach and more controllable.  NET is even more configurable and potentially much more secure and powerful.

    If you are just delivering text then HTML is probably the best solution.  If you need to dio some simple server-side variable stuff then ASP is fine, easy and light weight.

    If you need access to data and other more complex operations in a highly interactive way then NET is currently, in my opinion , the best solution yet.

    Admins would like to run certain console applications under IIS for adminstrative convenience.  Usually they want to run non-NET executables, batch files and scripts.  This always runs into issues of output formatting and access authorization and authentication. More than any pervious technology NET 2.0 addresses many of these issues and provides good and secure methods for returning information to admins and users however there is a larger amount of setup overhead necessary to accomplish this.

    Davids answer was more than I would have attempted.  In my opinion if it doesn’t run under IIS default setup then it shouldn’t be run.  NEt can accomplish a ping interface that is more flexible than running ping.exe and doesn’t require altering the default strong security.

    I love ASp book recommendations.  I read anything that may add to my understanding of almost anything but nothing compares to teh knowledge that you get by being there at the birthing of a fundamental technology.  Nothing compares to understanding the nuts and bolts that were used to build that technology.  My funfdamentals still sit on top of the bit toggling and flip-flop wiring that I did way back when…

    One question PL.  What kind of an application would one build using the code you posted?

    <%

    System.Diagnostics.Process.Start("whatever");

    %>

    What would the "whatever" do and why would you do it that way as apposed to some more IIS-like way.

    I am hoping you have an example that will make this useful knowledge because I spen some time running things this way and found that, for the process to be useful, I would need to hook it up to some form of input/output.  This is not hard to do if we modify the code to grab the process object but then it becomes more like normal aspx NET programming.

    Would you want to use this to start a service?  How about to run a job?  Well, in those cases, I would prefer other APIs designed for that so, then, what would we do with it.

    Davids discussion of "Console Apps from IIS" was very enlightening to me.  I had though I knew all of the ins and outs of this sort of thing but he put it all in a slightly new way which I found very helpful.  It will come in handy.

    However, you, PL, propose something that may also come in handy. I just don’t see what it is at this point.  I, too, need to run things remotely and have felt that IIS could be a big help in doing this.  Anything that could make this easier would be appreciated.

    Now for my question.  David.  What are your thoughts on using "middle-tier" objects with elevated permisions to allow users to read data from rpotected objects?  There are security riskd in this I believe but some sort of guidance might help point the way to allowing admins a way to access protected resources through a web server without exposing the network to harm.

    I know in other posts you have addressed this issue to some degree.  It seems that the pieces may not have been drawn tightly together for this particular arena.

  18. Jason says:

    I’ve been bashing my head against the monitor trying to get something like this to work consistently.  The issue I have is that i’ve pretty much turned my server into the definition of insecure by granting every know permission to every possible user/group.  A simple asp script to launch something like notepad

    <%

    set objShell = Server.CreateObject( "WScript.Shell" )

    objShell.Run( "notepad.exe" )

    %>

    will work perfectly until I logoff the server (i have IIS set to interact with the desktop so i can see the results).  Once I log off, the asp page causes an application error on the server:

    "The application failed to initialize properly (0xc0000142).."

    However, this error will magically go away after a few minutes/hours – that is, until I log off again.

    It seems like a permission error, but at this point an anonymous user from a kindergarten class could tell my server to format its hard drives.  Argh.

  19. Nigol says:

    I’ve been reading this blog over and over again and it helped me a lot – thanks.

    However, I’m still having trouble executing some commands from a CGI script (in Perl) when I’m not a user of the administrators group.

    I created a web page where a CGI script executes the CVS command line tool. I also restricted this page to some users of our Windows domain by creating a local group (adding all required users to the group) and granting access only to this group.

    If the user is as well within the local administrators group everything works fine. If the user is not in this group the command (and lots of others) is ignored.

    Can you help?

    Thanks very much,

    Nigol

  20. David.Wang says:

    Nigol – Did you read this link from this entry, which describes the behavior you observe?

    http://blogs.msdn.com/david.wang/archive/2006/04/09/HOWTO_Run_Console_Applications_from_IIS6_on_Windows_Server_2003.aspx

    I believe what you are seeing is that Perl launches commands using CMD.EXE, which only allows Administrators from IIS to use it on Windows Server 2003.

    Thus, the issue is not that the command is “ignored” but that the command silently failed with access denied.

    //David

  21. Nigol says:

    Hi David,

    Thanks very much for your reply. I had read the article you mentioned, but what I had done wrong was to set the ACL on the commands I wanted to execute (e.g. cvs.exe) instead of CMD.EXE !!!

    Having set the ACL on CMD.EXE everything is working now.

    Thanks once again,

    Nigol

  22. Altex says:

    I have IIS6 and PHP as ISAPI. I configured PHP to run as ISAPI module and configured application pool for it, so when i execute "whoami" from PHP i see "nt authoritylocal service".

    But the problem is that to run any external console executables from php script i have to set exec&read permitions to cmd.exe for NETWORK SERVICE user. But script runs with LOCAL SERVICE permition. And if i remove NETWORK SERVICE permitions and ad LOCAL SERVICE permitions – then script cat not run external program because of insufficient rights.

    Please, tell me how to configure IIS to run external programs from php (isapi) with local service user rights or what is wrong with cmd.exe permitions, why it needs network service permitions?

    Thanks for help!

  23. deandany says:

    Great article… it removed some blocks in my head regarding security… however i still have not been able to solve my problem after reading this article…. the following code in VB.net web application raises a hidden "Access Denied" error…

    Inside the batch file i am attempting foll. simple copy command

    ———————————–

    copy c:test.log d:test.log

    EXIT

    ———————————–

    In Code i do this on asp.net-button click event

    proc = New Process

           workdir = String.Format("E:")

           proc.StartInfo.WorkingDirectory = workdir

           proc.StartInfo.UseShellExecute = False

           proc.StartInfo.FileName = filepath

           proc.StartInfo.RedirectStandardError = True

           proc.StartInfo.Arguments = "/c E:test.bat"

           proc.StartInfo.CreateNoWindow = True

           proc.Start()

           sr = proc.StandardError

           While (sr.Peek <> -1)

               sr.ReadLine()

           End While

    "sr.readline" consistently gives an access denied error inspite of

    1) impersonation in web config

    2)deny authorization=? in web config

    3)anonymous access setting in Driectory Security in IIS5.0

    4)assign Full control to all the directories/files i used in code using Cacls.exe

    Can anyone solve this? the sample above is a simple test.. if this works i have several things to be done using batch files

    Thanks in advance

    Behappy,

    deandany

  24. David.Wang says:

    deandany – Your issue is described in the link I mentioned in the blog entry.

    http://blogs.msdn.com/david.wang/archive/2006/04/09/HOWTO_Run_Console_Applications_from_IIS6_on_Windows_Server_2003.aspx

    By default, batch file execution using CMD.EXE does not work from IIS6, no matter what you configure.

    //David

  25. Nino says:

    Can we to this on linux aswell ?

  26. Willis says:

    Our administrators can not get php 5 to execute a shell command under win2003/iis6.

    I don’t see why the administrator is not allowed to configure the system so that a specific executable can be called from php.

    It works on apache, but not 2003/iis6.

    Is IIS6 capable of this?

  27. ori says:

    how can i use the CreateProcessWithLogonW() API function from vc6.0 without installing any .net /asp/c# packege on my IDE / computer/

     (I understand it is essential to load a dll with this api, but this is the only thing I can use).

    i need CreateProcessWithLogonW() to do silent log on (with my known username and password) to a remote desktop. (I am a win2000 professional client rdp). I wish to open the remote desktop programatically without the user password popup/window/input fields.

     thenks

        ori

        kovacsio@hotmail.com

  28. David.Wang says:

    ori – I don’t understand your question. VC6.0 can directly call CreateProcessWithLogonW() API. No .Net/ASP/C# required.

    For example, this is exactly what IIS6 does when it launches a w3wp.exe, and IIS6 has no .Net/ASP/C# code.

    //David

  29. David.Wang says:

    Willis – Administrators have an explicit Allow ACL on CMD.EXE to allow them to run shell commands. This is explicitly allowed by IIS6 and Windows Server 2003 security model.

    You must have a system that locked down CMD.EXE or PHP is not running as Administrator to run shell commands. Something is wrong with your server.

    //David

  30. ron says:

    Hi All,

    I like all the discussions above, I used same way as following to load my application,

          ProcessStartInfo startInfo = new ProcessStartInfo(@"C:Program FilesMacromediaFlashPaper 2FlashPrinter.exe");

          Process proc= new Process();

          proc.StartInfo = startInfo;

          startInfo.UserName = "Administrator";

          startInfo.Password = _password;

          startInfo.UseShellExecute = false;

          startInfo.Arguments = …;

          proc.Start() ;

    My problem is, when I use "Administrator" the codes working OK, when I use other kind of users (except "Administrator") or remove username and password, it is not working.

    "Administrator" account is not allow to use in my application, can any one please tell me how to create a user which has a lower right as "Administrator", but still let the application works.

  31. ron says:

    Hi David,

    As my last post, my application is a traditional asp website, so  I can not use web config or add <identity impersonate="true" userName="accountname" password="password" />.

    I made properties to receive usernamse and password, the application is working OK only when user name is administrator,  but I am requested to use a non-admin account.

    Do you know how to fix it ?

    Thanks.

  32. Darwin George says:

    I have a console application that aquires data from external equipment via ethernet packets and builds data files of the raw data then creates PCL/HPGL-2 print files from this data.  I want to launch an external program, a PCL to PDF converter .exe, from within my console application.  I am using cygwin to develop the app with and can’t seem to locate the appropriate functions to allow me to launch the external .exe.  Any suggestions for me.

     deg@vidcoinc.com

    Thank you.

  33. David.Wang says:

    ron – You need to clarify whether FlashPrinter.exe requires the user to have administrative privileges.

    Also, your code snippet is in .Net, but you say your application is traditional ASP website, so that seems to conflict. And if you want to run an application as another user, just use RUNAS.EXE and no need to use .Net.

    As for how to run code using a specific user identity – the answer is linked off this blog post, so I shall leave that exercise to the reader.

    //David

  34. David.Wang says:

    Darwin – There are many ways to launch external programs in Win32, but since you want cygwin, I won’t be able to help.

    I suggest contacting cygwin support for your questions. This is a support forum for IIS/ISAPI related questions.

    //David

  35. Joao says:

    Hello David,

    I have a problem that is exactly what you describe here. I want to execute cvs commands from a web application. I am using XP pro and IIS 5.1. What I do is call cvs using Process.Start() passing arguments in C#.

    The CVS behaves exactly like the "net" command. Apparently it returns nothing. I have success running other things like ipconfig but not with cvs or net. That is, I can get the returned output.

    My problem is that I’m not used to work with CGI/ISAPI and in IIS 5.1 I dont have the Web Services Extension so I am not quite sure How to get this to work.

    I’ve searched everywhere and even tried to make a script in vbs that simply calls cvs and returns the output. But that behaves the same way.

    Can you help me here? I’m starting to get despered! You seem to be by far the person who best understands this issue.

    Thank you!

  36. Jan says:

    Thanks David for te detailed information, was very helpful to get my CGI’s working under IIS 60

    Thank you

  37. BobMonahon says:

    Hello,

    First, thank you for this write up.  It’s the first helpful document I’ve found.  However, I’m still having a problem: from an ASP page, running under IIS 6 on Win2K3, I’m trying to execute a batch file that launches a vb-script.  I get no error message, but nothing runs.  Here is a stripped-down version of my code. (Replacing brackets with parenthesis to avoid HTML hiding the bracketted code)

    ASP Page, located at (web root)pyepye_runload.asp

    (%

    Dim oShell: set oShell = Server.CreateObject("WScript.Shell")

    Dim zBatchFile: zBatchFile = Server.MapPath("..bintest_batch.bat")

    Dim iReturn: iReturn = oShell.run(zBatchFile,1,false)

    Response.Write "Batch launched, rc=" & iReturn

    %)

    If the test_batch.bat runs, it creates an output file in the /bin/ folder.  I gave write permissions to the necessary accounts.

    The result on the ASP Page is: Batch launched, rc=0

    But nothing runs – there is no output file.

    If I run this: iReturn = oShell.run(zBatchFile,1,True)

    It get this: Batch launched, rc=1

    And again, there is no output file.

    On my Dev machine (IIS 5, WinXP) the batch and vbs script run fine.

    When I remote into the Win2K3 server and launch the batch from a cmd.exe window, it runs fine.

    I went through your checklist, plus some of my own:

    1. The web-site has "Parent Paths" enabled.

    2. The web-site uses a domain account for anonymous access

    3. The web-site has Read, Script, and Execute access turned on

    4. ASP.DLL is enabled as a Web Services Extension

    5. The web-site has a ScriptMaps property which associates .asp to ASP.DLL

    6. I make the request to http://localhost/(website)/pye/pye_runload.asp

    Plus..:

    7. The Web-site Application Pool runs with a WAMUserName of the domain account

    8. All of these accounts have Read and Execute (and Write) access to the /bin/ folder:

    – the domain account

    – IUSR_machine_name

    – IWAM_machine_name

    Still, nothing.  To simplify, I copied cmd.exe into the /bin/ folder, and explicitly gave permissions to that copy of cmd.exe to the domain account and the IUSR_ and IWAM_ accounts. I put this in my ASP page:

    ASP Page, located at (web root)pyetest_batch.asp

    (%

    Dim oShell: set oShell = Server.CreateObject("WScript.Shell")

    Dim zBatchCmd: zBatchCmd = "D:BudgetTestbincmd.exe /c dir *.* > D:BudgetTestbintest_batch.txt"

    Dim iReturn: iReturn = oShell.run(zBatchCmd,1,True)

    Response.Write "Batch launched, rc=" & iReturn

    %)

    Result on the ASP Page is: Batch launched, rc=1

    .. and, nothing happens. No output file is created.

    Any help would be appreciated.

    Regards, Bob Monahon

  38. David.Wang says:

    BobManahon – CMD.EXE on Windows Server 2003 will not run when launched via Win32 CreateProcess() (that’s what oShell.Run boils down to) from IIS6, unless the Application Pool Identity is LocalSystem or if the Application Pool’s ProcessIdentity == Impersonated Identity from Authentication.

    This pretty much prevents launching remote command consoles by hackers, as well as IIS scripts launching legitimate administration batch files.

    //David

  39. Faiz says:

    Hey David,

    Thanks a ton for the article.

    One can also use processstartinfo class that is part of .net 2.0 to run process.

    However as long as i dont give explicit user name and password it works fine taking the app pool identity.

    But if i keep the app pool identity as network service and

    give user id and password(secure string) to the processstartinfo

    class it gives an exception "Application failed to initialize properly".

    Any help on this will be greatly appreciated.

    Thanks,

    Faiz

  40. monty says:

    I am trying to run a batch file which in turn calls a vbs file from iis6 but its failing with permission error 800a0046 permission denied.

    I have given permission to iusr_computer to run the file within iis all the extensions are allowed as this is behind a firewall and on the intranet and only used to display results of tests, in an attempt to automate the tests so they automatically restart is what I am trying to do.

    my page

    sendvuserend.asp only contains the following

    <%@language=vbscript%>

    <%

    set wschell=Server.CreateObject("Wscript.shell")

    wshell.run "c:restart.bat"

    %>

    I don’t understand why this doesn’t work

  41. David.Wang says:

    monty – for security reasons, that is no longer allowed by Windows Server 2003 — unless you match the Application Pool Identity to the Impersonated Identity, or if the Application Pool Identity is Local System (ultra-insecure).

    It does not matter what permissions you grant, nor what you enable within IIS, etc.

    //David

  42. alan c. says:

    hi david,

    I’m building my first web app, and I’ve run into the scenario as described by joao. I’ve been scouring the web to see what information I can find, and through that I’ve come across your post. I’m building and testing my application on win xp pro that has IIS 5.1 ( hopefully that won’t disqualify me from a response 🙂

    Let me describe my issue, and my understanding from the context of your post…

    In my application I call a batch file using an instance of System.Diagnostics.Process. The batch file calls the net.exe to map a drive on the webserver:

    start net.exe use X: <some path to a network resource>

    This is tied to a button click on my web application, and when its invoked, nothing happens. When I try variants of the batch file, like calling a vb script to generate a message box to make sure that the batch is called, I get errors with wscript or net.exe indicating that the application failed to initialize properly.

    So from what you said this is what I understand:

    1. Since net.exe doesn’t conform to the CGI specification, I shouldn’t attempt to run it directly, and I shouldn’t waste time looking at that.

    2. Since I’m using a batch file to invoke net.exe, IIS regards this as ASP. To get this to work, the extension needs to be scriptmapped to cmd.exe. (Done via application configuration of web app in IIS)

    3. cmd.exe is strict with security, the bat file executes under CreateProcessAsUser(), and will probably require the appropriate credentials, and if I have impersonation setup properly that should be sufficient.

    Could you confirm the above conclusions I made? Is that enough to get my bat file to work properly? I’m stuck right now, because I am blocked from creating new application extension mappings, so I can’t move forward with my testing.

    Also, by adding the mapping, are there some security issues I need to be concerned with?

    The post by Maurits was a little to terse for me, (the opposite of this post) but do we even need to worry about the newline/carriage return if we’re running net.exe scriptmapped?

  43. jf says:

    Great Article.

    Thank you.

    I can make this work for everything but shutdown -s -t 25 from xp pro 64 bit running php 64 bit with fastcgi under iis 64 bit.

    Any pointers?

  44. Thomas says:

    Hi David,

    I have similiar questions to some of the developers here as well.  Here is my code Snippet in my ASP page:

    Dim oShell: set oShell = Server.CreateObject("WScript.Shell")

    Dim cmd: cmd = "D:devBinCompress.exe d:devBinFilesabc.xml"

    Dim iReturn: iReturn = oShell.run(cmd,0,True)

    Response.Write "Shell executed, return value=" & iReturn

    I run the above script under win2003 server environment and it runs but does nothing.  However, the same code works on my Vista development machine.  Is this something related to the CreateProcess() you mentioned ?  Would it make a difference if I run cmd.exe /c in front of the compress.exe line ?  

    Furthermore, what does it mean by "match the Application Pool Identity to the Impersonated Identity".  I am not very familiar with IIS setting so excuse my stupid question.  I am looking forward hearing your response.  Thanks!

  45. Anand says:

    Hi David,

    I have this similar issue. My problem is I am able to call the exe but it remains in the memory. Means when I see the task manager, I could the exe being launched but it does no work.

    I had already enabled "Allow to interact with Desktop" on both IIS Admin and World Web Publishing service.

    SAMPLE CODE I AM USING

    ——————————————

    ProcessStartInfo myProcess = new ProcessStartInfo("notepad.exe");

               myProcess.UserName = "BLAH-Blah";

               myProcess.Password = convertToSecureString("DGHFDH");

               myProcess.Domain = "ABCD";

               myProcess.UseShellExecute = false;

               Process.Start(myProcess);

    It runs fine on XP machine but not on Server 2003.

    Any pointers ??

  46. Dieter Menne says:

    I studied these pages over and over when I had a problem recently, and everything recommended here was Ok, and yet my EXE did not run. Here is the scenario, and the solution:

    Test bed:

    IIS, Window 7 32 bit, php 5. Starting an external .NET 2.0 exe via popen from php for long running bulk processing. Everything works fine.

    Real world: Win 2003 server. There was no error message, no event viewer, no log entry, popen reported correct start, but the application simply did not run. Checked all settings for 2 days, no success. The EXE runs perfectly when started from command line, but did not start even when it was given "Full Access for Everyone".

    Solution: The server is 64 bit. I had compiled the .NET EXE with "All platforms" set. Recompiled with "x86", and everything worked Ok.

    <http://www.lostechies.com/…/force-net-application-to-run-in-32bit-process-on-64bit-os.aspx&gt;

    Dieter