Skip to content

Commit 5919df7

Browse files
authored
Validate exploit's setup before run and check commands (#367)
Introduce `Exploit.validate_setup` method in order to check whether Exploit's setup is correct. In most case scanarios we will check if `Exploit.target` is not `None`. When exploit need custom validation logic please overwrite `validate_setup`.
1 parent 5fd3e7d commit 5919df7

File tree

4 files changed

+84
-40
lines changed

4 files changed

+84
-40
lines changed

routersploit/exploits.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from itertools import chain
55
from weakref import WeakKeyDictionary
66

7+
from routersploit.exceptions import OptionValidationError
78
from routersploit.utils import print_status, NonStringIterable
89

910
GLOBAL_OPTS = {}
@@ -133,3 +134,12 @@ def run_threads(self, threads, target, *args, **kwargs):
133134
for worker in workers:
134135
worker.join()
135136
print_status('Elapsed time: ', time.time() - start, 'seconds')
137+
138+
def validate_setup(self):
139+
""" Validate exploit's setup before `run()`
140+
141+
If exploit need extra validation make sure to overwrite this with
142+
custom validation logic.
143+
"""
144+
if not self.target:
145+
raise OptionValidationError("Please set target address.")

routersploit/interpreter.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
from collections import Counter
77

88
from routersploit import utils
9-
from routersploit.exceptions import RoutersploitException
9+
from routersploit.exceptions import (
10+
RoutersploitException,
11+
OptionValidationError,
12+
)
1013
from routersploit.exploits import Exploit, GLOBAL_OPTS
1114
from routersploit.payloads import BasePayload
1215
from routersploit.printer import PrinterThread, printer_queue
@@ -328,12 +331,15 @@ def complete_use(self, text, *args, **kwargs):
328331

329332
@utils.module_required
330333
def command_run(self, *args, **kwargs):
331-
utils.print_status("Running module...")
332334
try:
335+
self.current_module.validate_setup()
336+
utils.print_status("Running module...")
333337
self.current_module.run()
334338
except KeyboardInterrupt:
335339
utils.print_info()
336340
utils.print_error("Operation cancelled by user")
341+
except OptionValidationError as err:
342+
utils.print_error(err)
337343
except Exception:
338344
utils.print_error(traceback.format_exc(sys.exc_info()))
339345

@@ -477,6 +483,7 @@ def complete_show(self, text, *args, **kwargs):
477483
@utils.module_required
478484
def command_check(self, *args, **kwargs):
479485
try:
486+
self.current_module.validate_setup()
480487
result = self.current_module.check()
481488
except Exception as error:
482489
utils.print_error(error)

tests/test_exploits.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import mock
55

6+
from routersploit.exceptions import OptionValidationError
67
from routersploit.exploits import Exploit, GLOBAL_OPTS, Option
78
from tests.test_case import RoutersploitTestCase
89

@@ -95,6 +96,11 @@ def test_exploit_options_property(self):
9596
self.assertEqual(self.exploit_bar.options, ['paa', 'target', 'doo'])
9697
self.assertEqual(self.exploit_foo.options, ['paa', 'target', 'doo'])
9798

99+
def test_validate_setup(self):
100+
with self.assertRaises(OptionValidationError):
101+
self.exploit_bar.target = ""
102+
self.exploit_bar.validate_setup()
103+
98104

99105
if __name__ == '__main__':
100106
unittest.main()

tests/test_interpreter.py

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -154,59 +154,80 @@ def test_command_unsetg_unknown_option(self, mock_print_error):
154154

155155
@mock.patch('routersploit.utils.print_status')
156156
def test_command_run(self, mock_print_status):
157-
with mock.patch.object(self.interpreter.current_module,
158-
'run') as mock_run:
159-
self.interpreter.command_run()
160-
mock_run.assert_called_once_with()
161-
mock_print_status.assert_called_once_with('Running module...')
157+
mock_run = mock.Mock()
158+
mock_validate_setup = mock.Mock()
159+
self.interpreter.current_module.run = mock_run
160+
self.interpreter.current_module.validate_setup = mock_validate_setup
161+
162+
self.interpreter.command_run()
163+
mock_validate_setup.assert_called_once()
164+
mock_run.assert_called_once_with()
165+
mock_print_status.assert_called_once_with('Running module...')
162166

