Nyhet

Sökvägen som läcker Windows-inloggningen: NT-prefixet som gick förbi UNC-kontrollen

Två rader i Claude Codes changelog beskriver ett fel som drabbar alla som bygger egna verktyg som öppnar filer på användarens uppmaning: en UNC-kontroll som läser strängen ser inte att samma fjärresurs kan stavas på flera sätt.

18 aug 2026

En mörk, repad skiva står mitt i bilden med en liten platta högt upp på ytan. Under plattan syns en oregelbunden mörk gestalt, och en tunn ljus tråd löper från den snett nedåt över det blanka golvet mot en smal grön ljusstrimma längst till höger. Till vänster svävar fristående, oregelbundet sammanflätade ljusa trådar och noder utan koppling till gestalten.

Kort sagt

Version 2.1.233 (14 aug 2026) rättade att Windows-sökvägar skrivna med NT-prefixet \??\ gick förbi UNC-kontrollen, vilket enligt changelogen stängde en läckvektor för NTLM-inloggningsuppgifter. Version 2.1.234 (17 aug 2026) utvidgade avvisningen till de filåtkomster som sker innan användaren hunnit svara på en fråga. Lärdomen för din egen kod: en kontroll som jämför tecken i en sökvägssträng kontrollerar inte en sökväg — den kontrollerar en stavning.

Vad som faktiskt ändrades

Raden i 2.1.233 lyder ordagrant: ”Fixed Windows paths spelled with the NT \??\ device prefix bypassing UNC path validation, closing an NTLM credential-leak vector”. Tre dagar senare, i 2.1.234, kommer uppföljningen: ”remote file reads, session restore, CLAUDE.md includes, workflow scripts and file uploads now reject Windows NT-namespace (\??\) paths, hardening the remaining pre-approval file accesses against the NTLM credential-leak vector”.

Det intressanta ligger i det sista ledet. Den första rättningen lagade kontrollen. Den andra gick igenom allt som läser en fil innan godkännandet: sessionsåterställning, inkluderade kontextfiler, arbetsflödesskript och uppladdningar. Källan säger ingenting om svårighetsgrad, spridning eller om felet utnyttjats, och den här texten påstår därför ingenting om det heller. Det som går att lära av den är felklassen, och den är inte verktygsspecifik.

Samma fil, flera stavningar

Windows har mer än ett sätt att skriva samma resurs. Microsofts dokumentation räknar upp traditionella DOS-sökvägar (C:\Test\Foo.txt), UNC-sökvägar (\\Server\Share\Test\Foo.txt) och DOS-enhetssökvägar med prefixen \\.\ och \\?\. Och där finns det som gör en teckenjämförelse otillräcklig: enhetsnamnrymden har en egen länk för UNC. Dokumentationen skriver ut den rakt av — ”There is a specific link for UNCs that is called, not surprisingly, UNC — med exemplen \\?\UNC\Server\Share\Test\Foo.txt och \\.\UNC\Server\Share\Test\Foo.txt.

Under Win32-lagret finns dessutom NT-namnrymden, där filer har objektnamn relativa till \DosDevices: filen C:\Directory\File kan enligt drivrutinsdokumentationen anges som \DosDevices\C:\Directory\File. Prefixet \??\ är stavningen som pekar in i just den namnrymden. Termen ”NT-namnrymd” om \??\ är changelogens egen; Microsoft Learn dokumenterar \\?\, \\.\, \DosDevices och GLOBALROOT, men jag hittade ingen Learn-sida som ordagrant förklarar \??\. Poängen står ändå: ett värdnamn kan nås utan att strängen börjar med två omvända snedstreck.

Ett steg till: \\?\ är inte bara en annan stavning, utan ett uttryckligt avstängningskommando. Dokumentationen är tydlig — prefixet ”tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system”. Normalisering är alltså inte något som alltid händer under dig. Den är något du kan råka stänga av genom att skicka vidare en sträng du inte tittat på.

Läckan är uppkopplingen, inte filen

Varför spelar det roll att en sökväg pekar bort från maskinen? För att Windows autentiserar när den ansluter. Microsofts egen beskrivning av varför man blockerar NTLM på SMB-klienten säger det rakt ut: blockeringen ”prevents bad actors from tricking clients into sending NTLM requests to malicious servers, counteracting brute force, cracking, and pass-the-hash attacks”.

Filen behöver inte finnas

Läckan sker i anslutningsförsöket, inte i läsningen. Ett verktyg som svarar ”kunde inte öppna filen” kan mycket väl redan ha presenterat användarens inloggning för värden i sökvägen. Behandla därför aldrig ett misslyckat filanrop som bevis på att ingenting hände — och kontrollera sökvägen före anropet, inte i felhanteringen.

På operativsystemsnivå finns en motåtgärd, men den täcker inte alla. SMB-klientens NTLM-blockering kräver enligt Microsoft Windows 11 version 24H2 eller Windows Server 2025 eller senare, plus en SMB-server som tillåter Kerberos. Den slås på med Set-SmbClientConfiguration -BlockNTLM $true, eller via grupprincip under Computer Configuration → Administrative Templates → Network → Lanman Workstation och inställningen Block NTLM (LM, NTLM, NTLMv2). Det är ett bra lager att ha, och ett dåligt argument för att slippa validera: du styr inte vilken Windows-version dina användare kör.

Testfallet som avslöjar en strängbaserad kontroll

