Säker inläsning av bibliotek för att förhindra DLL-förinläsning av attacker

Supporten för Windows Vista Service Pack 1 (SP1) upphör 12 juli 2011. Om du vill fortsätta att få säkerhetsuppdateringar för Windows kontrollerar du att du kör Windows Vista med Service Pack 2 (SP2). Mer information finns på den här Microsoft-webbsidan: Supporten upphör för vissa versioner av Windows.

När ett program dynamiskt läser in ett DLL-bibliotek (Dynamic Link Library) utan att ange en fullständig sökväg försöker Windows hitta DLL-biblioteket genom att söka i en väldefinierad uppsättning kataloger. Om en angripare får kontroll över en av katalogerna kan de tvinga programmet att läsa in en skadlig kopia av DLL-biblioteket i stället för DLL-biblioteket som förväntades. Dessa attacker kallas för DLL-förinläsningsattacker och är vanliga för alla operativsystem som stöder dynamisk inläsning av delade DLL-bibliotek. Effekten av sådana attacker kan vara att en angripare kan köra kod i sammanhanget för den användare som kör programmet. När programmet körs som administratör kan det leda till en lokal rättighetsökning. Vi känner till det förnyade intresset för dessa attacker. För att begränsa den effekt det här problemet har på våra gemensamma kunder släpper vi det här dokumentet till utvecklarcommunityn för att säkerställa att de känner till problemet och har de verktyg som krävs för att åtgärda problemet i sina program.

Sammanfattning

Beskrivning av DLL-förinläsningsattacker

LoadLibrary-baserade attacker

När ett program dynamiskt läser in en DLL-fil utan att ange en fullständig sökväg försöker Windows hitta DLL-biblioteket genom att linjärt söka igenom en väldefinierad uppsättning kataloger, dll-sökordning. Om Windows hittar DLL-biblioteket i DLL-sökordningen läses DLL-biblioteket in. Men om Windows inte hittar DLL-biblioteket i någon av katalogerna i DLL-sökordningen returneras ett fel i DLL-inläsningsåtgärden. Följande är DLL-sökordningen för funktionerna LoadLibrary och LoadLibraryEx , som används för dynamisk inläsning av DLL:er:

  1. Katalogen som programmet lästes in från
  2. Systemkatalogen
  3. 16-bitars systemkatalogen
  4. Windows-katalogen
  5. Den aktuella arbetskatalogen (CWD)
  6. Katalogerna som visas i PATH-miljövariabeln

                
Tänk dig följande scenario:

  • Ett program läser in en DLL-fil utan att ange en fullständigt kvalificerad sökväg som förväntas hittas i programmets CWD.
  • Programmet är redo att hantera ärendet när DLL-biblioteket inte hittas.
  • Angriparen känner till den här informationen om programmet och kontrollerar CWD.
  • Angriparen kopierar sin egen specialskapade version av DLL:et i CWD. Detta förutsätter att angriparen har behörighet att göra detta.
  • Windows söker igenom katalogerna i DLL-sökordningen och hittar DLL-biblioteket i programmets CWD.

I det här scenariot körs den specialskapade DLL-biblioteket i programmet och får den aktuella användarens behörigheter.

Rekommendation

För att förhindra denna attack kan program ta bort den aktuella arbetskatalogen (CWD) från DLL-sökvägen genom att anropa SetDllDirectory API med hjälp av en tom sträng (""). Om ett program är beroende av att läsa in en DLL-fil från den aktuella katalogen hämtar du den aktuella arbetskatalogen och använder den för att passera i en fullständigt kvalificerad sökväg med LoadLibrary.

Vi är också medvetna om att vissa utvecklare använder LoadLibrary för att verifiera om det finns en specifik DLL för att avgöra vilken version av Windows som körs av användaren. Du bör vara medveten om att detta kan göra programmet sårbart. Om det berörda biblioteket verkligen inte finns på Windows-versionen som programmet körs på, kan en angripare introducera ett bibliotek med samma namn i CWD. Vi rekommenderar starkt att du inte använder den här tekniken. Använd i stället de rekommenderade tekniker som beskrivs i MSDN-artikeln "Hämtar systemversionen".

