Tabulkový procesor Microsoft Excel podporuje uzamykání dokumentů a jejich částí už nějaký ten pátek. Uzamykání se hodí, když nechcete, aby vám někdo vrtal do buněk, do kterých by neměl nebo aby vaše pracně vymyšlené vzorečky zůstaly skryty. V takovém případě můžete odemknutí podmínit i heslem. Bohužel se Excel snaží zachovávat zpětnou kompatibilitu s dvacet let starým softwarem (resp. dokumenty jím vytvořenými), což přináší nejednu násilně zachovávanou regresi, takže vám je takové heslo úplně k ničemu.
It’s not a bug, it’s a feature!
Věděli jste například, že kvůli zpětné kompatibilitě s Lotus 1-2-3, Excel záměrně przní data v roce 1900 (což je mimochodem první rok, se kterým je schopen pracovat jako s datem)? Schválně, jestlipak si z dějepisu pamatujete, co se stalo nultého ledna roku 1900? Navíc jej považuje za přestupný, což je samozřejmě také špatně.
Nadpis článku slibuje prolomení hesla. Podotýkám, že se nejedná o hesla použitá k šifrování, která je třeba zadat, aby se dokument vůbec otevřel (i když na taková určitě také nějaké nástroje existují), nýbrž o hesla k listům a buňkám uzamknutým k úpravám. K násilnému odemknutí taky využijeme jednu nedokumentovanou featuru. Nevím přesně jak Excel ona hesla k sešitům a listům ukládá, ale pravděpodobně se bude jednat o nějaký velmi primitivní hash, ke kterému lze na dnešních počítačích najít kolizi v řádu jednotek sekund. Podle tvůrce VBS skriptu níže se však jedná o záměrně implementovaný backdoor, využívající hesla o délce 1 až 9 znaků, složených pouze ze znaků „A“ nebo „B“, přičemž poslední znak může být jakýkoliv s ASCII kódem od 32 do 255.
Jak na to?
- V Excelu si otevřete onen uzamčený dokument
- Stiskněte
Alt+F11
, otevře se vám IDE pro Visual Basic - Ve stromečku vlevo nahoře dvojklikněte na
ThisWorkbook
- Do nově otevřeného okna vložte následující kód
' modUnlockRoutines ' ' Module provides Excel workbook and sheet unlock routines. The algorithm ' relies on a backdoor password that can be 1 to 9 characters long where each ' character is either an "A" or "B" except the last which can be any character ' from ASCII code 32 to 255. ' ' Implemented as a regular module for use with any Excel VBA project. ' ' © 2007 Kevin M. Jones Option Explicit Private Sub DisplayStatus( _ ByVal PasswordsTried As Long _ ) ' Display the status in the Excel status bar. ' Syntax ' DisplayStatus(PasswordsTried) ' PasswordsTried - The number of passwords tried thus far. Static LastStatus As String LastStatus = Format(PasswordsTried / 57120, "0%") & " of possible passwords tried." If Application.StatusBar <> LastStatus Then Application.StatusBar = LastStatus DoEvents End If End Sub Private Function TrySheetPasswordSize( _ ByVal Size As Long, _ ByRef PasswordsTried As Long, _ ByRef Password As String, _ Optional ByVal Base As String _ ) As Boolean ' Try unlocking the sheet with all passwords of the specified size. ' TrySheetPasswordSize(Size, PasswordsTried, Password, [Base]) ' Size - The size of the password to try. ' PasswordsTried - The cummulative number of passwords tried thus far. ' Password - The current password. ' Base - The base password from the calling routine. Dim Index As Long On Error Resume Next If IsMissing(Base) Then Base = vbNullString If Len(Base) < Size - 1 Then For Index = 65 To 66 If TrySheetPasswordSize(Size, PasswordsTried, Password, Base & Chr(Index)) Then TrySheetPasswordSize = True Exit Function End If Next Index ElseIf Len(Base) < Size Then For Index = 32 To 255 ActiveSheet.Unprotect Base & Chr(Index) If Not ActiveSheet.ProtectContents Then TrySheetPasswordSize = True Password = Base & Chr(Index) Exit Function End If PasswordsTried = PasswordsTried + 1 Next Index End If On Error GoTo 0 DisplayStatus PasswordsTried End Function Private Function TryWorkbookPasswordSize( _ ByVal Size As Long, _ ByRef PasswordsTried As Long, _ ByRef Password As String, _ Optional ByVal Base As String _ ) As Boolean ' Try unlocking the workbook with all passwords of the specified size. ' TryWorkbookPasswordSize(Size, PasswordsTried, Password, [Base]) ' Size - The size of the password to try. ' PasswordsTried - The cummulative number of passwords tried thus far. ' Password - The current password. ' Base - The base password from the calling routine. Dim Index As Long On Error Resume Next If IsMissing(Base) Then Base = vbNullString If Len(Base) < Size - 1 Then For Index = 65 To 66 If TryWorkbookPasswordSize(Size, PasswordsTried, Password, Base & Chr(Index)) Then TryWorkbookPasswordSize = True Exit Function End If Next Index ElseIf Len(Base) < Size Then For Index = 32 To 255 ActiveWorkbook.Unprotect Base & Chr(Index) If Not ActiveWorkbook.ProtectStructure And Not ActiveWorkbook.ProtectWindows Then TryWorkbookPasswordSize = True Password = Base & Chr(Index) Exit Function End If PasswordsTried = PasswordsTried + 1 Next Index End If On Error GoTo 0 DisplayStatus PasswordsTried End Function Public Sub UnlockSheet() ' Unlock the active sheet using a backdoor Excel provides where an alternate ' password is created that is more limited. Dim PasswordSize As Variant Dim PasswordsTried As Long Dim Password As String PasswordsTried = 0 If Not ActiveSheet.ProtectContents Then MsgBox "The sheet is already unprotected." Exit Sub End If On Error Resume Next ActiveSheet.Protect "" ActiveSheet.Unprotect "" On Error GoTo 0 If ActiveSheet.ProtectContents Then For Each PasswordSize In Array(5, 4, 6, 7, 8, 3, 2, 1) If TrySheetPasswordSize(PasswordSize, PasswordsTried, Password) Then Exit For Next PasswordSize End If If Not ActiveSheet.ProtectContents Then MsgBox "The sheet " & ActiveSheet.Name & " has been unprotected with password '" & Password & "'." End If Application.StatusBar = False End Sub Public Sub UnlockWorkbook() ' Unlock the active workbook using a backdoor Excel provides where an alternate ' password is created that is more limited. Dim PasswordSize As Variant Dim PasswordsTried As Long Dim Password As String PasswordsTried = 0 If Not ActiveWorkbook.ProtectStructure And Not ActiveWorkbook.ProtectWindows Then MsgBox "The workbook is already unprotected." Exit Sub End If On Error Resume Next ActiveWorkbook.Unprotect vbNullString On Error GoTo 0 If ActiveWorkbook.ProtectStructure Or ActiveWorkbook.ProtectWindows Then For Each PasswordSize In Array(5, 4, 6, 7, 8, 3, 2, 1) If TryWorkbookPasswordSize(PasswordSize, PasswordsTried, Password) Then Exit For Next PasswordSize End If If Not ActiveWorkbook.ProtectStructure And Not ActiveWorkbook.ProtectWindows Then MsgBox "The workbook " & ActiveWorkbook.Name & " has been unprotected with password '" & Password & "'." End If Application.StatusBar = False End Sub
- Skiskem
Ctrl+S
nebo kliknutím na ikonu s disketou uložte změny (U *.xlsx dokumentů vás IDE zdrbe, že ukládáte makro do sešitu bez podpory maker, uložte jej tedy jako *.xlsm, s podporou) - Zavřete IDE
- Zpět v Excelu stiskněte
Alt+F8
, otevře se vám nabídka maker. Pokud jste vše udělali správně, uvidíte makraThisWorkbook.UnlockSheet ThisWorkbook.UnlockWorkbook
- Spusťte příslušné makro podle toho, jaký objekt potřebujete odemknout. Za chvilku vám skript oznámí např.
a rázem si s dokumentem můžete dělat, co se vám zlíbí.The sheet List1 has been unprotected with password 'ABAAAá'
Na závěr malá připomínka - Zjistěte si, zda onen sešit opravdu potřebujete odemknout, abyste nedopadli jako můj kolega, který po pracném odemknutí sešitu a všech jeho listů zjistil, že veškeré informace, které potřeboval, byly už od začátku přístupné. Stačilo si zobrazit skryté listy.
Update
Protože je tenhle článek docela frekventovaný a nezanedbatelné procento návštěvníků tvoří uživatelé Firefoxu, přidávám stažitelný textový soubor se skriptem. Firefox totiž vinou bugu 116083 nebere v potaz CSS kopírovaného textu a nectí mezery a konce řádků u elementů s white-space: pre
, takže z vykopírovaného kódu vznikne jednolitý blok textu a kód samozřejmě přestane fungovat. Jen tak mimochodem, bug byl otevřen v prosinci 2001 a stále nikoho z vývojářů kdovíjak netrápí.