def main():
    parser = build_cli_parser("Process utility")
    args = parser.parse_args()

    # BEGIN Common
    if args.prefix:
        output_filename = '{0}-processes.csv'.format(args.prefix)
    else:
        output_filename = 'processes.csv'

    if args.append == True or args.queryfile is not None:
        file_mode = 'a'
    else:
        file_mode = 'w'

    if args.days:
        query_base = ' start:-{0}m'.format(args.days * 1440)
    elif args.minutes:
        query_base = ' start:-{0}m'.format(args.minutes)
    else:
        query_base = ''

    if args.profile:
        cb = CbThreatHunterAPI(profile=args.profile)
    else:
        cb = CbThreatHunterAPI()

    queries = []
    if args.query:
        queries.append(args.query)
    elif args.queryfile:
        with open(args.queryfile, 'r') as f:
            for query in f.readlines():
                queries.append(query.strip())
        f.close()
    else:
        queries.append('')
    # END Common

    output_file = open(output_filename, file_mode)
    writer = csv.writer(output_file)
    writer.writerow([
        "proc_timestamp", "proc_hostname", "proc_username", "proc_path",
        "proc_cmdline", "proc_hashes", "proc_child_count",
        "proc_filemod_count", "proc_modload_count", "proc_netconn_count",
        "proc_url", "parent_name"
    ])

    for query in queries:
        result_set = process_search(cb, query, query_base)

        for row in result_set:
            if _python3 == False:
                row = [
                    col.encode('utf8') if isinstance(col, unicode) else col
                    for col in row
                ]
            writer.writerow(row)

    output_file.close()
Example #2
0
def get_feed_ids():
    cb = CbThreatHunterAPI()
    feeds = cb.select(Feed)
    if not feeds:
        log.info("No feeds are available for the org key {}".format(cb.credentials.org_key))
    else:
        for feed in feeds:
            log.info("Feed name: {:<20} \t Feed ID: {:>20}".format(feed.name, feed.id))
def get_feed_ids():
    cb = CbThreatHunterAPI()
    url = "/threathunter/feedmgr/v2/orgs/{}/feeds".format(
        cb.credentials.org_key)
    feeds = cb.get_object(url)
    if len(feeds['results']) == 0:
        print("No feeds are available for the org key {}".format(
            cb.credentials.org_key))
    else:
        for feed in feeds['results']:
            print("Feed name: {:<20} \t Feed ID: {:>20}".format(
                feed['name'], feed['id']))
Example #4
0
def get_cb_threathunter_object(args):
    if args.verbose:
        logging.basicConfig()
        logging.getLogger("cbapi").setLevel(logging.DEBUG)
        logging.getLogger("__main__").setLevel(logging.DEBUG)

    if args.cburl and args.apitoken:
        cb = CbThreatHunterAPI(url=args.cburl,
                               token=args.apitoken,
                               ssl_verify=(not args.no_ssl_verify))
    else:
        cb = CbThreatHunterAPI(profile=args.profile)

    return cb
Example #5
0
def activate_report(cb: CbThreatHunterAPI, report_id) -> Dict:
    """Set this report to active status"""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports/{report_id}/ignore"
    try:
        return cb.delete_object(url)
    except ServerError:
        LOGGER.error(f"Caught ServerError getting report {report_id}: {e}")
def main():
    parser = build_cli_parser()
    parser.add_argument("-thp",
                        "--threatprofile",
                        help="Threat Hunter profile",
                        default="default")
    commands = parser.add_subparsers(help="Feed commands", dest="command_name")

    list_command = commands.add_parser("list",
                                       help="List all configured feeds")

    list_reports_command = commands.add_parser(
        "list-reports", help="List all configured reports for a feed")
    list_reports_command.add_argument("-i", "--id", type=str, help="Feed ID")

    convert_feed_command = commands.add_parser(
        "convert", help="Convert feed from CB Response to CB Threat Hunter")
    convert_feed_command.add_argument("-i", "--id", type=str, help="Feed ID")

    args = parser.parse_args()
    cb = get_cb_response_object(args)
    cb_th = CbThreatHunterAPI(profile=args.threatprofile)

    if args.command_name == "list":
        return list_feeds(cb, parser, args)
    if args.command_name == "list-reports":
        return list_reports(cb, parser, args)
    if args.command_name == "convert":
        return convert_feed(cb, cb_th, parser, args)