Ett program som läser in plugin-program från tredje part och som inte kan tvinga plugin-programmen att använda en kvalificerad sökväg för sina LoadLibrary-samtal bör anropa SetDllDirectory("") för att ta bort CWD och sedan ringa SetDllDirectory("plugin installationsplats") för att lägga till plugin-installationskatalogen till DLL-sökvägen.

SearchPath-baserade attacker

En liknande attack uppstår när ett program använder SearchPath-API:et för att hitta en DLL-fil och dynamiskt läser in sökvägen som returneras av SearchPath. Följande är standardsökordningen för SearchPath API:

  • Katalogen som programmet lästes in från
  • Den aktuella arbetskatalogen (CWD)
  • Systemkatalogen
  • 16-bitars systemkatalogen
  • Windows-katalogen
  • Katalogerna som visas i PATH-miljövariabeln

Vi rekommenderar inte det här mönstret eftersom det inte är säkert. Vi rekommenderar inte funktionen SearchPath som en metod för att hitta en .dll fil om den avsedda användningen av utdata är i ett anrop till funktionen LoadLibrary. Det kan resultera i att fel .dll fil hittas eftersom sökordningen för funktionen SearchPath skiljer sig från sökordningen som används av funktionen LoadLibrary. Om du behöver söka efter och läsa in en .dll fil använder du funktionen LoadLibrary.

ShellExecute och CreateProcess

Varianter av dessa problem kan också finnas när utvecklare anropar liknande funktioner som ShellExecute och CreateProcess för att läsa in externa körbara filer. Vi rekommenderar att utvecklare är försiktiga när de läser in binärfiler och anger den fullständigt kvalificerade sökvägen. Detta bör innebära mindre komplexitet när du läser in en binär fil i stället för ett bibliotek.

Vi rekommenderar att utvecklare gör följande:

  • Verifiera sina program för instanser av biblioteksinläsningar som inte är osäkra (exempel på dem anges senare i den här artikeln). Det kan till exempel vara:

    • Användning av SearchPath för att identifiera platsen för ett bibliotek eller en komponent.
    • Användning av LoadLibrary för att identifiera versionen av operativsystemet.
  • Använd fullständigt kvalificerade sökvägar för alla anrop till LoadLibrary, CreateProcess och ShellExecute där du kan.

  • Implementera anrop till SetDllDirectory med en tom sträng ("") för att ta bort den aktuella arbetskatalogen från standard-DLL-sökordningen där det krävs. Tänk på att SetDllDirectory påverkar hela processen. Därför bör du göra detta en gång tidigare i processen, inte före och efter anrop till LoadLibrary. Eftersom SetDllDirectory påverkar hela processen kan flera trådar som anropar SetDllDirectory med olika värden orsaka odefinierat beteende. Om processen är utformad för att läsa in DLL-filer från tredje part krävs dessutom testning för att avgöra om en processomfattande inställning kommer att orsaka inkompatibilitet. Ett känt problem är att när ett program är beroende av Visual Basic for Applications kan en processomfattande inställning orsaka inkompatibilitet.

  • Använd funktionen SetSearchPathMode för att aktivera felsäkert processsökläge för processen. Då flyttas den aktuella arbetskatalogen till den sista platsen i söklistan i SearchPath under processens livstid.

  • Undvik att använda SearchPath för att kontrollera om det finns en DLL-fil utan att ange en fullständig sökväg, även om felsäkert sökläge är aktiverat, eftersom det fortfarande kan leda till DLL-förinläsningsattacker.

Vägledning om hur du identifierar icke-säker biblioteksinläsning

