ChromeFreak is a python script that lets you look at history, downloads, bookmarks, and cookies for a given Chrome client.
04ef8fca4c69d704bdadc41914416652c14a94a72450dca294bcd9fe0180976d
#!/usr/bin/python
'''
This is a open source forensic framework for Google Chrome. This application was fully written by me after researching
on web browser Chrome. It was really fun exploring, making mistakes, a lot of experience as a developer. Most of these code,
formatting, queries are my own.
If you plan to copy, redistribute it's okay but give credits to the original author.
Notes: Please note this tool may contain errors, and is provided "as it is". There is no guarantee
that it will work on your target systems(s), as the code may have to be adapted.
This is to avoid script kiddie abuse as well.
License:
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/.
Author: Osanda Malith Jayathissa
Website: http://osandamalith.wordpress.com
'''
import os
import sys
import sqlite3
import json
# Do not edit unless you know what you are doing :)
class chromeFreak(object):
def __init__(self, path):
self.path = path
def HistoryObj(self):
history = ''
try:
PathName = self.path + 'History'
connexion = sqlite3.connect(PathName)
c = connexion.cursor()
c.execute("SELECT urls.url, urls.title, urls.visit_count,urls.typed_count, \
datetime((urls.last_visit_time/1000000)-11644473600,'unixepoch', 'localtime'),\
datetime((visits.visit_time/1000000)-11644473600,'unixepoch', 'localtime'), \
CASE (visits.transition & 255)\
WHEN 0 THEN 'User clicked a link'\
WHEN 1 THEN 'User typed the URL in the URL bar'\
WHEN 2 THEN 'Got through a suggestion in the UI'\
WHEN 3 THEN 'Content automatically loaded in a non-toplevel frame - user may not realize'\
WHEN 4 THEN 'Subframe explicitly requested by the user'\
WHEN 5 THEN 'User typed in the URL bar and selected an entry from the list - such as a search bar'\
WHEN 6 THEN 'The start page of the browser'\
WHEN 7 THEN 'A form the user has submitted values to'\
WHEN 8 THEN 'The user reloaded the page, eg by hitting the reload button or restored a session'\
WHEN 9 THEN 'URL what was generated from a replacable keyword other than the default search provider'\
WHEN 10 THEN 'Corresponds to a visit generated from a KEYWORD'\
END AS Description\
FROM urls, visits WHERE urls.id = visits.url")
for row in c:
try:
history += 'URL = %s\n' %str(row[0])
history += 'URL Title = %s\n' %(row[1]).encode("utf-8")
history += 'Number of Visits = %s\n' %str(row[2])
history += 'Last Visit (UTC) = %s\n' %str(row[4])
history += 'First Visit (UTC) = %s\n' %str(row[5])
if (str(row[6]) == 'User typed the URL in the URL bar'):
history += 'Description = %s\n\n' %(str(row[6]))
history += 'Number of Times Typed = %s\n' %(str(row[3]))
else:
history += 'Description = %s\n\n' %(str(row[6]))
except UnicodeError:
continue
return history
except sqlite3.OperationalError, e:
e = str(e)
if (e == 'database is locked'):
print '[!] Make sure Google Chrome is not running in the background'
sys.exit(0)
elif (e == 'no such table: urls'):
print '[!] Something wrong with the database name'
sys.exit(0)
elif (e == 'unable to open database file'):
print '[!] Something wrong with the database path'
sys.exit(0)
else:
print e
def DownloadsObj(self):
downloads = ''
try:
PathName = self.path + 'History'
connexion = sqlite3.connect(PathName)
c = connexion.cursor()
c.execute("SELECT url, current_path, target_path,datetime((end_time/1000000)-11644473600,'unixepoch', 'localtime'),\
datetime((start_time/1000000)-11644473600,'unixepoch', 'localtime'),\
received_bytes, total_bytes FROM downloads,\
downloads_url_chains where downloads.id = downloads_url_chains.id")
for row in c:
try:
downloads += 'URL = %s\n' %str(row[0])
downloads += 'Current Path = %s\n' %str(row[1])
downloads += 'Target Path = %s\n' %str(row[2])
downloads += 'End Time = %s\n' %str(row[3])
downloads += 'Start Time = %s\n' %str(row[4])
if (float(row[5]) < 1024):
downloads += 'Received Bytes = %.2f Bytes\n' %(float(row[5]))
if (float(row[5]) > 1024 and float(row[5]) < 1048576):
downloads += 'Received Bytes = %.2f KB\n' %(float(row[5])/1024)
elif (float(row[5]) > 1048576 and float(row[5]) < 1073741824):
downloads += 'Received Bytes = %.2f MB\n' %(float(row[5])/1048576)
else:
downloads += 'Received Bytes = %.2f GB\n' %(float(row[5])/1073741824)
if (float(row[6]) < 1024):
downloads += 'Total Bytes = %.2f Bytes\n\n' %(float(row[6]))
if (float(row[6]) > 1024 and float(row[6]) < 1048576):
downloads += 'Total Bytes = %.2f KB\n\n' %(float(row[6])/1024)
elif (float(row[6]) > 1048576 and float(row[6]) < 1073741824):
downloads += 'Total Bytes = %.2f MB\n\n' %(float(row[6])/1048576)
else:
downloads += 'Total Bytes = %.2f GB\n\n' %(float(row[6])/1073741824)
except UnicodeError:
continue
return downloads
except sqlite3.OperationalError, e:
e = str(e)
if (e == 'database is locked'):
print '[!] Make sure Google Chrome is not running in the background'
sys.exit(0)
elif (e == 'no such table: downloads'):
print '[!] Something wrong with the database name'
sys.exit(0)
elif (e == 'unable to open database file'):
print '[!] Something wrong with the database path'
sys.exit(0)
else:
print e
sys.exit(0)
def BookmarksObj(self):
bookmarks = ''
try:
his = self.path + 'History'
PathName = self.path+'Bookmarks'
json_data=open(PathName)
data = json.load(json_data)
for i in range(0,2500):
try:
bookmarks += 'URL: %s\n' %str((data['roots']['bookmark_bar']['children'][i]['url']))
bookmarks += 'Name: %s\n' %(data['roots']['bookmark_bar']['children'][i]['name']).encode("utf-8")
bookmarks += 'Type: %s\n' %str((data['roots']['bookmark_bar']['children'][i]['type']))
date_time = str(data['roots']['bookmark_bar']['children'][i]['date_added'])
con = sqlite3.connect(his).cursor().execute("select datetime((" + date_time+ "/1000000)-11644473600,'unixepoch', 'localtime')")
for row in con:
bookmarks += 'Date: %s\n\n' %str(row[0])
except UnicodeError:
continue
except IndexError:
pass
return bookmarks
except IOError:
print '[!] Bookmarks file not found'
sys.exit(0)
except Exception, e:
print e
sys.exit(0)
except sqlite3.OperationalError, e:
e = str(e)
if (e == 'database is locked'):
print '[!] Make sure Google Chrome is not running in the background'
sys.exit(0)
elif (e == 'no such table: urls'):
print '[!] Something wrong with the database name'
sys.exit(0)
elif (e == 'unable to open database file'):
print '[!] Something wrong with the database path'
sys.exit(0)
else:
print e
sys.exit(0)
def CookiesObj(self):
cookie = ''
try:
PathName = self.path + 'Cookies'
connexion = sqlite3.connect(PathName)
c = connexion.cursor()
c.execute("select datetime((creation_utc/1000000)-11644473600,'unixepoch', 'localtime'),\
host_key,name,value,path,datetime((expires_utc/1000000)-11644473600,'unixepoch',\
'localtime'),secure,httponly,datetime((last_access_utc/1000000)-11644473600,'unixepoch',\
'localtime') from cookies;")
for row in c:
cookie += 'Date Created: %s\n' %(str(row[0]))
cookie += 'Host: %s\n' %(str(row[1]))
cookie += 'Name: %s\n' %(str(row[2]))
cookie += 'Value: %s\n' %(str(row[3]))
cookie += 'Path: %s\n' %(str(row[4]))
cookie += 'Expiry Date: %s\n' %(str(row[5]))
if (str(row[6]) == '0'):
cookie += 'Secure Cookie: %s\n' %('No')
else:
cookie += 'Secure Cookie: %s\n' %('Yes')
if (str(row[7]) == '0'):
cookie += 'HttpOnly Cookie: %s\n\n' %('No')
else:
cookie += 'HttpOnly Cookie: %s\n' %('Yes')
cookie += 'Last Access: %s\n\n' %(str(row[8]))
return cookie
while True:
msg = str(raw_input('[?] Do you want to save to a file? ')).lower()
try:
if msg[0] == 'y':
name = str(raw_input('[*] Enter a filename: '))
SaveObj = Savefile(name,cookie)
print SaveObj.save()
mainMenu()
if msg[0] == 'n':
print cookie
mainMenu()
break
else:
print '[!] Enter a valid choice'
except Exception, e:
print e
continue
except sqlite3.OperationalError, e:
e = str(e)
if (e == 'database is locked'):
print '[!] Make sure Google Chrome is not running in the background'
sys.exit(0)
elif (e == 'no such table: cookies'):
print '[!] Something wrong with the database name'
sys.exit(0)
elif (e == 'unable to open database file'):
print '[!] Something wrong with the database path'
sys.exit(0)
else:
print e
sys.exit(0)
class Savefile(object):
def __init__(self, filename, component):
self.name = filename
self.comp = component
self.comp += '[~] This file was generated by ChromeFreak'
self.comp += '\n[~] Website: http://osandamalith.github.io/ChromeFreak/\n'
def save(self):
file = open(self.name+'.txt' , "w")
file.write(self.comp)
file.close()
return '[~] File Saved to ' + os.path.abspath(self.name) + '.txt'
def fullReport(PathName):
full = ''
full += banner() + '\n\n'
full += '---------------\n[*] History\n---------------\n'
full += chromeFreak(PathName).HistoryObj() + '\n'
full += '---------------\n[*] Downloads\n---------------\n'
full += chromeFreak(PathName).DownloadsObj() + '\n'
full += '---------------\n[*] Bookmarks\n---------------\n'
full += chromeFreak(PathName).BookmarksObj() + '\n'
full += '---------------\n[*] Cookies\n---------------\n'
full += chromeFreak(PathName).CookiesObj()
while True:
msg = str(raw_input('[?] Do you want to save to a file? ')).lower()
try:
if msg[0] == 'y':
name = str(raw_input('[*] Enter a filename: '))
SaveObj = Savefile(name,full)
print SaveObj.save()
mainMenu()
if msg[0] == 'n':
print full
mainMenu()
break
else:
print '[!] Enter a valid choice'
except Exception, e:
print e
continue
def Start(component):
while True:
msg = str(raw_input('[?] Do you want to save to a file? ')).lower()
try:
if msg[0] == 'y':
name = str(raw_input('[*] Enter a filename: '))
SaveObj = Savefile(name,component)
print SaveObj.save()
mainMenu()
if msg[0] == 'n':
print component
mainMenu()
break
else:
print '[!] Enter a valid choice'
except Exception, e:
print e
continue
def mainMenu():
while True:
try:
choice = str(raw_input('[?] Do you want to go to the main menu?')).lower()
if choice[0] == 'y':
main()
break
if choice[0] == 'n':
sys.exit(0)
break
except ValueError:
sys.exit(0)
def banner():
banner = '''
,gggg,
,88"""Y8b,,dPYb,
d8" `Y8IP'`Yb
d8' 8b d8I8 8I
,8I "Y88P'I8 8'
I8' I8 dPgg, ,gggggg, ,ggggg, ,ggg,,ggg,,ggg, ,ggg,
d8 I8dP" "8I dP""""8I dP" "Y8ggg,8" "8P" "8P" "8, i8" "8i
Y8, I8P I8 ,8' 8I i8' ,8I I8 8I 8I 8I I8, ,8I
`Yba,,_____,,d8 I8,,dP Y8,,d8, ,d8' ,dP 8I 8I Yb, `YbadP'
`"Y888888888P `Y88P `Y8P"Y8888P" 8P' 8I 8I `Y8888P"Y888
,gggggggggggggg
dP""""""88"""""" ,dPYb,
Yb,_ 88 IP'`Yb
`"" 88 I8 8I
ggg88gggg I8 8bgg,
88 8,gggggg, ,ggg, ,gggg,gg I8 dP" "8
88 dP""""8I i8" "8i dP" "Y8I I8d8bggP"
gg, 88 ,8' 8I I8, ,8I i8' ,8I I8P' "Yb,
"Yb,,8P ,dP Y8, `YbadP' ,d8, ,d8b,,d8 `Yb,
"Y8P' 8P `Y8888P"Y888P"Y8888P"`Y888P Y8
[*] Author: Osanda Malith Jayathissa
[*] Follow @OsandaMalith
[*] Description: A Cross-Platform Forensic Framework for Google Chrome
'''
return banner
def main():
if os.name == "nt":
os.system('cls')
else:
os.system('clear')
print banner()
try:
if os.name == "nt":
# This is the Windows Path
PathName = os.getenv('localappdata') + '\\Google\\Chrome\\User Data\\Default\\'
if (os.path.isdir(PathName) == False):
print '[!] Chrome Doesn\'t exists'
sys.exit(0)
elif ((os.name == "posix") and (sys.platform == "darwin")):
# This is the OS X Path
PathName = os.getenv('HOME') + "/Library/Application Support/Google/Chrome/Default/"
if (os.path.isdir(PathName) == False):
print '[!] Chrome Doesn\'t exists'
sys.exit(0)
elif (os.name == "posix"):
# This is the Linux Path
PathName = os.getenv('HOME') + '/.config/google-chrome/Default/'
if (os.path.isdir(PathName) == False):
print '[!] Chrome Doesn\'t exists'
sys.exit(0)
while True:
try:
choice = int(raw_input("[?] What do you like to investigate? \
\n1. History\n2. Downloads\n3. Bookmarks\n4. Cookies\n5. Full Report\n6. Exit\n" ))
except ValueError:
print '[!] Enter Only a Number'
continue
if choice == 1:
history = chromeFreak(PathName).HistoryObj()
Start(history)
break
if choice == 2:
downloads = chromeFreak(PathName).DownloadsObj()
Start(downloads)
break
if choice == 3:
bookmarks = chromeFreak(PathName).BookmarksObj()
Start(bookmarks)
break
if choice == 4:
cookies = chromeFreak(PathName).CookiesObj()
Start(cookies)
break
if choice == 5:
fullReport(PathName)
if choice == 6:
sys.exit(0)
else:
print '[!] Invalid Choice'
except KeyboardInterrupt:
print '[!] Ctrl + C detected\n[!] Exiting'
sys.exit(0)
except EOFError:
print '[!] Ctrl + D detected\n[!] Exiting'
sys.exit(0)
if __name__ == "__main__":
main()