Merge branch 'master' of github.com:oasis-open/cti-python-stix2 into stix2.1
commit
b2ef77b322
|
@ -6,6 +6,11 @@ python:
|
|||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.7 # https://github.com/travis-ci/travis-ci/issues/9069#issuecomment-425720905
|
||||
dist: xenial
|
||||
sudo: true
|
||||
install:
|
||||
- pip install -U pip setuptools
|
||||
- pip install tox-travis pre-commit
|
||||
|
|
|
@ -125,8 +125,6 @@ select additional or substitute Maintainers, per `consensus agreements
|
|||
|
||||
**Current Maintainers of this TC Open Repository**
|
||||
|
||||
- `Greg Back <mailto:gback@mitre.org>`__; GitHub ID:
|
||||
https://github.com/gtback/; WWW: `MITRE Corporation <http://www.mitre.org/>`__
|
||||
- `Chris Lenk <mailto:clenk@mitre.org>`__; GitHub ID:
|
||||
https://github.com/clenk/; WWW: `MITRE Corporation <http://www.mitre.org/>`__
|
||||
|
||||
|
|
|
@ -881,7 +881,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.6.3"
|
||||
"version": "3.6.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# STIX2 Patterns\n",
|
||||
"\n",
|
||||
"The Python ``stix2`` library supports STIX 2 patterning insofar that patterns may be used for the pattern property of Indicators, identical to the STIX 2 specification. ``stix2`` does not evaluate patterns against STIX 2 content; for that functionality see [cti-pattern-matcher](https://github.com/oasis-open/cti-pattern-matcher).\n",
|
||||
"\n",
|
||||
"Patterns in the ``stix2`` library are built compositely from the bottom up, creating subcomponent expressions first before those at higher levels.\n",
|
||||
"\n",
|
||||
"## API Tips\n",
|
||||
"\n",
|
||||
"### ObservationExpression\n",
|
||||
"\n",
|
||||
"Within the STIX 2 Patterning specification, Observation Expressions denote a complete expression to be evaluated against a discrete observation. In other words, an Observation Expression must be created to apply to a single Observation instance. This is further made clear by the visual brackets(```[]```) that encapsulate an Observation Expression. Thus, whatever sub expressions that are within the Observation Expression are meant to be matched against the same Observable instance.\n",
|
||||
"\n",
|
||||
"This requirement manifests itself within the ``stix2`` library via ```ObservationExpression```. When creating STIX 2 observation expressions, whenever the current expression is complete, wrap it with ```ObservationExpression()```. This allows the complete pattern expression - no matter its complexity - to be rendered as a proper specification-adhering string. __*Note: When pattern expressions are added to Indicator objects, the expression objects are implicitly converted to string representations*__. While the extra step may seem tedious in the construction of simple pattern expressions, this explicit marking of observation expressions becomes vital when converting the pattern expressions to strings. \n",
|
||||
"\n",
|
||||
"In all the examples, you can observe how in the process of building pattern expressions, when an Observation Expression is completed, it is wrapped with ```ObservationExpression()```.\n",
|
||||
"\n",
|
||||
"### ParentheticalExpression\n",
|
||||
"\n",
|
||||
"Do not be confused by the ```ParentheticalExpression``` object. It is not a distinct expression type but is also used to properly craft pattern expressions by denoting order priority and grouping of expression components. Use it in a similar manner as ```ObservationExpression```, wrapping completed subcomponent expressions with ```ParentheticalExpression()``` if explicit ordering is required. For usage examples with ```ParentheticalExpression```'s, see [here](#Compound-Observation-Expressions).\n",
|
||||
"\n",
|
||||
"### BooleanExpressions vs CompoundObservationExpressions\n",
|
||||
"\n",
|
||||
"Be careful to note the difference between these two very similar pattern components. \n",
|
||||
"\n",
|
||||
"__BooleanExpressions__\n",
|
||||
"\n",
|
||||
" - [AndBooleanExpression](../api/stix2.patterns.rst#stix2.patterns.AndBooleanExpression)\n",
|
||||
" - [OrbooleanExpression](../api/stix2.patterns.rst#stix2.patterns.OrBooleanExpression)\n",
|
||||
" \n",
|
||||
" __Usage__: When the boolean sub-expressions refer to the *same* root object \n",
|
||||
"\n",
|
||||
" __Example__:\n",
|
||||
" ```[domain-name:value = \"www.5z8.info\" AND domain-name:resolvess_to_refs[*].value = \"'198.51.100.1/32'\"]```\n",
|
||||
" \n",
|
||||
" __Rendering__: when pattern is rendered, brackets or parenthesis will encapsulate boolean expression\n",
|
||||
" \n",
|
||||
"__CompoundObservationExpressions__\n",
|
||||
"\n",
|
||||
" - [AndObservationExpression](../api/stix2.patterns.rst#stix2.patterns.AndObservationExpression)\n",
|
||||
" - [OrObservationExpression](../api/stix2.patterns.rst#stix2.patterns.OrObservationExpression)\n",
|
||||
" \n",
|
||||
" __Usage__: When the boolean sub-expressions refer to *different* root objects\n",
|
||||
"\n",
|
||||
" __Example__:\n",
|
||||
" ```[file:name=\"foo.dll\"] AND [process:name = \"procfoo\"]```\n",
|
||||
" \n",
|
||||
" __Rendering__: when pattern is rendered, brackets will encapsulate each boolean sub-expression\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Examples\n",
|
||||
"\n",
|
||||
"### Comparison Expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from stix2 import DomainName, File, IPv4Address\n",
|
||||
"from stix2 import (ObjectPath, EqualityComparisonExpression, ObservationExpression,\n",
|
||||
" GreaterThanComparisonExpression, IsSubsetComparisonExpression,\n",
|
||||
" FloatConstant, StringConstant)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Equality Comparison expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\t[domain-name:value = 'site.of.interest.zaz']\n",
|
||||
"\n",
|
||||
"\t[file:parent_directory_ref.path = 'C:\\\\Windows\\\\System32']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"lhs = ObjectPath(\"domain-name\", [\"value\"])\n",
|
||||
"ece_1 = ObservationExpression(EqualityComparisonExpression(lhs, \"site.of.interest.zaz\"))\n",
|
||||
"print(\"\\t{}\\n\".format(ece_1))\n",
|
||||
"\n",
|
||||
"lhs = ObjectPath(\"file\", [\"parent_directory_ref\",\"path\"])\n",
|
||||
"ece_2 = ObservationExpression(EqualityComparisonExpression(lhs, \"C:\\\\Windows\\\\System32\"))\n",
|
||||
"print(\"\\t{}\\n\".format(ece_2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Greater-than Comparison expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\t[file:extensions.windows-pebinary-ext.sections[*].entropy > 7.0]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"lhs = ObjectPath(\"file\", [\"extensions\", \"windows-pebinary-ext\", \"sections[*]\", \"entropy\"])\n",
|
||||
"gte = ObservationExpression(GreaterThanComparisonExpression(lhs, FloatConstant(\"7.0\")))\n",
|
||||
"print(\"\\t{}\\n\".format(gte))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### IsSubset Comparison expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\t[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"lhs = ObjectPath(\"network-traffic\", [\"dst_ref\", \"value\"])\n",
|
||||
"iss = ObservationExpression(IsSubsetComparisonExpression(lhs, StringConstant(\"2001:0db8:dead:beef:0000:0000:0000:0000/64\")))\n",
|
||||
"print(\"\\t{}\\n\".format(iss))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Compound Observation Expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from stix2 import (IntegerConstant, HashConstant, ObjectPath,\n",
|
||||
" EqualityComparisonExpression, AndBooleanExpression,\n",
|
||||
" OrBooleanExpression, ParentheticalExpression,\n",
|
||||
" AndObservationExpression, OrObservationExpression,\n",
|
||||
" FollowedByObservationExpression, ObservationExpression)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### AND boolean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(AND)\n",
|
||||
"[email-message:sender_ref.value = 'stark@example.com' AND email-message:subject = 'Conference Info']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece3 = EqualityComparisonExpression(ObjectPath(\"email-message\", [\"sender_ref\", \"value\"]), \"stark@example.com\")\n",
|
||||
"ece4 = EqualityComparisonExpression(ObjectPath(\"email-message\", [\"subject\"]), \"Conference Info\")\n",
|
||||
"abe = ObservationExpression(AndBooleanExpression([ece3, ece4]))\n",
|
||||
"print(\"(AND)\\n{}\\n\".format(abe))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### OR boolean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(OR)\n",
|
||||
"[url:value = 'http://example.com/foo' OR url:value = 'http://example.com/bar']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece5 = EqualityComparisonExpression(ObjectPath(\"url\", [\"value\"]), \"http://example.com/foo\")\n",
|
||||
"ece6 = EqualityComparisonExpression(ObjectPath(\"url\", [\"value\"]), \"http://example.com/bar\")\n",
|
||||
"obe = ObservationExpression(OrBooleanExpression([ece5, ece6]))\n",
|
||||
"print(\"(OR)\\n{}\\n\".format(obe))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### ( OR ) AND boolean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(OR,AND)\n",
|
||||
"[(file:name = 'pdf.exe' OR file:size = 371712) AND file:created = 2014-01-13 07:03:17+00:00]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece7 = EqualityComparisonExpression(ObjectPath(\"file\", [\"name\"]), \"pdf.exe\")\n",
|
||||
"ece8 = EqualityComparisonExpression(ObjectPath(\"file\", [\"size\"]), IntegerConstant(\"371712\"))\n",
|
||||
"ece9 = EqualityComparisonExpression(ObjectPath(\"file\", [\"created\"]), \"2014-01-13T07:03:17Z\")\n",
|
||||
"obe1 = OrBooleanExpression([ece7, ece8])\n",
|
||||
"pobe = ParentheticalExpression(obe1)\n",
|
||||
"abe1 = ObservationExpression(AndBooleanExpression([pobe, ece9]))\n",
|
||||
"print(\"(OR,AND)\\n{}\\n\".format(abe1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### ( AND ) OR ( OR ) observation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(AND,OR,OR)\n",
|
||||
"([file:name = 'foo.dll'] AND [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) OR [process:name = 'fooproc' OR process:name = 'procfoo']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece20 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"file\", [\"name\"]), \"foo.dll\"))\n",
|
||||
"ece21 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"win-registry-key\", [\"key\"]), \"HKEY_LOCAL_MACHINE\\\\foo\\\\bar\"))\n",
|
||||
"ece22 = EqualityComparisonExpression(ObjectPath(\"process\", [\"name\"]), \"fooproc\")\n",
|
||||
"ece23 = EqualityComparisonExpression(ObjectPath(\"process\", [\"name\"]), \"procfoo\")\n",
|
||||
"# NOTE: we need to use AND/OR observation expression instead of just boolean \n",
|
||||
"# expressions as the operands are not on the same object-type\n",
|
||||
"aoe = ParentheticalExpression(AndObservationExpression([ece20, ece21]))\n",
|
||||
"obe2 = ObservationExpression(OrBooleanExpression([ece22, ece23]))\n",
|
||||
"ooe = OrObservationExpression([aoe, obe2])\n",
|
||||
"print(\"(AND,OR,OR)\\n{}\\n\".format(ooe))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### FOLLOWED-BY"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(FollowedBy)\n",
|
||||
"[file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece10 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"file\", [\"hashes\", \"MD5\"]), HashConstant(\"79054025255fb1a26e4bc422aef54eb4\", \"MD5\")))\n",
|
||||
"ece11 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"win-registry-key\", [\"key\"]), \"HKEY_LOCAL_MACHINE\\\\foo\\\\bar\"))\n",
|
||||
"fbe = FollowedByObservationExpression([ece10, ece11])\n",
|
||||
"print(\"(FollowedBy)\\n{}\\n\".format(fbe))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Qualified Observation Expressions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from stix2 import (TimestampConstant, HashConstant, ObjectPath, EqualityComparisonExpression,\n",
|
||||
" AndBooleanExpression, WithinQualifier, RepeatQualifier, StartStopQualifier,\n",
|
||||
" QualifiedObservationExpression, FollowedByObservationExpression,\n",
|
||||
" ParentheticalExpression, ObservationExpression)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### WITHIN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(WITHIN)\n",
|
||||
"([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece10 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"file\", [\"hashes\", \"MD5\"]), HashConstant(\"79054025255fb1a26e4bc422aef54eb4\", \"MD5\")))\n",
|
||||
"ece11 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"win-registry-key\", [\"key\"]), \"HKEY_LOCAL_MACHINE\\\\foo\\\\bar\"))\n",
|
||||
"fbe = FollowedByObservationExpression([ece10, ece11])\n",
|
||||
"par = ParentheticalExpression(fbe)\n",
|
||||
"qoe = QualifiedObservationExpression(par, WithinQualifier(300))\n",
|
||||
"print(\"(WITHIN)\\n{}\\n\".format(qoe))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### REPEATS, WITHIN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(REPEAT, WITHIN)\n",
|
||||
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 180 SECONDS\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece12 = EqualityComparisonExpression(ObjectPath(\"network-traffic\", [\"dst_ref\", \"type\"]), \"domain-name\")\n",
|
||||
"ece13 = EqualityComparisonExpression(ObjectPath(\"network-traffic\", [\"dst_ref\", \"value\"]), \"example.com\")\n",
|
||||
"abe2 = ObservationExpression(AndBooleanExpression([ece12, ece13]))\n",
|
||||
"qoe1 = QualifiedObservationExpression(QualifiedObservationExpression(abe2, RepeatQualifier(5)), WithinQualifier(180))\n",
|
||||
"print(\"(REPEAT, WITHIN)\\n{}\\n\".format(qoe1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### START, STOP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"(START-STOP)\n",
|
||||
"[file:name = 'foo.dll'] START t'2016-06-01T00:00:00Z' STOP t'2016-07-01T00:00:00Z'\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ece14 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"file\", [\"name\"]), \"foo.dll\"))\n",
|
||||
"ssq = StartStopQualifier(TimestampConstant('2016-06-01T00:00:00Z'), TimestampConstant('2016-07-01T00:00:00Z'))\n",
|
||||
"qoe2 = QualifiedObservationExpression(ece14, ssq)\n",
|
||||
"print(\"(START-STOP)\\n{}\\n\".format(qoe2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Attaching patterns to STIX2 Domain objects\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Example"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{\n",
|
||||
" \"type\": \"indicator\",\n",
|
||||
" \"id\": \"indicator--219bc5fc-fdbf-4b54-a2fc-921be7ab3acb\",\n",
|
||||
" \"created\": \"2018-08-29T23:58:00.548Z\",\n",
|
||||
" \"modified\": \"2018-08-29T23:58:00.548Z\",\n",
|
||||
" \"name\": \"Cryptotorch\",\n",
|
||||
" \"pattern\": \"[file:name = '$$t00rzch$$.elf']\",\n",
|
||||
" \"valid_from\": \"2018-08-29T23:58:00.548391Z\",\n",
|
||||
" \"labels\": [\n",
|
||||
" \"malware\",\n",
|
||||
" \"ransomware\"\n",
|
||||
" ]\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from stix2 import Indicator, EqualityComparisonExpression, ObservationExpression\n",
|
||||
"\n",
|
||||
"ece14 = ObservationExpression(EqualityComparisonExpression(ObjectPath(\"file\", [\"name\"]), \"$$t00rzch$$.elf\"))\n",
|
||||
"ind = Indicator(name=\"Cryptotorch\", labels=[\"malware\", \"ransomware\"], pattern=ece14)\n",
|
||||
"print(ind)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.6.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
1
setup.py
1
setup.py
|
@ -43,6 +43,7 @@ setup(
|
|||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
],
|
||||
keywords='stix stix2 json cti cyber threat intelligence',
|
||||
packages=find_packages(exclude=['*.test']),
|
||||
|
|
|
@ -334,3 +334,8 @@ class _Extension(_STIXBase):
|
|||
def _check_object_constraints(self):
|
||||
super(_Extension, self)._check_object_constraints()
|
||||
self._check_at_least_one_property()
|
||||
|
||||
|
||||
def _cls_init(cls, obj, kwargs):
|
||||
if getattr(cls, '__init__', object.__init__) is not object.__init__:
|
||||
cls.__init__(obj, **kwargs)
|
||||
|
|
|
@ -18,6 +18,11 @@ class _Constant(object):
|
|||
|
||||
|
||||
class StringConstant(_Constant):
|
||||
"""Pattern string constant
|
||||
|
||||
Args:
|
||||
value (str): string value
|
||||
"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
|
@ -26,17 +31,27 @@ class StringConstant(_Constant):
|
|||
|
||||
|
||||
class TimestampConstant(_Constant):
|
||||
"""Pattern timestamp constant
|
||||
|
||||
Args:
|
||||
value (datetime.datetime OR str): if string, must be a timestamp string
|
||||
"""
|
||||
def __init__(self, value):
|
||||
try:
|
||||
self.value = parse_into_datetime(value)
|
||||
except Exception:
|
||||
raise ValueError("must be a datetime object or timestamp string.")
|
||||
raise ValueError("Must be a datetime object or timestamp string.")
|
||||
|
||||
def __str__(self):
|
||||
return "t%s" % repr(self.value)
|
||||
|
||||
|
||||
class IntegerConstant(_Constant):
|
||||
"""Pattern interger constant
|
||||
|
||||
Args:
|
||||
value (int): integer value
|
||||
"""
|
||||
def __init__(self, value):
|
||||
try:
|
||||
self.value = int(value)
|
||||
|
@ -59,6 +74,13 @@ class FloatConstant(_Constant):
|
|||
|
||||
|
||||
class BooleanConstant(_Constant):
|
||||
"""Pattern boolean constant
|
||||
|
||||
Args:
|
||||
value (str OR int):
|
||||
(str) 'true', 't' for True; 'false', 'f' for False
|
||||
(int) 1 for True; 0 for False
|
||||
"""
|
||||
def __init__(self, value):
|
||||
if isinstance(value, bool):
|
||||
self.value = value
|
||||
|
@ -106,6 +128,15 @@ _HASH_REGEX = {
|
|||
|
||||
|
||||
class HashConstant(StringConstant):
|
||||
"""Pattern hash constant
|
||||
|
||||
Args:
|
||||
value (str): hash value
|
||||
type (str): hash algorithm name. Supported hash algorithms:
|
||||
"MD5", "MD6", RIPEMD160", "SHA1", "SHA224", "SHA256",
|
||||
"SHA384", "SHA512", "SHA3224", "SHA3256", "SHA3384",
|
||||
"SHA3512", "SSDEEP", "WHIRLPOOL"
|
||||
"""
|
||||
def __init__(self, value, type):
|
||||
key = type.upper().replace('-', '')
|
||||
if key in _HASH_REGEX:
|
||||
|
@ -116,7 +147,11 @@ class HashConstant(StringConstant):
|
|||
|
||||
|
||||
class BinaryConstant(_Constant):
|
||||
"""Pattern binary constant
|
||||
|
||||
Args:
|
||||
value (str): base64 encoded string value
|
||||
"""
|
||||
def __init__(self, value):
|
||||
try:
|
||||
base64.b64decode(value)
|
||||
|
@ -129,6 +164,11 @@ class BinaryConstant(_Constant):
|
|||
|
||||
|
||||
class HexConstant(_Constant):
|
||||
"""Pattern hexadecimal constant
|
||||
|
||||
Args:
|
||||
value (str): hexadecimal value
|
||||
"""
|
||||
def __init__(self, value):
|
||||
if not re.match('^([a-fA-F0-9]{2})+$', value):
|
||||
raise ValueError("must contain an even number of hexadecimal characters")
|
||||
|
@ -139,6 +179,11 @@ class HexConstant(_Constant):
|
|||
|
||||
|
||||
class ListConstant(_Constant):
|
||||
"""Pattern list constant
|
||||
|
||||
Args:
|
||||
value (list): list of values
|
||||
"""
|
||||
def __init__(self, values):
|
||||
self.value = values
|
||||
|
||||
|
@ -147,6 +192,12 @@ class ListConstant(_Constant):
|
|||
|
||||
|
||||
def make_constant(value):
|
||||
"""Convert value to Pattern constant, best effort attempt
|
||||
at determining root value type and corresponding conversion
|
||||
|
||||
Args:
|
||||
value: value to convert to Pattern constant
|
||||
"""
|
||||
if isinstance(value, _Constant):
|
||||
return value
|
||||
|
||||
|
@ -182,6 +233,16 @@ class _ObjectPathComponent(object):
|
|||
|
||||
|
||||
class BasicObjectPathComponent(_ObjectPathComponent):
|
||||
"""Basic object path component (for an observation or expression)
|
||||
|
||||
By "Basic", implies that the object path component is not a
|
||||
list, object reference or futher referenced property, i.e. terminal
|
||||
component
|
||||
|
||||
Args:
|
||||
property_name (str): object property name
|
||||
is_key (bool): is dictionary key, default: False
|
||||
"""
|
||||
def __init__(self, property_name, is_key=False):
|
||||
self.property_name = property_name
|
||||
# TODO: set is_key to True if this component is a dictionary key
|
||||
|
@ -192,6 +253,12 @@ class BasicObjectPathComponent(_ObjectPathComponent):
|
|||
|
||||
|
||||
class ListObjectPathComponent(_ObjectPathComponent):
|
||||
"""List object path component (for an observation or expression)
|
||||
|
||||
Args:
|
||||
property_name (str): list object property name
|
||||
index (int): index of the list property's value that is specified
|
||||
"""
|
||||
def __init__(self, property_name, index):
|
||||
self.property_name = property_name
|
||||
self.index = index
|
||||
|
@ -201,6 +268,11 @@ class ListObjectPathComponent(_ObjectPathComponent):
|
|||
|
||||
|
||||
class ReferenceObjectPathComponent(_ObjectPathComponent):
|
||||
"""Reference object path component (for an observation or expression)
|
||||
|
||||
Args:
|
||||
reference_property_name (str): reference object property name
|
||||
"""
|
||||
def __init__(self, reference_property_name):
|
||||
self.property_name = reference_property_name
|
||||
|
||||
|
@ -209,6 +281,12 @@ class ReferenceObjectPathComponent(_ObjectPathComponent):
|
|||
|
||||
|
||||
class ObjectPath(object):
|
||||
"""Pattern operand object (property) path
|
||||
|
||||
Args:
|
||||
object_type_name (str): name of object type for corresponding object path component
|
||||
property_path (_ObjectPathComponent OR str): object path
|
||||
"""
|
||||
def __init__(self, object_type_name, property_path):
|
||||
self.object_type_name = object_type_name
|
||||
self.property_path = [
|
||||
|
@ -221,11 +299,17 @@ class ObjectPath(object):
|
|||
return "%s:%s" % (self.object_type_name, ".".join(["%s" % x for x in self.property_path]))
|
||||
|
||||
def merge(self, other):
|
||||
"""Extend the object property with that of the supplied object property path"""
|
||||
self.property_path.extend(other.property_path)
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def make_object_path(lhs):
|
||||
"""Create ObjectPath from string encoded object path
|
||||
|
||||
Args:
|
||||
lhs (str): object path of left-hand-side component of expression
|
||||
"""
|
||||
path_as_parts = lhs.split(":")
|
||||
return ObjectPath(path_as_parts[0], path_as_parts[1].split("."))
|
||||
|
||||
|
@ -235,6 +319,14 @@ class _PatternExpression(object):
|
|||
|
||||
|
||||
class _ComparisonExpression(_PatternExpression):
|
||||
"""Pattern Comparison Expression
|
||||
|
||||
Args:
|
||||
operator (str): operator of comparison expression
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, operator, lhs, rhs, negated=False):
|
||||
if operator == "=" and isinstance(rhs, (ListConstant, list)):
|
||||
self.operator = "IN"
|
||||
|
@ -259,56 +351,134 @@ class _ComparisonExpression(_PatternExpression):
|
|||
|
||||
|
||||
class EqualityComparisonExpression(_ComparisonExpression):
|
||||
"""Pattern Equality Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(EqualityComparisonExpression, self).__init__("=", lhs, rhs, negated)
|
||||
|
||||
|
||||
class GreaterThanComparisonExpression(_ComparisonExpression):
|
||||
"""Pattern Greater-than Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(GreaterThanComparisonExpression, self).__init__(">", lhs, rhs, negated)
|
||||
|
||||
|
||||
class LessThanComparisonExpression(_ComparisonExpression):
|
||||
"""Pattern Less-than Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(LessThanComparisonExpression, self).__init__("<", lhs, rhs, negated)
|
||||
|
||||
|
||||
class GreaterThanEqualComparisonExpression(_ComparisonExpression):
|
||||
"""Pattern Greater-Than-or-Equal-to Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(GreaterThanEqualComparisonExpression, self).__init__(">=", lhs, rhs, negated)
|
||||
|
||||
|
||||
class LessThanEqualComparisonExpression(_ComparisonExpression):
|
||||
"""Pattern Less-Than-or-Equal-to Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(LessThanEqualComparisonExpression, self).__init__("<=", lhs, rhs, negated)
|
||||
|
||||
|
||||
class InComparisonExpression(_ComparisonExpression):
|
||||
"""'in' Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(InComparisonExpression, self).__init__("IN", lhs, rhs, negated)
|
||||
|
||||
|
||||
class LikeComparisonExpression(_ComparisonExpression):
|
||||
"""'like' Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(LikeComparisonExpression, self).__init__("LIKE", lhs, rhs, negated)
|
||||
|
||||
|
||||
class MatchesComparisonExpression(_ComparisonExpression):
|
||||
"""'Matches' Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(MatchesComparisonExpression, self).__init__("MATCHES", lhs, rhs, negated)
|
||||
|
||||
|
||||
class IsSubsetComparisonExpression(_ComparisonExpression):
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(IsSubsetComparisonExpression, self).__init__("ISSUBSET", lhs, rhs, negated)
|
||||
""" 'is subset' Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(IsSubsetComparisonExpression, self).__init__("ISSUBSET", lhs, rhs, negated)
|
||||
|
||||
|
||||
class IsSupersetComparisonExpression(_ComparisonExpression):
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(IsSupersetComparisonExpression, self).__init__("ISSUPERSET", lhs, rhs, negated)
|
||||
""" 'is super set' Comparison Expression
|
||||
|
||||
Args:
|
||||
lhs (ObjectPath OR str): object path of left-hand-side component of expression
|
||||
rhs (ObjectPath OR str): object path of right-hand-side component of expression
|
||||
negated (bool): comparison expression negated. Default: False
|
||||
"""
|
||||
def __init__(self, lhs, rhs, negated=False):
|
||||
super(IsSupersetComparisonExpression, self).__init__("ISSUPERSET", lhs, rhs, negated)
|
||||
|
||||
|
||||
class _BooleanExpression(_PatternExpression):
|
||||
"""Boolean Pattern Expression
|
||||
|
||||
Args:
|
||||
operator (str): boolean operator
|
||||
operands (list): boolean operands
|
||||
"""
|
||||
def __init__(self, operator, operands):
|
||||
self.operator = operator
|
||||
self.operands = []
|
||||
|
@ -324,21 +494,37 @@ class _BooleanExpression(_PatternExpression):
|
|||
def __str__(self):
|
||||
sub_exprs = []
|
||||
for o in self.operands:
|
||||
sub_exprs.append("%s" % o)
|
||||
sub_exprs.append(str(o))
|
||||
return (" " + self.operator + " ").join(sub_exprs)
|
||||
|
||||
|
||||
class AndBooleanExpression(_BooleanExpression):
|
||||
"""'AND' Boolean Pattern Expression. Only use if both operands are of
|
||||
the same root object.
|
||||
|
||||
Args:
|
||||
operands (list): AND operands
|
||||
"""
|
||||
def __init__(self, operands):
|
||||
super(AndBooleanExpression, self).__init__("AND", operands)
|
||||
|
||||
|
||||
class OrBooleanExpression(_BooleanExpression):
|
||||
"""'OR' Boolean Pattern Expression. Only use if both operands are of the same root object
|
||||
|
||||
Args:
|
||||
operands (list): OR operands
|
||||
"""
|
||||
def __init__(self, operands):
|
||||
super(OrBooleanExpression, self).__init__("OR", operands)
|
||||
|
||||
|
||||
class ObservationExpression(_PatternExpression):
|
||||
"""Observation Expression
|
||||
|
||||
Args:
|
||||
operand (str): observation expression operand
|
||||
"""
|
||||
def __init__(self, operand):
|
||||
self.operand = operand
|
||||
|
||||
|
@ -347,6 +533,12 @@ class ObservationExpression(_PatternExpression):
|
|||
|
||||
|
||||
class _CompoundObservationExpression(_PatternExpression):
|
||||
"""Compound Observation Expression
|
||||
|
||||
Args:
|
||||
operator (str): compound observation operator
|
||||
operands (str): compound observation operands
|
||||
"""
|
||||
def __init__(self, operator, operands):
|
||||
self.operator = operator
|
||||
self.operands = operands
|
||||
|
@ -359,21 +551,41 @@ class _CompoundObservationExpression(_PatternExpression):
|
|||
|
||||
|
||||
class AndObservationExpression(_CompoundObservationExpression):
|
||||
"""'AND' Compound Observation Pattern Expression
|
||||
|
||||
Args:
|
||||
operands (str): compound observation operands
|
||||
"""
|
||||
def __init__(self, operands):
|
||||
super(AndObservationExpression, self).__init__("AND", operands)
|
||||
|
||||
|
||||
class OrObservationExpression(_CompoundObservationExpression):
|
||||
"""Pattern 'OR' Compound Observation Expression
|
||||
|
||||
Args:
|
||||
operands (str): compound observation operands
|
||||
"""
|
||||
def __init__(self, operands):
|
||||
super(OrObservationExpression, self).__init__("OR", operands)
|
||||
|
||||
|
||||
class FollowedByObservationExpression(_CompoundObservationExpression):
|
||||
"""Pattern 'Followed by' Compound Observation Expression
|
||||
|
||||
Args:
|
||||
operands (str): compound observation operands
|
||||
"""
|
||||
def __init__(self, operands):
|
||||
super(FollowedByObservationExpression, self).__init__("FOLLOWEDBY", operands)
|
||||
|
||||
|
||||
class ParentheticalExpression(_PatternExpression):
|
||||
"""Pattern Parenthetical Observation Expression
|
||||
|
||||
Args:
|
||||
exp (str): observation expression
|
||||
"""
|
||||
def __init__(self, exp):
|
||||
self.expression = exp
|
||||
if hasattr(exp, "root_type"):
|
||||
|
@ -388,6 +600,11 @@ class _ExpressionQualifier(_PatternExpression):
|
|||
|
||||
|
||||
class RepeatQualifier(_ExpressionQualifier):
|
||||
"""Pattern Repeat Qualifier
|
||||
|
||||
Args:
|
||||
times_to_repeat (int): times the qualifiers is repeated
|
||||
"""
|
||||
def __init__(self, times_to_repeat):
|
||||
if isinstance(times_to_repeat, IntegerConstant):
|
||||
self.times_to_repeat = times_to_repeat
|
||||
|
@ -401,6 +618,11 @@ class RepeatQualifier(_ExpressionQualifier):
|
|||
|
||||
|
||||
class WithinQualifier(_ExpressionQualifier):
|
||||
"""Pattern 'Within' Qualifier
|
||||
|
||||
Args:
|
||||
number_of_seconds (int): seconds value for 'within' qualifier
|
||||
"""
|
||||
def __init__(self, number_of_seconds):
|
||||
if isinstance(number_of_seconds, IntegerConstant):
|
||||
self.number_of_seconds = number_of_seconds
|
||||
|
@ -414,6 +636,12 @@ class WithinQualifier(_ExpressionQualifier):
|
|||
|
||||
|
||||
class StartStopQualifier(_ExpressionQualifier):
|
||||
"""Pattern Start/Stop Qualifier
|
||||
|
||||
Args:
|
||||
start_time (TimestampConstant OR datetime.date): start timestamp for qualifier
|
||||
stop_time (TimestampConstant OR datetime.date): stop timestamp for qualifier
|
||||
"""
|
||||
def __init__(self, start_time, stop_time):
|
||||
if isinstance(start_time, TimestampConstant):
|
||||
self.start_time = start_time
|
||||
|
@ -433,6 +661,12 @@ class StartStopQualifier(_ExpressionQualifier):
|
|||
|
||||
|
||||
class QualifiedObservationExpression(_PatternExpression):
|
||||
"""Pattern Qualified Observation Expression
|
||||
|
||||
Args:
|
||||
observation_expression (PatternExpression OR _CompoundObservationExpression OR ): pattern expression
|
||||
qualifier (_ExpressionQualifier): pattern expression qualifier
|
||||
"""
|
||||
def __init__(self, observation_expression, qualifier):
|
||||
self.observation_expression = observation_expression
|
||||
self.qualifier = qualifier
|
||||
|
|
|
@ -368,5 +368,4 @@ def CustomObject(type='x-custom-type', properties=None):
|
|||
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
|
||||
]))
|
||||
return _custom_object_builder(cls, type, _properties, '2.0')
|
||||
|
||||
return wrapper
|
||||
|
|
Loading…
Reference in New Issue