Example #7
0
def get_alert(cb: CbThreatHunterAPI, alert_id) -> Dict:
    """Get alert by ID."""
    url = f"/appservices/v6/orgs/{cb.credentials.org_key}/alerts/{alert_id}"
    try:
        return cb.get_object(url)
    except ServerError:
        LOGGER.error(f"Caught ServerError getting report {report_id}: {e}")
Example #8
0
def get_file_content(cb: CbThreatHunterAPI, session_id: str, file_id: str):
    """Get file content stored in LR session and write the file locally."""
    from cbinterface.helpers import get_os_independent_filepath

    try:
        real_session_id, device_id = session_id.split(":", 1)
        filename = f"{real_session_id}_on_{device_id}"
        file_metadata = cb.get_object(
            f"{CBLR_BASE}/session/{session_id}/file/{file_id}")
        if file_metadata:
            filepath = get_os_independent_filepath(file_metadata["file_name"])
            filename = f"{filename}_{filepath.name}"
        result = cb.session.get(
            f"{CBLR_BASE}/session/{session_id}/file/{file_id}/content",
            stream=True)
        if result.status_code != 200:
            LOGGER.error(
                f"got {result.status_code} from server getting file {file_id} content for session {session_id}"
            )
            return
        with open(filename, "wb") as fp:
            for chunk in result.iter_content(io.DEFAULT_BUFFER_SIZE):
                fp.write(chunk)
        if os.path.exists(filename):
            LOGGER.info(f"wrote: {filename}")
        return os.path.exists(filename)
    except ObjectNotFoundError:
        LOGGER.warning(f"no file {file_id} content with session {session_id}")
        return
Example #9
0
def event_search_complete(cb: CbThreatHunterAPI, job_id):
    """Return true when a search is complete."""
    url = f"/api/investigate/v1/orgs/{cb.credentials.org_key}/enriched_events/search_jobs/{job_id}"
    result = cb.get_object(url)
    if result["completed"] == result["contacted"]:
        return True
    return False
Example #10
0
def activate_ioc(cb: CbThreatHunterAPI, report_id, ioc_id):
    """Activate IOC."""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports/{report_id}/iocs/{ioc_id}/ignore"
    resp = cb.delete_object(url)
    if resp.status_code == 204:
        return True
    return False
Example #11
0
def get_session_by_id(cb: CbThreatHunterAPI, session_id):
    """Get a LR session object by id."""
    try:
        return cb.get_object(f"{CBLR_BASE}/session/{session_id}")
    except ObjectNotFoundError:
        LOGGER.warning(f"no live resonse session by ID={session_id}")
        return None
