예제 #1
0
파일: run.py 프로젝트: xorilog/CHIRP
    async def _run(args: Tuple[int, str, dict]) -> Union[Dict[str, Any], None]:
        """Handle our multiprocessing tasks."""
        count, path, indicators = args
        _indicators = [(indicator["name"], indicator["indicator"]["rule"])
                       for indicator in indicators]
        yara_rules = compile_rules(tuple(_indicators))
        ignorelist = [
            "\\OneDrive\\",
            "\\OneDriveTemp\\",
            os.getcwd(),
        ]  # Ignore these paths, so we don't enumerate cloud drives or our current working directory
        if count % 50000 == 0 and count != 0:
            CONSOLE(
                "[cyan][YARA][/cyan] We're still working on scanning files. {} processed."
                .format(count))
        if count == 1:
            CONSOLE("[cyan][YARA][/cyan] Beginning processing.")

        # Sometimes glob.glob gives us paths with *
        if (os.path.exists(path)
                and (path != "." and path != "\\" and all(x not in path
                                                          for x in ignorelist))
                and not os.path.isdir(path)):
            try:
                matches = yara_rules.match(path)
                if matches:
                    for match in matches:
                        attrs = [
                            "meta", "namespace", "rule", "strings", "tags"
                        ]
                        match_dict = {k: str(getattr(match, k)) for k in attrs}
                        match_dict["file"] = path
                        return match_dict
            except yara.Error:
                pass
예제 #2
0
async def run(indicators: dict) -> None:
    """Accept a dict containing events indicators and write out to the OUTPUT_DIR specified by chirp.common.

    :param indicators: A dict containing parsed registry indicator files.
    :type indicators: dict
    """
    if not indicators:
        return
    CONSOLE("[cyan][REGISTRY][/cyan] Entered registry plugin.")
    report = {
        indicator["name"]: build_report(indicator)
        for indicator in indicators
    }
    for indicator in indicators:
        ind = indicator["indicator"]
        indicator_list = [(k, v) for k, v in ind.items()
                          if k != "registry_key"]
        CONSOLE("[cyan][REGISTRY][/cyan] Reading {}".format(
            ind["registry_key"]))
        async for value in enumerate_registry_values(ind["registry_key"]):
            if value == "ERROR":
                CONSOLE("[cyan][REGISTRY][/cyan] Hit an error, exiting.")
                return
            hits, search_criteria, match = await check_matches(
                indicator_list, value)
            if hits != len(indicator_list):
                continue
            report[indicator["name"]]["_search_criteria"] = search_criteria
            if match:
                report[indicator["name"]]["matches"].append(match)
    [await _report_hits(k, v) for k, v in report.items()]
    with open(os.path.join(OUTPUT_DIR, "registry.json"), "w+") as writeout:
        writeout.write(
            json.dumps({r: report[r]
                        for r in report if report[r]["matches"]}))
예제 #3
0
파일: scan.py 프로젝트: xorilog/CHIRP
async def run(indicators: dict) -> None:
    """Accept a dict containing events indicators and writes out to the OUTPUT_DIR specified by chirp.common.

    :param indicators: A dict containing parsed network indicator files.
    :type indicators: dict
    """
    if not indicators:
        return

    hits = 0
    CONSOLE("[cyan][NETWORK][/cyan] Entered network plugin.")
    saved_ns = parse_netstat(grab_netstat())
    saved_dns = parse_dns(grab_dns())

    report = {
        indicator["name"]: build_report(indicator)
        for indicator in indicators
    }

    for indicator in indicators:
        try:
            for ioc in indicator["indicator"]["ips"].splitlines():
                if await hunter(saved_ns + ["\n"] + saved_dns, ioc):
                    report[indicator["name"]]["matches"].append(ioc)
                    hits += 1
        except KeyError:
            ERROR("{} appears to be malformed.".format(indicator))
    CONSOLE(
        "[cyan][NETWORK][/cyan] Read {} records, found {} IoC hits.".format(
            len(saved_dns) + len(saved_ns), hits))

    with open(os.path.join(OUTPUT_DIR, "network.json"), "w+") as writeout:
        writeout.write(
            json.dumps({r: report[r]
                        for r in report if report[r]["matches"]}))
