Skip to content

Commit

Permalink
Add time check for time-consuming modules
Browse files Browse the repository at this point in the history
  • Loading branch information
bretfourbe committed Sep 22, 2023
1 parent 6be9bb3 commit 6e03786
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 6 deletions.
5 changes: 5 additions & 0 deletions wapitiCore/attack/attack.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def __init__(
self._stop_event = stop_event
self.options = attack_options
self.crawler_configuration = crawler_configuration
self.start = 0

# List of attack urls already launched in the current module
self.attacked_get = []
Expand Down Expand Up @@ -286,6 +287,10 @@ def internal_endpoint(self):
def external_endpoint(self):
return self.options.get("external_endpoint", "http://wapiti3.ovh")

@property
def max_attack_time(self):
return self.options.get("max_attack_time", 0)

@property
def proto_endpoint(self):
parts = urlparse(self.external_endpoint)
Expand Down
10 changes: 9 additions & 1 deletion wapitiCore/attack/mod_buster.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import asyncio
from datetime import datetime
from typing import Optional
from os.path import join as path_join

from httpx import RequestError

from wapitiCore.main.log import log_red, log_verbose
from wapitiCore.main.log import log_red, log_verbose, logging
from wapitiCore.attack.attack import Attack
from wapitiCore.net import Request, Response
from wapitiCore.definitions.buster import NAME, WSTG_CODE
Expand Down Expand Up @@ -99,6 +100,12 @@ async def test_directory(self, path: str):

with open(path_join(self.DATA_DIR, self.PATHS_FILE), encoding="utf-8", errors="ignore") as wordlist:
while True:
if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break

if pending_count < self.options["tasks"] and not self._stop_event.is_set():
try:
candidate = next(wordlist).strip()
Expand Down Expand Up @@ -132,6 +139,7 @@ async def test_directory(self, path: str):
tasks.remove(task)

async def attack(self, request: Request, response: Optional[Response] = None):
self.start = datetime.utcnow()
self.finished = True
if not self.do_get:
return
Expand Down
3 changes: 1 addition & 2 deletions wapitiCore/attack/mod_drupal_enum.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import hashlib
import json
import logging
from os.path import join as path_join
from typing import Tuple, Optional
from httpx import RequestError
Expand All @@ -11,7 +10,7 @@
from wapitiCore.net.response import Response
from wapitiCore.definitions.fingerprint_webapp import NAME as WEB_APP_VERSIONED
from wapitiCore.definitions.fingerprint import NAME as TECHNO_DETECTED, WSTG_CODE
from wapitiCore.main.log import log_blue
from wapitiCore.main.log import log_blue, logging

MSG_TECHNO_VERSIONED = "{0} {1} detected"
MSG_NO_DRUPAL = "No Drupal Detected"
Expand Down
9 changes: 8 additions & 1 deletion wapitiCore/attack/mod_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from datetime import datetime
from typing import Optional, Iterator
from os.path import join as path_join

from httpx import ReadTimeout, RequestError

from wapitiCore.main.log import log_red, log_verbose, log_orange
from wapitiCore.main.log import log_red, log_verbose, log_orange, logging
from wapitiCore.attack.attack import Attack
from wapitiCore.language.vulnerability import Messages
from wapitiCore.definitions.exec import NAME, WSTG_CODE
Expand Down Expand Up @@ -74,6 +75,7 @@ def _find_warning_in_response(data) -> str:
return vuln_info

async def attack(self, request: Request, response: Optional[Response] = None):
self.start = datetime.utcnow()
warned = False
timeouted = False
page = request.path
Expand All @@ -82,6 +84,11 @@ async def attack(self, request: Request, response: Optional[Response] = None):
vulnerable_parameter = False

for mutated_request, parameter, payload_info in self.mutator.mutate(request, self.get_payloads):
if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break
if current_parameter != parameter:
# Forget what we know about current parameter
current_parameter = parameter
Expand Down
10 changes: 9 additions & 1 deletion wapitiCore/attack/mod_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from datetime import datetime
from os.path import join as path_join
from collections import defaultdict, namedtuple
import re
from typing import Optional, Iterator

from httpx import ReadTimeout, RequestError

from wapitiCore.main.log import log_red, log_orange, log_verbose
from wapitiCore.main.log import log_red, log_orange, log_verbose, logging
from wapitiCore.attack.attack import Attack
from wapitiCore.model import PayloadInfo
from wapitiCore.parsers.ini_payload_parser import IniPayloadReader, replace_tags
Expand Down Expand Up @@ -144,6 +145,7 @@ async def is_false_positive(self, request, pattern):
return False

async def attack(self, request: Request, response: Optional[Response] = None):
self.start = datetime.utcnow()
warned = False
timeouted = False
page = request.path
Expand All @@ -152,6 +154,12 @@ async def attack(self, request: Request, response: Optional[Response] = None):
vulnerable_parameter = False

for mutated_request, parameter, payload_info in self.mutator.mutate(request, self.get_payloads):
if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break

if current_parameter != parameter:
# Forget what we know about current parameter
current_parameter = parameter
Expand Down
7 changes: 7 additions & 0 deletions wapitiCore/attack/mod_nikto.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import asyncio
import csv
from datetime import datetime
import re
import os
import random
Expand Down Expand Up @@ -129,6 +130,7 @@ async def is_false_positive(self, evil_request: Request, expected_status_codes:
return self.status_codes[request.path] in expected_status_codes

async def attack(self, request: Request, response: Optional[Response] = None):
self.start = datetime.utcnow()
try:
with open(os.path.join(self.user_config_dir, self.NIKTO_DB), encoding='utf-8') as nikto_db_file:
reader = csv.reader(nikto_db_file)
Expand All @@ -150,6 +152,11 @@ async def attack(self, request: Request, response: Optional[Response] = None):
with open(os.path.join(self.user_config_dir, self.NIKTO_DB), encoding='utf-8') as nikto_db_file:
reader = csv.reader(nikto_db_file)
while True:
if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break
if pending_count < self.options["tasks"] and not self._stop_event.is_set():
try:
line = next(reader)
Expand Down
8 changes: 8 additions & 0 deletions wapitiCore/attack/mod_timesql.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from datetime import datetime
from math import ceil
from typing import Optional, Iterator
from os.path import join as path_join
Expand Down Expand Up @@ -56,12 +57,19 @@ def get_payloads(self) -> Iterator[PayloadInfo]:
yield from parser

async def attack(self, request: Request, response: Optional[Response] = None):
self.start = datetime.utcnow()
page = request.path
saw_internal_error = False
current_parameter = None
vulnerable_parameter = False

for mutated_request, parameter, _payload in self.mutator.mutate(request, self.get_payloads):
if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break

if current_parameter != parameter:
# Forget what we know about current parameter
current_parameter = parameter
Expand Down
15 changes: 14 additions & 1 deletion wapitiCore/attack/mod_wp_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import xml
import xml.etree.ElementTree as ET
from datetime import datetime
from os.path import join as path_join
from typing import Match, Optional

Expand Down Expand Up @@ -98,6 +99,12 @@ async def detect_plugin(self, url):
if self._stop_event.is_set():
break

if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break

request = Request(f'{url}/wp-content/plugins/{plugin}/readme.txt', 'GET')
response = await self.crawler.async_send(request)

Expand Down Expand Up @@ -156,6 +163,12 @@ async def detect_theme(self, url):
if self._stop_event.is_set():
break

if (datetime.utcnow() - self.start).total_seconds() > self.max_attack_time >= 1:
logging.info(
f"Skipping: attack time reached for module {self.name}."
)
break

request = Request(f'{url}/wp-content/themes/{theme}/readme.txt', 'GET')
response = await self.crawler.async_send(request)
if response.is_success:
Expand Down Expand Up @@ -218,7 +231,7 @@ async def must_attack(self, request: Request, response: Optional[Response] = Non
return request.url == await self.persister.get_root_url()

async def attack(self, request: Request, response: Optional[Response] = None):

self.start = datetime.utcnow()
self.finished = True
request_to_root = Request(request.url)

Expand Down
1 change: 1 addition & 0 deletions wapitiCore/main/wapiti.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ async def wapiti_main():
"tasks": args.tasks,
"headless": wap.headless_mode,
"excluded_urls": wap.excluded_urls,
"max_attack_time" : args.max_attack_time
}

if "dns_endpoint" in args:
Expand Down

0 comments on commit 6e03786

Please sign in to comment.