2010-03-11 14 views
14

Estoy usando un servicio externo de Windows que maneja algunas tareas de automatización ejecutando scripts y ejecutables usando CreateProcessAsUser(). Estoy teniendo problemas en Windows Server 2008 debido a UAC y la forma en que la elevación de LUA se maneja a través de las API.iniciando un proceso elevado de UAC desde un servicio no interactivo (win32/.net/powershell)

El servicio se ejecuta como LocalSystem y no tiene habilitado "Interactuar con el escritorio". Los procesos se ejecutan como usuarios en el grupo Administradores, pero no en la cuenta de administrador (que está exenta de muchas restricciones de UAC). Todos los ajustes predeterminados de UAC en su lugar.

Puedo pasar comandos arbitrarios o código de PowerShell al servicio, pero parece que no puedo 'salir' del proceso no elevado y no interactivo que inicia el servicio.

El quid de la cuestión parece ser que la única opción de API (pública) para iniciar un proceso elevado es ShellExecute() con el verbo 'runas', pero hasta donde puedo decir que no se puede invocar desde un servicio no interactivo o recibe errores como "Esta operación requiere una estación de ventana interactiva".

La única solución que he encontrado es mencionado aquí: http://www.eggheadcafe.com/software/aspnet/29620442/how-to-proper-use-sendinp.aspx

En Vista, el funcionario documentado manera para elevar un proceso sólo se utiliza el API cáscara ShellExecute (Ex) (no CreateProcess o CreateProcessAsUser). Entonces su aplicación debe llamar al ShellExecute (Ex) para iniciar un helper elevado para llamar a SendInput. Además, debido a la sesión 0 aislamiento, un servicio sólo puede utilizar CreateProcessAsUser o CreateProcessWithLogonW (no se puede utilizar ShellExecute (Ex)) para especificar el escritorio interactivo.

.. Creo que no hay una forma directa de engendrar un proceso elevado a partir de un servicio de Windows . Solo podemos usar primero CreateProcessAsUser o CreateProcessWithLogonW para generar un proceso no elevado de en la sesión de usuario (escritorio interactivo). Luego, en el proceso no elevado, puede usar ShellExecute (Ex) para engendrar un proceso elevado para la tarea real.

Para hacer esto desde el código .net/PowerShell, parece que tendría que hacer algo elaborado P/Invoke cosas para llamar CreateProcessAsUser o CreateProcessWithLogonW ya que el .Net System.Diagnostics.ProcessStartInfo no tiene una equivalente de lpDesktop que pude configurar como "winsta0 \ default". Y no tengo claro si LocalSystem incluso tiene los derechos para llamar a CreateProcessAsUser o CreateProcessWithLogonW.

I, también vieron http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx y Process.Start with different credentials with UAC on

Sobre la base de todo esto, estoy llegando a la conclusión de que no hay manera fácil de hacer esto. ¿Me estoy perdiendo de algo? Esto realmente no parece que debería ser tan difícil. Parece que el UAC nunca fue diseñado para manejar casos de uso no interactivos.

Y si alguna persona de Microsoft termina leyendo esto, noté que la forma en que ShellExecute maneja internamente la elevación es llamando al Servicio de Información de Aplicación (AIS). ¿Por qué no está disponible esa misma llamada a AIS a través de alguna API Win32 o .NET? http://msdn.microsoft.com/en-us/library/bb756945.aspx

Disculpe que funcionó un poco largo. Gracias por cualquier idea

+0

Más bien diciendo que Server Core no es compatible con UAC. Parece confirmar mi evaluación. http://blogs.technet.com/server_core/archive/2009/01/19/user-account-control-uac-and-server-core.aspx –

+0

Ver mi publicación aquí que explica cómo, en particular, mira el LinkedToken sección: http://brianbondy.com/blog/id/100/understanding-windows-at-a-deeper-level-sessions-window-stations-and-desktops –

Respuesta

16