예제 #4
0
파일: registry.py 프로젝트: xorilog/CHIRP
    async def enumerate_registry_values(hkey: str) -> Iterator[dict]:
        """Enumerate the values of the given key.

        :param hkey: A registry key to enumerate
        :type hkey: str
        :yield: Registry key values
        :rtype: Iterator[dict]
        """
        hive, key = _normalize_key(hkey)
        if not hive or not key:
            CONSOLE("[red][!][/red] Unable to read key '{}'".format(hkey))
            return
        registry = winreg.ConnectRegistry(None, hive)
        try:
            with winreg.OpenKey(registry, key) as registry_key:
                for i in range(winreg.QueryInfoKey(registry_key)[1]):
                    value_tuple = winreg.EnumValue(registry_key, i)
                    yield {
                        "key": value_tuple[0],
                        "value": value_tuple[1],
                        "registry_type": REGISTRY_VALUE_TYPES[value_tuple[2]],
                    }
        except FileNotFoundError:
            CONSOLE(
                "[cyan][REGISTRY][/cyan] Key {} does not exist.".format(hkey))
예제 #5
0
파일: run.py 프로젝트: xorilog/CHIRP
    async def run(indicators: dict) -> None:
        """Accept a dict containing yara indicators and write out to the OUTPUT_DIR specified by chirp.common.

        :param indicators: A NamespaceDict containing parsed yara indicator files.
        :type indicators: dict
        """
        if not indicators:
            return

        CONSOLE("[cyan][YARA][/cyan] Entered yara plugin.")

        files = [i["indicator"]["files"] for i in indicators]
        files = "\\**" if "\\**" in files else ", ".join(files)

        if files == "\\**":
            blame = [
                i["name"] for i in indicators
                if i["indicator"]["files"] == "\\**"
            ]
            CONSOLE(
                "[cyan][YARA][/cyan] Enumerating the entire filesystem due to {}... this is going to take a while."
                .format(blame))

        report = {
            indicator["name"]: build_report(indicator)
            for indicator in indicators
        }

        hits = 0
        run_args = []

        # Normalize every path, for every path
        try:
            run_args = [(a, b, indicators)
                        for a, b in enumerate(normalize_paths(files), 1)]
            async with aiomp.Pool() as pool:
                try:
                    async for result in pool.map(_run, tuple(run_args)):
                        if result:
                            report[result["namespace"]]["matches"].append(
                                result)
                            hits += 1
                except KeyboardInterrupt:
                    pass
        except IndexError:
            pass

        count = len(run_args)

        CONSOLE("[cyan][YARA][/cyan] Done. Processed {} files.".format(count))
        CONSOLE(
            "[cyan][YARA][/cyan] Found {} hit(s) for yara indicators.".format(
                hits))

        with open(os.path.join(OUTPUT_DIR, "yara.json"), "w+") as writeout:
            writeout.write(
                json.dumps(
                    {r: report[r]
                     for r in report if report[r]["matches"]}))
예제 #6
0
def iter_evtx2xml(evtx_file):
    """
    Generator function to read events from evtx file and convert to xml
    :param evtx_file: file path string
    :return: generator to xml string representation of evtx event
    """
    global error_counter, event_counter
    error_counter = 0
    event_counter = 0
    try:
        with evtx.Evtx(evtx_file) as log:
            # process each log entry and return xml representation
            for record in log.records():
                event_counter += 1
                try:
                    yield record.xml()
                except Exception as err:
                    error_counter += 1
                    # logger.error("Failed to convert EVTX to XML for %s. Error count: %d" % (evtx_file, error_counter))
    except Exception as err:
        raise
    if error_counter:
        CONSOLE(
            "[cyan][evtx2json][/cyan] Failed to read {} events.".format(error_counter)
        )
