Skip to content

Commit a16a10a

Browse files
committed
Fix rapid7#6371, being able to report an exception in #job_run_proc
Fix rapid7#6371 When a browser fails to bind (probably due to an invalid port or server IP), the module actually fails to report this exception from exception, the method calls exploit.handle_exception(e). But since handle_exception is not a valid method for that object, it is unable to do so, and as a result the module fails to properly terminate the module, or show any error on the console. For the user, this will make it look like the module has started, the payload listener is up, but there is no exploit job. Rex::BindFailed actually isn't the only error that could be raised by #job_run_proc. As far as I can tell registering the same resource again could, too. With this patch, the user should be able to see this error too. Since the exploit object does not have access to the methods in Msf::Simple::Exploit, plus there is no other code using handle_exception and setup_fail_detail_from_exception, I decided to move these to lib/msf/core/exploit.rb so they are actually callable.
1 parent 21b628a commit a16a10a

File tree

2 files changed

+90
-84
lines changed

2 files changed

+90
-84
lines changed

lib/msf/base/simple/exploit.rb

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -154,90 +154,6 @@ def self.exploit_simple(oexploit, opts, &block)
154154
nil
155155
end
156156

157-
158-
def setup_fail_detail_from_exception e
159-
# Build a user-friendly error message
160-
msg = "#{e}"
161-
unless e.class == Msf::Exploit::Failed
162-
msg = "#{e.class} #{e}"
163-
end
164-
165-
self.error = e
166-
167-
# Record the detailed reason
168-
self.fail_detail ||= e.to_s
169-
msg
170-
end
171-
172-
#
173-
# Handle the exception
174-
#
175-
def handle_exception e
176-
msg = setup_fail_detail_from_exception e
177-
178-
case e
179-
when Msf::Exploit::Complete
180-
# Nothing to show in this case
181-
return
182-
183-
when Msf::Exploit::Failed
184-
self.print_error("Exploit aborted due to failure: #{self.fail_reason}: #{msg}")
185-
186-
# The caller should have already set self.fail_reason
187-
if self.fail_reason == Msf::Exploit::Failure::None
188-
self.fail_reason = Msf::Exploit::Failure::Unknown
189-
end
190-
191-
when Rex::ConnectionError
192-
self.fail_reason = Msf::Exploit::Failure::Unreachable
193-
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
194-
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
195-
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
196-
197-
when Timeout::Error
198-
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
199-
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
200-
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
201-
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
202-
else
203-
204-
# Compare as a string since not all error classes may be loaded
205-
case msg
206-
when /access.denied|Login Failed/i # Covers SMB as well as some generic errors
207-
self.fail_reason = Msf::Exploit::Failure::NoAccess
208-
when /connection reset/i
209-
self.fail_reason = Msf::Exploit::Failure::Disconnected
210-
when /connection timed out|SSL_connect|unreachable|connection was refused/i
211-
self.fail_reason = Msf::Exploit::Failure::Unreachable
212-
when /unable.*target/i
213-
self.fail_reason = Msf::Exploit::Failure::NoTarget
214-
when /execution expired/i
215-
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
216-
when /(doesn.t|not).*vulnerable|may.*patched/i
217-
self.fail_reason = Msf::Exploit::Failure::NotVulnerable
218-
end
219-
220-
# The caller should have already set self.fail_reason
221-
if self.fail_reason == Msf::Exploit::Failure::None
222-
self.fail_reason = Msf::Exploit::Failure::Unknown
223-
end
224-
225-
if self.fail_reason == Msf::Exploit::Failure::Unknown
226-
self.print_error("Exploit failed: #{msg}")
227-
else
228-
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
229-
end
230-
231-
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
232-
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
233-
end
234-
235-
# Record the error to various places
236-
self.framework.events.on_module_error(self, msg)
237-
238-
# Report the failure (and attempt) in the database
239-
self.report_failure
240-
end
241157
#
242158
# Calls the class method.
243159
#

lib/msf/core/exploit.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,96 @@ def fail_with(reason,msg=nil)
12631263
raise Msf::Exploit::Failed, (msg || "No failure message given")
12641264
end
12651265

1266+
def setup_fail_detail_from_exception e
1267+
# Build a user-friendly error message
1268+
msg = "#{e}"
1269+
unless e.class == Msf::Exploit::Failed
1270+
msg = "#{e.class} #{e}"
1271+
end
1272+
1273+
self.error = e
1274+
1275+
# Record the detailed reason
1276+
self.fail_detail ||= e.to_s
1277+
msg
1278+
end
1279+
1280+
#
1281+
# Handle the exception
1282+
#
1283+
def handle_exception e
1284+
msg = setup_fail_detail_from_exception e
1285+
1286+
case e
1287+
when Msf::Exploit::Complete
1288+
# Nothing to show in this case
1289+
return
1290+
1291+
when Msf::Exploit::Failed
1292+
self.print_error("Exploit aborted due to failure: #{self.fail_reason}: #{msg}")
1293+
1294+
# The caller should have already set self.fail_reason
1295+
if self.fail_reason == Msf::Exploit::Failure::None
1296+
self.fail_reason = Msf::Exploit::Failure::Unknown
1297+
end
1298+
1299+
when Rex::ConnectionError
1300+
self.fail_reason = Msf::Exploit::Failure::Unreachable
1301+
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
1302+
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
1303+
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
1304+
1305+
when Rex::BindFailed
1306+
self.fail_reason = Msf::Exploit::Failure::BadConfig
1307+
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
1308+
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
1309+
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
1310+
1311+
when Timeout::Error
1312+
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
1313+
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
1314+
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
1315+
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
1316+
else
1317+
1318+
# Compare as a string since not all error classes may be loaded
1319+
case msg
1320+
when /access.denied|Login Failed/i # Covers SMB as well as some generic errors
1321+
self.fail_reason = Msf::Exploit::Failure::NoAccess
1322+
when /connection reset/i
1323+
self.fail_reason = Msf::Exploit::Failure::Disconnected
1324+
when /connection timed out|SSL_connect|unreachable|connection was refused/i
1325+
self.fail_reason = Msf::Exploit::Failure::Unreachable
1326+
when /unable.*target/i
1327+
self.fail_reason = Msf::Exploit::Failure::NoTarget
1328+
when /execution expired/i
1329+
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
1330+
when /(doesn.t|not).*vulnerable|may.*patched/i
1331+
self.fail_reason = Msf::Exploit::Failure::NotVulnerable
1332+
end
1333+
1334+
# The caller should have already set self.fail_reason
1335+
if self.fail_reason == Msf::Exploit::Failure::None
1336+
self.fail_reason = Msf::Exploit::Failure::Unknown
1337+
end
1338+
1339+
if self.fail_reason == Msf::Exploit::Failure::Unknown
1340+
self.print_error("Exploit failed: #{msg}")
1341+
else
1342+
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
1343+
end
1344+
1345+
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
1346+
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
1347+
end
1348+
1349+
# Record the error to various places
1350+
self.framework.events.on_module_error(self, msg)
1351+
1352+
# Report the failure (and attempt) in the database
1353+
self.report_failure
1354+
end
1355+
12661356
def report_failure
12671357
return unless framework.db and framework.db.active
12681358

0 commit comments

Comments
 (0)