Processing of Chromium JSON cookie dumps

-Processing of Chromium JSON cookie dumps
This commit is contained in:
HarmJ0y
2023-09-12 16:14:38 -07:00
parent 6dd4e1a16c
commit cc2aba0f2b
3 changed files with 103 additions and 8 deletions
+4 -8
View File
@@ -7,14 +7,8 @@ from typing import List
import extra_streamlit_components as stx
import streamlit as st
import utils
from st_aggrid import (
AgGrid,
ColumnsAutoSizeMode,
DataReturnMode,
GridOptionsBuilder,
GridUpdateMode,
JsCode,
)
from st_aggrid import (AgGrid, ColumnsAutoSizeMode, DataReturnMode,
GridOptionsBuilder, GridUpdateMode, JsCode)
from streamlit_searchbox import st_searchbox
from streamlit_toggle import st_toggle_switch
@@ -191,6 +185,8 @@ if "authentication_status" in st.session_state and st.session_state["authenticat
df_cookies_download = df_cookies_download.drop("source", axis=1)
df_cookies_download = df_cookies_download.drop("username", axis=1)
df_cookies_download = df_cookies_download.drop("browser", axis=1)
df_cookies_download = df_cookies_download.drop("unique_db_id", axis=1)
df_cookies_download = df_cookies_download.drop("notes", axis=1)
df_cookies_download["hostOnly"] = False
df_cookies_download["httpOnly"] = False
df_cookies_download["secure"] = True
+92
View File
@@ -147,6 +147,11 @@ def convert_chromium_timestamp_to_datetime(timestamp: int) -> datetime.datetime:
return datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=timestamp)
def convert_epoch_seconds_to_datetime(timestamp: float) -> datetime.datetime:
"""Converts epoch seconds to a datetime."""
return datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=timestamp)
async def process_chromium_history(
object_id: str,
file_path: str,
@@ -289,6 +294,24 @@ async def process_chromium_logins(
await chromium_logins_q.Send(chromium_login_message.SerializeToString())
async def is_chromium_cookie_json(file_path: str) -> bool:
"""
Helper that downloads the specified file and tries to load it as a json,
checking various fields to see if it's likely a Chromium cookie json dump.
"""
try:
with open(file_path, "r") as f:
file_json = json.loads(f.read())
if type(file_json) == list and len(file_json) > 0:
if type(file_json[0]) == dict and len(file_json[0]) > 10 and len(file_json[0]) < 20:
fields = list(file_json[0].keys())
if "name" in fields and "domain" in fields and "sameSite" in fields and "httpOnly" in fields:
return True
return False
except:
return False
async def process_chromium_cookies(
object_id: str, file_path: str, metadata: pb.Metadata, parsed_data: pb.ParsedData, chromium_cookies_q: MessageQueueProducerInterface
) -> None:
@@ -370,6 +393,75 @@ async def process_chromium_cookies(
await chromium_cookies_q.Send(chromium_cookie_message.SerializeToString())
async def process_cookies_json(
object_id: str, file_path: str, metadata: pb.Metadata, parsed_data: pb.ParsedData, chromium_cookies_q: MessageQueueProducerInterface
) -> None:
"""
Helper that parses out the appropriate data from a (likely Chromium) cookies JSON, builds
the appropriate protobuf messages, and publishes them to the passed queue.
"""
try:
with open(file_path, "r") as f:
cookie_json_all = json.loads(f.read())
cookie_message = pb.ChromiumCookieMessage()
cookie_message.metadata.CopyFrom(metadata)
for cookie_json in cookie_json_all:
try:
cookie = pb.ChromiumCookie()
# user_data_directory is not known here
cookie.originating_object_id = object_id
cookie.host_key = cookie_json["domain"].lower()
cookie.path = cookie_json["path"]
cookie.name = cookie_json["name"]
cookie.is_decrypted = True
cookie.value_dec = cookie_json["value"]
expires_raw = 0
if "expires" in cookie_json:
expires_raw = cookie_json["expires"]
elif "expirationDate" in cookie_json:
expires_raw = cookie_json["expirationDate"]
expires_dt = convert_epoch_seconds_to_datetime(expires_raw)
cookie.expires.FromDatetime(expires_dt)
if "httpOnly" in cookie_json:
cookie.is_httponly = cookie_json["httpOnly"]
if "sameSite" in cookie_json:
if cookie_json["sameSite"].upper() == "NONE":
cookie.samesite = "NONE"
elif cookie_json["sameSite"].upper() == "LAX":
cookie.samesite = "LAX"
elif cookie_json["sameSite"].upper() == "STRICT":
cookie.samesite = "STRICT"
else:
cookie.samesite = "UNKNOWN"
if "session" in cookie_json:
cookie.is_session = cookie_json["session"]
if "secure" in cookie_json:
cookie.is_secure = cookie_json["secure"]
if "sourcePort" in cookie_json:
cookie.source_port = cookie_json["sourcePort"]
cookie_message.data.append(cookie)
except Exception as e:
await logger.awarning(f"Error parsing a specific cookie in process_chromium_cookies_json: {e}")
await chromium_cookies_q.Send(cookie_message.SerializeToString())
except Exception as e:
await logger.aerror(f"Error in process_chromium_cookies_json: {e}")
##################################################
#
# DPAPI helpers
@@ -455,6 +455,13 @@ class FileProcessor(TaskInterface):
await logger.ainfo("Detected Chromium state file, emitting ChromiumStateFileMessage")
await self.out_q_chromiumstatefile.Send(chromium_state_file_message.SerializeToString())
# we have a likely JSON Chromium cookie dump
elif file_data.magic_type == "JSON data" and (await helpers.is_chromium_cookie_json(file_path_on_disk)):
await logger.ainfo("Detected Chromium cookies JSON file, processing")
await helpers.process_cookies_json(
file_data.object_id, file_path_on_disk, metadata, file_data.parsed_data, self.out_q_chromiumcookies
)
# if this file is Seatbelt data, emit a raw_data message so the data is properly processed
elif file_data.magic_type == "JSON data" and helpers.scan_with_yara(file_path_on_disk, "seatbelt_json"):
skip_dpapi_carve = True