예제 #7
0
    async def enumerate_registry_values(hkey: str) -> Iterator[str]:
        """Return if the proper libraries can't be imported (like wrong OS).

        :param hkey: A registry key to query
        :type hkey: str
        :yield: Literally "ERROR"
        :rtype: Iterator[str]
        """
        CONSOLE("[red][!][/red] Registry plugin is only compatible with Windows.")
        yield "ERROR"
예제 #8
0
파일: scan.py 프로젝트: xorilog/CHIRP
async def _run(run_args):
    """Gather events and check for matches."""
    (
        event_type,
        indicators,
        report,
        num_logs,
    ) = run_args  # Unpack our arguments (bundled to passthrough for multiprocessing)
    CONSOLE("[cyan][EVENTS][/cyan] Reading {} event logs.".format(
        event_type.split("%4")[-1]))
    async for event_log in gather(event_type):  # Iterate over event logs
        if event_log == "ERROR":
            CONSOLE("[cyan][EVENTS][/cyan] Hit an error, exiting.")
            return
        if event_log:
            num_logs += 1
            for indicator in indicators:
                ind = indicator["indicator"]
                if (ind["event_type"] == event_type
                    ):  # Make sure the ioc is intended for this log
                    if "event_id" in ind and str(
                            event_log["event"]["system"]["event_id"]["$"]
                    ) != str(
                            ind["event_id"]
                    ):  # If ioc looks for event_id, but there is a mismatch then skip
                        continue
                    elif "event_id" not in ind:
                        ind["event_id"] = None
                    indicator_list = [(k, v) for k, v in ind.items()
                                      if k not in ["event_type", "event_id"]]
                    hits, search_criteria, match = await check_matches(
                        indicator_list, ind["event_id"], event_log
                    )  # Check to see if the indicator matches the event log
                    if hits != len(indicator_list):
                        continue
                    report[indicator["name"]][
                        "_search_criteria"] = search_criteria
                    if match:
                        report[indicator["name"]]["matches"].append(
                            match
                        )  # Append to report because there is a match.
    return report, num_logs
예제 #9
0
파일: scan.py 프로젝트: xorilog/CHIRP
async def run(indicators: dict) -> None:
    """Accept a dict containing events indicators and writes out to the OUTPUT_DIR specified by chirp.common.

    :param indicators: A dict containing parsed events indicator files.
    :type indicators: dict
    """
    if not indicators:
        return
    hits = 0
    num_logs = 0
    CONSOLE("[cyan][EVENTS][/cyan] Entered events plugin.")
    event_types = {
        indicator["indicator"]["event_type"]
        for indicator in indicators
    }
    report = {
        indicator["name"]: build_report(indicator)
        for indicator in indicators
    }
    run_args = [(event_type, indicators, report, num_logs)
                for event_type in event_types]
    async with aiomp.Pool() as pool:
        try:
            async for i in pool.map(_run, tuple(run_args)):
                _rep = i[0]
                num_logs += i[1]
                for k, v in _rep.items():
                    try:
                        report[k]["_search_criteria"] = v["_search_criteria"]
                    except KeyError:
                        pass
                    report[k]["matches"] += v["matches"]
        except KeyboardInterrupt:
            pass

    hits = sum(len(v["matches"]) for _, v in report.items())
    CONSOLE("[cyan][EVENTS][/cyan] Read {} logs, found {} matches.".format(
        num_logs, hits))
    with open(os.path.join(OUTPUT_DIR, "events.json"), "w+") as writeout:
        writeout.write(
            json.dumps({r: report[r]
                        for r in report if report[r]["matches"]}))
예제 #10
0
파일: __main__.py 프로젝트: xorilog/CHIRP
"""Main method for CHIRP (Used when compiled)."""

# Standard Python Libraries
from multiprocessing import freeze_support
import os