I källkod är följande exempel på icke-säkra biblioteksinläsningar:

  • I följande kodexempel söker programmet efter "schannel.dll" med den minst säkra sökvägen. Om en angripare kan placera schannel.dll i CWD läses den in redan innan programmet söker i Windows-katalogerna efter rätt bibliotek.

    DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); 
    HMODULE handle = LoadLibrary(result);
    
  • I följande kodexempel försöker programmet läsa in biblioteket från olika program- och operativsystemsplatser som beskrivs i början av det här dokumentet för LoadLibrary()-anropet. Om det finns risk för att filen inte finns kan programmet försöka läsa in filen från den aktuella arbetskatalogen. Det här scenariot är något mindre farligt än i föregående exempel. Men programmets användare utsätts fortfarande för risker om miljön inte är helt förutsägbar.

    HMODULE handle = LoadLibrary("schannel.dll");
    

                
                
Här är exempel på bättre och säkrare biblioteksinläsningar:

  • I följande kodexempel läses biblioteket in direkt med hjälp av en fullständigt kvalificerad sökväg. Det finns ingen risk för att angriparen inför skadlig kod om han inte redan har skrivbehörighet till programmets målkatalog.

    HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll");
    

    Obs! Mer information om hur du bestämmer systemkatalogen finns i följande resurser:

    GetSystemDirectory
    http://msdn.microsoft.com/en-us/library/ms724373%28VS.85%29.aspx SHGetKnownFolderPath
    http://msdn.microsoft.com/en-us/library/bb762188%28v=VS.85%29.aspx

  • I följande kodexempel tas den aktuella arbetskatalogen bort från sökvägen innan LoadLibrary anropas. Detta minskar risken avsevärt eftersom angriparen måste styra antingen programkatalogen, Windows-katalogen eller kataloger som anges i användarens sökväg för att kunna använda en DLL-förinläsningsattack.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
  • På alla system som har installerat säkerhetsuppdatering 963027 (beskrivs i MS09-014) skulle följande kod permanent flytta CWD till den sista platsen i sökordningen. Alla senare anrop till funktionen SetSearchPathMode inifrån processen som försöker ändra sökläget misslyckas.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
  • I följande kodexempel tas den aktuella arbetskatalogen bort från sökvägen innan LoadLibrary anropas. Detta minskar risken avsevärt eftersom angriparen måste styra antingen programkatalogen, Windows-katalogen eller kataloger som anges i användarens sökväg för att kunna använda en DLL-förinläsningsattack.

    SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT );
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
    

Använda Processövervakaren för att dynamiskt identifiera icke-osäkra inläsningar

Microsoft publicerar ett verktyg med namnet Processövervakare. Det här verktyget gör det möjligt för utvecklare och administratörer att noga följa upp en pågående process. Processövervakaren kan användas för att dynamiskt identifiera om ett av dina program kan vara sårbart för den här typen av problem.

  • Om du vill ladda ned Processövervakaren går du till följande Microsoft-webbsida:
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

  • Prova att starta programmet med CWD inställt på en specifik katalog. Dubbelklicka till exempel på en fil som har ett filnamnstillägg vars filhanterare har tilldelats till programmet.

  • Konfigurera processövervakaren med följande filter:

    371495f2-14de-f99c-c55a-f75d31fe9ca8

  • Om en sårbar väg drabbas ser du något som liknar följande: 9acdd1ae-29b9-e499-9de9-8bc665b95e76

     Samtalet till fjärrresursen för att läsa in en DLL-fil indikerar att detta är ett sårbart program.

Mer information

Mer information finns på följande Microsoft-webbsidor:

Sökordning för dynamiskt länkbibliotek

http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx MSDN-dokumentation om funktionen SearchPath

http://msdn.microsoft.com/en-us/library/aa365527(VS.85).aspx MSDN-dokumentation om funktionen LoadLibrary

http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx MSDN-dokumentation om funktionen SetDllDirectory

http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx MSDN-dokumentation om funktionen SetSearchPathMode

http://msdn.microsoft.com/en-us/library/dd266735(VS.85).aspx Blogginlägg av David Leblanc, Principal Security Engineer med Microsoft Office

http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx Blogginlägg av Andrew Roths, MSRC Engineering-teamet om DLL-förinläsning av attacker

http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bomb-vulnerability.aspx

Ytterligare resurser