-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
""" | ||
This example uses a YAML file to load rules and other configuration | ||
The YAML file contains declarations to load the backend and rules for the mule. | ||
The sample collects all A records and tags them with the first number of the IP address in the record. | ||
The backend is the builtin DNSPythonBackend. | ||
The storage is not defined in the YAML file, so it will default to the builtin DictStorage. | ||
""" | ||
|
||
from pathlib import Path | ||
|
||
from dnsmule import DNSMule | ||
|
||
mule = DNSMule.load(Path(__file__).parent / 'example.yml') | ||
|
||
if __name__ == '__main__': | ||
mule.scan('example.com') | ||
print('Loaded Rules: ', *mule.rules) | ||
print('Loaded Backend: ', mule.backend) | ||
print('Loaded Storage: ', mule.storage) | ||
print('Produced Result:', mule.result('example.com')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
""" | ||
This example uses a custom backend for DNSMule to read records from a file | ||
The backend only needs to implement a single method that produces records. | ||
The scan is then run with an arbitrary string to produce all records from the file. | ||
A single rule is implemented to print out all CNAME records in the file. | ||
""" | ||
from pathlib import Path | ||
from typing import Iterable | ||
|
||
from dnsmule import DNSMule, Record, Domain, RRType | ||
from dnsmule.backends.abstract import Backend | ||
|
||
|
||
class FileBackend(Backend): | ||
"""Implements a Backend for DNSMule that reads and splits lines in a file into a Record | ||
""" | ||
file: str | ||
|
||
def _query(self, _: Domain, *types: RRType) -> Iterable[Record]: | ||
_types = {*types} | ||
with open(self.file, 'r') as f: | ||
for line in f: | ||
_type, _domain, _value = line.split(',') | ||
_type = RRType.from_any(_type) | ||
if _type in _types: | ||
yield Record(domain=Domain(_domain), type=_type, data=_value) | ||
|
||
|
||
mule = DNSMule.make(backend=FileBackend(file=Path(__file__).parent / 'example.domains')) | ||
|
||
|
||
@mule.rules.add.CNAME | ||
def aliases(record: Record): | ||
"""Prints out all CNAME records found from the example file | ||
""" | ||
print('FOUND A CNAME: ', record.domain, '>>', record.text) | ||
|
||
|
||
if __name__ == '__main__': | ||
mule.scan('all domains in file') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
""" | ||
This example shows how to register new rule creators in code | ||
These rule creators can then be used when loading yaml files or creating rules in code. | ||
""" | ||
from dnsmule import Rules, Rule, Record, DNSMule | ||
from dnsmule.loader import ConfigLoader | ||
|
||
rules = Rules() | ||
mule = DNSMule.make(rules=rules) | ||
|
||
|
||
@rules.register('custom.factory') | ||
def create_rule(**definition) -> Rule: | ||
""" | ||
Defines a handler function for creating rules | ||
This can be used for creating any kind of rules and could be used for example defining presets for rules. | ||
""" | ||
print('Registering Rule from Function with definition:', definition) | ||
return Rule(f=lambda r: r.tag('FACTORY')) | ||
|
||
|
||
@rules.register | ||
class CustomRule(Rule): | ||
""" | ||
Rules can also be registered as types inheriting from Rule | ||
These can be useful as you can directly register classes with the _id attribute set to whatever | ||
will be used in the YAML file. This is a good way to register rules if you prefer working with classes. | ||
""" | ||
_id = 'custom.rule' | ||
|
||
def __init__(self, **kwargs): | ||
super(CustomRule, self).__init__(**kwargs) | ||
print('Registering Rule from Class with definition: ', kwargs) | ||
|
||
def __call__(self, record: Record): | ||
record.tag('RULE') | ||
|
||
|
||
if __name__ == '__main__': | ||
# Directly setting up and loading as opposed to loading the configuration from file | ||
# using Mule.append(file) | ||
loader = ConfigLoader(mule) | ||
loader.config = { | ||
'rules': [ | ||
{ | ||
'name': 'FACTORY', | ||
'type': 'custom.factory', | ||
'record': 'A', | ||
'config': { | ||
'custom': 'value', | ||
} | ||
}, | ||
{ | ||
'name': 'DIRECT', | ||
'type': 'custom.rule', | ||
'record': 'A', | ||
'config': { | ||
'hello': 'world', | ||
} | ||
}, | ||
] | ||
} | ||
loader.append_rules() | ||
print('Loaded Rules:', *mule.rules) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
This example defines rules in code to implement a simple scanner for TXT and CNAME records | ||
The rules are added directly by registering handlers in code. | ||
It is important to remember to register the Backend implementation as it will default to the NOOPBackend. | ||
The NOOPBackend is usually only useful when testing custom rules or loading rules. | ||
""" | ||
|
||
from dnsmule import DNSMule, Record | ||
from dnsmule.backends.dnspython import DNSPythonBackend | ||
from dnsmule.storages.abstract import result_to_json_data | ||
from dnsmule.storages.dictstorage import DictStorage | ||
from dnsmule.utils import extend_set | ||
|
||
mule = DNSMule.make( | ||
storage=DictStorage(), | ||
backend=DNSPythonBackend(), | ||
) | ||
|
||
|
||
@mule.rules.add.CNAME | ||
def alias_catcher(record: Record): | ||
""" | ||
Adds any CNAME records for a domain into the result | ||
This can be useful in further analysis as the DNSPythonBackend uses the domain from recursive queries on | ||
for example A records, which can lead to seemingly "missing" data as it gets attributed to a different domain | ||
if a CNAME exists for the queried domain. | ||
""" | ||
extend_set(record.result.data, 'aliases', [record.text]) | ||
|
||
|
||
@mule.rules.add.TXT | ||
def text_analyzer(record: Record): | ||
"""Scans TXT records and matches a predefined list of interesting values | ||
""" | ||
text_record = record.text | ||
if any(interesting_value in text_record for interesting_value in [ | ||
'verification', | ||
'verifier', | ||
'verify', | ||
'domain', | ||
'code', | ||
'secret', | ||
'key', | ||
]): | ||
extend_set(record.result.data, 'records', [text_record]) | ||
record.tag('INTERESTING') | ||
|
||
|
||
def analyze(*domains): | ||
"""Analyzes a set of domains and returns the results in a json compatible dict | ||
""" | ||
mule.scan(*domains) | ||
return { | ||
k: result_to_json_data(v) | ||
for k, v in mule.storage.items() | ||
} | ||
|
||
|
||
if __name__ == '__main__': | ||
import json | ||
import argparse | ||
|
||
parser = argparse.ArgumentParser(description='searches for interesting TXT records and any CNAME records') | ||
parser.add_argument('domains', nargs='+', type=str, help='list of domains to scan') | ||
arguments = parser.parse_args() | ||
|
||
print('Rules: ', *mule.rules) | ||
print('Results:', json.dumps( | ||
analyze(*arguments.domains), | ||
ensure_ascii=False, | ||
indent=4, | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
TXT,example.com,site-verification=abcd | ||
CNAME,www.example.com,example.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
version: '0.5.0' | ||
backend: | ||
name: dnspython.DNSPythonBackend | ||
config: | ||
timeout: 2 | ||
description: This Storage has been configured in the YAML file | ||
rules: | ||
- name: DIGITS | ||
type: dns.regex | ||
record: A | ||
description: Matches first Digit of the IP address | ||
config: | ||
pattern: '^(\d)' | ||
group: 1 | ||
description: This Rule has been configured in the YAML file |