Source code for plaso.analysis.chrome_extension

"""Analysis plugin to gather information about Chrome extensions."""

import re

import requests

from plaso.analysis import interface
from plaso.analysis import logger
from plaso.analysis import manager
from plaso.containers import analysis_results


[docs] class ChromeExtensionPlugin(interface.AnalysisPlugin): """Analysis plugin to gather information about Chrome extensions.""" NAME = "chrome_extension" _SUPPORTED_EVENT_DATA_TYPES = frozenset(["fs:stat"]) _TITLE_RE = re.compile(r"<title>([^<]+)</title>") _WEB_STORE_URL = "https://chrome.google.com/webstore/detail/{xid}?hl=en-US" _REQUEST_TIMEOUT = 60
[docs] def __init__(self): """Initializes an Chrome extension analysis plugin.""" super().__init__() # Saved list of already looked up extensions. self._extensions = {} self._extensions_per_user = {}
def _GetChromeWebStorePage(self, extension_identifier): """Retrieves the page for the extension from the Chrome store website. Args: extension_identifier (str): Chrome extension identifier. Returns: str: page content or None. """ web_store_url = self._WEB_STORE_URL.format(xid=extension_identifier) try: response = requests.get(web_store_url, timeout=self._REQUEST_TIMEOUT) except (requests.ConnectionError, requests.HTTPError) as exception: logger.warning( ( f"[{self.NAME:s}] unable to retrieve URL: {web_store_url:s} with " f"error: {exception!s}" ) ) return None return response.text def _GetPathSegmentSeparator(self, path): """Given a path give back the path separator as a best guess. Args: path (str): path. Returns: str: path segment separator. """ if path[0] in ("\\", "/"): return path[0] if path[1:].startswith(":\\"): return "\\" backward_slash_count = path.count("\\") forward_slash_count = path.count("/") if backward_slash_count > forward_slash_count: return "\\" return "/" def _GetTitleFromChromeWebStore(self, extension_identifier): """Retrieves the name of the extension from the Chrome store website. Args: extension_identifier (str): Chrome extension identifier. Returns: str: name of the extension or None. """ # Check if we have already looked this extension up. if extension_identifier in self._extensions: return self._extensions.get(extension_identifier) page_content = self._GetChromeWebStorePage(extension_identifier) if not page_content: logger.warning( ( f"[{self.NAME:s}] no data returned for extension identifier: " f"{extension_identifier:s}" ) ) return None first_line, _, _ = page_content.partition("\n") match = self._TITLE_RE.search(first_line) name = None if match: title = match.group(1) if title.startswith("Chrome Web Store - "): name = title[19:] elif title.endswith("- Chrome Web Store"): name = title[:-19] if not name: self._extensions[extension_identifier] = None return None self._extensions[extension_identifier] = name return name
[docs] def CompileReport(self, analysis_mediator): """Compiles an analysis report. Args: analysis_mediator (AnalysisMediator): mediates interactions between analysis plugins and other components, such as storage and dfVFS. Returns: AnalysisReport: analysis report. """ for username, extensions in sorted(self._extensions_per_user.items()): for extension, extension_identifier in sorted(extensions): analysis_result = analysis_results.ChromeExtensionAnalysisResult( extension=extension, extension_identifier=extension_identifier, username=username, ) analysis_mediator.ProduceAnalysisResult(analysis_result) self._analysis_counter[username] += 1 return super().CompileReport(analysis_mediator)
# pylint: disable=unused-argument
[docs] def ExamineEvent(self, analysis_mediator, event, event_data, event_data_stream): """Analyzes an event. Args: analysis_mediator (AnalysisMediator): mediates interactions between analysis plugins and other components, such as storage and dfVFS. event (EventObject): event to examine. event_data (EventData): event data. event_data_stream (EventDataStream): event data stream. """ if event_data.data_type not in self._SUPPORTED_EVENT_DATA_TYPES: return filename = getattr(event_data, "filename", None) if not filename: return separator = self._GetPathSegmentSeparator(filename) path_segments = filename.lower().split(separator) # Determine if we have a Chrome extension ID. if "chrome" not in path_segments and "chromium" not in path_segments: return if path_segments[-2] != "extensions": return # TODO: use a regex to check extension identifier extension_identifier = path_segments[-1] if extension_identifier == "Temp": return username = analysis_mediator.GetUsernameForPath(filename) # We still want this information in here, so that we can # manually deduce the username. if not username: if len(filename) > 25: username = f"Not found ({filename[:25]:s}...)" else: username = f"Not found ({filename:s})" extension_string = self._GetTitleFromChromeWebStore(extension_identifier) if not extension_string: extension_string = extension_identifier self._extensions_per_user.setdefault(username, []) extension_tuple = (extension_string, extension_identifier) if extension_tuple not in self._extensions_per_user[username]: self._extensions_per_user[username].append(extension_tuple)
manager.AnalysisPluginManager.RegisterPlugin(ChromeExtensionPlugin)