Example #12
0
def make_process_query(
    cb: CbThreatHunterAPI,
    query: str,
    start_time: datetime.datetime = None,
    last_time: datetime.datetime = None,
    raise_exceptions=True,
    validate_query=False,
) -> AsyncProcessQuery:
    """Query the CbThreatHunterAPI environment and interface results.

    Args:
        cb: A CbThreatHunterAPI object to use
        query: The process query
        start_time: Set the process start time (UTC).
        last_time: Set the process last time (UTC). Only processes with a start
        time that falls before this last_time.
        raise_exceptions: Let any exceptions raise up (library use)
        validate_query: If True, validate the query before attempting to use it.
    Returns: AsyncProcessQuery or empty list.
    """

    LOGGER.debug(
        f"buiding query: {query} between '{start_time}' and '{last_time}'")
    processes = []
    try:
        processes = cb.select(Process).where(query)
        if validate_query and not is_valid_process_query(processes):
            LOGGER.info(
                f"For help, refer to {cb.url}/#userGuideLocation=search-guide/investigate-th&fullscreen"
            )
            LOGGER.info(
                f"Is this a legacy query? ... Attempting to convert to PSC query ..."
            )
            converted_query = convert_from_legacy_query(cb, query)
            if not converted_query:
                LOGGER.info(
                    f"failed to convert to PSC query... 🤡 your query is jacked up."
                )
                return []
            if is_valid_process_query_string(cb, converted_query):
                LOGGER.info(
                    "successfully converted and validated the query you supplied to a PSC query 👍, see below."
                )
                LOGGER.info(
                    f"👇👇 try again with the following query 👇👇 - also, hint, single quotes are your friend. "
                )
                LOGGER.info(f"query: '{converted_query}'")
            return []
        if start_time or last_time:
            start_time = start_time.isoformat() if start_time else "*"
            end_time = last_time.isoformat() if last_time else "*"
            processes = processes.where(
                f"process_start_time:[{start_time} TO {end_time}]")
        LOGGER.info(f"got {len(processes)} process results.")
    except Exception as e:
        if raise_exceptions:
            raise (e)
        LOGGER.error(f"unexpected exception: {e}")

    return processes
Example #13
0
def alert_search(
    cb: CbThreatHunterAPI,
    search_data: Dict = {},
    criteria: Dict = {},
    query: str = None,
    rows=40,
    sort: List[Dict] = [{"field": "first_event_time", "order": "DESC"}],
    start: int = 0,
    workflow_state=["OPEN", "DISMISSED"],
) -> Dict:
    """Perform an Alert search

    One request and return the result.
    """
    url = f"/appservices/v6/orgs/{cb.credentials.org_key}/alerts/watchlist/_search"
    if not search_data:
        if "workflow" not in criteria:
            criteria["workflow"] = workflow_state
        search_data = {"criteria": criteria, "query": query, "rows": rows, "start": start, "sort": sort}
    try:
        result = cb.post_object(url, search_data)
        return result.json()

    except ServerError as e:
        LOGGER.error(f"Caught ServerError searching alerts: {e}")
        return False
    except ClientError as e:
        LOGGER.warning(f"got ClientError searching alerts: {e}")
        return False
    except ValueError:
        LOGGER.warning(f"got unexpected {result}")
        return False
Example #14
0
def delete_watchlist(cb: CbThreatHunterAPI, watchlist_id) -> Dict:
    """Set this report to ignore status"""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/watchlists/{watchlist_id}"
    try:
        return cb.delete_object(url)
    except ServerError:
        LOGGER.error(f"Caught ServerError deleting watchlist {watchlist_id}: {e}")
Example #15
0
def get_session_commands(cb: CbThreatHunterAPI, session_id: str):
    """List commands for this session."""
    try:
        return cb.get_object(f"{CBLR_BASE}/session/{session_id}/command")
    except ObjectNotFoundError:
        LOGGER.warning(f"no live resonse session by ID={session_id}")
        return None
Example #16
0
def is_ioc_ignored(cb: CbThreatHunterAPI, report_id, ioc_id, check_existence=False):
    """Return status of IOC."""
    if check_existence:
        if not ioc_does_exist(cb, report_id, ioc_id):
            LOGGER.warning("IOC does not exist.")
            return None
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports/{report_id}/iocs/{ioc_id}/ignore"
    return cb.get_object(url)["ignored"]
Example #17
0
def get_report_with_IOC_status(cb: CbThreatHunterAPI, report_id) -> Dict:
    """Get report and include status of every report IOC."""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports/{report_id}/iocs"
    report = get_report(cb, report_id)
    if not report:
        return None
    for ioc in report["iocs_v2"]:
        ioc["ignored"] = cb.get_object(f"{url}/{ioc['id']}/ignore")["ignored"]
    return report
