Skip to content

Commit

Permalink
Merge pull request #198 from punk-security/chore/readme-and-shebang
Browse files Browse the repository at this point in the history
Add shebang to main.py and clarify supported versions of Python
  • Loading branch information
imnotbrandon authored Oct 1, 2024
2 parents c6af601 + cf47bf4 commit 5dd7141
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 139 deletions.
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,25 @@ We currently support AWS Route53, Cloudflare, and Azure. Documentation on adding
You can run DNS Reaper in a pipeline, feeding it a list of domains that you intend to provision, and it will exit Non-Zero if it detects a takeover is possible. You can prevent takeovers before they are even possible!

## Usage

To run DNS Reaper, you can use the docker image or run it with python 3.11.
To run DNS Reaper, you can use the docker image or run it with Python 3.11.

**Findings are returned in the output and more detail is provided in a local "results.csv" file. We also support json output as an option.**

### Run it with docker

``` docker run punksecurity/dnsreaper --help ```
```shell
docker run punksecurity/dnsreaper --help
```

### Run it locally
> [!IMPORTANT]
> The minimum version of Python that dnsReaper supports is 3.9, but 3.11 is recommended. We attempt to maintain support for stable versions of Python that are not end-of-life.
We will not provide support or accept pull requests for issues that affect end-of-life versions of Python. See [Status of Python versions](https://devguide.python.org/versions/) for more information.

When running locally, we recommend using a virtual environment (venv) to avoid dependency conflicts.
Instructions available [Here](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#create-and-use-virtual-environments).

### Run it with python
```
```shell
pip install -r requirements.txt
python main.py --help
```
Expand Down
269 changes: 135 additions & 134 deletions main.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,134 +1,135 @@
from scan import scan_domain
import signatures
import output
import detection_enums
import providers
from os import linesep
from domain import Domain
from resolver import Resolver

from functools import partial

import logging
from sys import stderr, exit, argv

import argparsing

import colorama

import time

import asyncio

import dns.resolver

start_time = time.time()

if "--nocolour" in argv:
print(argparsing.banner, file=stderr)
else:
colorama.init()
print(argparsing.banner_with_colour, file=stderr)

args = argparsing.parse_args()

###### verbosity

if args.verbose == 0:
verbosity_level = logging.WARN
if args.verbose == 1:
verbosity_level = logging.INFO
if args.verbose > 1:
verbosity_level = logging.DEBUG

logging.basicConfig(format="%(message)s", level=verbosity_level)
logging.StreamHandler(stderr)

if not args.verbose > 2:
for module in ["boto", "requests"]:
logger = logging.getLogger(module)
logger.setLevel(logging.CRITICAL)

###### signatures

signatures = [getattr(signatures, signature) for signature in signatures.__all__]

# replace name for each signature
for signature in signatures:
signature.__name__ = signature.__name__.replace("signatures.", "")

if args.signature:
signatures = [s for s in signatures if s.__name__ in args.signature]

if args.exclude_signature:
signatures = [s for s in signatures if s.__name__ not in args.exclude_signature]

if not args.enable_unlikely:
signatures = [
s
for s in signatures
if s.test.CONFIDENCE != detection_enums.CONFIDENCE.UNLIKELY
]

if args.disable_probable:
signatures = [
s
for s in signatures
if s.test.CONFIDENCE != detection_enums.CONFIDENCE.POTENTIAL
]

logging.warning(f"Testing with {len(signatures)} signatures")


###### scanning

findings = []

if "--out" not in argv:
# using default out location, need to append our format
args.out = f"{args.out}.{args.out_format}"


async def main():
###### domain ingestion
nameservers = (
dns.resolver.Resolver().nameservers
if args.resolver == ""
else args.resolver.replace(" ", "").split(",")
)
Domain.resolver = Resolver(nameservers=nameservers, parallelism=args.parallelism)
provider = getattr(providers, args.provider)
domains = list(provider.fetch_domains(**args.__dict__))

if len(domains) == 0:
logging.error("ERROR: No domains to scan")
exit(-1)

with output.Output(args.out_format, args.out) as o:
scan = partial(
scan_domain,
signatures=signatures,
output_handler=o,
findings=findings,
)

await asyncio.gather(*[asyncio.create_task(scan(domain)) for domain in domains])

###### exit
logging.warning(f"\n\nWe found {len(findings)} takeovers ☠️")
for finding in findings:
msg = f"-- DOMAIN '{finding.domain}' :: SIGNATURE '{finding.signature}' :: CONFIDENCE '{finding.confidence}'"
msg += f"{linesep}{finding.populated_records()}"
if args.nocolour == False:
msg = colorama.Fore.RED + msg + colorama.Fore.RESET
logging.warning(msg)
logging.warning(
f"\n⏱️ We completed in {round(time.time() - start_time, 2)} seconds"
)
logging.warning(f"...Thats all folks!")
if args.pipeline:
logging.debug(f"Pipeline flag set - Exit code: {len(findings)}")
exit(len(findings))


asyncio.run(main())
#!/usr/bin/env python3
from scan import scan_domain
import signatures
import output
import detection_enums
import providers
from os import linesep
from domain import Domain
from resolver import Resolver

from functools import partial

import logging
from sys import stderr, exit, argv

import argparsing

import colorama

import time

import asyncio

import dns.resolver

start_time = time.time()

if "--nocolour" in argv:
print(argparsing.banner, file=stderr)
else:
colorama.init()
print(argparsing.banner_with_colour, file=stderr)

args = argparsing.parse_args()

###### verbosity

if args.verbose == 0:
verbosity_level = logging.WARN
if args.verbose == 1:
verbosity_level = logging.INFO
if args.verbose > 1:
verbosity_level = logging.DEBUG

logging.basicConfig(format="%(message)s", level=verbosity_level)
logging.StreamHandler(stderr)

if not args.verbose > 2:
for module in ["boto", "requests"]:
logger = logging.getLogger(module)
logger.setLevel(logging.CRITICAL)

###### signatures

signatures = [getattr(signatures, signature) for signature in signatures.__all__]

# replace name for each signature
for signature in signatures:
signature.__name__ = signature.__name__.replace("signatures.", "")

if args.signature:
signatures = [s for s in signatures if s.__name__ in args.signature]

if args.exclude_signature:
signatures = [s for s in signatures if s.__name__ not in args.exclude_signature]

if not args.enable_unlikely:
signatures = [
s
for s in signatures
if s.test.CONFIDENCE != detection_enums.CONFIDENCE.UNLIKELY
]

if args.disable_probable:
signatures = [
s
for s in signatures
if s.test.CONFIDENCE != detection_enums.CONFIDENCE.POTENTIAL
]

logging.warning(f"Testing with {len(signatures)} signatures")


###### scanning

findings = []

if "--out" not in argv:
# using default out location, need to append our format
args.out = f"{args.out}.{args.out_format}"


async def main():
###### domain ingestion
nameservers = (
dns.resolver.Resolver().nameservers
if args.resolver == ""
else args.resolver.replace(" ", "").split(",")
)
Domain.resolver = Resolver(nameservers=nameservers, parallelism=args.parallelism)
provider = getattr(providers, args.provider)
domains = list(provider.fetch_domains(**args.__dict__))

if len(domains) == 0:
logging.error("ERROR: No domains to scan")
exit(-1)

with output.Output(args.out_format, args.out) as o:
scan = partial(
scan_domain,
signatures=signatures,
output_handler=o,
findings=findings,
)

await asyncio.gather(*[asyncio.create_task(scan(domain)) for domain in domains])

###### exit
logging.warning(f"\n\nWe found {len(findings)} takeovers ☠️")
for finding in findings:
msg = f"-- DOMAIN '{finding.domain}' :: SIGNATURE '{finding.signature}' :: CONFIDENCE '{finding.confidence}'"
msg += f"{linesep}{finding.populated_records()}"
if args.nocolour == False:
msg = colorama.Fore.RED + msg + colorama.Fore.RESET
logging.warning(msg)
logging.warning(
f"\n⏱️ We completed in {round(time.time() - start_time, 2)} seconds"
)
logging.warning(f"...Thats all folks!")
if args.pipeline:
logging.debug(f"Pipeline flag set - Exit code: {len(findings)}")
exit(len(findings))


asyncio.run(main())

0 comments on commit 5dd7141

Please sign in to comment.