-
Notifications
You must be signed in to change notification settings - Fork 196
Multi threat libs #263
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: master
Are you sure you want to change the base?
Multi threat libs #263
Changes from all commits
e0b5c1a
7be52ea
370ccb6
2ec73a6
544fa6f
6da8601
b546efb
9dc0f1f
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 |
---|---|---|
|
@@ -126,3 +126,4 @@ plantuml.jar | |
tm/ | ||
/sqldump | ||
/tests/.config.pytm | ||
.aider* | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,10 +60,9 @@ make | |
All available arguments: | ||
|
||
```text | ||
usage: tm.py [-h] [--sqldump SQLDUMP] [--debug] [--dfd] [--report REPORT] | ||
[--exclude EXCLUDE] [--seq] [--list] [--describe DESCRIBE] | ||
[--list-elements] [--json JSON] [--levels LEVELS [LEVELS ...]] | ||
[--stale_days STALE_DAYS] | ||
usage: tm.py [-h] [--sqldump SQLDUMP] [--debug] [--dfd] [--report REPORT] [--exclude EXCLUDE] [--seq] [--list] [--colormap] | ||
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. Are you adding 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. it should be there already, it is an old one. It appears on the currently checked code on the master branch. 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. oh this is the readme! let me fix that |
||
[--describe DESCRIBE] [--list-elements] [--json JSON] [--levels LEVELS [LEVELS ...]] [--stale_days STALE_DAYS] | ||
[--threat-files THREAT_FILES [THREAT_FILES ...]] | ||
|
||
optional arguments: | ||
-h, --help show this help message and exit | ||
|
@@ -87,10 +86,18 @@ optional arguments: | |
checks if the delta between the TM script and the code | ||
described by it is bigger than the specified value in | ||
days | ||
--threat-files THREAT_FILES [THREAT_FILES ...] | ||
Files containing libraries of threats. | ||
``` | ||
|
||
The *stale_days* argument tries to determine how far apart in days the model script (which you are writing) is from the code that implements the system being modeled. Ideally, they should be pretty close in most cases of an actively developed system. You can run this periodically to measure the pulse of your project and the 'freshness' of your threat model. | ||
|
||
The *THREAT_FILES* argument can list proprietary threat files. The keyword 'default' stands for the pytm library when it is wanted together with the proprietary files: | ||
|
||
* nothing in the command line: uses the default library | ||
* --threat-files foo.json : uses the threats in foo.json only | ||
* --threat-files foo.json default : uses the threats in foo.json and the default ones | ||
|
||
Currently available elements are: TM, Element, Server, ExternalEntity, Datastore, Actor, Process, SetOfProcesses, Dataflow, Boundary and Lambda. | ||
|
||
The available properties of an element can be listed by using `--describe` followed by the name of an element: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ def __init__(self, e, context): | |
|
||
|
||
logger = logging.getLogger(__name__) | ||
_defaultThreatsFile = os.path.dirname(__file__) + "/threatlib/threats.json" | ||
|
||
|
||
class var(object): | ||
|
@@ -787,11 +788,7 @@ class TM: | |
) | ||
name = varString("", required=True, doc="Model name") | ||
description = varString("", required=True, doc="Model description") | ||
threatsFile = varString( | ||
os.path.dirname(__file__) + "/threatlib/threats.json", | ||
onSet=lambda i, v: i._init_threats(), | ||
doc="JSON file with custom threats", | ||
) | ||
threatsFile = varStrings([_defaultThreatsFile]) | ||
izar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
isOrdered = varBool(False, doc="Automatically order all Dataflows") | ||
mergeResponses = varBool(False, doc="Merge response edges in DFDs") | ||
ignoreUnused = varBool(False, doc="Ignore elements not used in any Dataflow") | ||
|
@@ -838,16 +835,19 @@ def _init_threats(self): | |
self._add_threats() | ||
|
||
def _add_threats(self): | ||
try: | ||
with open(self.threatsFile, "r", encoding="utf8") as threat_file: | ||
threats_json = json.load(threat_file) | ||
except (FileNotFoundError, PermissionError, IsADirectoryError) as e: | ||
raise UIError( | ||
e, f"while trying to open the the threat file ({self.threatsFile})." | ||
|
||
for tf in self.threatsFile: | ||
try: | ||
with open(tf, "r", encoding="utf8") as threat_file: | ||
threats_json = json.load(threat_file) | ||
except (FileNotFoundError, PermissionError, IsADirectoryError) as e: | ||
raise UIError( | ||
e, f"while trying to open the the threat file ({tf})." | ||
) | ||
active_threats = (threat for threat in threats_json if "DEPRECATED" not in threat) | ||
for threat in active_threats: | ||
TM._threats.append(Threat(**threat)) | ||
|
||
active_threats = (threat for threat in threats_json if "DEPRECATED" not in threat) | ||
for threat in active_threats: | ||
TM._threats.append(Threat(**threat)) | ||
|
||
def resolve(self): | ||
finding_count = 0 | ||
|
@@ -1129,6 +1129,21 @@ def _process(self): | |
result = get_args() | ||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") | ||
|
||
# delaying loading of threats to accomodate multiple threat files in the | ||
# command line | ||
if result.threat_files: | ||
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. Why do extra work on line 791 to load the default when you can just do it here? You will always reach this line at least once, correct? 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 remember the details but there is a timing issue there. Something around things being used before loaded. |
||
# start by removing the default | ||
del self.threatsFile[0] | ||
if "default" in result.threat_files: | ||
index = result.threat_files.index("default") | ||
result.threat_files[index] = _defaultThreatsFile | ||
for x in result.threat_files: | ||
self.threatsFile.append(x) | ||
else: | ||
# it is just the default file, so no need to do anything | ||
pass | ||
self._init_threats() | ||
|
||
if result.debug: | ||
logger.setLevel(logging.DEBUG) | ||
|
||
|
@@ -1178,6 +1193,7 @@ def _process(self): | |
if result.stale_days is not None: | ||
print(self._stale(result.stale_days)) | ||
|
||
|
||
def _stale(self, days): | ||
try: | ||
base_path = os.path.dirname(sys.argv[0]) | ||
|
@@ -2158,6 +2174,11 @@ def get_args(): | |
help="""checks if the delta between the TM script and the code described by it is bigger than the specified value in days""", | ||
type=int, | ||
) | ||
_parser.add_argument( | ||
"--threat-files", | ||
nargs="+", | ||
help="Files containing libraries of threats." | ||
) | ||
|
||
_args = _parser.parse_args() | ||
return _args |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[ | ||
{ | ||
"SID": "FOO", | ||
"target": [ | ||
"Lambda", | ||
"Process" | ||
], | ||
"description": "FOOOOOOOOOOOOOO", | ||
"details": "This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.", | ||
"Likelihood Of Attack": "High", | ||
"severity": "High", | ||
"condition": "target.usesEnvironmentVariables is True and target.controls.sanitizesInput is False and target.controls.checksInputBounds is False", | ||
"prerequisites": "The application uses environment variables.An environment variable exposed to the user is vulnerable to a buffer overflow.The vulnerable environment variable uses untrusted data.Tainted data used in the environment variables is not properly validated. For instance boundary checking is not done before copying the input data to a buffer.", | ||
"mitigations": "Do not expose environment variable to the user.Do not use untrusted data in your environment variables. Use a language or compiler that performs automatic bounds checking. There are tools such as Sharefuzz [R.10.3] which is an environment variable fuzzer for Unix that support loading a shared library. You can use Sharefuzz to determine if you are exposing an environment variable vulnerable to buffer overflow.", | ||
"example": "Attack Example: Buffer Overflow in $HOME A buffer overflow in sccw allows local users to gain root access via the $HOME environmental variable. Attack Example: Buffer Overflow in TERM A buffer overflow in the rlogin program involves its consumption of the TERM environmental variable.", | ||
"references": "https://capec.mitre.org/data/definitions/10.html, CVE-1999-0906, CVE-1999-0046, http://cwe.mitre.org/data/definitions/120.html, http://cwe.mitre.org/data/definitions/119.html, http://cwe.mitre.org/data/definitions/680.html" | ||
} | ||
] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[ | ||
{ | ||
"SID": "BAR", | ||
"target": [ | ||
"Lambda", | ||
"Process" | ||
], | ||
"description": "FOOOOOOOOOOOOOO", | ||
"details": "This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.", | ||
"Likelihood Of Attack": "High", | ||
"severity": "High", | ||
"condition": "target.usesEnvironmentVariables is True and target.controls.sanitizesInput is False and target.controls.checksInputBounds is False", | ||
"prerequisites": "The application uses environment variables.An environment variable exposed to the user is vulnerable to a buffer overflow.The vulnerable environment variable uses untrusted data.Tainted data used in the environment variables is not properly validated. For instance boundary checking is not done before copying the input data to a buffer.", | ||
"mitigations": "Do not expose environment variable to the user.Do not use untrusted data in your environment variables. Use a language or compiler that performs automatic bounds checking. There are tools such as Sharefuzz [R.10.3] which is an environment variable fuzzer for Unix that support loading a shared library. You can use Sharefuzz to determine if you are exposing an environment variable vulnerable to buffer overflow.", | ||
"example": "Attack Example: Buffer Overflow in $HOME A buffer overflow in sccw allows local users to gain root access via the $HOME environmental variable. Attack Example: Buffer Overflow in TERM A buffer overflow in the rlogin program involves its consumption of the TERM environmental variable.", | ||
"references": "https://capec.mitre.org/data/definitions/10.html, CVE-1999-0906, CVE-1999-0046, http://cwe.mitre.org/data/definitions/120.html, http://cwe.mitre.org/data/definitions/119.html, http://cwe.mitre.org/data/definitions/680.html" | ||
} | ||
] | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1405,5 +1405,5 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"name": "my test tm", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"onDuplicates": "Action.NO_ACTION", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"threatsExcluded": [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"threatsFile": "pytm/threatlib/threats.json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"threatsFile": "{'pytm/threatlib/threats.json'}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Why is the output a string and not a list of strings? 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. that's what varStrings with only one value put out... 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. Is this a bug? 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. in the immortal words of just about everybody..."works for me"! 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. Ok I had a quick look and the issue is that the default Lines 2015 to 2018 in 9dc0f1f
This is just the same as json.dumps(str(set(["./a.json"]))) The issue is created by Line 2023 in 9dc0f1f
Because of this check for Line 2064 in 9dc0f1f
Lines 2035 to 2070 in 9dc0f1f
This whole serialize function is a bit overloaded with special handling of member variables and might require a rewrite. A quick fix would be something like Should this be a new issue? 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. sounds like something to be addressed. @nineinchnick , you there? 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. There appears to be only 1 check for nested. If nested is true, the behavior seems to be potentially undefined (if the code reaches the 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. Yes. I started to rewrite the code see #268, but it is difficult to understand what the intention here was. The last one seems to be a fix for a specific class. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -414,6 +414,23 @@ def test_json_loads(self): | |
[f.name for f in tm._flows], ["Request", "Insert", "Select", "Response"] | ||
) | ||
|
||
def test_threat_files(self): | ||
dir_path = os.path.dirname(os.path.realpath(__file__)) | ||
foo_file = f"{dir_path}/1.json" | ||
bar_file = f"{dir_path}/2.json" | ||
threat_files = [os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) | ||
+ "/pytm/threatlib/threats.json", foo_file, bar_file] | ||
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. Do you have a test for when the default is not included in threatsfile? 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. Hm. No. Gotta do one, you're right. |
||
|
||
TM.reset() | ||
tm = TM("testing multiple threat library files", | ||
description="aaa", | ||
threatsFile=threat_files) | ||
ctr = 0 | ||
for t in TM._threats: | ||
if t.id == "FOO" or t.id == "BAR": | ||
ctr += 1 | ||
self.assertTrue(ctr == 2) | ||
|
||
def test_report(self): | ||
random.seed(0) | ||
dir_path = os.path.dirname(os.path.realpath(__file__)) | ||
|
Uh oh!
There was an error while loading. Please reload this page.