Osäker lösenordshantering i SQL-Server

SQL-Server har två svagheter som gör det enkelt för en elak hacker att ta reda på en sql-serveranvändares lösenord.

Problem 1 – Lösenorden skickas över nätverket i ”klartext”

När man använder SQL-Server autentisering så innebär det att när man ansluter till SQL-Server så måste man skicka med användarnamn och lösenord, till skillnad från Windows autentisering där allt sker automatiskt i bakgrunden. Vid Windows autentisering används samma inloggningsinformation som man använde när man loggade in i Windows.

Problemet vi skall gå igenom här gäller bara vid SQL-Server autentisering.

När det byggs applikationer och det handlar om att kryptera och dekryptera lösenord så är det en strid mellan de som vill ha det säkert och de som vill ha prestanda. Bra säkerhet innebär ofta lägre prestanda och därför kan det lätt bli så att de som till slut väljer vilket alternativ som skall användas väljer det alternativet som går snabbast. De väljer detta alternativet eftersom i deras ögon så är lösenordet fortfarande gömt och krypterat i viss mån, men nackdelen med dessa alternativ är att det endast hindrar från att lösenordet skrivs ut i klartext, de hindrar sällan en hacker eller kryptoanalytiker.

Problemet ligger i att det som ofta används för att kryptera är en vanlig XOR operation.
Innan vi går vidare är det bra att du känner till hur XOR fungerar, om du inte vet exakt hur det fungerar eller om du vill färska upp minnet så kan du läsa mitt inlägg om XOR.

När man matar in lösenordet så krypteras det innan det skickas över nätverket. Kortfattat sker krypteringen på följande sätt.

  1. Hämta decimalvärdet för varje tecken och konvertera till hex.
  2. Byt plats på de första 4 bitarna och de 4 sista bitarna i varje byte.
  3. Kör XOR på varje byte mot 0xA5.

Nu tänkte vi gå igenom hur man går tillväga för att sniffa upp ett lösenord och sedan dekryptera det. Vi kommer då även gå igenom mer i detalj vad som händer i varje steg.

Till att börja med måste vi ha någonting att dekryptera och tanken är att vi skall sniffa nätverkspaket. I detta exempel har vi valt att använda ethereal som kan laddas ner kostnadsfritt från www.ethereal.com.

Det finns mängder av filter du kan aktivera för att exempelvis endast sniffa inloggningar mot en SQL-Server, men det kommer vi inte att gå igenom här. Just ethereal gör det enkelt för oss eftersom vi ser att det är ett ”login packet” som har skickats, och om vi går in på den och klickar oss fram till TDS (Tabular Data Stream), efter det ser vi användarnamn (i klartext) och lösenord. Om vi klickar på ”password” så markeras de bitar som hör till lösenordet. I andra fall när vi sniffar så kanske vi inte får den hjälpen. Då kan vi istället leta efter en TDS där varannan byte innehåller 0xA5. Anledningen till att vi kan leta efter detta är att lösenordet konverteras till en unicodesträng och unicodesträngar byggs upp av två byte istället för en. På grund av att den andra byten i en unicode ofta är 0x00 så vet vi att nyckeln som används i SQL-Servers XOR-operation är 0xA5 eftersom varannan byte är just 0xA5. Detta eftersom 0x00 XOR 0xA5 är just 0xA5. Eftersom vi inte behöver eller vill läsa 0xA5 så tar vi bort dessa och kvar blir då: E2 F3 92 E2 B6 86 96. Redan nu vet vi hur många tecken lösenordet är, det som återstår är att få fram det i klartext.

Det vi gör nu är att köra XOR (med nyckeln 0xA5) mot varje byte. (Använd gärna kalkylatorn om du inte vill räkna själv)

E2 XOR A5 = 47
F3 XOR A5 = 56

Efter att vi kört XOR på varje byte så måste vi byta plats på de fyra första och de fyra sista bitarna i varje byte. I praktiken är det bara att byta plats på de värden vi har fått fram hittills.

47 (0100 0111) = 74 (0111 0100)
56 (0101 0110) = 65 (0110 0101)

För nöjes skull kan vi berätta att platsbytet på bitarna även sker på 0x00 (den andra byten i unicodesträngen), men eftersom 0x00 betyder 0000 0000 och att byta plats på dessa gör ingen skillnad i slutändan.

När vi har fått fram detta så konverterar vi alla värden från hex till decimal. Denna siffra representerar nu tecknet som finns på motsvarande plats i en asciitabell. En asciitabell finns att tillgå på exempelvis http://www.asciitable.com. I vårt exempel skulle detta innebära att vårt tal 74 decimalt blir 116 vilket motsvarar tecknet t i en asciitabell. På samma sätt blir vårt tal 65 decimalt 101 vilket representerar tecknet e. Se nedan för en komplett uppställning på dekrypteringen av vårt exempel.

	1	2	3	4	5	6	7