La manera "oficial" de romper el aislamiento de sesión cero es usar una combinación de la API de servicios de terminal y CreateProcessAsUser() para iniciar un proceso dentro de la sesión de un usuario. En mi trabajo anterior, hicimos precisamente eso, ya que necesitábamos mostrar un diálogo al usuario desde un servicio antes de instalar una actualización descargada. Por lo tanto, sé que funciona, al menos en WinXP, Win2K3, Vista y Win7, pero no espere que Win 2K8 sea demasiado diferente. Básicamente, el proceso es el siguiente:

  1. llamada WTSGetActiveConsoleSessionId() para obtener la sesión de consola activa Identificación (muy importante, ya que la sesión interactiva es NO siempre la sesión 1, incluso en los sistemas cliente). Esta API también devolverá un -1 si no hay un usuario activo conectado a la sesión interactiva (es decir, conectado de forma local a la máquina física, en lugar de usar RDP).
  2. Pase la identificación de la sesión de la llamada API anterior al WTSQueryUserToken() para obtener un token abierto que reprents el usuario inició sesión en la consola.
  3. Llame al DuplicateTokenEx() para convertir el token de suplantación (de WTSQueryUserToken) en un token primario.
  4. Llame al CreateEnvironmentBlock() para crear un nuevo entorno para el proceso (opcional, pero si no lo hace, el proceso no tendrá uno).
  5. Pase el token primario del paso 3 a una llamada al CreateProccessAsUser(), junto con la línea de comando del ejecutable. Si creó un bloque de entorno desde el paso 4, también debe pasar el indicador CREATE_UNICODE_ENVIRONMENT (siempre). Esto puede parecer tonto, pero la API falla horriblemente si no lo haces (con ERROR_INVALID_PARAMTER).
  6. Si creó un bloque de entorno, debe llamar al DestroyEnvironmentBlock, de lo contrario generará una pérdida de memoria. El proceso recibe una copia separada del bloque de entorno cuando se inicia, por lo que solo está destruyendo los datos locales.

¡Y listo! Windows hace algo de magia interna, y ves el lanzamiento de la aplicación. Sin embargo, aunque esto se iniciará y el proceso interactivo de un servicio, no estoy seguro de si se omitirá el UAC (pero no me cite sobre eso). En otras palabras, es posible que no se inicie como un proceso elevado a menos que el registro o el manifiesto interno indique que lo haga, y aun así, es posible que reciba un aviso de UAC. Si el token que obtienes del paso 3 es un token restringido, puedes usar AdjustTokenPrivileges() para restaurar el token elevado (completo), pero tampoco me lo menciones. Sin embargo, como se indica en los documentos de MSDN, tenga en cuenta que no es posible "agregar" privilegios en un token que no los tenía (por ejemplo, no puede convertir un token de usuario restringido en un administrador usando AdjustTokenPrivileges; el usuario debería ser un administrador para comenzar).

Es técnicamente posible hacer todo esto desde Win2K hacia adelante. Sin embargo, es realmente solo factible comenzando con WinXP, ya que Win2K carece de las API WTSGetActiveConsoleSessionId() y WTSQueryUserToken() (junto con WTSEnumerateProcesses() para Win2K Pro). Puede codificar 0 como ID de la sesión (ya que siempre es el caso en Win2K), y supongo que puede obtener el token del usuario enumerando los procesos en ejecución y duplicando uno de sus tokens (debe ser uno que tenga el SID interactivo presente).De todos modos, el CreateProcessAsUser() se comportará de la misma manera cuando pase un token de usuario interactivo, incluso si no selecciona "Interactuar con el escritorio" desde la configuración del servicio. También es más seguro que lanzar directamente desde el servicio de todos modos, ya que el proceso no heredará el piadoso token de acceso LocalSystem.

Ahora, no sé si su aplicación de terceros hace algo de esto cuando ejecuta el script/proceso, pero si quiere hacerlo desde un servicio, así es (y con Vista o Win7, es la única forma de superar el aislamiento de la sesión 0).

+3

No es necesario llamar a 'DuplicateTokenEx'. 'CreateProcessAsUser' ejecutaría el proceso con el token obtenido de' WTSQueryUserToken'. Su respuesta es bastante detallada, pero no responde "¿Cómo iniciar un proceso elevado desde el servicio? (UAC habilitado)" – Ajay

+1

De hecho, no es así. Esto te dejará con un token restringido. – Joshua

+0

Hay algunas cosas que debe tener en cuenta: 1) Como @Ajay menciona 'WTSQueryUserToken' ya devuelve un token primario, por lo que puede omitir el paso 3. 2) Con UAC habilitado, realmente necesita obtener el llamado token vinculado después del paso 2 , lo cual haces llamando 'GetTokenInformation' con la clase de información' TokenLinkedToken', pero solo si consultas el 'TokenElevationType' dice que la elevación es limitada. 3) Si no crea un bloque de entorno, el nuevo proceso tendrá el entorno del proceso de creación, que es su servicio –

1

Dependiendo de su caso de uso, podría hacer lo que hago. Busco el proceso de winlogon para la sesión activa y robo su token. Si no hay sesión activa (API devolvió -1), use 1 si WINVER> = 6 de lo contrario 0. Esto da como resultado SYSTEM en la sesión activa.

Cuestiones relacionadas