Module: PWN::Plugins::Httpx

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

Overview

httpx JSONL probe: live URLs, status, title, and detected tech stack.

Class Method Summary collapse

Class Method Details

.authorsObject



37
38
39
# File 'lib/pwn/plugins/httpx.rb', line 37

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

.helpObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/pwn/plugins/httpx.rb', line 41

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

    # Probe URLs with httpx JSONL or ingest an existing JSONL file.
    #{self}.probe(
      target: 'optional - single URL or hostname (required unless jsonl or targets is set)',
      targets: 'optional - Array of URLs',
      urls: 'optional - alias for targets',
      jsonl: 'optional - httpx JSONL file path or raw JSONL text (skips the httpx binary)'
    )

    # Flatten tech/product names from parsed httpx JSONL rows.
    #{self}.techs(
      rows: 'optional - Array of parsed httpx JSON objects',
      hosts: 'optional - alias for rows'
    )

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

.probe(opts = {}) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/pwn/plugins/httpx.rb', line 14

public_class_method def self.probe(opts = {})
  stdout = jsonl_body(opts)
  unless stdout
    PWN::Plugins::PreflightChecker.require_bin!(name: 'httpx')
    targets = Array(opts[:targets] || opts[:target] || opts[:urls]).map(&:to_s).reject(&:empty?)
    raise 'ERROR: target is required' if targets.empty?

    cmd = ['httpx', '-silent', '-json', '-title', '-tech-detect', '-status-code']
    cmd += ['-u', *targets] if targets.length == 1
    stdout, stderr, status = capture(cmd: cmd, stdin: targets.length > 1 ? "#{targets.join("\n")}\n" : nil)
  end
  rows = parse_jsonl(text: stdout)
  { hosts: rows, techs: techs(rows: rows), stderr: stderr, exit: status&.exitstatus }
end

.required_binsObject



10
11
12
# File 'lib/pwn/plugins/httpx.rb', line 10

public_class_method def self.required_bins
  %w[httpx]
end

.techs(opts = {}) ⇒ Object



29
30
31
32
33
34
35
# File 'lib/pwn/plugins/httpx.rb', line 29

public_class_method def self.techs(opts = {})
  names = Array(opts[:rows] || opts[:hosts]).flat_map do |row|
    row = row.transform_keys(&:to_s) if row.is_a?(Hash)
    Array(row['tech'] || row['technologies'] || row[:tech]) + [row['webserver'], row['title']].compact
  end
  names.map { |item| item.to_s.downcase }.uniq.reject(&:empty?)
end