Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| feb836cbf2 | |||
| 8cc16d252a | |||
| 038f205f0f | |||
| fbe138ed90 | |||
| faeccf0c3d | |||
| a0beda240c | |||
| ef0af10747 | |||
| 9835950f04 | |||
| be62fad5cc | |||
| 465e41bfbb | |||
| 8b7560c2f4 | |||
| ef14ee542d | |||
| 97d13660f7 | |||
| 7a81054cdd | |||
| 1b9054c1f3 | |||
| b7018bcd4a | |||
| c2e621cf08 | |||
| 0c6b9e532b | |||
| 6f2f1d2bd7 | |||
| 0b9a3f3a08 | |||
| 9aedb8f764 | |||
| 102ab3081b | |||
| e52f29dda9 | |||
| c60563e546 | |||
| 5786688f97 | |||
| 54c75167ce | |||
| 0c64992276 | |||
| a5579fa8cd | |||
| c0f1b12833 | |||
| 66d9de460d | |||
| 4f19ef5708 | |||
| 8af2b70594 | |||
| c9eb921f68 | |||
| 0117dac1db | |||
| 68fd20cb66 | |||
| 3107c0c268 | |||
| 4741b6a4d6 | |||
| d661771608 | |||
| 3ac28f3eed | |||
| 219f00e3fb | |||
| 2eeccf48e0 | |||
| f4e9690d6b | |||
| 78d8ca2b41 | |||
| 40df0d4534 | |||
| 6eb49fc1ce | |||
| b6403793c1 | |||
| ab5556ae8c | |||
| aafab2e936 | |||
| f31750e567 | |||
| cd20e4a3fc | |||
| 632c45843b | |||
| f01913c996 | |||
| 6a76f5950b | |||
| f6fb9c7f5f | |||
| 2873e1ded3 | |||
| 8ff85499c8 | |||
| 1a3444d0ef | |||
| a7003c2aa3 | |||
| d019cef439 | |||
| a57a7b58cf | |||
| 60b20a76a6 | |||
| 0fe72d6133 | |||
| f43300af8e | |||
| 91e48d8c1b | |||
| 8d866b0868 | |||
| bc5e9bd03a | |||
| 8cc3990aef | |||
| 4d65b62063 | |||
| 30cc7ee809 | |||
| 77329714c5 | |||
| 823d86c7d9 | |||
| bba43c7a86 | |||
| 66a32549f1 | |||
| 42808b7eb8 | |||
| 30948b9c1a | |||
| a5ec6722a1 | |||
| 86c1b4ae4b | |||
| 3d4ce9d175 | |||
| b3654947bc | |||
| 0f02f2bdfc | |||
| 3bd3e724f1 | |||
| 439045a87b | |||
| 4e7ad5c948 | |||
| e8963b2599 | |||
| b815b15255 | |||
| c8b5b91815 | |||
| 9457f01c29 | |||
| f8d8eb7948 | |||
| a2753ba5a6 |
@@ -0,0 +1,97 @@
|
||||
# Release Notes
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
from version 0.14.0.
|
||||
|
||||
## Unreleased
|
||||
|
||||
Changes from this section will be contained in the next release.
|
||||
|
||||
## 0.14
|
||||
|
||||
### Added
|
||||
|
||||
* sigma-similarity tool
|
||||
* LimaCharlie backend
|
||||
* Default configurations for some backends that are used if no configuration is passed.
|
||||
* Regular expression support for es-dsl backend (propagates to backends derived from this like elastalert-dsl)
|
||||
* Value modifiers:
|
||||
* startswith
|
||||
* endswith
|
||||
|
||||
### Changed
|
||||
|
||||
* Removal of line breaks in elastalert output
|
||||
* Searches not bound to fields are restricted to keyword fields in es-qs backend
|
||||
* Graylog backend now based on es-qs backend
|
||||
|
||||
### Fixed
|
||||
|
||||
* Removed ProcessCommandLine mapping for Windows Security EventID 4688 in generic
|
||||
process creation log source configuration.
|
||||
|
||||
## 0.13
|
||||
|
||||
### Added
|
||||
|
||||
* Index mappings for Sumologic
|
||||
* Malicious cmdlets in wdatp
|
||||
* QRadar support for keyword searches
|
||||
* QRadar mapping improvements
|
||||
* QRadar field selection
|
||||
* QRadar type regex modifier support
|
||||
* Elasticsearch keyword field blacklisting with wildcards
|
||||
* Added dateField configuration parameter in xpack-watcher backend
|
||||
* Field mappings in configurations
|
||||
* Field name mapping for conditional fields
|
||||
* Value modifiers:
|
||||
* utf16
|
||||
* utf16le
|
||||
* wide
|
||||
* utf16be
|
||||
|
||||
### Changed
|
||||
|
||||
* Improved --backend-config help text
|
||||
|
||||
### Fixed
|
||||
|
||||
* Backend errors in ala
|
||||
* Slash escaping within es-dsl wildcard queries
|
||||
* QRadar backend config
|
||||
* QRadar field name and value escaping and handling
|
||||
* Elasticsearch wildcard detection pattern
|
||||
* Aggregation on keyword field in es-dsl backend
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Fixed
|
||||
|
||||
* Missing build dependency
|
||||
|
||||
## 0.12
|
||||
|
||||
### Added
|
||||
|
||||
* Usage of "Channel" field in ELK Windows configuration
|
||||
* Fields to mappings
|
||||
* xpack-watcher actions index and webhook
|
||||
* Config for Winlogbeat 7.x
|
||||
* Value modifiers
|
||||
* Regular expression support
|
||||
|
||||
### Changed
|
||||
|
||||
* Warning/error messages
|
||||
* Sumologic value cleaning
|
||||
* Explicit OR for Elasticsearch query strings
|
||||
* Listing of available configurations on missing configuration error
|
||||
|
||||
### Fixed
|
||||
|
||||
* Conditions in es-dsl backend
|
||||
* Sumologic handling of null values
|
||||
* Ignore timeframe detection keyword in all/any of conditions
|
||||
@@ -0,0 +1,38 @@
|
||||
## {{ version.minor }}.{{ version.major }}.{{ version.patch }} ({{ date }})
|
||||
|
||||
### Added
|
||||
|
||||
{% for item in added %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
### Changed
|
||||
|
||||
{% for item in changed %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
### Deprecated
|
||||
|
||||
{% for item in deprecated %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
### Removed
|
||||
|
||||
{% for item in removed %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
### Fixed
|
||||
|
||||
{% for item in fixed %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
### Security
|
||||
|
||||
{% for item in security %}
|
||||
* {{ item | indent(2) }}
|
||||
{% endfor %}
|
||||
|
||||
@@ -39,6 +39,7 @@ test-sigmac:
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t powershell -c tools/config/powershell.yml -Ocsv rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t arcsight -c tools/config/arcsight.yml rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t qradar -c tools/config/qradar.yml rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t limacharlie -c tools/config/limacharlie.yml rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t qualys -c tools/config/qualys.yml rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t netwitness -c tools/config/netwitness.yml rules/ > /dev/null
|
||||
coverage run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t sumologic -O rulecomment -c tools/config/sumologic.yml rules/ > /dev/null
|
||||
|
||||
@@ -12,6 +12,7 @@ elasticsearch = "*"
|
||||
elasticsearch-async = "*"
|
||||
pymisp = "*"
|
||||
PyYAML = ">=3.11"
|
||||
progressbar2 = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
Generated
+86
-60
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "5e571aa1a1b4f78e71563cc30e3f457c8359c36888c76b6ed68376dadce445bb"
|
||||
"sha256": "f3f1c14d8b9cfcd5608e018017012b8712a94fb7a56f633ae179bd3451d636fb"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -18,30 +18,20 @@
|
||||
"default": {
|
||||
"aiohttp": {
|
||||
"hashes": [
|
||||
"sha256:00d198585474299c9c3b4f1d5de1a576cc230d562abc5e4a0e81d71a20a6ca55",
|
||||
"sha256:0155af66de8c21b8dba4992aaeeabf55503caefae00067a3b1139f86d0ec50ed",
|
||||
"sha256:09654a9eca62d1bd6d64aa44db2498f60a5c1e0ac4750953fdd79d5c88955e10",
|
||||
"sha256:199f1d106e2b44b6dacdf6f9245493c7d716b01d0b7fbe1959318ba4dc64d1f5",
|
||||
"sha256:296f30dedc9f4b9e7a301e5cc963012264112d78a1d3094cd83ef148fdf33ca1",
|
||||
"sha256:368ed312550bd663ce84dc4b032a962fcb3c7cae099dbbd48663afc305e3b939",
|
||||
"sha256:40d7ea570b88db017c51392349cf99b7aefaaddd19d2c78368aeb0bddde9d390",
|
||||
"sha256:629102a193162e37102c50713e2e31dc9a2fe7ac5e481da83e5bb3c0cee700aa",
|
||||
"sha256:6d5ec9b8948c3d957e75ea14d41e9330e1ac3fed24ec53766c780f82805140dc",
|
||||
"sha256:87331d1d6810214085a50749160196391a712a13336cd02ce1c3ea3d05bcf8d5",
|
||||
"sha256:9a02a04bbe581c8605ac423ba3a74999ec9d8bce7ae37977a3d38680f5780b6d",
|
||||
"sha256:9c4c83f4fa1938377da32bc2d59379025ceeee8e24b89f72fcbccd8ca22dc9bf",
|
||||
"sha256:9cddaff94c0135ee627213ac6ca6d05724bfe6e7a356e5e09ec57bd3249510f6",
|
||||
"sha256:a25237abf327530d9561ef751eef9511ab56fd9431023ca6f4803f1994104d72",
|
||||
"sha256:a5cbd7157b0e383738b8e29d6e556fde8726823dae0e348952a61742b21aeb12",
|
||||
"sha256:a97a516e02b726e089cffcde2eea0d3258450389bbac48cbe89e0f0b6e7b0366",
|
||||
"sha256:acc89b29b5f4e2332d65cd1b7d10c609a75b88ef8925d487a611ca788432dfa4",
|
||||
"sha256:b05bd85cc99b06740aad3629c2585bda7b83bd86e080b44ba47faf905fdf1300",
|
||||
"sha256:c2bec436a2b5dafe5eaeb297c03711074d46b6eb236d002c13c42f25c4a8ce9d",
|
||||
"sha256:cc619d974c8c11fe84527e4b5e1c07238799a8c29ea1c1285149170524ba9303",
|
||||
"sha256:d4392defd4648badaa42b3e101080ae3313e8f4787cb517efd3f5b8157eaefd6",
|
||||
"sha256:e1c3c582ee11af7f63a34a46f0448fca58e59889396ffdae1f482085061a2889"
|
||||
"sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e",
|
||||
"sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326",
|
||||
"sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a",
|
||||
"sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654",
|
||||
"sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a",
|
||||
"sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4",
|
||||
"sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17",
|
||||
"sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec",
|
||||
"sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd",
|
||||
"sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48",
|
||||
"sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59",
|
||||
"sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"
|
||||
],
|
||||
"version": "==3.5.4"
|
||||
"version": "==3.6.2"
|
||||
},
|
||||
"async-timeout": {
|
||||
"hashes": [
|
||||
@@ -52,17 +42,17 @@
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
|
||||
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
|
||||
],
|
||||
"version": "==19.1.0"
|
||||
"version": "==19.3.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
|
||||
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
|
||||
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
|
||||
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
|
||||
],
|
||||
"version": "==2019.6.16"
|
||||
"version": "==2019.9.11"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
@@ -118,11 +108,11 @@
|
||||
},
|
||||
"elasticsearch": {
|
||||
"hashes": [
|
||||
"sha256:cbc73831c63fa2824538df76fcb2c4be007b43dbd9e7788ae70ea6d24109925b",
|
||||
"sha256:d1b176b87a7fb75dca82978c82a4023e8b21cbc98f4018cb51190fb0b8b43764"
|
||||
"sha256:693935914d59a517dfffdaab547ff906712a386d9e25027517464960221cbd4c",
|
||||
"sha256:7644fa0a9ae524344185bda561826a781a5c6bd4d3eb98a24515c567aab88327"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.0.2"
|
||||
"version": "==7.0.5"
|
||||
},
|
||||
"elasticsearch-async": {
|
||||
"hashes": [
|
||||
@@ -146,12 +136,26 @@
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
|
||||
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
|
||||
],
|
||||
"version": "==0.23"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
"sha256:5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f",
|
||||
"sha256:8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"
|
||||
"sha256:2fa0684276b6333ff3c0b1b27081f4b2305f0a36cf702a23db50edb141893c3f",
|
||||
"sha256:94c0a13b4a0616458b42529091624e66700a17f847453e52279e35509a5b7631"
|
||||
],
|
||||
"version": "==3.0.2"
|
||||
"version": "==3.1.1"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
||||
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
||||
],
|
||||
"version": "==7.2.0"
|
||||
},
|
||||
"multidict": {
|
||||
"hashes": [
|
||||
@@ -189,31 +193,46 @@
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:54a5eab895d89f342b52ba2bffe70930ef9f8d96e398cccf530d21fa0516a873"
|
||||
"sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c"
|
||||
],
|
||||
"version": "==0.5.9"
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"progressbar2": {
|
||||
"hashes": [
|
||||
"sha256:7538d02045a1fd3aa2b2834bfda463da8755bd3ff050edc6c5ddff3bc616215f",
|
||||
"sha256:eb774d1e0d03ea4730f381c13c2c6ae7abb5ddfb14d8321d7a58a61aa708f0d0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.47.0"
|
||||
},
|
||||
"pymisp": {
|
||||
"hashes": [
|
||||
"sha256:5bff5e7705d2697fd6e7110d1f316688d6106795cba4d453eec8c78c18b0e9f7",
|
||||
"sha256:85d319e0e1d4e53a901501ad74679f3802201b5e12df2da443aaae1d2443e3b1",
|
||||
"sha256:a2fe66bada1186abc6237dc151473e307619685b8168aaeb31b6112528638d9e"
|
||||
"sha256:17b145dbc39a1ba4ebce60e8b75a479d2c8fd3c2a239f32682f2e1a3636469ec",
|
||||
"sha256:814023f346f9e1dcf6763d93450df44ff0157f2061c612a7eaf2020280f588a3",
|
||||
"sha256:de67196f6a8916b9c52a84a1c45ea967c53fa9d2b3795b070ad2c1cbc28d79d7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.4.112"
|
||||
"version": "==2.4.117.2"
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"
|
||||
"sha256:eb6545dbeb1aa69ab1fb4809bfbf5a8705e44d92ef8fc7c2361682a47c46c778"
|
||||
],
|
||||
"version": "==0.15.4"
|
||||
"version": "==0.15.5"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
|
||||
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
|
||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
"version": "==2.8.1"
|
||||
},
|
||||
"python-utils": {
|
||||
"hashes": [
|
||||
"sha256:34aaf26b39b0b86628008f2ae0ac001b30e7986a8d303b61e1357dfcdad4f6d3",
|
||||
"sha256:e25f840564554eaded56eaa395bca507b0b9e9f0ae5ecb13a8cb785305c56d25"
|
||||
],
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
@@ -243,26 +262,26 @@
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
"version": "==1.13.0"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95",
|
||||
"sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87",
|
||||
"sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"
|
||||
"sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2",
|
||||
"sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d",
|
||||
"sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==3.7.4"
|
||||
"version": "==3.7.4.1"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
|
||||
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
|
||||
"sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398",
|
||||
"sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"
|
||||
],
|
||||
"version": "==1.25.3"
|
||||
"version": "==1.25.6"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
@@ -272,11 +291,11 @@
|
||||
},
|
||||
"yamllint": {
|
||||
"hashes": [
|
||||
"sha256:9a4fec2d40804979de5f54453fd1551bc1f8b59a7ad4a26fd7f26aeca34a83af",
|
||||
"sha256:f97cd763fe7b588444a94cc44fd3764b832a613b5250baa2bfe8b84c91e4c330"
|
||||
"sha256:24f05b7ff1a604120eeb5ff7afb7ed8792253bfa96ee83db9cec6d5c20feaf64",
|
||||
"sha256:d42dbb35b3d28722a8c5c25de4593add0a6215b2732eb6932d89f38482c3d01c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.16.0"
|
||||
"version": "==1.18.0"
|
||||
},
|
||||
"yarl": {
|
||||
"hashes": [
|
||||
@@ -293,6 +312,13 @@
|
||||
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
|
||||
"sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
||||
@@ -100,7 +100,7 @@ merges multiple YAML documents of a Sigma rule collection into simple Sigma rule
|
||||
|
||||
```
|
||||
usage: sigmac [-h] [--recurse] [--filter FILTER]
|
||||
[--target {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}]
|
||||
[--target {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,limacharlie,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}]
|
||||
[--target-list] [--config CONFIG] [--output OUTPUT]
|
||||
[--backend-option BACKEND_OPTION] [--defer-abort]
|
||||
[--ignore-backend-errors] [--verbose] [--debug]
|
||||
@@ -125,7 +125,7 @@ optional arguments:
|
||||
tag that must appear in the rules tag list, case-
|
||||
insensitive matching. Multiple log source
|
||||
specifications are AND linked.
|
||||
--target {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}, -t {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}
|
||||
--target {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,limacharlie,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}, -t {arcsight,es-qs,es-dsl,kibana,xpack-watcher,elastalert,graylog,limacharlie,logpoint,grep,netwitness,powershell,qradar,qualys,splunk,splunkxml,sumologic,fieldlist,wdatp}
|
||||
Output target format
|
||||
--target-list, -l List available output target formats
|
||||
--config CONFIG, -c CONFIG
|
||||
@@ -195,6 +195,7 @@ tools/sigmac -t splunk -c ~/my-splunk-mapping.yml -c tools/config/generic/window
|
||||
* [RSA NetWitness](https://www.rsa.com/en-us/products/threat-detection-response)
|
||||
* [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/getting-started/getting-started-with-windows-powershell?view=powershell-6)
|
||||
* [Grep](https://www.gnu.org/software/grep/manual/grep.html) with Perl-compatible regular expression support
|
||||
* [LimaCharlie](https://limacharlie.io)
|
||||
|
||||
Current work-in-progress
|
||||
* [Splunk Data Models](https://docs.splunk.com/Documentation/Splunk/7.1.0/Knowledge/Aboutdatamodels)
|
||||
@@ -271,12 +272,13 @@ These tools are not part of the main toolchain and maintained separately by thei
|
||||
# Projects or Products that use Sigma
|
||||
|
||||
* [MISP](http://www.misp-project.org/2017/03/26/MISP.2.4.70.released.html) (since version 2.4.70, March 2017)
|
||||
* [TA-Sigma-Searches](https://github.com/dstaulcu/TA-Sigma-Searches) (Splunk App)
|
||||
* [SOC Prime - Sigma Rule Editor](https://tdm.socprime.com/sigma/)
|
||||
* [ypsilon](https://github.com/P4T12ICK/ypsilon) - Automated Use Case Testing
|
||||
* [uncoder.io](https://uncoder.io/) - Online Translator for SIEM Searches
|
||||
* [SPARK](https://www.nextron-systems.com/2018/06/28/spark-applies-sigma-rules-in-eventlog-scan/) - Scan with Sigma rules on endpoints
|
||||
* [THOR](https://www.nextron-systems.com/2018/06/28/spark-applies-sigma-rules-in-eventlog-scan/) - Scan with Sigma rules on endpoints
|
||||
* [Joe Sandbox](https://www.joesecurity.org/)
|
||||
* [ypsilon](https://github.com/P4T12ICK/ypsilon) - Automated Use Case Testing
|
||||
* [RANK VASA](https://globenewswire.com/news-release/2019/03/04/1745907/0/en/RANK-Software-to-Help-MSSPs-Scale-Cybersecurity-Offerings.html)
|
||||
* [TA-Sigma-Searches](https://github.com/dstaulcu/TA-Sigma-Searches) (Splunk App)
|
||||
|
||||
# Contribution
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ logsource:
|
||||
category: proxy
|
||||
detection:
|
||||
selection:
|
||||
c-uri-query: '*/asp.asp?ui=*'
|
||||
c-uri: '*/asp.asp?ui=*'
|
||||
condition: selection
|
||||
fields:
|
||||
- ClientIP
|
||||
|
||||
@@ -10,7 +10,7 @@ logsource:
|
||||
category: proxy
|
||||
detection:
|
||||
selection:
|
||||
c-uri-query: '*/list/suc?name=*'
|
||||
c-uri: '*/list/suc?name=*'
|
||||
condition: selection
|
||||
fields:
|
||||
- ClientIP
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
title: Crypto Miner User Agent
|
||||
status: experimental
|
||||
description: Detects suspicious user agent strings used by crypto miners in proxy logs
|
||||
references:
|
||||
- https://github.com/xmrig/xmrig/blob/da22b3e6c45825f3ac1f208255126cb8585cd4fc/src/base/kernel/Platform_win.cpp#L65
|
||||
- https://github.com/xmrig/xmrig/blob/427b6516e0550200c17ca28675118f0fffcc323f/src/version.h
|
||||
author: Florian Roth
|
||||
date: 2019/10/21
|
||||
logsource:
|
||||
category: proxy
|
||||
detection:
|
||||
selection:
|
||||
UserAgent:
|
||||
# XMRig
|
||||
- 'XMRig *'
|
||||
# CCMiner
|
||||
- 'ccminer*'
|
||||
condition: selection
|
||||
fields:
|
||||
- ClientIP
|
||||
- URL
|
||||
- UserAgent
|
||||
falsepositives:
|
||||
- Unknown
|
||||
level: high
|
||||
@@ -50,7 +50,7 @@ detection:
|
||||
- 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/5.0)' # Fareit / Pony
|
||||
- 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' # https://goo.gl/g43qjs
|
||||
- 'Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1)' # MacControl malware https://goo.gl/sqY3Ja https://www.symantec.com/connect/blogs/osxmacontrol-back-it-again
|
||||
|
||||
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' # used by Zebrocy malware https://app.any.run/tasks/7d7fa4a0-6970-4428-828b-29572abf9ceb/
|
||||
# Others
|
||||
- '* pxyscand*'
|
||||
- '* asd'
|
||||
|
||||
@@ -14,7 +14,8 @@ detection:
|
||||
selection:
|
||||
EventID: 4704
|
||||
keywords:
|
||||
- 'SeEnableDelegationPrivilege'
|
||||
Message:
|
||||
- '*SeEnableDelegationPrivilege*'
|
||||
condition: all of them
|
||||
falsepositives:
|
||||
- Unknown
|
||||
|
||||
@@ -15,11 +15,13 @@ detection:
|
||||
selection:
|
||||
EventID: 4738
|
||||
keywords:
|
||||
- 'DES'
|
||||
- 'Preauth'
|
||||
- 'Encrypted'
|
||||
Message:
|
||||
- '*DES*'
|
||||
- '*Preauth*'
|
||||
- '*Encrypted*'
|
||||
filters:
|
||||
- 'Enabled'
|
||||
Message:
|
||||
- '*Enabled*'
|
||||
condition: selection and keywords and filters
|
||||
falsepositives:
|
||||
- Unknown
|
||||
|
||||
@@ -14,6 +14,7 @@ logsource:
|
||||
product: windows
|
||||
detection:
|
||||
keywords:
|
||||
Message:
|
||||
- "* mimikatz *"
|
||||
- "* mimilib *"
|
||||
- "* <3 eo.oe *"
|
||||
|
||||
@@ -6,29 +6,31 @@ logsource:
|
||||
service: application
|
||||
detection:
|
||||
keywords:
|
||||
- HTool
|
||||
- Hacktool
|
||||
- ASP/Backdoor
|
||||
- JSP/Backdoor
|
||||
- PHP/Backdoor
|
||||
- Backdoor.ASP
|
||||
- Backdoor.JSP
|
||||
- Backdoor.PHP
|
||||
- Webshell
|
||||
- Portscan
|
||||
- Mimikatz
|
||||
- WinCred
|
||||
- PlugX
|
||||
- Korplug
|
||||
- Pwdump
|
||||
- Chopper
|
||||
- WmiExec
|
||||
- Xscan
|
||||
- Clearlog
|
||||
- ASPXSpy
|
||||
Message:
|
||||
- "*HTool*"
|
||||
- "*Hacktool*"
|
||||
- "*ASP/Backdoor*"
|
||||
- "*JSP/Backdoor*"
|
||||
- "*PHP/Backdoor*"
|
||||
- "*Backdoor.ASP*"
|
||||
- "*Backdoor.JSP*"
|
||||
- "*Backdoor.PHP*"
|
||||
- "*Webshell*"
|
||||
- "*Portscan*"
|
||||
- "*Mimikatz*"
|
||||
- "*WinCred*"
|
||||
- "*PlugX*"
|
||||
- "*Korplug*"
|
||||
- "*Pwdump*"
|
||||
- "*Chopper*"
|
||||
- "*WmiExec*"
|
||||
- "*Xscan*"
|
||||
- "*Clearlog*"
|
||||
- "*ASPXSpy*"
|
||||
filters:
|
||||
- Keygen
|
||||
- Crack
|
||||
Message:
|
||||
- "*Keygen*"
|
||||
- "*Crack*"
|
||||
condition: keywords and not 1 of filters
|
||||
falsepositives:
|
||||
- Some software piracy tools (key generators, cracks) are classified as hack tools
|
||||
|
||||
@@ -15,9 +15,10 @@ detection:
|
||||
EventID:
|
||||
- 7045
|
||||
keywords:
|
||||
- 'WCE SERVICE'
|
||||
- 'WCESERVICE'
|
||||
- 'DumpSvc'
|
||||
Message:
|
||||
- '*WCE SERVICE*'
|
||||
- '*WCESERVICE*'
|
||||
- '*DumpSvc*'
|
||||
quarkspwdump:
|
||||
EventID: 16
|
||||
HiveName: '*\AppData\Local\Temp\SAM*.dmp'
|
||||
|
||||
@@ -25,4 +25,4 @@ detection:
|
||||
condition: selection1 or (selection2 and not selection3)
|
||||
falsepositives:
|
||||
- Migration of an account into a new domain
|
||||
level: medium
|
||||
level: low
|
||||
|
||||
@@ -21,8 +21,9 @@ detection:
|
||||
Source: 'Windows Error Reporting'
|
||||
EventID: 1001
|
||||
keywords:
|
||||
- 'MsMpEng.exe'
|
||||
- 'mpengine.dll'
|
||||
Message:
|
||||
- '*MsMpEng.exe*'
|
||||
- '*mpengine.dll*'
|
||||
condition: 1 of selection* and all of keywords
|
||||
falsepositives:
|
||||
- MsMpEng.exe can crash when C:\ is full
|
||||
|
||||
@@ -13,7 +13,8 @@ detection:
|
||||
selection:
|
||||
EventID: 16
|
||||
keywords:
|
||||
- '*\AppData\Local\Temp\SAM-*.dmp *'
|
||||
Message:
|
||||
- '*\AppData\Local\Temp\SAM-*.dmp *'
|
||||
condition: all of them
|
||||
falsepositives:
|
||||
- Penetration testing
|
||||
|
||||
@@ -16,14 +16,15 @@ detection:
|
||||
selection:
|
||||
EventID: 5861
|
||||
keywords:
|
||||
- 'ActiveScriptEventConsumer'
|
||||
- 'CommandLineEventConsumer'
|
||||
- 'CommandLineTemplate'
|
||||
Message:
|
||||
- '*ActiveScriptEventConsumer*'
|
||||
- '*CommandLineEventConsumer*'
|
||||
- '*CommandLineTemplate*'
|
||||
# - 'Binding EventFilter' # too many false positive with HP Health Driver
|
||||
selection2:
|
||||
EventID: 5859
|
||||
condition: selection and 1 of keywords or selection2
|
||||
falsepositives:
|
||||
- Unknown (data set is too small; further testing needed)
|
||||
level: high
|
||||
level: medium
|
||||
|
||||
|
||||
@@ -14,100 +14,101 @@ logsource:
|
||||
definition: 'It is recommended to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277'
|
||||
detection:
|
||||
keywords:
|
||||
- Invoke-DllInjection
|
||||
- Invoke-Shellcode
|
||||
- Invoke-WmiCommand
|
||||
- Get-GPPPassword
|
||||
- Get-Keystrokes
|
||||
- Get-TimedScreenshot
|
||||
- Get-VaultCredential
|
||||
- Invoke-CredentialInjection
|
||||
- Invoke-Mimikatz
|
||||
- Invoke-NinjaCopy
|
||||
- Invoke-TokenManipulation
|
||||
- Out-Minidump
|
||||
- VolumeShadowCopyTools
|
||||
- Invoke-ReflectivePEInjection
|
||||
- Invoke-UserHunter
|
||||
- Find-GPOLocation
|
||||
- Invoke-ACLScanner
|
||||
- Invoke-DowngradeAccount
|
||||
- Get-ServiceUnquoted
|
||||
- Get-ServiceFilePermission
|
||||
- Get-ServicePermission
|
||||
- Invoke-ServiceAbuse
|
||||
- Install-ServiceBinary
|
||||
- Get-RegAutoLogon
|
||||
- Get-VulnAutoRun
|
||||
- Get-VulnSchTask
|
||||
- Get-UnattendedInstallFile
|
||||
- Get-ApplicationHost
|
||||
- Get-RegAlwaysInstallElevated
|
||||
- Get-Unconstrained
|
||||
- Add-RegBackdoor
|
||||
- Add-ScrnSaveBackdoor
|
||||
- Gupt-Backdoor
|
||||
- Invoke-ADSBackdoor
|
||||
- Enabled-DuplicateToken
|
||||
- Invoke-PsUaCme
|
||||
- Remove-Update
|
||||
- Check-VM
|
||||
- Get-LSASecret
|
||||
- Get-PassHashes
|
||||
- Show-TargetScreen
|
||||
- Port-Scan
|
||||
- Invoke-PoshRatHttp
|
||||
- Invoke-PowerShellTCP
|
||||
- Invoke-PowerShellWMI
|
||||
- Add-Exfiltration
|
||||
- Add-Persistence
|
||||
- Do-Exfiltration
|
||||
- Start-CaptureServer
|
||||
- Get-ChromeDump
|
||||
- Get-ClipboardContents
|
||||
- Get-FoxDump
|
||||
- Get-IndexedItem
|
||||
- Get-Screenshot
|
||||
- Invoke-Inveigh
|
||||
- Invoke-NetRipper
|
||||
- Invoke-EgressCheck
|
||||
- Invoke-PostExfil
|
||||
- Invoke-PSInject
|
||||
- Invoke-RunAs
|
||||
- MailRaider
|
||||
- New-HoneyHash
|
||||
- Set-MacAttribute
|
||||
- Invoke-DCSync
|
||||
- Invoke-PowerDump
|
||||
- Exploit-Jboss
|
||||
- Invoke-ThunderStruck
|
||||
- Invoke-VoiceTroll
|
||||
- Set-Wallpaper
|
||||
- Invoke-InveighRelay
|
||||
- Invoke-PsExec
|
||||
- Invoke-SSHCommand
|
||||
- Get-SecurityPackages
|
||||
- Install-SSP
|
||||
- Invoke-BackdoorLNK
|
||||
- PowerBreach
|
||||
- Get-SiteListPassword
|
||||
- Get-System
|
||||
- Invoke-BypassUAC
|
||||
- Invoke-Tater
|
||||
- Invoke-WScriptBypassUAC
|
||||
- PowerUp
|
||||
- PowerView
|
||||
- Get-RickAstley
|
||||
- Find-Fruit
|
||||
- HTTP-Login
|
||||
- Find-TrustedDocuments
|
||||
- Invoke-Paranoia
|
||||
- Invoke-WinEnum
|
||||
- Invoke-ARPScan
|
||||
- Invoke-PortScan
|
||||
- Invoke-ReverseDNSLookup
|
||||
- Invoke-SMBScanner
|
||||
- Invoke-Mimikittenz
|
||||
Message:
|
||||
- "*Invoke-DllInjection*"
|
||||
- "*Invoke-Shellcode*"
|
||||
- "*Invoke-WmiCommand*"
|
||||
- "*Get-GPPPassword*"
|
||||
- "*Get-Keystrokes*"
|
||||
- "*Get-TimedScreenshot*"
|
||||
- "*Get-VaultCredential*"
|
||||
- "*Invoke-CredentialInjection*"
|
||||
- "*Invoke-Mimikatz*"
|
||||
- "*Invoke-NinjaCopy*"
|
||||
- "*Invoke-TokenManipulation*"
|
||||
- "*Out-Minidump*"
|
||||
- "*VolumeShadowCopyTools*"
|
||||
- "*Invoke-ReflectivePEInjection*"
|
||||
- "*Invoke-UserHunter*"
|
||||
- "*Find-GPOLocation*"
|
||||
- "*Invoke-ACLScanner*"
|
||||
- "*Invoke-DowngradeAccount*"
|
||||
- "*Get-ServiceUnquoted*"
|
||||
- "*Get-ServiceFilePermission*"
|
||||
- "*Get-ServicePermission*"
|
||||
- "*Invoke-ServiceAbuse*"
|
||||
- "*Install-ServiceBinary*"
|
||||
- "*Get-RegAutoLogon*"
|
||||
- "*Get-VulnAutoRun*"
|
||||
- "*Get-VulnSchTask*"
|
||||
- "*Get-UnattendedInstallFile*"
|
||||
- "*Get-ApplicationHost*"
|
||||
- "*Get-RegAlwaysInstallElevated*"
|
||||
- "*Get-Unconstrained*"
|
||||
- "*Add-RegBackdoor*"
|
||||
- "*Add-ScrnSaveBackdoor*"
|
||||
- "*Gupt-Backdoor*"
|
||||
- "*Invoke-ADSBackdoor*"
|
||||
- "*Enabled-DuplicateToken*"
|
||||
- "*Invoke-PsUaCme*"
|
||||
- "*Remove-Update*"
|
||||
- "*Check-VM*"
|
||||
- "*Get-LSASecret*"
|
||||
- "*Get-PassHashes*"
|
||||
- "*Show-TargetScreen*"
|
||||
- "*Port-Scan*"
|
||||
- "*Invoke-PoshRatHttp*"
|
||||
- "*Invoke-PowerShellTCP*"
|
||||
- "*Invoke-PowerShellWMI*"
|
||||
- "*Add-Exfiltration*"
|
||||
- "*Add-Persistence*"
|
||||
- "*Do-Exfiltration*"
|
||||
- "*Start-CaptureServer*"
|
||||
- "*Get-ChromeDump*"
|
||||
- "*Get-ClipboardContents*"
|
||||
- "*Get-FoxDump*"
|
||||
- "*Get-IndexedItem*"
|
||||
- "*Get-Screenshot*"
|
||||
- "*Invoke-Inveigh*"
|
||||
- "*Invoke-NetRipper*"
|
||||
- "*Invoke-EgressCheck*"
|
||||
- "*Invoke-PostExfil*"
|
||||
- "*Invoke-PSInject*"
|
||||
- "*Invoke-RunAs*"
|
||||
- "*MailRaider*"
|
||||
- "*New-HoneyHash*"
|
||||
- "*Set-MacAttribute*"
|
||||
- "*Invoke-DCSync*"
|
||||
- "*Invoke-PowerDump*"
|
||||
- "*Exploit-Jboss*"
|
||||
- "*Invoke-ThunderStruck*"
|
||||
- "*Invoke-VoiceTroll*"
|
||||
- "*Set-Wallpaper*"
|
||||
- "*Invoke-InveighRelay*"
|
||||
- "*Invoke-PsExec*"
|
||||
- "*Invoke-SSHCommand*"
|
||||
- "*Get-SecurityPackages*"
|
||||
- "*Install-SSP*"
|
||||
- "*Invoke-BackdoorLNK*"
|
||||
- "*PowerBreach*"
|
||||
- "*Get-SiteListPassword*"
|
||||
- "*Get-System*"
|
||||
- "*Invoke-BypassUAC*"
|
||||
- "*Invoke-Tater*"
|
||||
- "*Invoke-WScriptBypassUAC*"
|
||||
- "*PowerUp*"
|
||||
- "*PowerView*"
|
||||
- "*Get-RickAstley*"
|
||||
- "*Find-Fruit*"
|
||||
- "*HTTP-Login*"
|
||||
- "*Find-TrustedDocuments*"
|
||||
- "*Invoke-Paranoia*"
|
||||
- "*Invoke-WinEnum*"
|
||||
- "*Invoke-ARPScan*"
|
||||
- "*Invoke-PortScan*"
|
||||
- "*Invoke-ReverseDNSLookup*"
|
||||
- "*Invoke-SMBScanner*"
|
||||
- "*Invoke-Mimikittenz*"
|
||||
false_positives:
|
||||
- Get-SystemDriveInfo # http://bheltborg.dk/Windows/WinSxS/amd64_microsoft-windows-maintenancediagnostic_31bf3856ad364e35_10.0.10240.16384_none_91ef7543a4514b5e/CL_Utility.ps1
|
||||
condition: keywords and not false_positives
|
||||
|
||||
@@ -14,26 +14,27 @@ logsource:
|
||||
definition: 'It is recommended to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277'
|
||||
detection:
|
||||
keywords:
|
||||
- AdjustTokenPrivileges
|
||||
- IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||
- Microsoft.Win32.UnsafeNativeMethods
|
||||
- ReadProcessMemory.Invoke
|
||||
- SE_PRIVILEGE_ENABLED
|
||||
- LSA_UNICODE_STRING
|
||||
- MiniDumpWriteDump
|
||||
- PAGE_EXECUTE_READ
|
||||
- SECURITY_DELEGATION
|
||||
- TOKEN_ADJUST_PRIVILEGES
|
||||
- TOKEN_ALL_ACCESS
|
||||
- TOKEN_ASSIGN_PRIMARY
|
||||
- TOKEN_DUPLICATE
|
||||
- TOKEN_ELEVATION
|
||||
- TOKEN_IMPERSONATE
|
||||
- TOKEN_INFORMATION_CLASS
|
||||
- TOKEN_PRIVILEGES
|
||||
- TOKEN_QUERY
|
||||
- Metasploit
|
||||
- Mimikatz
|
||||
Message:
|
||||
- "*AdjustTokenPrivileges*"
|
||||
- "*IMAGE_NT_OPTIONAL_HDR64_MAGIC*"
|
||||
- "*Microsoft.Win32.UnsafeNativeMethods*"
|
||||
- "*ReadProcessMemory.Invoke*"
|
||||
- "*SE_PRIVILEGE_ENABLED*"
|
||||
- "*LSA_UNICODE_STRING*"
|
||||
- "*MiniDumpWriteDump*"
|
||||
- "*PAGE_EXECUTE_READ*"
|
||||
- "*SECURITY_DELEGATION*"
|
||||
- "*TOKEN_ADJUST_PRIVILEGES*"
|
||||
- "*TOKEN_ALL_ACCESS*"
|
||||
- "*TOKEN_ASSIGN_PRIMARY*"
|
||||
- "*TOKEN_DUPLICATE*"
|
||||
- "*TOKEN_ELEVATION*"
|
||||
- "*TOKEN_IMPERSONATE*"
|
||||
- "*TOKEN_INFORMATION_CLASS*"
|
||||
- "*TOKEN_PRIVILEGES*"
|
||||
- "*TOKEN_QUERY*"
|
||||
- "*Metasploit*"
|
||||
- "*Mimikatz*"
|
||||
condition: keywords
|
||||
falsepositives:
|
||||
- Penetration tests
|
||||
|
||||
@@ -17,7 +17,8 @@ detection:
|
||||
selection:
|
||||
EventID: 4104
|
||||
keyword:
|
||||
- 'PromptForCredential'
|
||||
Message:
|
||||
- '*PromptForCredential*'
|
||||
condition: all of them
|
||||
falsepositives:
|
||||
- Unknown
|
||||
|
||||
@@ -10,8 +10,9 @@ logsource:
|
||||
service: powershell
|
||||
detection:
|
||||
keywords:
|
||||
- 'System.Net.WebClient).DownloadString('
|
||||
- 'system.net.webclient).downloadfile('
|
||||
Message:
|
||||
- '*System.Net.WebClient).DownloadString(*'
|
||||
- '*system.net.webclient).downloadfile(*'
|
||||
condition: keywords
|
||||
falsepositives:
|
||||
- PowerShell scripts that download content from the Internet
|
||||
|
||||
@@ -10,12 +10,13 @@ logsource:
|
||||
service: powershell
|
||||
detection:
|
||||
keywords:
|
||||
- ' -nop -w hidden -c * [Convert]::FromBase64String'
|
||||
- ' -w hidden -noni -nop -c "iex(New-Object'
|
||||
- ' -w hidden -ep bypass -Enc'
|
||||
- 'powershell.exe reg add HKCU\software\microsoft\windows\currentversion\run'
|
||||
- 'bypass -noprofile -windowstyle hidden (new-object system.net.webclient).download'
|
||||
- 'iex(New-Object Net.WebClient).Download'
|
||||
Message:
|
||||
- '* -nop -w hidden -c * [Convert]::FromBase64String*'
|
||||
- '* -w hidden -noni -nop -c "iex(New-Object*'
|
||||
- '* -w hidden -ep bypass -Enc*'
|
||||
- '*powershell.exe reg add HKCU\software\microsoft\windows\currentversion\run*'
|
||||
- '*bypass -noprofile -windowstyle hidden (new-object system.net.webclient).download*'
|
||||
- '*iex(New-Object Net.WebClient).Download*'
|
||||
condition: keywords
|
||||
falsepositives:
|
||||
- Penetration tests
|
||||
|
||||
@@ -14,7 +14,8 @@ logsource:
|
||||
definition: 'It is recommended to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277'
|
||||
detection:
|
||||
keywords:
|
||||
- System.Reflection.Assembly.Load
|
||||
Message:
|
||||
- "*[System.Reflection.Assembly]::Load*"
|
||||
condition: keywords
|
||||
falsepositives:
|
||||
- Penetration tests
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
title: Mustang Panda Dropper
|
||||
status: experimental
|
||||
description: Detects specific process parameters as used by Mustang Panda droppers
|
||||
author: Florian Roth
|
||||
date: 2019/10/30
|
||||
references:
|
||||
- https://app.any.run/tasks/7ca5661d-a67b-43ec-98c1-dd7a8103c256/
|
||||
- https://app.any.run/tasks/b12cccf3-1c22-4e28-9d3e-c7a6062f3914/
|
||||
- https://www.anomali.com/blog/china-based-apt-mustang-panda-targets-minority-groups-public-and-private-sector-organizations
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection1:
|
||||
CommandLine:
|
||||
- '*Temp\wtask.exe /create*'
|
||||
- '*%windir:~-3,1%%PUBLIC:~-9,1%*'
|
||||
- '*/E:vbscript * C:\Users\*.txt" /F'
|
||||
- '*/tn "Security Script *'
|
||||
- '*%windir:~-1,1%*'
|
||||
selection2:
|
||||
Image:
|
||||
- '*Temp\winwsh.exe'
|
||||
condition: 1 of them
|
||||
fields:
|
||||
- CommandLine
|
||||
- ParentCommandLine
|
||||
falsepositives:
|
||||
- Unlikely
|
||||
level: high
|
||||
@@ -0,0 +1,30 @@
|
||||
title: Suspicious HWP Sub Processes
|
||||
description: Detects suspicious Hangul Word Processor (Hanword) sub processes that could indicate an exploitation
|
||||
status: experimental
|
||||
references:
|
||||
- https://www.securitynewspaper.com/2016/11/23/technical-teardown-exploit-malware-hwp-files/
|
||||
- https://www.hybrid-analysis.com/search?query=context:74940dcc5b38f9f9b1a0fea760d344735d7d91b610e6d5bd34533dd0153402c5&from_sample=5db135000388385a7644131f&block_redirect=1
|
||||
- https://twitter.com/cyberwar_15/status/1187287262054076416
|
||||
- https://blog.alyac.co.kr/1901
|
||||
- https://en.wikipedia.org/wiki/Hangul_(word_processor)
|
||||
tags:
|
||||
- attack.execution
|
||||
- attack.defense_evasion
|
||||
- attack.initial_access
|
||||
- attack.t1059
|
||||
- attack.t1202
|
||||
- attack.t1193
|
||||
- attack.g0032
|
||||
author: Florian Roth
|
||||
date: 2019/10/24
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
ParentImage: '*\Hwp.exe'
|
||||
Image: '*\gbb.exe'
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- Unknown
|
||||
level: high
|
||||
@@ -0,0 +1,22 @@
|
||||
title: DTRACK Process Creation
|
||||
status: experimental
|
||||
description: Detects specific process parameters as seen in DTRACK infections
|
||||
author: Florian Roth
|
||||
date: 2019/10/30
|
||||
references:
|
||||
- https://securelist.com/my-name-is-dtrack/93338/
|
||||
- https://app.any.run/tasks/4bc9860d-ab51-4077-9e09-59ad346b92fd/
|
||||
- https://app.any.run/tasks/ce4deab5-3263-494f-93e3-afb2b9d79f14/
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
CommandLine: '* echo EEEE > *'
|
||||
condition: selection
|
||||
fields:
|
||||
- CommandLine
|
||||
- ParentCommandLine
|
||||
falsepositives:
|
||||
- Unlikely
|
||||
level: critical
|
||||
@@ -3,10 +3,12 @@ status: experimental
|
||||
description: Detects Formbook like process executions that inject code into a set of files in the System32 folder, which executes a special command command line to delete the dropper from the AppData Temp folder. We avoid false positives by excluding all parent process with command line parameters.
|
||||
author: Florian Roth
|
||||
date: 2019/09/30
|
||||
modified: 2019/10/31
|
||||
references:
|
||||
- https://inquest.net/blog/2018/06/22/a-look-at-formbook-stealer
|
||||
- https://app.any.run/tasks/388d5802-aa48-4826-b069-250420504758/
|
||||
- https://app.any.run/tasks/8e22486b-5edc-4cef-821c-373e945f296c/
|
||||
- https://app.any.run/tasks/62bb01ae-25a4-4180-b278-8e464a90b8d7/
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
@@ -15,10 +17,13 @@ detection:
|
||||
# Parent command line should not contain a space value
|
||||
# This avoids false positives not caused by process injection
|
||||
# e.g. wscript.exe /B sysmon-install.vbs
|
||||
ParentCommandLine: 'C:\Windows\System32\\*.exe'
|
||||
ParentCommandLine:
|
||||
- 'C:\Windows\System32\\*.exe'
|
||||
- 'C:\Windows\SysWOW64\\*.exe'
|
||||
CommandLine:
|
||||
- '*\cmd.exe /c del "C:\Users\\*\AppData\Local\Temp\\*.exe'
|
||||
- '*\cmd.exe /c del "C:\Users\\*\Desktop\\*.exe'
|
||||
- '* /c del "C:\Users\\*\AppData\Local\Temp\\*.exe'
|
||||
- '* /c del "C:\Users\\*\Desktop\\*.exe'
|
||||
- '* /C type nul > "C:\Users\\*\Desktop\\*.exe'
|
||||
condition: selection
|
||||
fields:
|
||||
- CommandLine
|
||||
|
||||
@@ -36,7 +36,7 @@ detection:
|
||||
- '*\schtasks.exe'
|
||||
- '*\regsvr32.exe'
|
||||
- '*\hh.exe'
|
||||
- '*\wmic.exe'
|
||||
- '*\wmic.exe' # https://app.any.run/tasks/c903e9c8-0350-440c-8688-3881b556b8e0/
|
||||
- '*\mshta.exe'
|
||||
- '*\rundll32.exe'
|
||||
- '*\msiexec.exe'
|
||||
|
||||
@@ -21,7 +21,7 @@ detection:
|
||||
- '*\installutil.exe*'
|
||||
- '*\regsvcs.exe*'
|
||||
- '*\regasm.exe*'
|
||||
- '*\regsvr32.exe*'
|
||||
# - '*\regsvr32.exe*' # too many FPs, very noisy
|
||||
- '*\msbuild.exe*'
|
||||
- '*\ieexec.exe*'
|
||||
- '*\mshta.exe*'
|
||||
|
||||
@@ -8,6 +8,7 @@ references:
|
||||
- https://www.13cubed.com/downloads/windows_process_genealogy_v2.pdf
|
||||
- https://attack.mitre.org/techniques/T1036/
|
||||
date: 2019/02/23
|
||||
modified: 2019/08/20
|
||||
tags:
|
||||
- attack.defense_evasion
|
||||
- attack.t1036
|
||||
|
||||
@@ -18,7 +18,7 @@ detection:
|
||||
ParentImage:
|
||||
- '*\mshta.exe'
|
||||
- '*\powershell.exe'
|
||||
- '*\cmd.exe'
|
||||
# - '*\cmd.exe' # too many false positives
|
||||
- '*\rundll32.exe'
|
||||
- '*\cscript.exe'
|
||||
- '*\wscript.exe'
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
title: Firewall Disabled via Netsh
|
||||
description: Detects netsh commands that turns off the Windows firewall
|
||||
references:
|
||||
- https://www.winhelponline.com/blog/enable-and-disable-windows-firewall-quickly-using-command-line/
|
||||
- https://app.any.run/tasks/210244b9-0b6b-4a2c-83a3-04bd3175d017/
|
||||
date: 2019/11/01
|
||||
status: experimental
|
||||
author: Fatih Sirin
|
||||
tags:
|
||||
- attack.defense_evasion
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
CommandLine:
|
||||
- netsh firewall set opmode mode=disable
|
||||
- netsh advfirewall set * state off
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- Legitimate administration
|
||||
level: medium
|
||||
@@ -14,7 +14,7 @@ logsource:
|
||||
detection:
|
||||
selection:
|
||||
CommandLine:
|
||||
- '* msiexec*:\/\/*'
|
||||
- '* msiexec*://*'
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- False positives depend on scripts and administrative tools used in the monitored environment
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
title: Suspicious Call by Ordinal
|
||||
description: Detects suspicious calls of DLLs in rundll32.dll exports by ordinal
|
||||
status: experimental
|
||||
references:
|
||||
- https://techtalk.pcmatic.com/2017/11/30/running-dll-files-malware-analysis/
|
||||
- https://github.com/Neo23x0/DLLRunner
|
||||
- https://twitter.com/cyb3rops/status/1186631731543236608
|
||||
tags:
|
||||
- attack.defense_evasion
|
||||
- attack.execution
|
||||
- attack.t1085
|
||||
author: Florian Roth
|
||||
date: 2019/10/22
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
CommandLine: '*\rundll32.exe *,#*'
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- False positives depend on scripts and administrative tools used in the monitored environment
|
||||
- Windows contol panel elements have been identified as source (mmc)
|
||||
level: high
|
||||
@@ -3,6 +3,7 @@ status: experimental
|
||||
description: Detects Possible Squirrel Packages Manager as Lolbin
|
||||
references:
|
||||
- http://www.hexacorn.com/blog/2019/03/30/sqirrel-packages-manager-as-a-lolbin-a-k-a-many-electron-apps-are-lolbins-by-default/
|
||||
- http://www.hexacorn.com/blog/2018/08/16/squirrel-as-a-lolbin/
|
||||
tags:
|
||||
- attack.execution
|
||||
author: Karneades / Markus Neis
|
||||
@@ -49,7 +50,8 @@ detection:
|
||||
- '*\update.exe' # Check if folder Name matches executed binary \\(?P<first>[^\\]*)\\Update.*Start.{2}(?P<second>\1)\.exe (example: https://regex101.com/r/SGSQGz/2)
|
||||
CommandLine:
|
||||
- '*--processStart*.exe*'
|
||||
- '*--processStartAndWait*.exe*'
|
||||
- '*–createShortcut*.exe*'
|
||||
condition: selection
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
title: Suspicious Userinit Child Process
|
||||
status: experimental
|
||||
description: Detects the creation of a process from Windows task manager
|
||||
description: Detects a suspicious child process of userinit
|
||||
references:
|
||||
- https://twitter.com/SBousseaden/status/1139811587760562176
|
||||
author: Florian Roth (rule), Samir Bousseaden (idea)
|
||||
@@ -11,14 +11,14 @@ logsource:
|
||||
detection:
|
||||
selection:
|
||||
ParentImage: '*\userinit.exe'
|
||||
filter:
|
||||
CommandLine:
|
||||
- '*\explorer.exe*'
|
||||
- '*\\netlogon\\*'
|
||||
condition: selection and not filter
|
||||
filter1:
|
||||
CommandLine: '*\\netlogon\\*'
|
||||
filter2:
|
||||
Image: '*\explorer.exe'
|
||||
condition: selection and not filter1 and not filter2
|
||||
fields:
|
||||
- CommandLine
|
||||
- ParentCommandLine
|
||||
falsepositives:
|
||||
- Administrative scripts
|
||||
level: high
|
||||
level: medium
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
title: Webshell Detection With Command Line Keywords
|
||||
description: Detects certain command line parameters often used during reconnaissance activity via web shells
|
||||
author: Florian Roth
|
||||
reference:
|
||||
- https://www.fireeye.com/blog/threat-research/2013/08/breaking-down-the-china-chopper-web-shell-part-ii.html
|
||||
date: 2017/01/01
|
||||
modified: 2019/10/26
|
||||
tags:
|
||||
- attack.privilege_escalation
|
||||
- attack.persistence
|
||||
- attack.t1100
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
@@ -14,18 +22,16 @@ detection:
|
||||
- '*\nginx.exe'
|
||||
- '*\httpd.exe'
|
||||
CommandLine:
|
||||
- whoami
|
||||
- net user
|
||||
- ping -n
|
||||
- systeminfo
|
||||
- '*whoami*'
|
||||
- '*net user *'
|
||||
- '*ping -n *'
|
||||
- '*systeminfo'
|
||||
- '*&cd&echo*'
|
||||
- '*cd /d*' # https://www.computerhope.com/cdhlp.htm
|
||||
condition: selection
|
||||
fields:
|
||||
- CommandLine
|
||||
- ParentCommandLine
|
||||
tags:
|
||||
- attack.privilege_escalation
|
||||
- attack.persistence
|
||||
- attack.t1100
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: high
|
||||
|
||||
@@ -15,20 +15,27 @@ detection:
|
||||
exec_selection:
|
||||
EventID: 1 # Migration to process_creation requires multipart YAML
|
||||
ParentImage: '*\userinit.exe'
|
||||
exec_exclusion:
|
||||
exec_exclusion1:
|
||||
Image: '*\explorer.exe'
|
||||
exec_exclusion2:
|
||||
CommandLine: '*\netlogon.bat'
|
||||
create_selection:
|
||||
create_selection_cli:
|
||||
EventID:
|
||||
- 1
|
||||
create_selection_reg:
|
||||
EventID:
|
||||
- 11
|
||||
- 12
|
||||
- 13
|
||||
- 14
|
||||
create_keywords:
|
||||
- UserInitMprLogonScript
|
||||
condition: (exec_selection and not exec_exclusion) or (create_selection and create_keywords)
|
||||
create_keywords_reg:
|
||||
TargetObject:
|
||||
- '*UserInitMprLogonScript*'
|
||||
create_keywords_cli:
|
||||
CommandLine:
|
||||
- '*UserInitMprLogonScript*'
|
||||
condition: (exec_selection and not exec_exclusion1 and not exec_exclusion2) or (create_selection_reg and create_keywords_reg) or (create_selection_cli and create_keywords_cli)
|
||||
falsepositives:
|
||||
- exclude legitimate logon scripts
|
||||
- penetration tests, red teaming
|
||||
level: high
|
||||
level: high
|
||||
|
||||
@@ -6,6 +6,7 @@ references:
|
||||
- https://www.virustotal.com/#/file/276a765a10f98cda1a38d3a31e7483585ca3722ecad19d784441293acf1b7beb/detection
|
||||
author: Markus Neis
|
||||
date: 2018/11/22
|
||||
modified: 2019/11/09
|
||||
tags:
|
||||
- attack.defense_evasion
|
||||
- attack.execution
|
||||
@@ -29,4 +30,4 @@ fields:
|
||||
- ParentCommandLine
|
||||
falsepositives:
|
||||
- Unknown
|
||||
level: high
|
||||
level: medium
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
title: Svchost DLL Search Order Hijack
|
||||
status: experimental
|
||||
description: IKEEXT and SessionEnv service, as they call LoadLibrary on files that do not exist within C:\Windows\System32\ by default. An attacker can place their malicious logic within the PROCESS_ATTACH block of their library and restart the aforementioned services "svchost.exe -k netsvcs" to gain code execution on a remote machine.
|
||||
references:
|
||||
- https://posts.specterops.io/lateral-movement-scm-and-dll-hijacking-primer-d2f61e8ab992
|
||||
author: SBousseaden
|
||||
date: 2019/10/28
|
||||
tags:
|
||||
- attack.persistence
|
||||
- attack.defense_evasion
|
||||
- attack.t1073
|
||||
- attack.t1038
|
||||
- attack.t1112
|
||||
logsource:
|
||||
product: windows
|
||||
service: sysmon
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7
|
||||
Image:
|
||||
- '*\svchost.exe'
|
||||
ImageLoaded:
|
||||
- '*\tsmsisrv.dll'
|
||||
- '*\tsvipsrv.dll'
|
||||
- '*\wlbsctrl.dll'
|
||||
filter:
|
||||
EventID: 7
|
||||
Image:
|
||||
- '*\svchost.exe'
|
||||
ImageLoaded:
|
||||
- 'C:\Windows\WinSxS\*'
|
||||
condition: selection and not filter
|
||||
falsepositives:
|
||||
- Pentest
|
||||
level: high
|
||||
@@ -13,4 +13,6 @@ detection:
|
||||
- foo
|
||||
- bar
|
||||
- bla
|
||||
end|endswith: test
|
||||
start|startswith: test
|
||||
condition: selection
|
||||
|
||||
@@ -12,4 +12,3 @@ logsources:
|
||||
fieldmappings:
|
||||
Image: NewProcessName
|
||||
ParentImage: ParentProcessName
|
||||
CommandLine: ProcessCommandLine
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
title: LimaCharlie
|
||||
backends:
|
||||
- limacharlie
|
||||
order: 20
|
||||
logsources:
|
||||
windows:
|
||||
product: windows
|
||||
linux:
|
||||
product: linux
|
||||
netflow:
|
||||
product: netflow
|
||||
+4
-2
@@ -13,7 +13,7 @@ with open(path.join(here, 'README.md'), encoding='utf-8') as f:
|
||||
|
||||
setup(
|
||||
name='sigmatools',
|
||||
version='0.13',
|
||||
version='0.14',
|
||||
description='Tools for the Generic Signature Format for SIEM Systems',
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
@@ -36,7 +36,7 @@ setup(
|
||||
keywords='security monitoring siem logging signatures elasticsearch splunk ids sysmon',
|
||||
packages=['sigma', 'sigma.backends', 'sigma.config', 'sigma.parser', 'sigma.parser.modifiers'],
|
||||
python_requires='~=3.6',
|
||||
install_requires=['PyYAML', 'pymisp'],
|
||||
install_requires=['PyYAML', 'pymisp', 'progressbar2'],
|
||||
extras_require={
|
||||
'test': ['coverage', 'yamllint'],
|
||||
},
|
||||
@@ -60,6 +60,7 @@ setup(
|
||||
'config/winlogbeat-modules-enabled.yml',
|
||||
'config/winlogbeat.yml',
|
||||
'config/winlogbeat-old.yml',
|
||||
'config/limacharlie.yml',
|
||||
]),
|
||||
('etc/sigma/generic', [
|
||||
'config/generic/sysmon.yml',
|
||||
@@ -69,5 +70,6 @@ setup(
|
||||
'sigmac',
|
||||
'merge_sigma',
|
||||
'sigma2misp',
|
||||
'sigma-similarity',
|
||||
]
|
||||
)
|
||||
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
# Calculates similarity of Sigma rules by transformation into a normalized
|
||||
# string form and calculation of a string distance.
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import itertools
|
||||
import difflib
|
||||
|
||||
import progressbar
|
||||
|
||||
from sigma.parser.collection import SigmaCollectionParser
|
||||
from sigma.backends.base import SingleTextQueryBackend
|
||||
from sigma.configuration import SigmaConfiguration
|
||||
|
||||
argparser = argparse.ArgumentParser(description="Calculate a similarity score between Sigma rules.")
|
||||
argparser.add_argument("--recursive", "-r", action="store_true", help="Recurse into directories")
|
||||
argparser.add_argument("--verbose", "-v", action="count", help="Be verbose. Use once more for debug output.")
|
||||
argparser.add_argument("--top", "-t", type=int, help="Only output the n most similar rule pairs.")
|
||||
argparser.add_argument("--min-similarity", "-m", type=int, help="Only output pairs with a similarity above this threshold (percent)")
|
||||
argparser.add_argument("inputs", nargs="+", help="Sigma input files")
|
||||
args = argparser.parse_args()
|
||||
|
||||
def print_verbose(level, *args, **kwargs):
|
||||
if args.verbose >= level:
|
||||
print(*args, **kwargs)
|
||||
|
||||
class SigmaNormalizationBackend(SingleTextQueryBackend):
|
||||
"""Normalization of a Sigma rule into a non-existing query language that supports all Sigma features"""
|
||||
andToken = " AND "
|
||||
orToken = " OR "
|
||||
notToken = " NOT "
|
||||
subExpression = "(%s)"
|
||||
listExpression = "[%s]"
|
||||
listSeparator = ","
|
||||
valueExpression = "%s"
|
||||
typedValueExpression = dict()
|
||||
nullExpression = "NULL(%s)"
|
||||
notNullExpression = "NOTNULL(%s)"
|
||||
mapExpression = "{'%s':'%s'}"
|
||||
|
||||
sort_condition_lists = True
|
||||
|
||||
def generateListNode(self, node):
|
||||
"""Return sorted list"""
|
||||
return super().generateListNode(list(sorted([ str(item) for item in node ])))
|
||||
|
||||
def generateTypedValueNode(self, node):
|
||||
"""Return normalized form of typed values"""
|
||||
return "type_{}({})".format(node.identifier, str(node))
|
||||
|
||||
def generateAggregation(self, agg):
|
||||
if agg.aggfunc_notrans == "near":
|
||||
return " near in={} ex={}".format(str(agg.include), str(agg.exclude))
|
||||
else:
|
||||
return " | {}({}) by {} {} {}".format(agg.aggfunc_notrans, agg.aggfield, agg.groupfield, agg.cond_op, agg.condition)
|
||||
|
||||
backend = SigmaNormalizationBackend(SigmaConfiguration())
|
||||
|
||||
if args.recursive:
|
||||
paths = [ p for pathname in args.inputs for p in pathlib.Path(pathname).glob("**/*") if p.is_file() ]
|
||||
else:
|
||||
paths = [ pathlib.Path(pathname) for pathname in args.inputs ]
|
||||
|
||||
parsed = {
|
||||
str(path): SigmaCollectionParser(path.open().read())
|
||||
for path in paths
|
||||
}
|
||||
converted = {
|
||||
str(path): list(sigma_collection.generate(backend))
|
||||
for path, sigma_collection in parsed.items()
|
||||
}
|
||||
converted_flat = (
|
||||
(path, i, normalized)
|
||||
for path, nlist in converted.items()
|
||||
for i, normalized in zip(range(len(nlist)), nlist)
|
||||
)
|
||||
converted_pairs = list(itertools.combinations(converted_flat, 2))
|
||||
similarities = [
|
||||
(item1[:2], item2[:2], difflib.SequenceMatcher(None, item1[2], item2[2]).ratio())
|
||||
for item1, item2 in progressbar.progressbar(converted_pairs)
|
||||
]
|
||||
|
||||
i = 0
|
||||
for similarity in sorted(similarities, key=lambda s: s[2], reverse=True):
|
||||
if args.min_similarity and similarity[2] * 100 < args.min_similarity: # finish after similarity drops below minimum
|
||||
break
|
||||
print("{:70} | {:2} | {:70} | {:2} | {:>3.2%}".format(*similarity[0], *similarity[1], similarity[2]))
|
||||
i += 1
|
||||
if args.top and i >= args.top: # end after $top pairs
|
||||
break
|
||||
@@ -89,8 +89,9 @@ class BaseBackend:
|
||||
file_list = None
|
||||
options = tuple() # a list of tuples with following elements: option name, default value, help text, target attribute name (option name if None)
|
||||
config_required = True
|
||||
default_config = None
|
||||
|
||||
def __init__(self, sigmaconfig, backend_options=None):
|
||||
def __init__(self, sigmaconfig, backend_options=dict()):
|
||||
"""
|
||||
Initialize backend. This gets a sigmaconfig object, which is notified about the used backend class by
|
||||
passing the object instance to it.
|
||||
@@ -221,10 +222,14 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin):
|
||||
mapListsSpecialHandling = False # Same handling for map items with list values as for normal values (strings, integers) if True, generateMapItemListNode method is called with node
|
||||
mapListValueExpression = None # Syntax for field/value condititons where map value is a list
|
||||
|
||||
sort_condition_lists = False # Sort condition items for AND and OR conditions
|
||||
|
||||
def generateANDNode(self, node):
|
||||
generated = [ self.generateNode(val) for val in node ]
|
||||
filtered = [ g for g in generated if g is not None ]
|
||||
if filtered:
|
||||
if self.sort_condition_lists:
|
||||
filtered = sorted(filtered)
|
||||
return self.andToken.join(filtered)
|
||||
else:
|
||||
return None
|
||||
@@ -233,6 +238,8 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin):
|
||||
generated = [ self.generateNode(val) for val in node ]
|
||||
filtered = [ g for g in generated if g is not None ]
|
||||
if filtered:
|
||||
if self.sort_condition_lists:
|
||||
filtered = sorted(filtered)
|
||||
return self.orToken.join(filtered)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -22,6 +22,7 @@ import sys
|
||||
import sigma
|
||||
import yaml
|
||||
from sigma.parser.modifiers.type import SigmaRegularExpressionModifier
|
||||
from sigma.parser.condition import ConditionOR, ConditionAND, NodeSubexpression
|
||||
from .base import BaseBackend, SingleTextQueryBackend
|
||||
from .mixins import RulenameCommentMixin, MultiRuleOutputMixin
|
||||
from .exceptions import NotSupportedError
|
||||
@@ -109,6 +110,29 @@ class ElasticsearchQuerystringBackend(ElasticsearchWildcardHandlingMixin, Single
|
||||
if expression:
|
||||
return "(%s%s)" % (self.notToken, expression)
|
||||
|
||||
def generateSubexpressionNode(self, node):
|
||||
"""Check for search not bound to a field and restrict search to keyword fields"""
|
||||
nodetype = type(node.items)
|
||||
if nodetype in { ConditionAND, ConditionOR } and type(node.items.items) == list and { type(item) for item in node.items.items }.issubset({str, int}):
|
||||
newitems = list()
|
||||
for item in node.items:
|
||||
newitem = item
|
||||
if type(item) == str:
|
||||
if not item.startswith("*"):
|
||||
newitem = "*" + newitem
|
||||
if not item.endswith("*"):
|
||||
newitem += "*"
|
||||
newitems.append(newitem)
|
||||
else:
|
||||
newitems.append(item)
|
||||
newnode = NodeSubexpression(nodetype(None, None, *newitems))
|
||||
self.matchKeyword = True
|
||||
result = "\\*.keyword:" + super().generateSubexpressionNode(newnode)
|
||||
self.matchKeyword = False # one of the reasons why the converter needs some major overhaul
|
||||
return result
|
||||
else:
|
||||
return super().generateSubexpressionNode(node)
|
||||
|
||||
class ElasticsearchDSLBackend(RulenameCommentMixin, ElasticsearchWildcardHandlingMixin, BaseBackend):
|
||||
"""ElasticSearch DSL backend"""
|
||||
identifier = 'es-dsl'
|
||||
@@ -188,8 +212,6 @@ class ElasticsearchDSLBackend(RulenameCommentMixin, ElasticsearchWildcardHandlin
|
||||
|
||||
def generateMapItemNode(self, node):
|
||||
key, value = node
|
||||
if type(value) not in (str, int, list, type(None)):
|
||||
raise TypeError("Map values must be strings, numbers, lists or null, not " + str(type(value)))
|
||||
if type(value) is list:
|
||||
res = {'bool': {'should': []}}
|
||||
for v in value:
|
||||
@@ -206,7 +228,7 @@ class ElasticsearchDSLBackend(RulenameCommentMixin, ElasticsearchWildcardHandlin
|
||||
elif value is None:
|
||||
key_mapped = self.fieldNameMapping(key, value)
|
||||
return { "bool": { "must_not": { "exists": { "field": key_mapped } } } }
|
||||
else:
|
||||
elif type(value) in (str, int):
|
||||
key_mapped = self.fieldNameMapping(key, value)
|
||||
if self.matchKeyword: # searches against keyowrd fields are wildcard searches, phrases otherwise
|
||||
queryType = 'wildcard'
|
||||
@@ -215,6 +237,11 @@ class ElasticsearchDSLBackend(RulenameCommentMixin, ElasticsearchWildcardHandlin
|
||||
queryType = 'match_phrase'
|
||||
value_cleaned = self.cleanValue(str(value))
|
||||
return {queryType: {key_mapped: value_cleaned}}
|
||||
elif isinstance(value, SigmaRegularExpressionModifier):
|
||||
key_mapped = self.fieldNameMapping(key, value)
|
||||
return { 'regexp': { key_mapped: str(value) } }
|
||||
else:
|
||||
raise TypeError("Map values must be strings, numbers, lists, null or regular expression, not " + str(type(value)))
|
||||
|
||||
def generateValueNode(self, node):
|
||||
return {'multi_match': {'query': node, 'fields': [], 'type': 'phrase'}}
|
||||
@@ -888,7 +915,7 @@ class ElastalertBackend(MultiRuleOutputMixin):
|
||||
def finalize(self):
|
||||
result = ""
|
||||
for rulename, rule in self.elastalert_alerts.items():
|
||||
result += yaml.dump(rule, default_flow_style=False)
|
||||
result += yaml.dump(rule, default_flow_style=False, width=10000)
|
||||
result += '\n'
|
||||
return result
|
||||
|
||||
|
||||
@@ -15,24 +15,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
from .base import SingleTextQueryBackend
|
||||
from .elasticsearch import ElasticsearchQuerystringBackend
|
||||
|
||||
class GraylogQuerystringBackend(SingleTextQueryBackend):
|
||||
class GraylogQuerystringBackend(ElasticsearchQuerystringBackend):
|
||||
"""Converts Sigma rule into Graylog query string. Only searches, no aggregations."""
|
||||
identifier = "graylog"
|
||||
active = True
|
||||
config_required = False
|
||||
|
||||
reEscape = re.compile("([+\\-!(){}\\[\\]^\"~:/]|(?<!\\\\)\\\\(?![*?\\\\])|&&|\\|\\|)")
|
||||
reClear = None
|
||||
andToken = " AND "
|
||||
orToken = " OR "
|
||||
notToken = "NOT "
|
||||
subExpression = "(%s)"
|
||||
listExpression = "(%s)"
|
||||
listSeparator = " "
|
||||
valueExpression = "\"%s\""
|
||||
nullExpression = "NOT _exists_:%s"
|
||||
notNullExpression = "_exists_:%s"
|
||||
mapExpression = "%s:%s"
|
||||
mapListsSpecialHandling = False
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
# LimaCharlie backend for sigmac created by LimaCharlie.io
|
||||
# Copyright 2019 Refraction Point, Inc
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import yaml
|
||||
from collections import namedtuple
|
||||
from .base import BaseBackend
|
||||
from sigma.parser.modifiers.base import SigmaTypeModifier
|
||||
from sigma.parser.modifiers.type import SigmaRegularExpressionModifier
|
||||
|
||||
# A few helper functions for cases where field mapping cannot be done
|
||||
# as easily one by one, or can be done more efficiently.
|
||||
def _windowsEventLogFieldName(fieldName):
|
||||
if 'EventID' == fieldName:
|
||||
return 'Event/System/EventID'
|
||||
return 'Event/EventData/%s' % (fieldName,)
|
||||
|
||||
def _mapProcessCreationOperations(node):
|
||||
# Here we fix some common pitfalls found in rules
|
||||
# in a consistent fashion (already processed to D&R rule).
|
||||
|
||||
# First fixup is looking for a specific path prefix
|
||||
# based on a specific drive letter. There are many cases
|
||||
# where the driver letter can change or where the early
|
||||
# boot process refers to it as "\Device\HarddiskVolume1\".
|
||||
if ("starts with" == node["op"] and
|
||||
"event/FILE_PATH" == node["path"] and
|
||||
node["value"].lower().startswith("c:\\")):
|
||||
node["op"] = "matches"
|
||||
node["re"] = "^(?:(?:.:)|(?:\\\\Device\\\\HarddiskVolume.))\\\\%s" % (re.escape(node["value"][3:]),)
|
||||
del(node["value"])
|
||||
|
||||
return node
|
||||
|
||||
# We support many different log sources so we keep different mapping depending
|
||||
# on the log source and category.
|
||||
# The mapping key is product/category/service.
|
||||
# The mapping value is tuple like:
|
||||
# - top-level parameters
|
||||
# - pre-condition is a D&R rule node filtering relevant events.
|
||||
# - field mappings is a dict with a mapping or a callable to convert the field name.
|
||||
# Individual mapping values can also be callabled(fieldname, value) returning a new fieldname and value.
|
||||
# - isAllStringValues is a bool indicating whether all values should be converted to string.
|
||||
# - keywordField is the field name to alias for keywords if supported or None if not.
|
||||
# - postOpMapper is a callback that can modify an operation once it has been generated.
|
||||
SigmaLCConfig = namedtuple('SigmaLCConfig', [
|
||||
'topLevelParams',
|
||||
'preConditions',
|
||||
'fieldMappings',
|
||||
'isAllStringValues',
|
||||
'keywordField',
|
||||
'postOpMapper',
|
||||
])
|
||||
_allFieldMappings = {
|
||||
"windows/process_creation/": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"events": [
|
||||
"NEW_PROCESS",
|
||||
"EXISTING_PROCESS",
|
||||
]
|
||||
},
|
||||
preConditions = {
|
||||
"op": "is windows",
|
||||
},
|
||||
fieldMappings = {
|
||||
"CommandLine": "event/COMMAND_LINE",
|
||||
"Image": "event/FILE_PATH",
|
||||
"ParentImage": "event/PARENT/FILE_PATH",
|
||||
"ParentCommandLine": "event/PARENT/COMMAND_LINE",
|
||||
"User": "event/USER_NAME",
|
||||
# This field is redundant in LC, it seems to always be used with Image
|
||||
# so we will ignore it.
|
||||
"OriginalFileName": lambda fn, fv: ("event/FILE_PATH", "*" + fv),
|
||||
# Custom field names coming from somewhere unknown.
|
||||
"NewProcessName": "event/FILE_PATH",
|
||||
"ProcessCommandLine": "event/COMMAND_LINE",
|
||||
# Another one-off command line.
|
||||
"Command": "event/COMMAND_LINE",
|
||||
},
|
||||
isAllStringValues = False,
|
||||
keywordField = "event/COMMAND_LINE",
|
||||
postOpMapper = _mapProcessCreationOperations
|
||||
),
|
||||
"windows//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"target": "log",
|
||||
"log type": "wel",
|
||||
},
|
||||
preConditions = None,
|
||||
fieldMappings = _windowsEventLogFieldName,
|
||||
isAllStringValues = True,
|
||||
keywordField = None,
|
||||
postOpMapper = None
|
||||
),
|
||||
"windows_defender//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"target": "log",
|
||||
"log type": "wel",
|
||||
},
|
||||
preConditions = None,
|
||||
fieldMappings = _windowsEventLogFieldName,
|
||||
isAllStringValues = True,
|
||||
keywordField = None,
|
||||
postOpMapper = None
|
||||
),
|
||||
"dns//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"event": "DNS_REQUEST",
|
||||
},
|
||||
preConditions = None,
|
||||
fieldMappings = {
|
||||
"query": "event/DOMAIN_NAME",
|
||||
},
|
||||
isAllStringValues = False,
|
||||
keywordField = None,
|
||||
postOpMapper = None
|
||||
),
|
||||
"linux//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"events": [
|
||||
"NEW_PROCESS",
|
||||
"EXISTING_PROCESS",
|
||||
]
|
||||
},
|
||||
preConditions = {
|
||||
"op": "is linux",
|
||||
},
|
||||
fieldMappings = {
|
||||
"exe": "event/FILE_PATH",
|
||||
"type": None,
|
||||
},
|
||||
isAllStringValues = False,
|
||||
keywordField = 'event/COMMAND_LINE',
|
||||
postOpMapper = None
|
||||
),
|
||||
"unix//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"events": [
|
||||
"NEW_PROCESS",
|
||||
"EXISTING_PROCESS",
|
||||
]
|
||||
},
|
||||
preConditions = {
|
||||
"op": "is linux",
|
||||
},
|
||||
fieldMappings = {
|
||||
"exe": "event/FILE_PATH",
|
||||
"type": None,
|
||||
},
|
||||
isAllStringValues = False,
|
||||
keywordField = 'event/COMMAND_LINE',
|
||||
postOpMapper = None
|
||||
),
|
||||
"netflow//": SigmaLCConfig(
|
||||
topLevelParams = {
|
||||
"event": "NETWORK_CONNECTIONS",
|
||||
},
|
||||
preConditions = None,
|
||||
fieldMappings = {
|
||||
"destination.port": "event/NETWORK_ACTIVITY/DESTINATION/PORT",
|
||||
"source.port": "event/NETWORK_ACTIVITY/SOURCE/PORT",
|
||||
},
|
||||
isAllStringValues = False,
|
||||
keywordField = None,
|
||||
postOpMapper = None
|
||||
),
|
||||
}
|
||||
|
||||
class LimaCharlieBackend(BaseBackend):
|
||||
"""Converts Sigma rule into LimaCharlie D&R rules. Contributed by LimaCharlie. https://limacharlie.io"""
|
||||
identifier = "limacharlie"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["limacharlie"]
|
||||
|
||||
def generate(self, sigmaparser):
|
||||
# Take the log source information and figure out which set of mappings to use.
|
||||
ruleConfig = sigmaparser.parsedyaml
|
||||
ls_rule = ruleConfig['logsource']
|
||||
try:
|
||||
category = ls_rule['category']
|
||||
except KeyError:
|
||||
category = ""
|
||||
try:
|
||||
product = ls_rule['product']
|
||||
except KeyError:
|
||||
product = ""
|
||||
# try:
|
||||
# service = ls_rule['service']
|
||||
# except KeyError:
|
||||
# service = ""
|
||||
|
||||
# Don't use service for now, most Windows Event Logs
|
||||
# uses a different service with no category, since we
|
||||
# treat all Windows Event Logs together we can ignore
|
||||
# the service.
|
||||
service = ""
|
||||
|
||||
# See if we have a definition for the source combination.
|
||||
mappingKey = "%s/%s/%s" % (product, category, service)
|
||||
topFilter, preCond, mappings, isAllStringValues, keywordField, postOpMapper = _allFieldMappings.get(mappingKey, tuple([None, None, None, None, None, None]))
|
||||
if mappings is None:
|
||||
raise NotImplementedError("Log source %s/%s/%s not supported by backend." % (product, category, service))
|
||||
|
||||
# Field name conversions.
|
||||
self._fieldMappingInEffect = mappings
|
||||
|
||||
# LC event type pre-selector for the type of data.
|
||||
self._preCondition = preCond
|
||||
|
||||
# Are all the values treated as strings?
|
||||
self._isAllStringValues = isAllStringValues
|
||||
|
||||
# Are we supporting keywords full text search?
|
||||
self._keywordField = keywordField
|
||||
|
||||
# Call to fixup all operations after the fact.
|
||||
self._postOpMapper = postOpMapper
|
||||
|
||||
# Call the original generation code.
|
||||
detectComponent = super().generate(sigmaparser)
|
||||
|
||||
# We expect a string (yaml) as output, so if
|
||||
# we get anything else we assume it's a core
|
||||
# library value and just return it as-is.
|
||||
if not isinstance( detectComponent, str):
|
||||
return detectComponent
|
||||
|
||||
# This redundant to deserialize it right after
|
||||
# generating the yaml, but we try to use the parent
|
||||
# official class code as much as possible for future
|
||||
# compatibility.
|
||||
detectComponent = yaml.safe_load(detectComponent)
|
||||
|
||||
# Check that we got a proper node and not just a string
|
||||
# which we don't really know what to do with.
|
||||
if not isinstance(detectComponent, dict):
|
||||
raise NotImplementedError("Selection combination not supported.")
|
||||
|
||||
# Apply top level filter.
|
||||
detectComponent.update(topFilter)
|
||||
|
||||
# Now prepare the Response component.
|
||||
respondComponents = [{
|
||||
"action": "report",
|
||||
"name": ruleConfig["title"],
|
||||
}]
|
||||
|
||||
# Add a lot of the metadata available to the report.
|
||||
if ruleConfig.get("tags", None) is not None:
|
||||
respondComponents[0].setdefault("metadata", {})["tags"] = ruleConfig["tags"]
|
||||
|
||||
if ruleConfig.get("description", None) is not None:
|
||||
respondComponents[0].setdefault("metadata", {})["description"] = ruleConfig["description"]
|
||||
|
||||
if ruleConfig.get("references", None) is not None:
|
||||
respondComponents[0].setdefault("metadata", {})["references"] = ruleConfig["references"]
|
||||
|
||||
if ruleConfig.get("level", None) is not None:
|
||||
respondComponents[0].setdefault("metadata", {})["level"] = ruleConfig["level"]
|
||||
|
||||
if ruleConfig.get("author", None) is not None:
|
||||
respondComponents[0].setdefault("metadata", {})["author"] = ruleConfig["author"]
|
||||
|
||||
# Assemble it all as a single, complete D&R rule.
|
||||
return yaml.safe_dump({
|
||||
"detect": detectComponent,
|
||||
"respond": respondComponents,
|
||||
})
|
||||
|
||||
def generateQuery(self, parsed):
|
||||
# We override the generateQuery function because
|
||||
# we generate proper JSON structures internally
|
||||
# and only convert to string (yaml) once the
|
||||
# whole thing is assembled.
|
||||
result = self.generateNode(parsed.parsedSearch)
|
||||
|
||||
if self._preCondition is not None:
|
||||
result = {
|
||||
"op": "and",
|
||||
"rules": [
|
||||
self._preCondition,
|
||||
result,
|
||||
]
|
||||
}
|
||||
if self._postOpMapper is not None:
|
||||
result = self._postOpMapper(result)
|
||||
return yaml.safe_dump(result)
|
||||
|
||||
def generateANDNode(self, node):
|
||||
generated = [ self.generateNode(val) for val in node ]
|
||||
filtered = [ g for g in generated if g is not None ]
|
||||
if not filtered:
|
||||
return None
|
||||
|
||||
# Map any possible keywords.
|
||||
filtered = self._mapKeywordVals(filtered)
|
||||
|
||||
if 1 == len(filtered):
|
||||
if self._postOpMapper is not None:
|
||||
filtered[0] = self._postOpMapper(filtered[0])
|
||||
return filtered[0]
|
||||
result = {
|
||||
"op": "and",
|
||||
"rules": filtered,
|
||||
}
|
||||
if self._postOpMapper is not None:
|
||||
result = self._postOpMapper(result)
|
||||
return result
|
||||
|
||||
def generateORNode(self, node):
|
||||
generated = [self.generateNode(val) for val in node]
|
||||
filtered = [g for g in generated if g is not None]
|
||||
if not filtered:
|
||||
return None
|
||||
|
||||
# Map any possible keywords.
|
||||
filtered = self._mapKeywordVals(filtered)
|
||||
|
||||
if 1 == len(filtered):
|
||||
if self._postOpMapper is not None:
|
||||
filtered[0] = self._postOpMapper(filtered[0])
|
||||
return filtered[0]
|
||||
result = {
|
||||
"op": "or",
|
||||
"rules": filtered,
|
||||
}
|
||||
if self._postOpMapper is not None:
|
||||
result = self._postOpMapper(result)
|
||||
return result
|
||||
|
||||
def generateNOTNode(self, node):
|
||||
generated = self.generateNode(node.item)
|
||||
if generated is None:
|
||||
return None
|
||||
if not isinstance(generated, dict):
|
||||
raise NotImplementedError("Not operator not available on non-dict nodes.")
|
||||
generated["not"] = not generated.get("not", False)
|
||||
return generated
|
||||
|
||||
def generateSubexpressionNode(self, node):
|
||||
return self.generateNode(node.items)
|
||||
|
||||
def generateListNode(self, node):
|
||||
return [self.generateNode(value) for value in node]
|
||||
|
||||
def generateMapItemNode(self, node):
|
||||
fieldname, value = node
|
||||
|
||||
fieldNameAndValCallback = None
|
||||
|
||||
# The mapping can be a dictionary of mapping or a callable
|
||||
# to get the correct value.
|
||||
if callable(self._fieldMappingInEffect):
|
||||
fieldname = self._fieldMappingInEffect(fieldname)
|
||||
else:
|
||||
try:
|
||||
# The mapping can also be a callable that will
|
||||
# return a mapped key AND value.
|
||||
if callable(self._fieldMappingInEffect[fieldname]):
|
||||
fieldNameAndValCallback = self._fieldMappingInEffect[fieldname]
|
||||
else:
|
||||
fieldname = self._fieldMappingInEffect[fieldname]
|
||||
except:
|
||||
raise NotImplementedError("Field name %s not supported by backend." % (fieldname,))
|
||||
|
||||
# If fieldname returned is None, it's a special case where we
|
||||
# ignore the node.
|
||||
if fieldname is None:
|
||||
return None
|
||||
|
||||
if isinstance(value, (int, str)):
|
||||
if fieldNameAndValCallback is not None:
|
||||
fieldname, value = fieldNameAndValCallback(fieldname, value)
|
||||
op, newVal = self._valuePatternToLcOp(value)
|
||||
newOp = {
|
||||
"op": op,
|
||||
"path": fieldname,
|
||||
"case sensitive": False,
|
||||
}
|
||||
if op == "matches":
|
||||
newOp["re"] = newVal
|
||||
else:
|
||||
newOp["value"] = newVal
|
||||
if self._postOpMapper is not None:
|
||||
newOp = self._postOpMapper(newOp)
|
||||
return newOp
|
||||
elif isinstance(value, list):
|
||||
subOps = []
|
||||
for v in value:
|
||||
if fieldNameAndValCallback is not None:
|
||||
fieldname, v = fieldNameAndValCallback(fieldname, v)
|
||||
op, newVal = self._valuePatternToLcOp(v)
|
||||
newOp = {
|
||||
"op": op,
|
||||
"path": fieldname,
|
||||
"case sensitive": False,
|
||||
}
|
||||
if op == "matches":
|
||||
newOp["re"] = newVal
|
||||
else:
|
||||
newOp["value"] = newVal
|
||||
if self._postOpMapper is not None:
|
||||
newOp = self._postOpMapper(newOp)
|
||||
subOps.append(newOp)
|
||||
if 1 == len(subOps):
|
||||
return subOps[0]
|
||||
return {
|
||||
"op": "or",
|
||||
"rules": subOps
|
||||
}
|
||||
elif isinstance(value, SigmaTypeModifier):
|
||||
if isinstance(value, SigmaRegularExpressionModifier):
|
||||
if fieldNameAndValCallback is not None:
|
||||
fieldname, value = fieldNameAndValCallback(fieldname, value)
|
||||
result = {
|
||||
"op": "matches",
|
||||
"path": fieldname,
|
||||
"re": re.compile(value),
|
||||
}
|
||||
if self._postOpMapper is not None:
|
||||
result = self._postOpMapper(result)
|
||||
return result
|
||||
else:
|
||||
raise TypeError("Backend does not support TypeModifier: %s" % (str(type(value))))
|
||||
elif value is None:
|
||||
if fieldNameAndValCallback is not None:
|
||||
fieldname, value = fieldNameAndValCallback(fieldname, value)
|
||||
result = {
|
||||
"op": "exists",
|
||||
"not": True,
|
||||
"path": fieldname,
|
||||
}
|
||||
if self._postOpMapper is not None:
|
||||
result = self._postOpMapper(result)
|
||||
return result
|
||||
else:
|
||||
raise TypeError("Backend does not support map values of type " + str(type(value)))
|
||||
|
||||
def generateValueNode(self, node):
|
||||
return node
|
||||
|
||||
def _valuePatternToLcOp(self, val):
|
||||
# Here we convert the string values supported by Sigma that
|
||||
# can include wildcards into either proper values (string or int)
|
||||
# or into altered values to be functionally equivalent using
|
||||
# a few different LC D&R rule operators.
|
||||
|
||||
# No point evaluating non-strings.
|
||||
if not isinstance(val, str):
|
||||
return ("is", str(val) if self._isAllStringValues else val)
|
||||
|
||||
# Is there any wildcard in this string? If not, we can short circuit.
|
||||
if "*" not in val and "?" not in val:
|
||||
return ("is", val)
|
||||
|
||||
# Now we do a small optimization for the shortcut operators
|
||||
# available in LC. We try to see if the wildcards are around
|
||||
# the main value, but NOT within. If that's the case we can
|
||||
# use the "starts with", "ends with" or "contains" operators.
|
||||
isStartsWithWildcard = False
|
||||
isEndsWithWildcard = False
|
||||
tmpVal = val
|
||||
if tmpVal.startswith("*"):
|
||||
isStartsWithWildcard = True
|
||||
tmpVal = tmpVal[1:]
|
||||
if tmpVal.endswith("*") and not (tmpVal.endswith("\\*") and not tmpVal.endswith("\\\\*")):
|
||||
isEndsWithWildcard = True
|
||||
if tmpVal.endswith("\\\\*"):
|
||||
# An extra \ had to be there so it didn't escapte the
|
||||
# *, but since we plan on removing the *, we can also
|
||||
# remove one \.
|
||||
tmpVal = tmpVal[:-2]
|
||||
else:
|
||||
tmpVal = tmpVal[:-1]
|
||||
|
||||
# Check to see if there are any other wildcards. If there are
|
||||
# we cannot use our shortcuts.
|
||||
if "*" not in tmpVal and "?" not in tmpVal:
|
||||
if isStartsWithWildcard and isEndsWithWildcard:
|
||||
return ("contains", tmpVal)
|
||||
|
||||
if isStartsWithWildcard:
|
||||
return ("ends with", tmpVal)
|
||||
|
||||
if isEndsWithWildcard:
|
||||
return ("starts with", tmpVal)
|
||||
|
||||
# This is messy, but it is accurate in generating a RE based on
|
||||
# the simplified wildcard system, while also supporting the
|
||||
# escaping of those wildcards.
|
||||
segments = []
|
||||
tmpVal = val
|
||||
while True:
|
||||
nEscapes = 0
|
||||
for i in range(len(tmpVal)):
|
||||
# We keep a running count of backslash escape
|
||||
# characters we see so that if we meet a wildcard
|
||||
# we can tell whether the wildcard is escaped
|
||||
# (with odd number of escapes) or if it's just a
|
||||
# backslash literal before a wildcard (even number).
|
||||
if "\\" == tmpVal[i]:
|
||||
nEscapes += 1
|
||||
continue
|
||||
|
||||
if "*" == tmpVal[i]:
|
||||
if 0 == nEscapes:
|
||||
segments.append(re.escape(tmpVal[:i]))
|
||||
segments.append(".*")
|
||||
elif nEscapes % 2 == 0:
|
||||
segments.append(re.escape(tmpVal[:i - nEscapes]))
|
||||
segments.append(tmpVal[i - nEscapes:i])
|
||||
segments.append(".*")
|
||||
else:
|
||||
segments.append(re.escape(tmpVal[:i - nEscapes]))
|
||||
segments.append(tmpVal[i - nEscapes:i + 1])
|
||||
tmpVal = tmpVal[i + 1:]
|
||||
break
|
||||
|
||||
if "?" == tmpVal[i]:
|
||||
if 0 == nEscapes:
|
||||
segments.append(re.escape(tmpVal[:i]))
|
||||
segments.append(".")
|
||||
elif nEscapes % 2 == 0:
|
||||
segments.append(re.escape(tmpVal[:i - nEscapes]))
|
||||
segments.append(tmpVal[i - nEscapes:i])
|
||||
segments.append(".")
|
||||
else:
|
||||
segments.append(re.escape(tmpVal[:i - nEscapes]))
|
||||
segments.append(tmpVal[i - nEscapes:i + 1])
|
||||
tmpVal = tmpVal[i + 1:]
|
||||
break
|
||||
|
||||
nEscapes = 0
|
||||
else:
|
||||
segments.append(re.escape(tmpVal))
|
||||
break
|
||||
|
||||
val = ''.join(segments)
|
||||
|
||||
return ("matches", val)
|
||||
|
||||
def _mapKeywordVals(self, values):
|
||||
# This function ensures that the list of values passed
|
||||
# are proper D&R operations, if they are strings it indicates
|
||||
# they were requested as keyword matches. We only support
|
||||
# keyword matches when specified in the config. We generally just
|
||||
# map them to the most common field in LC that makes sense.
|
||||
mapped = []
|
||||
|
||||
for val in values:
|
||||
# Non-keywords are just passed through.
|
||||
if not isinstance(val, str):
|
||||
mapped.append(val)
|
||||
continue
|
||||
|
||||
if self._keywordField is None:
|
||||
raise NotImplementedError("Full-text keyboard searches not supported.")
|
||||
|
||||
# This seems to be indicative only of "keywords" which are mostly
|
||||
# representative of full-text searches. We don't suport that but
|
||||
# in some data sources we can alias them to an actual field.
|
||||
op, newVal = self._valuePatternToLcOp(val)
|
||||
newOp = {
|
||||
"op": op,
|
||||
"path": self._keywordField,
|
||||
}
|
||||
if op == "matches":
|
||||
newOp["re"] = newVal
|
||||
else:
|
||||
newOp["value"] = newVal
|
||||
mapped.append(newOp)
|
||||
|
||||
return mapped
|
||||
@@ -22,6 +22,8 @@ class LogPointBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into LogPoint query"""
|
||||
identifier = "logpoint"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["sysmon", "logpoint-windows"]
|
||||
|
||||
# \ -> \\
|
||||
# \* -> \*
|
||||
|
||||
@@ -24,6 +24,8 @@ from .mixins import MultiRuleOutputMixin
|
||||
class NetWitnessBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into NetWitness saved search. Contributed by @tuckner"""
|
||||
identifier = "netwitness"
|
||||
config_required = False
|
||||
default_config = ["sysmon", "netwitness"]
|
||||
active = True
|
||||
reEscape = re.compile('(")')
|
||||
reClear = None
|
||||
|
||||
@@ -23,6 +23,8 @@ class PowerShellBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into PowerShell event log cmdlets."""
|
||||
identifier = "powershell"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["sysmon", "powershell"]
|
||||
options = (
|
||||
("csv", False, "Return the results in CSV format instead of Powershell objects", None),
|
||||
)
|
||||
|
||||
@@ -27,6 +27,8 @@ class QRadarBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into Qradar saved search. Contributed by SOC Prime. https://socprime.com"""
|
||||
identifier = "qradar"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["sysmon", "qradar"]
|
||||
reEscape = re.compile('(")')
|
||||
reClear = None
|
||||
andToken = " and "
|
||||
|
||||
@@ -22,6 +22,8 @@ class QualysBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into Qualys saved search. Contributed by SOC Prime. https://socprime.com"""
|
||||
identifier = "qualys"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["sysmon", "qualys"]
|
||||
andToken = " and "
|
||||
orToken = " or "
|
||||
notToken = "not "
|
||||
|
||||
@@ -32,6 +32,8 @@ class SumoLogicBackend(SingleTextQueryBackend):
|
||||
"""Converts Sigma rule into SumoLogic query"""
|
||||
identifier = "sumologic"
|
||||
active = True
|
||||
config_required = False
|
||||
default_config = ["sysmon", "sumologic"]
|
||||
|
||||
index_field = "_index"
|
||||
reClear = None
|
||||
|
||||
@@ -202,11 +202,11 @@ class ConditionAND(ConditionBase):
|
||||
"""AND Condition"""
|
||||
op = COND_AND
|
||||
|
||||
def __init__(self, sigma=None, op=None, val1=None, val2=None):
|
||||
if sigma == None and op == None and val1 == None and val2 == None: # no parameters given - initialize empty
|
||||
def __init__(self, sigma=None, op=None, *args):
|
||||
if sigma == None and op == None and len(args) == 0: # no parameters given - initialize empty
|
||||
self.items = list()
|
||||
else: # called by parser, use given values
|
||||
self.items = [ val1, val2 ]
|
||||
self.items = args
|
||||
|
||||
class ConditionOR(ConditionAND):
|
||||
"""OR Condition"""
|
||||
|
||||
@@ -31,6 +31,26 @@ class SigmaContainsModifier(ListOrStringModifierMixin, SigmaTransformModifier):
|
||||
val += "*"
|
||||
return val
|
||||
|
||||
class SigmaStartswithModifier(ListOrStringModifierMixin, SigmaTransformModifier):
|
||||
"""Add *-wildcard before and after all string(s)"""
|
||||
identifier = "startswith"
|
||||
active = True
|
||||
|
||||
def apply_str(self, val : str):
|
||||
if not val.endswith("*"):
|
||||
val += "*"
|
||||
return val
|
||||
|
||||
class SigmaEndswithModifier(ListOrStringModifierMixin, SigmaTransformModifier):
|
||||
"""Add *-wildcard before and after all string(s)"""
|
||||
identifier = "endswith"
|
||||
active = True
|
||||
|
||||
def apply_str(self, val : str):
|
||||
if not val.startswith("*"):
|
||||
val = "*" + val
|
||||
return val
|
||||
|
||||
class SigmaAllValuesModifier(SigmaTransformModifier):
|
||||
"""Override default OR-linking behavior for list with AND-linking of all list values"""
|
||||
identifier = "all"
|
||||
|
||||
+11
-6
@@ -168,6 +168,16 @@ if cmdargs.filter:
|
||||
sys.exit(ERR_RULE_FILTER_PARSING)
|
||||
|
||||
sigmaconfigs = SigmaConfigurationChain()
|
||||
backend_class = backends.getBackend(cmdargs.target)
|
||||
if cmdargs.config is None:
|
||||
if backend_class.config_required and not cmdargs.shoot_yourself_in_the_foot:
|
||||
print("The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.", file=sys.stderr)
|
||||
print("Available choices for this backend (get complete list with --lists/-l):")
|
||||
list_configurations(cmdargs.target)
|
||||
sys.exit(ERR_CONFIG_REQUIRED)
|
||||
if backend_class.default_config is not None:
|
||||
cmdargs.config = backend_class.default_config
|
||||
|
||||
if cmdargs.config:
|
||||
order = 0
|
||||
for conf_name in cmdargs.config:
|
||||
@@ -198,12 +208,7 @@ if cmdargs.config:
|
||||
exit(ERR_CONFIG_PARSING)
|
||||
|
||||
backend_options = BackendOptions(cmdargs.backend_option, cmdargs.backend_config)
|
||||
backend = backends.getBackend(cmdargs.target)(sigmaconfigs, backend_options)
|
||||
if backend.config_required and cmdargs.config is None and not cmdargs.shoot_yourself_in_the_foot:
|
||||
print("The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.", file=sys.stderr)
|
||||
print("Available choices for this backend (get complete list with --lists/-l):")
|
||||
list_configurations(cmdargs.target)
|
||||
sys.exit(ERR_CONFIG_REQUIRED)
|
||||
backend = backend_class(sigmaconfigs, backend_options)
|
||||
|
||||
filename = cmdargs.output
|
||||
if filename:
|
||||
|
||||
Reference in New Issue
Block a user