Skip to content

Commit

Permalink
Refactored code base
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberphor committed Oct 13, 2024
1 parent 74d076a commit 1368705
Show file tree
Hide file tree
Showing 16 changed files with 734 additions and 423 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
cov.xml
dist/
docs/_build
backup/
backup/
dataset/
rules/
41 changes: 13 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
![Tests](https://github.com/cyberphor/pySigma-backend-powershell/actions/workflows/test.yml/badge.svg)
![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/cyberphor/d3f7db7182e7819f3748e64a2ab2d126/raw/cyberphor-pySigma-backend-powershell.json)
![Status](https://img.shields.io/badge/Status-pre--release-orange)
# pySigma Powershell Backend
![Status](https://img.shields.io/badge/Status-pre--release-orange)

# pySigma PowerShell Backend
The pySigma PowerShell backend uses [pySigma](https://github.com/SigmaHQ/pySigma) to convert [Sigma rules](https://github.com/SigmaHQ/sigma) into PowerShell queries. It was designed to be used in conjunction with the [Soap](https://github.com/cyberphor/Soap) PowerShell module (i.e., the `Read-WinEvent` function).

## Overview
The pySigma PowerShell backend includes two Python packages:
* `sigma.pipelines.powershell`: normalizes Sigma rules for PowerShell.
* `sigma.backends.powershell`: declares the `PowerShellBackend` class and multiple output methods.

It currently supports the following output formats:
- [x] default: plain PowerShell queries
- [ ] script: a PowerShell script
- [ ] xml: XML documents
- [ ] xpath: XML strings
- [ ] subscription: Windows event subscriptions
The pySigma PowerShell Backend converts Sigma rules into PowerShell-based queries. It was designed to be used in conjunction with the the [`Read-WinEvent`](/scripts/Read-WinEvent.ps1) filter.

## Usage
**Step 1.** After downloading this repository, install this Python-based project using `poetry`.
```bash
poetry run python sigma2powershell.py -p rules/
poetry install
```

## Testing
```python
python -m pip install --user pytest
python -m pytest # test all functions
python -m pytest tests/test_backend_powershell.py::test_powershell_and_expression # test a specific function
**Step 2.** Next, use the provided PowerShell script to import the `Read-WinEvent` filter. You will need to do this everytime you start a new PowerShell session (pro-tip: add this filter to your PowerShell profile).
```bash
./scripts/Read-WinEvent.ps1
```

## Updating to the Latest Version of pySigma
```python
python -m poetry add pysigma@latest
**Step 3** Convert whatever Sigma rules you have to PowerShell queries.
```bash
sigma2powershell -r rules/demo.yml
```

## References
* [Understanding XML and XPath by the Microsoft Scripting Guy, Ed Wilson](https://devblogs.microsoft.com/scripting/understanding-xml-and-xpath/)
## Copyright
This project is licensed under the terms of the [MIT license](/LICENSE).
18 changes: 0 additions & 18 deletions get_sigma_rules.py

This file was deleted.

468 changes: 297 additions & 171 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions poetry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[virtualenvs]
in-project = true
path = ".venv"
12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pySigma-backend-powershell"
version = "0.1.1"
version = "0.1.2"
description = "pySigma PowerShell Backend"
authors = ["Victor Fernandez III <@cyberphor>"]
license = "MIT"
Expand All @@ -10,8 +10,14 @@ packages = [
]

[tool.poetry.dependencies]
python = "^3.8"
pysigma = "^0.10.10"
python = "^3.11"
pysigma = "^0.11.14"

[tool.poetry.scripts]
sigma2powershell = "scripts.sigma2powershell:main"

[tool.poetry.group.dev.dependencies]
black = "^24.10.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
2 changes: 1 addition & 1 deletion rules/net_connection_win_wscript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ detection:
selection:
Image|endswith: '\wscript.exe'
Initiated: 'true'
Destination Port:
DestinationPort:
- 25
condition: selection
10 changes: 5 additions & 5 deletions rules/win_alert_mimikatz_keywords.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
title: Mimikatz Usage
title: Mimikatz Use
id: 06d71506-7beb-4f22-8888-e2e5e2ca7fd8
status: test
description: This method detects mimikatz keywords in different Eventlogs (some of them only appear in older Mimikatz version that are however still used by different threat groups)
references:
- https://tools.thehacker.recipes/mimikatz/modules
author: Florian Roth (Nextron Systems), David ANDRE (additional keywords)
date: 2017/01/10
modified: 2022/01/05
date: 2017-01-10
modified: 2022-01-05
tags:
- attack.s0002
- attack.lateral_movement
- attack.credential_access
- attack.lateral-movement
- attack.credential-access
- car.2013-07-001
- car.2019-04-004
- attack.t1003.002
Expand Down
39 changes: 39 additions & 0 deletions scripts/Read-WinEvent.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
filter Read-WinEvent {
<#
.EXAMPLE
Get-WinEvent -FilterHashTable @{LogName="Security";Id=4625} | Read-WinEvent | Select-Object -Property TimeCreated,Hostname,TargetUserName,LogonType | Format-Table -AutoSize
TimeCreated TargetUserName LogonType
----------- -------------- ---------
9/12/2021 8:23:27 AM Victor 2
9/12/2021 8:23:27 AM Victor 2
9/12/2021 7:49:37 AM Victor 2
9/12/2021 7:49:37 AM Victor 2
#>
$WinEvent = [ordered]@{}
$XmlData = [xml]$_.ToXml()
$SystemData = $XmlData.Event.System
$SystemData |
Get-Member -MemberType Properties |
Select-Object -ExpandProperty Name |
ForEach-Object {
$Field = $_
if ($Field -eq 'TimeCreated') {
$WinEvent.$Field = Get-Date -Format 'yyyy-MM-dd HH:mm:ss K' $SystemData[$Field].SystemTime
} elseif ($SystemData[$Field].'#text') {
$WinEvent.$Field = $SystemData[$Field].'#text'
} else {
$SystemData[$Field] |
Get-Member -MemberType Properties |
Select-Object -ExpandProperty Name |
ForEach-Object {
$WinEvent.$Field = @{}
$WinEvent.$Field.$_ = $SystemData[$Field].$_
}
}
}
$XmlData.Event.EventData.Data |
ForEach-Object {
$WinEvent.$($_.Name) = $_.'#text'
}
return New-Object -TypeName PSObject -Property $WinEvent
}
26 changes: 26 additions & 0 deletions scripts/get_dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Downloads a dataset."""

from os import remove, rename
from requests import session
from zipfile import ZipFile

EXIT_SUCCESS = 0
DATASET = "dataset.zip"
URL = "https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES/archive/refs/heads/master.zip"


def main() -> int:
"""Downloads a dataset."""
with session() as client:
repo = client.get(URL)
with open(DATASET, "wb") as download:
download.write(repo.content)
with ZipFile(DATASET, "r") as dataset:
dataset.extractall(".")
rename("EVTX-ATTACK-SAMPLES-master", "dataset")
remove(DATASET)
return EXIT_SUCCESS


if __name__ == "__main__":
main()
22 changes: 22 additions & 0 deletions scripts/get_sigma_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from shutil import move
from urllib.request import urlretrieve
from glob import glob
from os import remove
from zipfile import ZipFile
import shutil


def main():
urlretrieve(
"https://github.com/SigmaHQ/sigma/archive/refs/heads/master.zip", "tmp.zip"
)
with ZipFile("tmp.zip", "r") as zf:
zf.extractall("tmp")
for rule_dir in glob("tmp/sigma-master/rule*"):
move(rule_dir, "rules")
remove("tmp.zip")
shutil.rmtree("tmp")


if __name__ == "__main__":
main()
25 changes: 15 additions & 10 deletions sigma2powershell.py → scripts/sigma2powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,40 @@
from sigma.pipelines.powershell import powershell_pipeline
from sigma.backends.powershell import PowerShellBackend

def Sigma2PowerShell(path: str, show_errors: bool, foo: str):
rules = SigmaCollection.load_ruleset(inputs=[path])

def Sigma2PowerShell(path: str, output: str, show_errors: bool):
rule_collection = SigmaCollection.load_ruleset(inputs=[path])
pipeline = powershell_pipeline()
backend = PowerShellBackend(processing_pipeline=pipeline, collect_errors=show_errors)
return backend.convert(rule_collection=rules, output_format=foo)
backend = PowerShellBackend(
processing_pipeline=pipeline, collect_errors=show_errors
)
return backend.convert(rule_collection)


if "__main__" == __name__:
def main():
parser = ArgumentParser()
parser.add_argument(
"-p",
"-r",
"--rules",
type=str,
required=True,
help="path to Sigma rule(s)",
metavar="<PATH_TO_RULESET>"
metavar="<PATH_TO_RULESET>",
)
parser.add_argument(
"-o",
"--output",
default="default",
type=str,
choices=["default", "script"],
help="output format",
)
parser.add_argument(
"-e",
"--show-rule-errors",
action="store_false",
default=True,
help="show rule errors",
)
args = parser.parse_args()
queries = Sigma2PowerShell(args.p, args.e, args.o)
if None not in queries:
print("\n".join(queries))
print(Sigma2PowerShell(args.rules, args.output, args.show_rule_errors))
5 changes: 3 additions & 2 deletions sigma/backends/powershell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .powershell import PowerShellBackend

# TODO: add all backend classes that should be exposed to the user of your backend in the import statement above.

backends = { # Mapping between backend identifiers and classes. This is used by the pySigma plugin system to recognize backends and expose them with the identifier.
backends = { # Mapping between backend identifiers and classes. This is used by the pySigma plugin system to recognize backends and expose them with the identifier.
"powershell": PowerShellBackend,
}
}
Loading

0 comments on commit 1368705

Please sign in to comment.