-
Notifications
You must be signed in to change notification settings - Fork 23
Update: Reachability Plugin #426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e818133
0865396
3164728
694d1dc
694e100
7e8a80e
b268551
2d5730c
42ce123
a6a3a2f
9dab6b4
4fd81fa
f0a7593
4baf14f
26f478a
604429a
e96a8cb
70e68af
d634754
8102e10
9dc83fc
ee5aa1d
622a38d
1dcb0d3
24a73e2
6d5c585
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Import Reachability Plugin for SBOM Surfactant | ||
|
|
||
| A plugin for Surfactant that checks the reachability of imported functions within exported functions to narrow down reachable code and reduse the amount of deadcode analysts are having to view. | ||
|
|
||
| ## Quickstart | ||
|
|
||
| To install this plugin within the same virtual environment as Surfactant, use the command `pip install .`. | ||
|
|
||
| For developers modifying the plugin, the editable installation can be achieved with `pip install -e .`. | ||
|
|
||
| After installing the plugin, run Surfactant to generate an SBOM as usual and entries for ELF | ||
| and PE files will contain a metadata object with the information that checksec.py was able | ||
| to get about security related features. | ||
|
|
||
| After the plugin installation, run Surfactant as you normally would to create an SBOM. For binary files analyzed by this plugin, additional JSON files will be generated containing vulnerability data extracted from the binaries. If there are duplicate hashed files, the extractor will check if they have the exported functions entries and skip remaking the output file if so. | ||
|
|
||
| Example: | ||
| Output Filename: `reachability.json` | ||
|
|
||
| ```json | ||
| { | ||
| "filename": { | ||
| "exp_func": { | ||
| "library": [ | ||
| "imp_func1", | ||
| "imp_func2" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
Comment on lines
+17
to
+31
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this description is accurate either -- the json blob looks slightly different if I'm remembering correctly, and is added to the metadata list for software entries rather than being in a separate file. |
||
|
|
||
| The plugin's functionality can be toggled via Surfactant's plugin management features, using the plugin name `surfactantplugin_reachability.py` as defined in the `pyproject.toml` under the `project.entry-points."surfactant"` section. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The plugin name to enable/disable it is |
||
|
|
||
| ## Uninstalling | ||
|
|
||
| Remove the plugin from your environment with `pip uninstall surfactantplugin_reachability`. | ||
|
|
||
| ## Important Licensing Information | ||
| Main Project License (Surfactant): MIT License. | ||
|
|
||
| Plugin License: MIT License, but it includes and uses cve-bin-tool, which is GPL-3.0 licensed. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cve-bin-tool line here does not apply to this plugin. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| [build-system] | ||
| requires = ["setuptools", "setuptools-scm"] | ||
| build-backend = "setuptools.build_meta" | ||
|
|
||
| [project] | ||
| name = "surfactantplugin_reachability" | ||
| authors = [ | ||
| {name = "Seth Bredbenner", email = "[email protected]"}, | ||
| ] | ||
| description = "Surfactant plugin for running grype on files" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Description here needs updating |
||
| readme = "README.md" | ||
| requires-python = ">=3.8" | ||
| keywords = ["surfactant"] | ||
| license = {text = "MIT License"} | ||
| classifiers = [ | ||
| "Programming Language :: Python :: 3", | ||
| "Environment :: Console", | ||
| "Operating System :: MacOS", | ||
| "Operating System :: Microsoft :: Windows", | ||
| "Operating System :: POSIX :: Linux", | ||
| "License :: OSI Approved :: MIT License", | ||
| ] | ||
| dependencies = [ | ||
| "angr", | ||
| "surfactant", | ||
| ] | ||
| dynamic = ["version"] | ||
|
|
||
| [project.entry-points."surfactant"] | ||
| "surfactantplugin_reachability" = "surfactantplugin_reachability" | ||
|
|
||
| [tool.setuptools] | ||
| py-modules=["surfactantplugin_reachability"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Copyright 2023 Lawrence Livermore National Security, LLC | ||
| # See the top-level LICENSE file for details. | ||
| # | ||
| # SPDX-License-Identifier: MIT | ||
| from pathlib import Path | ||
|
|
||
| import angr | ||
| from cle import CLECompatibilityError | ||
| from loguru import logger | ||
|
|
||
| import surfactant.plugin | ||
| from surfactant.sbomtypes import SBOM, Software | ||
|
|
||
|
|
||
| @surfactant.plugin.hookimpl(specname="extract_file_info") | ||
| # extract_strings(sbom: SBOM, software: Software, filename: str, filetype: str): | ||
| # def angrimport_finder(filename: str, filetype: str, filehash: str): | ||
| def reachability(sbom: SBOM, software: Software, filename: str, filetype: str): | ||
| """ | ||
| :param sbom(SBOM): The SBOM that the software entry/file is being added to. Can be used to add observations or analysis data. | ||
| :param software(Software): The software entry associated with the file to extract information from. | ||
| :param filename (str): The full path to the file to extract information from. | ||
| :param filetype (str): File type information based on magic bytes. | ||
| """ | ||
|
|
||
| # Only parsing executable files | ||
| if filetype not in ["ELF", "PE"]: | ||
| pass | ||
| filename = Path(filename) | ||
|
|
||
| database = {} | ||
|
|
||
| try: | ||
| if not filename.exists(): | ||
| raise FileNotFoundError(f"No such file: '{filename}'") | ||
| # Add your extraction code here. | ||
| # Create an angr project | ||
| project = angr.Project(filename, load_options={"auto_load_libs": True}) | ||
|
|
||
| # library dependencies {import: library} | ||
| lookup = {} | ||
| for obj in project.loader.main_object.imports.keys(): | ||
| library = project.loader.find_symbol(obj) | ||
| if library is None: | ||
| continue | ||
| lookup[obj] = library.owner.provides | ||
|
|
||
| # recreates our angr project without the libraries loaded to save on time | ||
| project = angr.Project(filename, load_options={"auto_load_libs": False}) | ||
|
|
||
| # holds every export address error is here | ||
| exports = [ | ||
| func.rebased_addr for func in project.loader.main_object.symbols if func.is_export | ||
| ] # _exports is only available for PE files | ||
|
|
||
| cfg = project.analyses.CFGFast( | ||
| start_at_entry=False, force_smart_scan=False, function_starts=exports | ||
| ) | ||
|
|
||
| # go through every exported function | ||
| for exp_addr in exports: | ||
| exp_name = cfg.functions.get(exp_addr) | ||
| if exp_name is None: | ||
| continue | ||
| database[exp_name.name] = {} | ||
| exp_name = exp_name.name | ||
|
|
||
| # goes through every function that is reachable from exported function | ||
| for imported_address in cfg.functions.callgraph.successors(exp_addr): | ||
| imported_function = cfg.functions.get(imported_address) | ||
|
|
||
| # checks if the function is imported | ||
| if imported_function.name in project.loader.main_object.imports.keys(): | ||
| library = lookup[imported_function.name] | ||
|
|
||
| if library not in database[exp_name].keys(): | ||
| database[exp_name][library] = [] | ||
|
|
||
| # adds our reachable imported function as a dependency | ||
| if imported_function.name not in database[exp_name][library]: | ||
| database[exp_name][library].append(imported_function.name) | ||
|
|
||
| return {"export_fn_reachability": database} | ||
|
|
||
| except CLECompatibilityError as e: | ||
| logger.info(f"Angr Error {filename} {e}") | ||
| return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines don't describe the reachability plugin.