
Primero algunos conceptos básicos:
- Estoy ejecutando una aplicación de servidor de base de datos que se ejecuta como un servicio ServiceA.
- ServiceA está instalado en el Servidor1 y el Servidor2
- ServiceA solo se ejecuta en uno de los dos servidores a la vez (son servidores redundantes, si un servidor falla, ServiceA en el otro servidor se inicia automáticamente)
- No tengo control sobre el código ServiceA
- ServiceA inicia sesión como la cuenta del sistema local
- ServiceB debe ejecutarse tanto en el Servidor1 como en el Servidor2
- ServiceB inicia sesión como la cuenta del sistema local
- Puedo crear objetos de configuración en la base de datos ServiceA. Un tipo de objeto es como un lenguaje de programación que ejecuta ServiceA.
- El lenguaje de programación tiene funciones que permiten la manipulación de archivos y la ejecución de comandos en el sistema local: OPEN_FILE(), WRITE_FILE(), CLOSE_FILE() y SYSTEM().
- Cuando se ejecutan estos comandos, se ejecutan con las credenciales de la cuenta del sistema local.
- ServiceA genera archivos de configuración para ServiceB utilizando los comandos de manipulación de archivos anteriores
- ServiceB debe cerrarse antes de que se creen los nuevos archivos de configuración (de hecho, los creo en una carpeta temporal, cierro ServiceB, copio los archivos y luego inicio ServiceB).
- Server1 y Server2 son ambos Windows 7 Enterprise
- Servidor1 y Servidor2 están ambos en el mismo dominio
Estoy intentando que ServiceA haga lo siguiente:
- Apague ServiceB tanto en el Servidor1 como en el Servidor2
- Copie algunos archivos al Servidor1 y al Servidor2
- Inicie ServiceB tanto en el Servidor1 como en el Servidor2
Que he hecho:
- Creó una cuenta de dominio (DominioX/UsuarioY) con una contraseña (ContraseñaZ)
- Creó un recurso compartido en ambos servidores (ShareW)
- Se le dio permiso a DomainX/UserY para acceder a ShareW en ambos servidores
- Se utilizó OPEN_FILE(), WRITE_FILE() y CLOSE_FILE() para crear un archivo INSTALL.BAT.
- SISTEMA usado ('C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1') para ejecutar INSTALL.BAT (ejecutado por el Servicio A como sistema local) y crear un registro de depuración
Qué funciona desde el archivo BAT
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
Entonces el recurso compartido y los permisos están configurados correctamente.
Mi problema es intentar detener e iniciar el servicio B desde INSTALL.BAT.
Yo he tratado:
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
El comando SC funciona para la máquina local, pero falla para la máquina remota. Este es el resultado de INSTALL.BAT ejecutándose en el Servidor2:
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.
(Lo intenté con ServiceA ejecutándose en el Servidor1 y el Servidor2; mismos resultados; funciona localmente, falla de forma remota)
Si uso 'Ejecutar como usuario diferente' para ejecutar CMD.EXE como DomainX/UserY, el comando SC funciona perfectamente. Entonces, en el Servidor2, al ejecutar INSTALL.BAT desde CMD.EXE como DomainX/UserY obtengo:
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 :
Entonces, DomainX/UserY tiene los permisos necesarios para detener e iniciar el servicio en el servidor remoto. Parece ser algún tipo de bloqueo de la escalada de privilegios desde la cuenta del sistema local.
He leído acerca de configurar LocalAccountTokenFilterPolicy en HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ en 1 y reiniciar. He hecho esto sin cambios en el resultado.
Intenté usar psservice.exe con resultados prácticamente idénticos (salida de INSTALL.BAT ejecutado por ServiceA en 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:
Luego intenté usar psexec (de una manera bastante complicada para atrapar todos los registros)
INSTALAR2.BAT
C:\Utilities\psexec -accepteula -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL3.BAT >> C:\Temp\INSTALL3.LOG 2>&1
INSTALAR3.BAT
C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Cuando ejecuto INSTALL2.BAT desde la línea de comando (ejecutando como DomainX/UserY), todo funciona bien.
Pero ejecutándolo desde ServiceA, en INSTALL3.LOG obtengo:
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:
Así que agregué explícitamente DomainX/UserY con control total a la seguridad de los tres archivos BAT. El resultado fue el mismo (Acceso denegado. en INSTALL3.LOG)
También intenté apagar UAC sin ningún impacto perceptible.
Así que ahora estoy bastante atascado: ¿Cómo puedo hacer que un servicio que se ejecuta como sistema local ejecute un archivo BAT para hacerse pasar por otro usuario y permitirle detener e iniciar servicios en una máquina remota?
Respuesta1
La respuesta resulta ser muy simple: use la opción -h de psexec
El comando SYSTEM() del script ejecutado por ServiceA ahora es:
C:\Utilities\psexec -accepteula -h -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Funciona perfectamente