Disassembler

Artificial intelligence is no match for natural stupidity.
29března2012

Lámání hesel k Excelovským dokumentům


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?


  1. V Excelu si otevřete onen uzamčený dokument
  2. Stiskněte Alt+F11, otevře se vám IDE pro Visual Basic
  3. Ve stromečku vlevo nahoře dvojklikněte na ThisWorkbook
  4. 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
  5. 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)
  6. Zavřete IDE
  7. 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 makra
    ThisWorkbook.UnlockSheet
    ThisWorkbook.UnlockWorkbook
  8. Spusťte příslušné makro podle toho, jaký objekt potřebujete odemknout. Za chvilku vám skript oznámí např.
    The sheet List1 has been unprotected with password 'ABAAAá'
    a rázem si s dokumentem můžete dělat, co se vám zlíbí.

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í.