Example #18
0
def get_feed_report(cb: CbThreatHunterAPI, feed_id: str, report_id: str) -> Dict:
    """Get a specific report from a specific feed."""
    url = f"/threathunter/feedmgr/v2/orgs/{cb.credentials.org_key}/feeds/{feed_id}/reports/{report_id}"
    try:
        return cb.get_object(url)
    except ServerError:
        LOGGER.error(f"Caught ServerError getting feed report {feed_id}: {e}")
    except ObjectNotFoundError:
        LOGGER.warning(f"No feed {feed_id} or report {report_id} in the feed")
Example #19
0
def get_feed(cb: CbThreatHunterAPI, feed_id: str) -> Dict:
    """Get a specific feed by ID."""
    url = f"/threathunter/feedmgr/v2/orgs/{cb.credentials.org_key}/feeds"
    try:
        return cb.get_object(f"{url}/{feed_id}")
    except ServerError:
        LOGGER.error(f"Caught ServerError getting feed {feed_id}: {e}")
    except ObjectNotFoundError:
        LOGGER.warning(f"No feed by feed id {feed_id}")
Example #20
0
def get_watchlist(cb: CbThreatHunterAPI, watchlist_id):
    """Get a watchlist by ID."""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/watchlists"
    try:
        return cb.get_object(f"{url}/{watchlist_id}")
    except ServerError:
        LOGGER.error(f"Caught ServerError getting watchlist {watchlist_id}: {e}")
    except ObjectNotFoundError:
        LOGGER.warning(f"No watchlist with ID {watchlist_id}")
Example #21
0
def get_all_feeds(cb: CbThreatHunterAPI, include_public=True) -> Dict:
    """Retrieve all feeds owned by the caller.

    Provide include_public=true parameter to also include public community feeds.
    """
    url = f"/threathunter/feedmgr/v2/orgs/{cb.credentials.org_key}/feeds"
    params = {"include_public": include_public}
    result = cb.get_object(url, query_parameters=params)
    return result.get("results", [])
Example #22
0
def get_report(cb: CbThreatHunterAPI, report_id) -> Dict:
    """Get report by report id."""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports/{report_id}"
    try:
        report = cb.get_object(url)
        report["ignored"] = get_report_status(cb, report["id"])["ignored"]
        return report
    except ServerError:
        LOGGER.error(f"Caught ServerError getting report {report_id}: {e}")
    except ObjectNotFoundError:
        LOGGER.warning(f"report {report_id} does not exist")
Example #23
0
def get_command_result(cb: CbThreatHunterAPI, session_id: str,
                       command_id: str):
    """Get results of a LR session command."""
    try:
        return cb.get_object(
            f"{CBLR_BASE}/session/{session_id}/command/{command_id}")
    except ObjectNotFoundError:
        LOGGER.warning(
            f"no live resonse session and/or command combination for {session_id}:{command_id}"
        )
        return None
Example #24
0
def create_watchlist(cb: CbThreatHunterAPI, watchlist_data: Dict):
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/watchlists"
    try:
        result = cb.post_object(url, watchlist_data)
    except ServerError as e:
        LOGGER.error(f"Caught ServerError creating watchlist: {e}")
        return False
    except ClientError as e:
        LOGGER.warning(f"got ClientError creating watchlist: {e}")
        return False

    return result.json()
Example #25
0
def create_report(cb: CbThreatHunterAPI, report_data) -> Dict:
    """Create an intel Report."""
    url = f"/threathunter/watchlistmgr/v3/orgs/{cb.credentials.org_key}/reports"
    try:
        result = cb.post_object(url, report_data)
    except ServerError as e:
        LOGGER.error(f"Caught ServerError creating report: {e}")
        return False
    try:
        return result.json()
    except ValueError:
        return False