Det här är det snabbaste sättet att få veta om din egen validering normaliserar eller bara jämför tecken. Kör dina sökvägsformer genom valideringen och se vilken som slinker igenom. Siffrorna nedan är mätta på Node.js v24.13.0 under Windows 11 den 18 augusti 2026, utan nätverksanrop.

const p = require('node:path');
const s = '\\??\\UNC\\attacker.example\\share\\a.txt';

p.win32.isAbsolute(s)      // true
p.win32.normalize(s)       // '\??\UNC\attacker.example\share\a.txt'  (oförändrad)
p.win32.parse(s).root      // '\'   OBS: inte '\\attacker.example\share\'
p.win32.resolve(s)         // 'C:\??\UNC\attacker.example\share\a.txt'
s.startsWith('\\\\')       // false

Jämför med samma resurs skriven som \\?\UNC\attacker.example\share\a.txt: där blir parse().root lika med \\?\UNC\ och startsWith sant. Och skriver du den med snedstreck, //attacker.example/share/a.txt, är startsWith falskt på originalsträngen medan normalize gör om den till \\attacker.example\share\a.txt. Tre av de vanligaste hemmasnickrade kontrollerna — ”börjar med två omvända snedstreck”, ”roten börjar med två omvända snedstreck” och ”absolut men inte UNC” — svarar alltså lokal fil på minst en form som inte är det.

Sex strängar som ska avvisas av din validering

En viktig begränsning som är lika mätt som resten: Node:s egen fs når inte nätverket från \??\-strängen. fs.statSync('\\??\\C:\\Windows\\win.ini') ger ENOENT, eftersom sökvägen löses upp till C:\??\C:\Windows\win.ini, medan \\?\C:\Windows\win.ini öppnas utan problem. Risken ligger alltså inte i att körläget självt kopplar upp sig. Den ligger i att din kontroll säger ”säker lokal sökväg” och att originalsträngen sedan skickas vidare till något annat som löser upp den: ett skal, en barnprocess, ett annat verktyg, ett systemanrop längre ned.

Kontrollen som normaliserar först

Ordningen är hela poängen. Avvisa innan du löser upp, lös upp innan du jämför, och använd sedan det du löste upp.

Avvisa enhets- och NT-prefix direkt. En sökväg som en användare eller en modell pekar ut behöver aldrig \??\, \\?\ eller \\.\. Neka dem som klass i stället för att försöka tolka dem rätt.
Normalisera med plattformens egen funktion. Låt path.win32.resolve eller motsvarande göra separatorer, . och .. — inte din egen regex. Microsofts normaliseringssteg är dokumenterade och fler än man tror.
Jämför mot roten med separator. Kontrollera att den upplösta sökvägen ligger under en tillåten rot inklusive avslutande separator, annars matchar C:\projekt även C:\projekt-hemligt.
Lös upp länkar och kontrollera igen. En symbolisk länk eller junction inne i den tillåtna roten kan peka ut ur den. Samma changelog rättade i 2.1.232 en variant av just detta: Git Bash följde Cygwin-stilens symlänkar som sökvägskontrollen såg som vanliga filer.
Använd den upplösta strängen i själva anropet. Att validera en sträng och sedan öppna en annan är samma fel som beskrivs här, bara ett steg senare.
Tillåtlista värdar, inte prefix. Behöver din applikation verkligen läsa från nätverksresurser, lista de värdnamn som får nås. Att öppna upp en stavning öppnar upp alla värdar.

Var kontrollen måste sitta

Listan i 2.1.234 är i praktiken en inventering över de ställen man glömmer: fjärrläsning, sessionsåterställning, inkluderade kontextfiler, arbetsflödesskript och filuppladdningar. Alla har en sak gemensamt — de rör en fil innan användaren har godkänt något. Bygger du ett verktyg med en godkännandedialog är det lätt att tro att dialogen är skyddet. Men koden som läser filen för att kunna visa dialogen, räkna ut en storlek eller bygga en förhandsvisning har redan gjort anropet. Ett godkännande som kommer efter anslutningen skyddar ingenting.

Samma changelog visar också priset för att ta i för mycket. I 2.1.232 skärptes Bash-kontrollen för symlänkar och för indataomdirigeringar (< file). I 2.1.233 rullades båda tillbaka, efter att automatläget börjat stanna för manuellt godkännande på helt vanliga kommandon av typen cd <katalog> && <kommando> > fil — beskrivet i källan som en regression — med beskedet att en smalare version kommer senare. Lärdomen är obekväm men användbar: en sökvägskontroll som ger falsklarm på vardagsarbete blir avstängd, och då skyddar den ingen. Gör regeln smal och exakt hellre än bred och irriterande.

Den här felklassen hör hemma bredvid två andra saker vi skrivit om. Promptinjektion i din egen app handlar om att text blir instruktioner; det här handlar om att en tillåten filoperation missbrukas genom hur argumentet stavas. Och där agentavgränsningen bestämmer vad verktyget får göra, bestämmer sökvägsvalideringen var det får göra det. Ingen av dem ersätter den andra. Ska du gå bredare på en färdig kodbas är säkerhetsgranskningen av en AI-byggd kodbas nästa steg.

Källor

Uppgifterna kontrollerades mot källorna den 18 augusti 2026. Mätvärdena i testavsnittet kommer från en egen körning samma dag på Node.js v24.13.0 under Windows 11; kör dem mot ditt eget körläge innan du drar slutsatser om det.

Läs vidare
Säkerhetsgranska en kodbas som AI:n byggt