Module: PWN::Plugins::ExploitDB

Defined in:
lib/pwn/plugins/exploit_db.rb

Overview

searchsploit / ExploitDB plus a CPE string builder for nmap versions.

Constant Summary collapse

EXPLOITDB_CSV_URL =
'https://gitlab.com/exploit-database/exploitdb/-/raw/main/files_exploits.csv'
GTFOBINS_TAR_URL =
'https://codeload.github.com/GTFOBins/GTFOBins.github.io/tar.gz/master'

Class Method Summary collapse

Class Method Details

.authorsObject



92
93
94
# File 'lib/pwn/plugins/exploit_db.rb', line 92

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <[email protected]>\n"
end

.cve_for_cpe(opts = {}) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/pwn/plugins/exploit_db.rb', line 41

public_class_method def self.cve_for_cpe(opts = {})
  cpe = opts[:cpe].to_s
  product = opts[:product].to_s
  version = opts[:version].to_s
  q = cpe
  q = "#{product} #{version}".strip if q.empty?
  raise 'ERROR: cpe or product is required' if q.empty?

  search(query: q).merge(cpe: cpe, product: product, version: version)
end

.gtfobins(opts = {}) ⇒ Object



52
53
54
55
56
57
58
59
60
# File 'lib/pwn/plugins/exploit_db.rb', line 52

public_class_method def self.gtfobins(opts = {})
  name = (opts[:binary] || opts[:query] || opts[:q]).to_s.downcase
  raise 'ERROR: binary is required' if name.empty?

  path = File.join(intel_dir, 'gtfobins.json')
  data = File.file?(path) ? JSON.parse(File.read(path)) : {}
  row = data[name] || data.values.find { |v| v.is_a?(Hash) && v['binary'].to_s.downcase == name }
  { binary: name, hit: row, source: 'local' }
end

.helpObject



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
141
142
143
144
# File 'lib/pwn/plugins/exploit_db.rb', line 96

public_class_method def self.help
  puts "USAGE:
    # List host binaries this module expects to be installed.
    #{self}.required_bins

    # Search local ExploitDB via searchsploit --json.
    #{self}.search(
      query: 'required - product, CVE, or keyword',
      q: 'optional - alias for query'
    )

    # Build a search from an nmap CPE/product/version tuple.
    #{self}.cve_for_cpe(
      cpe: 'optional - CPE string (e.g. cpe:/a:apache:http_server:2.4.49)',
      product: 'optional - product name when no CPE',
      version: 'optional - version string from nmap_it'
    )

    # Look up a local GTFOBins-style primitive by binary name.
    #{self}.gtfobins(
      binary: 'required - host binary name (defaults to query/q)',
      query: 'optional - alias for binary',
      q: 'optional - alias for binary'
    )

    # Offline intel lookup by kind (cve, binary, technique).
    #{self}.lookup(
      kind: 'optional - cve|binary|technique (defaults to cve)',
      query: 'optional - search text',
      cve: 'optional - CVE id',
      binary: 'optional - GTFOBins binary name',
      technique: 'optional - technique keyword'
    )

    # Fetch latest ExploitDB CSV and GTFOBins into ~/.pwn/intel (not the pwn tree).
    #{self}.sync(
      dir: 'optional - destination directory (defaults to ~/.pwn/intel)',
      dry_run: 'optional - true to list source URLs without writing',
      timeout: 'optional - HTTP timeout seconds',
      csv_url: 'optional - ExploitDB files_exploits.csv URL override',
      gtfobins_url: 'optional - GTFOBins tarball or JSON URL override',
      get: 'optional - proc taking a URL string and returning the response body'
    )

    # Print the AUTHOR(S) string for this module.
    #{self}.authors
  "
  constants.sort
end

.lookup(opts = {}) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/pwn/plugins/exploit_db.rb', line 62

public_class_method def self.lookup(opts = {})
  kind = (opts[:kind] || 'cve').to_s
  q = (opts[:query] || opts[:cve] || opts[:binary] || opts[:technique]).to_s
  return gtfobins(binary: q) if %w[binary gtfobins].include?(kind)

  search(query: q).merge(kind: kind)
end

.required_binsObject



20
21
22
# File 'lib/pwn/plugins/exploit_db.rb', line 20

public_class_method def self.required_bins
  %w[searchsploit]
end

.search(opts = {}) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/pwn/plugins/exploit_db.rb', line 24

public_class_method def self.search(opts = {})
  q = (opts[:query] || opts[:q]).to_s
  raise 'ERROR: query is required' if q.empty?

  local = local_hits(query: q)
  return { query: q, results: local, source: 'local' } unless local.empty?
  return { error: 'searchsploit missing', query: q, results: [] } unless PWN::Plugins::PreflightChecker.bin?(name: 'searchsploit')

  stdout, stderr, status = Open3.capture3('searchsploit', '--json', q)
  parsed = begin
    JSON.parse(stdout)
  rescue JSON::ParserError
    {}
  end
  { query: q, results: parsed['RESULTS_EXPLOIT'] || parsed['results'] || [], stderr: stderr, exit: status.exitstatus }
end

.sync(opts = {}) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pwn/plugins/exploit_db.rb', line 70

public_class_method def self.sync(opts = {})
  dest = opts[:dir].to_s
  dest = intel_dir if dest.empty?
  FileUtils.mkdir_p(dest)
  fetched = []
  csv_url = (opts[:csv_url] || EXPLOITDB_CSV_URL).to_s
  bins_url = (opts[:gtfobins_url] || GTFOBINS_TAR_URL).to_s
  return { dest: dest, dry_run: true, urls: [csv_url, bins_url], fetched: [] } if opts[:dry_run]

  csv_body = http_get(url: csv_url, get: opts[:get], timeout: opts[:timeout])
  csv_path = File.join(dest, 'files_exploits.csv')
  File.binwrite(csv_path, csv_body)
  fetched << 'files_exploits.csv'

  bins_body = http_get(url: bins_url, get: opts[:get], timeout: opts[:timeout])
  json_path = File.join(dest, 'gtfobins.json')
  File.write(json_path, gtfobins_json(body: bins_body))
  fetched << 'gtfobins.json'

  { dest: dest, fetched: fetched, dry_run: false }
end