Module: PWN::Plugins::AFLplusplus
- Defined in:
- lib/pwn/plugins/aflplusplus.rb
Overview
AFL++ campaign wrapper.
Class Method Summary collapse
- .authors ⇒ Object
- .crash_triage(opts = {}) ⇒ Object
- .fuzz(opts = {}) ⇒ Object
- .help ⇒ Object
- .minimize(opts = {}) ⇒ Object
- .parse_stats(opts = {}) ⇒ Object
- .required_bins ⇒ Object
Class Method Details
.authors ⇒ Object
74 75 76 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 74 public_class_method def self. "AUTHOR(S):\n 0day Inc. <[email protected]>\n" end |
.crash_triage(opts = {}) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 44 public_class_method def self.crash_triage(opts = {}) out_dir = (opts[:out_dir] || opts[:output]).to_s raise 'ERROR: out_dir is required' if out_dir.empty? crashes = Dir[File.join(out_dir, '**/crashes/id:*')] hashes = crashes.map { |p| [p, Digest::SHA256.file(p).hexdigest] } rows = hashes.map do |path, sha| art = File.join(out_dir, "crash-#{sha[0, 12]}.json") File.write(art, JSON.generate(path: path, sha256: sha)) { path: path, sha256: sha, artifact: art } end { crashes: crashes, unique: hashes.map(&:last).uniq, asan: crashes.any? { |p| File.binread(p).include?('AddressSanitizer') }, artifacts: rows } end |
.fuzz(opts = {}) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 15 public_class_method def self.fuzz(opts = {}) PWN::Plugins::PreflightChecker.require_bin!(name: 'afl-fuzz') in_dir = opts[:in_dir] || opts[:corpus] out_dir = opts[:out_dir] || opts[:output] target = opts[:target] raise 'ERROR: in_dir, out_dir, and target are required' if in_dir.to_s.empty? || out_dir.to_s.empty? || target.to_s.empty? cmd = ['afl-fuzz', '-i', in_dir.to_s, '-o', out_dir.to_s, '--', target.to_s] cmd.concat(Array(opts[:args])) stdout, stderr, status = Open3.capture3(*cmd) { stdout: stdout, stderr: stderr, exit: status.exitstatus } end |
.help ⇒ Object
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 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 78 public_class_method def self.help puts "USAGE: # List host binaries this module expects to be installed. #{self}.required_bins # Run fuzz and return its result #{self}.fuzz( in_dir: 'required - in dir value consumed by #fuzz (defaults to opts[:corpus])', corpus: 'optional - corpus value consumed by #fuzz', out_dir: 'required - out dir value consumed by #fuzz (defaults to opts[:output])', output: 'optional - output value consumed by #fuzz', target: 'required - hostname, IP, or CIDR to scan', args: 'optional - Array args value consumed by #fuzz' ) # Parse AFL fuzzer_stats (execs_per_sec, paths, crashes) from out_dir. #{self}.parse_stats( out_dir: 'required - AFL output directory containing fuzzer_stats', output: 'optional - alias for out_dir' ) # Dedup AFL++ crash files by sha256 and note AddressSanitizer hits. #{self}.crash_triage( out_dir: 'required - AFL output directory containing crashes/', output: 'optional - alias for out_dir' ) # Minimize a crash input with afl-tmin when present. #{self}.minimize( crash: 'required - path to a crashing input', out: 'required - path to write the minimized input', target: 'required - fuzz target binary' ) # Print the AUTHOR(S) string for this module. #{self}.authors " constants.sort end |
.minimize(opts = {}) ⇒ Object
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 63 public_class_method def self.minimize(opts = {}) crash = opts[:crash].to_s out = opts[:out].to_s target = opts[:target].to_s raise 'ERROR: crash, out, and target are required' if crash.empty? || out.empty? || target.empty? return { error: 'afl-tmin missing', hint: 'pwn setup --profile re' } unless PWN::Plugins::PreflightChecker.bin?(name: 'afl-tmin') stdout, stderr, status = Open3.capture3('afl-tmin', '-i', crash, '-o', out, '--', target) { stdout: stdout, stderr: stderr, exit: status.exitstatus, out: out } end |
.parse_stats(opts = {}) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 28 public_class_method def self.parse_stats(opts = {}) out_dir = (opts[:out_dir] || opts[:output]).to_s raise 'ERROR: out_dir is required' if out_dir.empty? path = File.join(out_dir, 'fuzzer_stats') path = File.join(out_dir, 'default', 'fuzzer_stats') unless File.file?(path) return {} unless File.file?(path) File.read(path).each_line.with_object({}) do |line, acc| key, val = line.split(':', 2) next if key.to_s.strip.empty? acc[key.strip.to_sym] = val.to_s.strip end end |
.required_bins ⇒ Object
11 12 13 |
# File 'lib/pwn/plugins/aflplusplus.rb', line 11 public_class_method def self.required_bins %w[afl-fuzz] end |