Module: Msf::Module::Failure

Included in:
Msf::Module
Defined in:
lib/msf/core/module/failure.rb

Overview

Constants indicating the reason for an unsuccessful module attempt

Constant Summary collapse

BadConfig =

The exploit settings were incorrect

'bad-config'
Disconnected =

The network service disconnected us mid-attempt

'disconnected'
NoAccess =

The application replied indication we do not have access

'no-access'
None =

No confidence in success or failure

'none'
NoTarget =

The target is not compatible with this exploit or settings

'no-target'
NotFound =

The application endpoint or specific service was not found

'not-found'
NotVulnerable =

The application response indicated it was not vulnerable

'not-vulnerable'
PayloadFailed =

The payload was delivered but no session was opened (AV, network, etc)

'payload-failed'
TimeoutExpired =

The exploit triggered some form of timeout

'timeout-expired'
UnexpectedReply =

The application replied in an unexpected fashion

'unexpected-reply'
Unknown =

No confidence in success or failure

'unknown'
Unreachable =

The network service was unreachable (connection refused, etc)

'unreachable'
UserInterrupt =

The exploit was interrupted by the user

'user-interrupt'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.fail_reason_from_check_code(check_code) ⇒ String?

Map a Exploit::CheckCode to the corresponding fail_reason constant.

Parameters:

Returns:

  • (String, nil)

    a Failure constant, or nil if unmapped



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/msf/core/module/failure.rb', line 46

def self.fail_reason_from_check_code(check_code)
  return nil unless check_code.respond_to?(:code)

  case check_code.code
  when Msf::Exploit::CheckCode::Vulnerable.code, Msf::Exploit::CheckCode::Appears.code
    None
  when Msf::Exploit::CheckCode::Safe.code
    NotVulnerable
  when Msf::Exploit::CheckCode::Detected.code, Msf::Exploit::CheckCode::Unknown.code
    Unknown
  end
end

Instance Method Details

#report_failureObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/msf/core/module/failure.rb', line 60

def report_failure
  return unless framework.db and framework.db.active

  info = {
    timestamp: Time.now.utc,
    workspace: framework.db.find_workspace(self.workspace),
    module: self.fullname,
    fail_reason: self.fail_reason,
    fail_detail: self.fail_detail,
    username: self.owner,
    refs: self.references,
    run_id: self.datastore['RUN_ID']
  }
  info[:target_name] = self.target.name if self.respond_to?(:target)

  # Enrich attempt data with check result details when available
  if self.respond_to?(:check_code) && self.check_code.is_a?(Msf::Exploit::CheckCode)
    info[:check_code] = self.check_code.code
    info[:check_detail] = self.check_code.reason || self.check_code.message
    mapped_reason = Msf::Module::Failure.fail_reason_from_check_code(self.check_code)
    info[:fail_reason] = mapped_reason if mapped_reason
  end

  if self.datastore['RHOST'] && (self.options['RHOST'] || self.options['RHOSTS'])
    # Only include RHOST if it's a single valid host, not a multi-value string or file path
    rhost = self.datastore['RHOST'].to_s
    # Check if RHOST is a valid IP address to avoid ActiveRecord issues on validation
    if Rex::Socket.is_ip_addr?(rhost)
      info[:host] = rhost
    end
  end

  if self.datastore['RPORT'] and self.options['RPORT']
    info[:port] = self.datastore['RPORT']
    if self.class.ancestors.include?(Msf::Exploit::Remote::Tcp)
      info[:proto] = 'tcp'
    elsif self.class.ancestors.include?(Msf::Exploit::Remote::Udp)
      info[:proto] = 'udp'
    end
  end

  # When the check identified a vulnerability, ensure the vuln record exists
  # before report_exploit_failure tries to look it up.  The UI-level
  # check_simple also calls report_vuln, but that happens *after* this
  # ensure block, so the vuln wouldn't exist yet for the attempt lookup.
  if info[:host] && self.respond_to?(:check_code) &&
     self.check_code.is_a?(Msf::Exploit::CheckCode) &&
     [Msf::Exploit::CheckCode::Vulnerable, Msf::Exploit::CheckCode::Appears].include?(self.check_code)
    vuln_info = if self.check_code == Msf::Exploit::CheckCode::Appears
      "Target appears vulnerable based on check of #{self.fullname}."
    else
      "Vulnerability confirmed by check of #{self.fullname}."
    end
    vuln_opts = {
      workspace: info[:workspace],
      host: info[:host],
      name: self.name,
      refs: self.references,
      info: vuln_info
    }
    # Include port so that checks against different ports on the same
    # host create distinct vuln records instead of collapsing into one.
    vuln_opts[:port] = info[:port] if info[:port]
    vuln_opts[:proto] = info[:proto] if info[:proto]
    framework.db.report_vuln(vuln_opts)
  end

  # Skip creating a duplicate vuln attempt if one was already recorded
  # during this run (e.g. by Auxiliary::Report#report_vuln).  When a
  # check_code is available, update the existing attempt so it carries the
  # check result details (the attempt created by report_vuln may not have
  # had the check_code yet because it runs before job_run_proc stores it).
  if self.respond_to?(:last_vuln_attempt) && self.last_vuln_attempt
    if self.respond_to?(:check_code) && self.check_code.is_a?(Msf::Exploit::CheckCode)
      _enrich_existing_vuln_attempt(info, self.last_vuln_attempt)
    end
    info[:skip_vuln_attempt] = true
  end

  framework.db.report_exploit_failure(info)
end