163167
@mock.patch('routersploit.utils.print_success')
164168
def test_command_check_target_vulnerable(self, mock_print_success):
165-
with mock.patch.object(self.interpreter.current_module,
166-
'check') as mock_check:
167-
mock_check.return_value = True
168-
self.interpreter.command_check()
169-
mock_check.assert_called_once_with()
170-
mock_print_success.assert_called_once_with('Target is vulnerable')
169+
mock_check = mock.Mock()
170+
mock_validate_setup = mock.Mock()
171+
self.interpreter.current_module.check = mock_check
172+
self.interpreter.current_module.validate_setup = mock_validate_setup
173+
mock_check.return_value = True
174+
175+
self.interpreter.command_check()
176+
mock_validate_setup.assert_called_once()
177+
mock_check.assert_called_once_with()
178+
mock_print_success.assert_called_once_with('Target is vulnerable')
171179

172180
@mock.patch('routersploit.utils.print_error')
173181
def test_command_check_target_not_vulnerable(self, print_error):
174-
with mock.patch.object(self.interpreter.current_module,
175-
'check') as mock_check:
176-
mock_check.return_value = False
177-
self.interpreter.command_check()
178-
mock_check.assert_called_once_with()
179-
print_error.assert_called_once_with('Target is not vulnerable')
182+
mock_check = mock.Mock()
183+
mock_validate_setup = mock.Mock()
184+
self.interpreter.current_module.check = mock_check
185+
self.interpreter.current_module.validate_setup = mock_validate_setup
186+
mock_check.return_value = False
187+
188+
self.interpreter.command_check()
189+
mock_validate_setup.assert_called_once()
190+
mock_check.assert_called_once_with()
191+
print_error.assert_called_once_with('Target is not vulnerable')
180192

181193
@mock.patch('routersploit.utils.print_status')
182194
def test_command_check_target_could_not_be_verified_1(self, print_status):
183-
with mock.patch.object(self.interpreter.current_module,
184-
'check') as mock_check:
185-
mock_check.return_value = "something"
186-
self.interpreter.command_check()
187-
mock_check.assert_called_once_with()
188-
print_status.assert_called_once_with(
189-
'Target could not be verified')
195+
mock_check = mock.Mock()
196+
mock_validate_setup = mock.Mock()
197+
self.interpreter.current_module.check = mock_check
198+
self.interpreter.current_module.validate_setup = mock_validate_setup
199+
mock_check.return_value = "something"
200+
201+
self.interpreter.command_check()
202+
mock_validate_setup.assert_called_once()
203+
mock_check.assert_called_once_with()
204+
print_status.assert_called_once_with('Target could not be verified')
190205

191206
@mock.patch('routersploit.utils.print_status')
192207
def test_command_check_target_could_not_be_verified_2(self, print_status):
193-
with mock.patch.object(self.interpreter.current_module,
194-
'check') as mock_check:
195-
mock_check.return_value = None
196-
self.interpreter.command_check()
197-
mock_check.assert_called_once_with()
198-
print_status.assert_called_once_with(
199-
'Target could not be verified')
208+
mock_check = mock.Mock()
209+
mock_validate_setup = mock.Mock()
210+
self.interpreter.current_module.check = mock_check
211+
self.interpreter.current_module.validate_setup = mock_validate_setup
212+
mock_check.return_value = None
213+
214+
self.interpreter.command_check()
215+
mock_validate_setup.assert_called_once()
216+
mock_check.assert_called_once_with()
217+
print_status.assert_called_once_with('Target could not be verified')
200218

201219
@mock.patch('routersploit.utils.print_error')
202220
def test_command_check_not_supported_by_module(self, print_error):
203-
with mock.patch.object(self.interpreter.current_module,
204-
'check') as mock_check:
205-
exception = NotImplementedError("Not available")
206-
mock_check.side_effect = exception
207-
self.interpreter.command_check()
208-
mock_check.assert_called_once_with()
209-
print_error.assert_called_once_with(exception)
221+
mock_check = mock.Mock()
222+
mock_validate_setup = mock.Mock()
223+
self.interpreter.current_module.check = mock_check
224+
self.interpreter.current_module.validate_setup = mock_validate_setup
225+
exception = NotImplementedError("Not available")
226+
mock_check.side_effect = exception
227+
228+
self.interpreter.command_check()
229+
mock_check.assert_called_once_with()
230+
print_error.assert_called_once_with(exception)
210231

211232
@mock.patch('sys.exc_info')
212233
@mock.patch('traceback.format_exc')

0 commit comments

Comments
 (0)