Skip to content

Commit

Permalink
Merge branch 'CERT-Polska:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
kshitijk4poor authored Aug 5, 2024
2 parents 2feb681 + 526a2ec commit dc01949
Show file tree
Hide file tree
Showing 44 changed files with 408 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker_nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:
main:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 60
steps:
- name: Checkout source code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
jobs:
main:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 60
steps:
- name: Checkout source code
uses: actions/checkout@v4
Expand Down
3 changes: 3 additions & 0 deletions artemis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ class Nuclei:
# Too small impact to report
"http/exposed-panels/webeditors-check-detect.yaml",
# Online stores, CRMs and ticketing systems - it's a standard practice to have them exposed in a small organization
"http/exposed-panels/bitrix-panel.yaml",
"http/exposed-panels/dynamicweb-panel.yaml",
"http/exposed-panels/jira-detect.yaml",
"http/exposed-panels/kanboard-login.yaml",
Expand Down Expand Up @@ -533,6 +534,8 @@ class Nuclei:
"custom:xss-inside-tag-top-params",
"http/miscellaneous/defaced-website-detect.yaml",
"http/misconfiguration/google/insecure-firebase-database.yaml",
# This catches other Open Redirects as well
"http/cves/2018/CVE-2018-11784.yaml",
# Until https://github.com/projectdiscovery/nuclei-templates/issues/8657
# gets fixed, these templates return a FP on phpinfo(). Let's not spam
# our recipients with FPs.
Expand Down
49 changes: 33 additions & 16 deletions artemis/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from starlette.datastructures import Headers