# cisagov Libraries
from chirp.common import CONSOLE, ERROR, OUTPUT_DIR, save_log
import chirp.run

if __name__ == "__main__":
    try:
        freeze_support()
        chirp.run.run()
        CONSOLE(
            "[green][+][/green] DONE! Your results can be found in {}.".format(
                os.path.abspath(OUTPUT_DIR)
            )
        )
    except KeyboardInterrupt:
        ERROR("Received an escape sequence. Goodbye.")
    finally:
        save_log()
        input()
예제 #11
0
파일: run.py 프로젝트: xorilog/CHIRP
                    pass
        except IndexError:
            pass

        count = len(run_args)

        CONSOLE("[cyan][YARA][/cyan] Done. Processed {} files.".format(count))
        CONSOLE(
            "[cyan][YARA][/cyan] Found {} hit(s) for yara indicators.".format(
                hits))

        with open(os.path.join(OUTPUT_DIR, "yara.json"), "w+") as writeout:
            writeout.write(
                json.dumps(
                    {r: report[r]
                     for r in report if report[r]["matches"]}))

else:
    CONSOLE(
        "[red][!][/red] yara-python is a required dependency for the yara plugin. Please install yara-python with pip."
    )
    CONSOLE("[cyan][YARA][/cyan] Hit an error, exiting.")

    async def run(indicators: dict) -> None:
        """Return if there is an import error.

        :param indicators: Parsed yara indicator files.
        :type indicators: dict
        """
        return
예제 #12
0
import string
import sys
from typing import Any, Dict, Iterator, List, Union

# cisagov Libraries
from chirp.common import CONSOLE, JSON, OS

HAS_LIBS = False
try:
    # cisagov Libraries
    from chirp.plugins.events.evtx2json import iter_evtx2xml, splunkify, xml2json

    HAS_LIBS = True
except ImportError:
    CONSOLE(
        "[red][!][/red] python-evtx, dict-toolbox, and xmljson are required dependencies for the events plugin. Please install requirements with pip."
    )

if OS == "Windows":
    # Standard Python Libraries
    from ctypes import windll

PATH = Path(sys.executable)


def _get_drives() -> List[str]:
    """
    Return a list of valid drives.

    Reference: `RichieHindle, StackOverflow <https://stackoverflow.com/a/827398>`_
    """
예제 #13
0
async def _report_hits(indicator: str, vals: dict) -> None:
    """Write to the log the number of hits for a given indicator."""
    CONSOLE("[cyan][REGISTRY][/cyan] Found {} hit(s) for {} indicator.".format(
        len(vals["matches"]), indicator))
예제 #14
0
import sys
from typing import Any, Dict, Iterator, List, Union

# cisagov Libraries
from chirp.common import CONSOLE, JSON

HAS_LIBS = False
try:
    # cisagov Libraries
    from chirp.common import OS
    from chirp.plugins.events.evtx2json import iter_evtx2xml, splunkify, xml2json

    HAS_LIBS = True
except ImportError:
    CONSOLE(
        "[red][!][/red] python-evtx, dict-toolbox, and xmljson are required dependencies for the events plugin. Please install requirements with pip."
    )

PATH = Path(sys.executable)

# Depending on if this is 32bit or 64bit Windows, the logs can be at System32 or Sysnative.
if os.path.exists(PATH.drive + "\\Windows\\Sysnative\\winevt"):
    default_dir = PATH.drive + "\\Windows\\Sysnative\\winevt\\Logs\\{}.evtx"
elif os.path.exists(PATH.drive + "\\Windows\\System32\\winevt"):
    default_dir = PATH.drive + "\\Windows\\System32\\winevt\\Logs\\{}.evtx"
else:
    # TODO: Implement switch
    if OS == "Windows":
        CONSOLE(
            "[red][!][/red] We can't find the windows event logs at {}\\System32\\winevt or at {}\\Sysnative\\winevt."
            .format(PATH.drive, PATH.drive))