lictor 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "time"
5
+
6
+ module Lictor
7
+ # Append-only JSONL trail: one line per event, timestamped. An engagement
8
+ # you cannot replay is an engagement you cannot defend. (SQLite/sequel
9
+ # comes when queries are actually needed; JSONL is the honest v1.)
10
+ class Trail
11
+ def initialize(path)
12
+ @path = path
13
+ end
14
+
15
+ def record(event)
16
+ line = JSON.generate(event.merge(timestamp: Time.now.utc.iso8601))
17
+ # 0600 at creation: the record of offensive operations is private
18
+ # from birth (existing files keep the operator's perms)
19
+ File.open(@path, "a", 0o600) { |f| f.puts(line) }
20
+ self
21
+ end
22
+
23
+ def events
24
+ File.readlines(@path).map { |l| JSON.parse(l) }
25
+ rescue Errno::ENOENT
26
+ []
27
+ end
28
+
29
+ # Plan and outcome events grouped per run, in run order.
30
+ def runs
31
+ grouped = {}
32
+ events.each do |event|
33
+ run = grouped[event["run"]] ||= {run: event["run"], outcomes: []}
34
+ case event["type"]
35
+ when "plan"
36
+ run.merge!(event.slice("mode", "selected", "applicable", "explain",
37
+ "facts_file", "profiled", "priors_adapted")
38
+ .transform_keys(&:to_sym))
39
+ when "profile"
40
+ run[:profiled] = event["binary"]
41
+ when "outcome"
42
+ run[:outcomes] << {technique: event["technique"], success: event["success"],
43
+ executed: event["executed"] || false}
44
+ when "scan"
45
+ run[:scanned] = event["target"]
46
+ when "fuzz"
47
+ run[:fuzzed] = event["binary"]
48
+ run[:crashes] = event["crashes"]
49
+ when "triage"
50
+ run[:triage] = {signal: event["signal"], control: event["control"]}
51
+ when "rule_install"
52
+ run[:taught] = event["technique"]
53
+ when "acquire"
54
+ run[:acquired] = event["binary"]
55
+ when "build"
56
+ run[:built] = {technique: event["technique"], offset: event["offset"],
57
+ ret: event["ret"], size: event["size"], out: event["out"]}
58
+ when "scaffold"
59
+ run[:scaffolded] = event["file"]
60
+ when "harvest"
61
+ run[:harvested] = event["addresses"]
62
+ when "verify"
63
+ run[:verified] = event["remote_crashed"]
64
+ when "suggest"
65
+ run[:suggested] = event["added"]
66
+ when "leak"
67
+ run[:leaked] = event["addresses"]
68
+ when "measure"
69
+ run[:measured] = {sink: event["sink"], offset: event["offset"], source: event["source"]}
70
+ when "pack_draft"
71
+ run[:drafted] = event["technique"]
72
+ end
73
+ end
74
+ grouped.values
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+
5
+ module Lictor
6
+ class TubeError < StandardError; end
7
+
8
+ # The ONE TCP implementation behind the banner readers (harvest,
9
+ # verify): connect, read_until (EOF/reset/deadline/limit), write,
10
+ # reset tracking, close. The run channel stays the ronin subprocess
11
+ # (ADR 0001's honest boundary).
12
+ # UDP, TLS and HTTP-aware delivery become flavors of the same seam
13
+ # (ADR lictor 0001). The timeout/deadline semantics live here, once.
14
+ class Tube
15
+ attr_reader :reset_seen
16
+
17
+ def self.tcp(host, port, timeout: 5)
18
+ sock = Socket.tcp(host, port.to_i, connect_timeout: timeout)
19
+ new(sock, deadline: Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout)
20
+ rescue SystemCallError => e
21
+ raise TubeError, "connect #{host}:#{port} failed: #{e.message}"
22
+ end
23
+
24
+ def initialize(sock, deadline:)
25
+ @sock = sock
26
+ @deadline = deadline
27
+ @reset_seen = false
28
+ end
29
+
30
+ # Read until the connection dies (EOF or reset), the deadline or the
31
+ # limit; returns everything captured. A reset is recorded, not
32
+ # raised: the callers' crash heuristics need the distinction.
33
+ def read_bytes(limit: 512)
34
+ reply = +""
35
+ loop do
36
+ break if reply.bytesize >= limit
37
+ break if Process.clock_gettime(Process::CLOCK_MONOTONIC) > @deadline
38
+
39
+ chunk = @sock.read_nonblock(limit - reply.bytesize, exception: false)
40
+ case chunk
41
+ # nil: read_nonblock(exception: false) signals EOF as nil, "" as
42
+ # a truly empty read - both mean the socket will yield no more
43
+ when "", nil then break
44
+ when :wait_readable
45
+ break unless IO.select([@sock], nil, nil, 0.5)
46
+ else
47
+ reply << chunk
48
+ end
49
+ end
50
+ reply
51
+ rescue Errno::ECONNRESET, Errno::EPIPE
52
+ @reset_seen = true
53
+ reply
54
+ end
55
+
56
+ def gets(delim = "\n", wait: 2)
57
+ @sock.wait_readable(wait) ? @sock.gets(delim) : nil
58
+ end
59
+
60
+ def write(data)
61
+ @sock.write(data)
62
+ self
63
+ rescue Errno::EPIPE, Errno::ECONNRESET
64
+ @reset_seen = true
65
+ self
66
+ end
67
+
68
+ def close
69
+ @sock.close unless @sock.closed?
70
+ rescue IOError, Errno::ENOTCONN
71
+ nil
72
+ end
73
+
74
+ def reset? = @reset_seen
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lictor
4
+ VERSION = '0.4.0'
5
+ end
data/lib/lictor.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "lictor/version"
5
+ require_relative "lictor/config"
6
+ require_relative "lictor/acquisition"
7
+ require_relative "lictor/recon"
8
+ require_relative "lictor/scanner"
9
+ require_relative "lictor/profiler"
10
+ require_relative "lictor/handoff"
11
+ require_relative "lictor/technique_packs"
12
+ require_relative "lictor/suggestion"
13
+ require_relative "lictor/payload_builder" # lazy-loads metasm (the profiler) on first build
14
+ require_relative "lictor/exploit_scaffold"
15
+ require_relative "lictor/pack_draft"
16
+ require_relative "lictor/tube"
17
+ require_relative "lictor/harvest"
18
+ require_relative "lictor/executor"
19
+ require_relative "lictor/priors"
20
+ require_relative "lictor/trail"
21
+ require_relative "lictor/report"
22
+ require_relative "lictor/targets"
23
+ require_relative "lictor/run"
24
+
25
+ module Lictor
26
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lictor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Gregory Epp
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: augure
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.1.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby-nmap
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ description: 'Carries out the decisions augure judged: handoff by default, everything
41
+ trailed, the operator is the authorization. Scan, acquire, fuzz, verify, plan, build,
42
+ run, report. Lictor executes; it never decides.'
43
+ email:
44
+ - log0u7@users.noreply.github.com
45
+ executables:
46
+ - lictor
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - CHANGELOG.md
51
+ - LICENSE
52
+ - README.md
53
+ - bin/lictor
54
+ - lib/config/build_layouts.yml
55
+ - lib/lictor.rb
56
+ - lib/lictor/acquisition.rb
57
+ - lib/lictor/config.rb
58
+ - lib/lictor/executor.rb
59
+ - lib/lictor/exploit_scaffold.rb
60
+ - lib/lictor/handoff.rb
61
+ - lib/lictor/harvest.rb
62
+ - lib/lictor/pack_draft.rb
63
+ - lib/lictor/payload_builder.rb
64
+ - lib/lictor/priors.rb
65
+ - lib/lictor/profiler.rb
66
+ - lib/lictor/recon.rb
67
+ - lib/lictor/report.rb
68
+ - lib/lictor/run.rb
69
+ - lib/lictor/scanner.rb
70
+ - lib/lictor/suggestion.rb
71
+ - lib/lictor/targets.rb
72
+ - lib/lictor/technique_packs.rb
73
+ - lib/lictor/templates/scaffold_exploit.rb.erb
74
+ - lib/lictor/trail.rb
75
+ - lib/lictor/tube.rb
76
+ - lib/lictor/version.rb
77
+ homepage: https://github.com/log0u7/lictor
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ homepage_uri: https://github.com/log0u7/lictor
82
+ source_code_uri: https://github.com/log0u7/lictor
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '3.4'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.6.9
98
+ specification_version: 4
99
+ summary: The orchestrator that carries out augure's judgments - handoff by default,
100
+ everything trailed.
101
+ test_files: []