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 authored and tarraschk committed Sep 27, 2023
1 parent 6be9bb3 commit f487f64
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 13 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
12 changes: 10 additions & 2 deletions 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 typing import Optional
from os.path import join as path_join
from time import monotonic
from typing import Optional

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 monotonic() - self.start > 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 = monotonic()
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
11 changes: 9 additions & 2 deletions 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 typing import Optional, Iterator
from os.path import join as path_join
from time import monotonic
from typing import Optional, Iterator

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 = monotonic()
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 monotonic() - self.start > 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
14 changes: 11 additions & 3 deletions 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 os.path import join as path_join
from collections import defaultdict, namedtuple
import re
from collections import defaultdict, namedtuple
from os.path import join as path_join
from time import monotonic
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 = monotonic()
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 monotonic() - self.start > 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
11 changes: 9 additions & 2 deletions wapitiCore/attack/mod_nikto.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import asyncio
import csv
import re
import os
import random
from urllib.parse import urlparse
import re
from time import monotonic
from typing import List, Optional
from urllib.parse import urlparse

from httpx import RequestError

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 = monotonic()
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 monotonic() - self.start > 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
10 changes: 9 additions & 1 deletion wapitiCore/attack/mod_timesql.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from math import ceil
from typing import Optional, Iterator
from os.path import join as path_join
from time import monotonic
from typing import Optional, Iterator

from httpx import ReadTimeout, RequestError

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 = monotonic()
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 monotonic() - self.start > 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 @@ -3,6 +3,7 @@
import xml
import xml.etree.ElementTree as ET
from os.path import join as path_join
from time import monotonic
from typing import Match, Optional

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

if monotonic() - self.start > 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 monotonic() - self.start > 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 = monotonic()
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 f487f64

Please sign in to comment.