from artemis import csrf
from artemis.binds import TaskType
from artemis.config import Config
from artemis.db import DB, ColumnOrdering, ReportGenerationTaskStatus, TaskFilter
from artemis.json_utils import JSONEncoderAdditionalTypes
Expand Down Expand Up @@ -153,26 +154,42 @@ async def post_add(
targets = targets.strip()
total_list += (x.strip() for x in targets.split("\n"))

validation_messages = []
for task in total_list:
if not Classifier.is_supported(task):
binds = sorted(get_binds_that_can_be_disabled(), key=lambda bind: bind.identity.lower())

return csrf.csrf_form_template_response(
"add.jinja2",
{
"validation_message": f"{task} is not supported - Artemis supports domains, IPs or IP ranges. Domains and IPs may also optionally be followed by port number.",
"request": request,
"binds": binds,
"priority": priority,
"priorities": list(TaskPriority),
"tasks": total_list,
"tag": tag or "",
"disabled_modules": disabled_modules,
"modules_disabled_by_default": Config.Miscellaneous.MODULES_DISABLED_BY_DEFAULT,
},
csrf_protect,
validation_messages.append(
f"{task} is not supported - Artemis supports domains, IPs or IP ranges. Domains and IPs may also optionally be followed by port number."
)

for dependency, task_types in [
("port_scanner", [TaskType.SERVICE.value, TaskType.WEBAPP.value]),
("device_identifier", [TaskType.DEVICE.value]),
]:
if dependency in disabled_modules:
for bind in get_binds_that_can_be_disabled():
if bind.identity not in disabled_modules:
for item in bind.filters:
if "type" in item and item["type"] in task_types:
validation_messages.append(f"Module {bind.identity} requires {dependency} to be enabled")

if validation_messages:
binds = sorted(get_binds_that_can_be_disabled(), key=lambda bind: bind.identity.lower())
return csrf.csrf_form_template_response(
"add.jinja2",
{
"validation_message": ", ".join(validation_messages),
"request": request,
"binds": binds,
"priority": priority,
"priorities": list(TaskPriority),
"tasks": total_list,
"tag": tag or "",
"disabled_modules": disabled_modules,
"modules_disabled_by_default": Config.Miscellaneous.MODULES_DISABLED_BY_DEFAULT,
},
csrf_protect,
)

create_tasks(total_list, tag, disabled_modules, TaskPriority(priority))
if redirect:
return RedirectResponse("/", status_code=301)
Expand Down
19 changes: 19 additions & 0 deletions artemis/load_risk_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from enum import Enum
from typing import Any, Callable


class LoadRiskClass(str, Enum):
LOW = "🟢 Scanned system load/risk: low."
MEDIUM = "🟡 Scanned system load/risk: medium."
HIGH = "🔴 Scanned system load/risk: high."


def load_risk_class(c: LoadRiskClass) -> Callable[[Any], Any]:
def decorator(decorated_class: Any) -> Any:
if decorated_class.__doc__:
decorated_class.__doc__ = decorated_class.__doc__.strip() + "\n\n" + c.value
else:
decorated_class.__doc__ = c.value
return decorated_class

return decorator
3 changes: 2 additions & 1 deletion artemis/modules/bruter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.config import Config
from artemis.karton_utils import check_connection_to_base_url_and_save_error
Expand Down Expand Up @@ -55,6 +55,7 @@ class BruterResult:
checked_paths: List[str]


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.MEDIUM)
class Bruter(ArtemisBase):
"""
Brute-forces common paths such as /index.php.bak. Tries commonly found paths on each target and experiments with random other paths
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from karton.core import Task
from publicsuffixlist import PublicSuffixList

from artemis import load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.config import Config
from artemis.domains import is_domain
Expand All @@ -16,6 +17,7 @@
PUBLIC_SUFFIX_LIST = PublicSuffixList()


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class Classifier(ArtemisBase):
"""
Collects `type: new` and converts them to `type: HAS_DOMAIN` or `type: HAS_IP`
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/device_identifier.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import Device, Service, TaskStatus, TaskType
from artemis.module_base import ArtemisBase
from artemis.task_utils import get_target_host, get_target_url


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class DeviceIdentifier(ArtemisBase):
"""
Tries to identify the device (FortiOS, ...) and produces a DEVICE task with proper type (e.g. Device.FORTIOS)
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/directory_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from bs4 import BeautifulSoup
from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.config import Config
from artemis.models import FoundURL
Expand All @@ -21,6 +21,7 @@
S3_BASE_DOMAIN = "s3.amazonaws.com"


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class DirectoryIndex(ArtemisBase):
"""
Detects directory index enabled on the server by checking paths mentioned in the home page source (e.g. with <link href="/styles/..." ...>).
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/dns_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import dns.zone
from karton.core import Task

from artemis import load_risk_class
from artemis.binds import TaskStatus, TaskType
from artemis.module_base import ArtemisBase

KNOWN_BAD_NAMESERVERS = ["fns1.42.pl", "fns2.42.pl"]


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class DnsScanner(ArtemisBase):
"""
Check for domain transfer and known bad nameservers.
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/domain_expiration_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

from karton.core import Task

from artemis import load_risk_class
from artemis.binds import TaskStatus, TaskType
from artemis.config import Config
from artemis.domains import is_main_domain
from artemis.module_base import ArtemisBase
from artemis.utils import perform_whois_or_sleep


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class DomainExpirationScanner(ArtemisBase):
"""
Alerts if domain expiration date is coming.
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/drupal_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import bs4
from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import TaskStatus, TaskType, WebApplication
from artemis.modules.base.base_newer_version_comparer import (
BaseNewerVersionComparerModule,
Expand All @@ -14,6 +14,7 @@ class DrupalScannerException(Exception):
pass


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class DrupalScanner(BaseNewerVersionComparerModule):
"""
Drupal scanner - checks whether the version is obsolete.
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/example.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from karton.core import Task

from artemis import load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.module_base import ArtemisBase
from artemis.task_utils import get_target_url


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class Example(ArtemisBase):
"""
An example Artemis module that shows how to implement one.
Look into artemis/reporting/modules/example/ to know how to add findings
from this module to the HTML reports.
"""
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/ftp_bruter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from karton.core import Task
from pydantic import BaseModel

from artemis import load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.config import Config
from artemis.module_base import ArtemisBase
Expand All @@ -35,6 +36,7 @@ class FTPBruterResult(BaseModel):
is_writable: bool = False


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.MEDIUM)
class FTPBruter(ArtemisBase):
"""
Performs a brute force attack on FTP servers to guess login and password.
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/http_service_to_url.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env python3
from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.module_base import ArtemisBase
from artemis.task_utils import get_target_url


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class HTTPServiceToURL(ArtemisBase):
"""
Converts HTTP SERVICE tasks to URL tasks for the service root URL so that the URLs can be consumed by other kartons
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/humble.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from karton.core import Task

from artemis import load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.config import Config
from artemis.module_base import ArtemisBase
Expand Down Expand Up @@ -73,6 +74,7 @@ def process_json_data(result: Dict[str, Any]) -> List[Message]:
return list(messages.values())


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class Humble(ArtemisBase):
"""
Runs humble -> A HTTP Headers Analyzer
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/ip_lookup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/usr/bin/env python3
from karton.core import Task

from artemis import load_risk_class
from artemis.binds import TaskType
from artemis.module_base import ArtemisBase
from artemis.resolvers import lookup


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class IPLookup(ArtemisBase):
"""
Collects `type: domain`, performs IP lookup and produces `type: NEW`.
Expand Down
3 changes: 2 additions & 1 deletion artemis/modules/joomla_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

from karton.core import Task

from artemis import http_requests
from artemis import http_requests, load_risk_class
from artemis.binds import TaskStatus, TaskType, WebApplication
from artemis.fallback_api_cache import FallbackAPICache
from artemis.modules.base.base_newer_version_comparer import (
BaseNewerVersionComparerModule,
)


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class JoomlaScanner(BaseNewerVersionComparerModule):
"""
Joomla scanner - checks whether the version is old or registration is enabled.
Expand Down
16 changes: 6 additions & 10 deletions artemis/modules/mail_dns_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from libmailgoose.scan import ScanningException, scan_domain
from publicsuffixlist import PublicSuffixList

from artemis import load_risk_class
from artemis.binds import TaskStatus, TaskType
from artemis.domains import is_main_domain
from artemis.module_base import ArtemisBase
Expand All @@ -22,6 +23,7 @@ class MailDNSScannerResult:
spf_dmarc_scan_result: Optional[SPFDMARCScanResult] = None


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.LOW)
class MailDNSScanner(ArtemisBase):
"""
Checks whether there is a mail server associated with the current domain and checks if SPF and DMARC records are present.
Expand Down Expand Up @@ -115,18 +117,12 @@ def run(self, current_task: Task) -> None:
result = self.scan(current_task, domain)

status_reasons: List[str] = []
if (
result.spf_dmarc_scan_result
and result.spf_dmarc_scan_result.spf
and not result.spf_dmarc_scan_result.spf.valid
):
if result.spf_dmarc_scan_result and result.spf_dmarc_scan_result.spf:
status_reasons.extend(result.spf_dmarc_scan_result.spf.errors)
if (
result.spf_dmarc_scan_result
and result.spf_dmarc_scan_result.dmarc
and not result.spf_dmarc_scan_result.dmarc.valid
):
status_reasons.extend(result.spf_dmarc_scan_result.spf.warnings)
if result.spf_dmarc_scan_result and result.spf_dmarc_scan_result.dmarc:
status_reasons.extend(result.spf_dmarc_scan_result.dmarc.errors)
status_reasons.extend(result.spf_dmarc_scan_result.dmarc.warnings)

if status_reasons:
status = TaskStatus.INTERESTING
Expand Down
2 changes: 2 additions & 0 deletions artemis/modules/mysql_bruter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from karton.core import Task
from pydantic import BaseModel

from artemis import load_risk_class
from artemis.binds import Service, TaskStatus, TaskType
from artemis.module_base import ArtemisBase
from artemis.modules.data.common_sql_credentials import COMMON_SQL_CREDENTIALS
Expand All @@ -21,6 +22,7 @@ class MySQLBruterResult(BaseModel):
credentials: List[Tuple[str, str]] = []


@load_risk_class.load_risk_class(load_risk_class.LoadRiskClass.MEDIUM)
class MySQLBruter(ArtemisBase):
"""
Performs a brute force attack on MySQL servers to guess login and password.
Expand Down
Loading

0 comments on commit dc01949

Please sign in to comment.