Windows – Executing a BAT file as a domain user from a service in Windows 7

batch fileremote-executionserviceswindows 7windows-domain

Firstly a few basics:

  • I am running a database server application which runs as a service ServiceA.
  • ServiceA is installed on Server1 and Server2
  • ServiceA only runs on one of the two servers at any one time (they are redundant servers, if one server fails, ServiceA on the other server automatically starts)
  • I have no control over the code ServiceA
  • ServiceA logs on as the Local System account
  • ServiceB needs to run on both Server1 and Server2
  • ServiceB logs on as the Local System account
  • I can create configuration objects in the ServiceA database. One type of object is like a scripting language which ServiceA executes
  • The scripting language has functions to allow file manipulation and execution of commands on the local system – OPEN_FILE(), WRITE_FILE(), CLOSE_FILE(), and SYSTEM()
  • When these commands are executed, they run with the credentials of the Local System account
  • ServiceA generates configuration files for ServiceB using the above file manipulation commands
  • ServiceB needs to be shutdown before the new configuration files are created (I actually create them in a temporary folder, shutdown ServiceB, copy the files, and then start ServiceB)
  • Server1 and Server2 are both Windows 7 Enterprise
  • Server1 and Server2 are both on the same domain

I'm trying to get ServiceA to:

  • Shutdown ServiceB on both Server1 and Server2
  • Copy some files to Server1 and Server2
  • Start ServiceB on both Server1 and Server2

What I have done:

  • Created a Domain Account (DomainX/UserY) with a password (PasswordZ)
  • Created a share on both servers (ShareW)
  • Given DomainX/UserY permission to access ShareW on both servers
  • Used OPEN_FILE(), WRITE_FILE(), and CLOSE_FILE() to create an INSTALL.BAT file
  • Used SYSTEM('C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1') to run the INSTALL.BAT (run by Service A as Local System) and create a debug log

What works from within the BAT file

NET USE \\Server1\ShareW PasswordZ /USER:DomainX/UserY
NET USE \\Server2\ShareW PasswordZ /USER:DomainX/UserY
DEL /F /Q "\\Server1\ShareW\*.*
DEL /F /Q "\\Server2\ShareW\*.*
XCOPY /Y "C:\Temp\*.*" "\\Server1\ShareW\"
XCOPY /Y "C:\Temp\*.*" "\\Server2\ShareW\"
NET USE \\Server1\ShareW /DELETE
NET USE \\Server2\ShareW /DELETE

So the share and permissions are setup correctly

My problem is trying to Stop and Start Service B from within INSTALL.BAT

I have tried:

NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY
SC \\Server1 STOP "Service B"
NET USE \\Server1\IPC$ /DELETE
NET USE \\Server2\IPC$ PasswordZ /USER:DomainX/UserY
SC \\Server2 STOP "Service B"
NET USE \\Server2\IPC$ /DELETE

The SC command works for the local machine, but fails for the remote machine. This is the output from INSTALL.BAT running on Server2:

C:\Windows\system32>NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY 
The command completed successfully.

C:\Windows\system32>SC \\Server1 STOP ServiceB 
[SC] OpenService FAILED 5:

Access is denied.

(I've tried with ServiceA running on Server1 and Server2 – same results – Works locally, fails remotely)

If I use 'Run as different user' to run CMD.EXE as DomainX/UserY, the SC command works perfectly. So on Server2, running INSTALL.BAT from CMD.EXE as DomainX/UserY I get:

C:\Temp>NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY

The command completed successfully.
C:\Temp>SC \\Server1 START ServiceB

SERVICE_NAME: ServiceB
        TYPE               : 110  WIN32_OWN_PROCESS  (interactive)
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x1
        WAIT_HINT          : 0xbb8
        PID                : 4856
        FLAGS              :

So the DomainX/UserY has the necessary permissions to Stop and Start the service on the remote server. It appears to be some kind of blocking of privilege escalation from the Local System account.

I have read about setting LocalAccountTokenFilterPolicy in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ to 1 and rebooting. I've done this with no changes to the outcome.

I've tried using psservice.exe with practically identical results (output from INSTALL.BAT run by ServiceA on Server2)

C:\Windows\system32>C:\Utilities\psservice.exe \\Server1 -u DomainX/UserY -p PasswordZ -accepteula stop ServiceB 

PsService v2.24 - Service information and configuration utility
Copyright (C) 2001-2010 Mark Russinovich
Sysinternals - www.sysinternals.com

Access is denied.
Access is denied.
Error querying services on Server1:
Error opening ServiceB on Server1:

I then tried to use psexec (in a rather convoluted manner to trap all the logging)

INSTALL2.BAT

C:\Utilities\psexec -accepteula -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL3.BAT >> C:\Temp\INSTALL3.LOG 2>&1

INSTALL3.BAT

C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1

When I run INSTALL2.BAT from the command line (running as DomainX/UserY), everything works fine.

But run from ServiceA, In INSTALL3.LOG I get:

PsExec v2.11 - Execute processes remotely
Copyright (C) 2001-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

Access is denied.

PsExec could not start C:\Temp\INSTALL3.BAT:

So I explicitly added DomainX/UserY with Full Control to the security of all three BAT files. The result was the same (Access is denied. in INSTALL3.LOG)

I've also tried turning off UAC with no perceivable impact

So I'm pretty much stuck now – How can I get a BAT file executed by a service running as Local System to impersonate another user and allow it to Stop and Start services on a remote machine?

Best Answer

The answer turns out to be very simple - use the -h option of psexec

The SYSTEM() command of the script run by ServiceA is now:

C:\Utilities\psexec -accepteula -h -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1

Works perfectly

Related Question