Example #26
0
def make_device_query(cb: CbThreatHunterAPI,
                      device_query: str) -> DeviceSearchQuery:
    """Construct a DeviceSearchQuery object."""
    try:
        if ":" not in device_query:
            LOGGER.info(
                "No field specification passed. Use 'FIELDS' for help.")
        devices = cb.select(Device).where(device_query)
    except ValueError as e:
        LOGGER.error(f"{e}")
        return False
    LOGGER.info(f"got {len(devices)} device results.")
    return devices
Example #27
0
def toggle_device_quarantine(cb: CbThreatHunterAPI,
                             devices: Union[DeviceSearchQuery, List[Device]],
                             quarantine: bool) -> bool:
    """Toggle device quarantine state.

    Args:
        devices: DeviceSearchQuery
        quarantine: set quarantine if True, else set quarantine to off state.
    """
    if len(devices) > 0:
        if len(devices) > 10 and quarantine:
            LOGGER.error(
                f"For now, not going to quarnantine {len(devices)} devices as a safe gaurd "
                f"to prevent mass device impact... use the GUI if you must.")
            return False
        verbiage = "quarantine" if quarantine else "NOT quarantine"
        emotion = "­ЪЉђ" if quarantine else "­ЪЉЈ"
        LOGGER.info(
            f"setting {verbiage} on {len(devices)} devices... {emotion}")

        device_ids = []
        for d in devices:
            if d.quarantined == quarantine:
                LOGGER.warning(
                    f"device {d.id}:{d.name} is already set to {verbiage}.")
                continue
            if not is_device_online(d):
                LOGGER.info(
                    f"device {d.id}:{d.name} hasn't checked in for: {time_since_checkin(d, refresh=False)}"
                )
                LOGGER.warning(f"device {d.id}:{d.name} appears offline ­Ъњц")
                LOGGER.info(
                    f"device {d.id}:{d.name} will change quarantine state when it comes online ­ЪЉї"
                )
            device_ids.append(d.id)
            cb.device_quarantine(device_ids, quarantine)
        return True
Example #28
0
def get_event_search_results(cb: CbThreatHunterAPI, job_id) -> Dict:
    """Return any results of an event search."""
    url = f"/api/investigate/v2/orgs/{cb.credentials.org_key}/enriched_events/search_jobs/{job_id}/results"
    try:
        while not event_search_complete(cb, job_id):
            time.sleep(0.1)
    except Exception as e:
        LOGGER.error(
            f"got exception waiting for event search to complete: {e}")
        return None
    try:
        return cb.get_object(url)
    except Exception as e:
        LOGGER.error("could not get results: {e}")
        return None
Example #29
0
def is_valid_process_query_string(cb: CbThreatHunterAPI, query: str) -> bool:
    """
    Validates a process query string is valid for PSC.

    Args:
        cb: Cb PSC connection object
        query (str): The query.
    Returns:
        True or False
    """
    args = {"q": query}
    url = f"/api/investigate/v1/orgs/{cb.credentials.org_key}/processes/search_validation"
    validated = cb.get_object(url, query_parameters=args)
    if not validated.get("valid"):
        return False
    return True
Example #30
0
    def __init__(self, cb, timeout=30, custom_session_keepalive=False):
        # First, get a CB object with the LR API permissions
        cblr = CbThreatHunterAPI(url=cb.credentials.url,
                                 token=cb.credentials.lr_token,
                                 org_key=cb.credentials.org_key)
        super().__init__(cblr, timeout=timeout, keepalive_sessions=False)
        # so now self._cb == cblr -- store a reference to the regular cb
        self.psc_cb = cb

        if custom_session_keepalive:
            self._cleanup_thread = threading.Thread(
                target=self._keep_active_sessions_alive_thread)
            self._cleanup_thread.daemon = True
            self._cleanup_thread.start()

        # for storing initiatied commands
        self.commands = []