Sniff	E2	F3	92	E2	B6	86	96
XOR	47	56	37	47	13	23	33
Bitbyte	74	65	73	74	31	32	33
Decimal	116	101	115	116	49	50	51
Tecken	t	e	s	t	1	2	3

Vi kom alltså fram till att lösenordet är test123

Det bästa sättet att skydda sig mot detta är att gå över till Windows autentisering för att på det viset undvika att användarnamn och lösenord skickas ”okrypterat” över nätverket. I vissa lägen kan man inte undvika SQL-Server autentisering och då är vårt förslag att man använder en säker anslutning, exempelvis SSL eller IPSec.

Viktigt att komma ihåg är dock att om du kör med windows autentisering så kommer all datatrafik fortfarande skickas helt okrypterat, därför kan det vara en bra idé att alltid köra SSL eller IPSec för att på det sättet även skydda data.

Problem 2 – Stora bokstäver i lösenorden

SQL-Server lagrar användarnas lösenord i tabellen sysxlogins i masterdatabasen. Dessa lösenord lagras hashade, problemet är att man har valt att dubbellagra lösenordet, första gången i det formatet som man matade in det och andra gången helt i versaler. Detta gör att hashningen blir väldigt mycket svagare. Såhär går det till när SQL-Server skapar lösenordshashen.

  1. Ett salt skapas där fröet är aktuell tid.
  2. Det inmatade lösenordet tillsammans med saltet hashas med SHA-algoritmen.
  3. Det inmatade lösenordet konverteras till versaler och hashas återigen med SHA-algoritmen.
  4. Allt konkateneras, först ett statiskt värde 0x0100, sedan saltet, sedan hashvärdet från punkt 2, sedan hashvärdet från punkt 3.

Låt oss visa hur du själv kan se hur detta går till.

Kör följande kod:

SELECT pwdencrypt('aaa')
SELECT pwdencrypt('AAA')

Du kommer att få fram andra värden än de som är i detta exempel eftersom saltet är tidsbaserad och skiljer sig därför åt varje gång du kör koden.

Såhär kan resultatet se ut:

0x01004D500234606C06CE60B43137BB1F46E1AF73A9E41B1131971BAD3A1
967D3758275E30BE32254861272C8DDA7

0x01004D5002341BAD3A1967D3758275E30BE32254861272C8DDA71BAD3A1
967D3758275E30BE32254861272C8DDA7

Om vi delar upp dessa i de fyra delar vi pratade om förut så får vi fram följande:

Statiskt värde: 0x0100
Salt: 4D500234
Första hashen: 606C06CE60B43137BB1F46E1AF73A9E41B113197
Andra hashen: 1BAD3A1967D3758275E30BE32254861272C8DDA7

Statiskt värde: 0x0100
Salt: 4D500234
Första hashen: 1BAD3A1967D3758275E30BE32254861272C8DDA7
Andra hashen: 1BAD3A1967D3758275E30BE32254861272C8DDA7

Här kan vi tydligt se att när vi skapade ett hashat lösenord med endast versaler så blir den första och den andra hashen identiska, det är detta vi kan utnyttja när vi knäcker lösenord.

Om vi tänker oss att någon har lagrat ett lösenord som är ”pASSwoRd” så hade vi egentligen behövt testa 256 (2^8) varianter av stora och små bokstäver av det ordet. Men på grund av missen med att lösenordet lagras med endast stora bokstäver så behöver vi bara testa varje ord en gång.

Ordlistor kan man snabbt gå igenom och om det inte skulle räcka så kan man gå vidare och använda brute force. NGSSoftware har ett program som kan göra detta åt dig www.ngssoftware.com.

Det bästa sättet att skydda sig mot detta är att vara noggrann med till vem man ger sysadmin-rollen. Förutom det är det viktigt att ha långa lösenord, längre än 15 tecken är inte helt orimligt att använda. Utöver att ha långa lösenord måste man också blanda siffror/bokstäver och stora/små för att göra det svårare att knäcka. Men hur du än gör så är det bara en tidsfråga innan lösenordet blir knäckt. Med dagens datorer knäcker man ett lösenord på 8 tecken på några minuter, redan vid 9 tecken tar det avsevärt längre tid, men som vi alla vet går utvecklingen fort framåt och det kommer bara att gå fortare och fortare att knäcka SQL-Server-hashar.

Kommentarer inaktiverade.

%d bloggare gillar detta: