heitt 0.4.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fbc73d80ab64c2fa158725b6278866a05696a7eb14e9fb4a1f97182e26b64cc3
4
+ data.tar.gz: 4b0a23a93530cf86b5791e3bec8757bb0f959949980d6481c37c9c2010a4b6b6
5
+ SHA512:
6
+ metadata.gz: 1b89a08f063a90d0a26ccec811425914f8af7ff5973e33b07599ea240c8ce76b070a1803271039f9e2f381269095e1ec5b874c4727cbf2b5cc64a241d03eb998
7
+ data.tar.gz: 786a8226e42a20d8466bc256fe1b0ab37cf8fe725b5a20df029410abe52985ea4c91f00a5cf654aee8625abf3b26c8faf207f86ca03c1130793e2e50ad6fda07
data/bin/heitt ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'io/console'
4
+ require 'heitt'
5
+
6
+
7
+ module HEITT
8
+ class CLI
9
+ attr_accessor :inputs, :extended, :verbose, :output, :json, :database, :show_regex_match, :min_entropy
10
+
11
+ def initialize
12
+ @inputs = []
13
+ @extended = false
14
+ @verbose = false
15
+ @output = ""
16
+ @json = false
17
+ @database = ""
18
+ @show_regex_match = false
19
+ @min_entropy = 3.5
20
+ end
21
+
22
+
23
+ def parse!
24
+ OptionParser.new do |opts|
25
+ opts.banner = ""
26
+ opts.program_name = "heitt"
27
+ opts.separator "#{header("=========================================================================")}"
28
+ opts.separator "#{header("HEITT v#{HEITT::VERSION} - Hash Extraction, Identification & Triage Tool")}"
29
+ opts.separator "#{header("=========================================================================")}"
30
+ opts.separator ""
31
+ opts.separator "Extract and identify hashes from any input."
32
+ opts.separator "Input may be hash string, a file or read from stin"
33
+ opts.separator ""
34
+ opts.separator "Usage: heitt [<INPUT(S)>] [OPTIONS]"
35
+ opts.separator ""
36
+ opts.separator "ARGUMENTS:"
37
+ opts.separator " [<INPUT(S)>] Hash string or filepath"
38
+ opts.separator ""
39
+ opts.separator "GENERAL OPTIONS:"
40
+ opts.on("-h", "--help", "Show this help message and exit"){puts opts; exit}
41
+ opts.on("-v", "--version", "Show version information"){puts "heitt v#{HEITT::VERSION} by jobotow (#{HEITT::GITHUB})"; exit}
42
+ opts.separator ""
43
+
44
+ opts.separator ""
45
+ opts.separator "OUTPUT OPTIONS:"
46
+ opts.on("-V", "--verbose", "Show description and notes for each candidate") {@verbose = true}
47
+ opts.on("-j", "--json","Output in json format") {@json = true}
48
+ opts.on("-o", "--output FILEPATH", String, "File to write output to") {|v| @output = v}
49
+
50
+ opts.separator ""
51
+ opts.separator "FILTERING OPTIONS:"
52
+ opts.on("-e", "--extended", "Show extended candidates") {@extended = true}
53
+ opts.on("-r", "--regex-match", "Show regex-matched candidates") {@show_regex_match = true}
54
+ opts.on("-E", "--min-entropy FLOAT", Float, "Minimum entropy threshold[default: 3.5]") {|v| puts "MIN ENTROPY: #{v}"; @min_entropy = v}
55
+
56
+ opts.separator ""
57
+ opts.separator "DATABASE OPTIONS:"
58
+ opts.on("-D", "--database FILEPATH", String, "Use custom database") {|v| @database = v}
59
+
60
+ opts.separator ""
61
+ opts.separator "EXAMPLES:"
62
+ opts.separator " heitt b1946ac92492d2347c6235b4d2611184"
63
+ opts.separator " heitt auth.log"
64
+ opts.separator " heitt auth.log --json --output result.json"
65
+ opts.separator " heitt auth.log --extended --regex-match"
66
+ opts.separator " heitt auth.log --min-entropy 4.0"
67
+ opts.separator " cat auth.log | heitt "
68
+ opts.separator ""
69
+ opts.separator ""
70
+ opts.separator "NOTES:"
71
+ opts.separator " JSON format is default when output is redirected or piped."
72
+ opts.separator " Regex-match candidates are hidden by default, use '-r' to show."
73
+ opts.separator " Running without input starts interactive mode."
74
+ opts.separator "#{header("=========================================================================")}"
75
+ opts.separator "#{header("END OF HELP")}"
76
+ opts.separator "#{header("=========================================================================")}"
77
+ end.parse!
78
+
79
+ @inputs = if ARGV.empty?
80
+ if $stdin.tty?
81
+ # Interactive mode
82
+ puts "#{header("=== HEITT INTERACTIVE MODE ====\n")}"
83
+ puts "Enter a hash or file"
84
+ loop do
85
+ print "heitt> "
86
+ input = $stdin.gets&.strip
87
+ break if input.nil? || input.empty?
88
+ results = HEITT::Scanner.scan(input, min_entropy: @min_entropy)
89
+ groups = HEITT::Grouper.group(results)
90
+ format = @json ? :json : :tree
91
+ output = case format
92
+ when :json
93
+ HEITT::Formatter.json(groups, extended: @extended, show_regex_match: @show_regex_match)
94
+ else
95
+ HEITT::Formatter.tree(groups, extended: @extended, show_regex_match: @show_regex_match)
96
+ end
97
+ puts output
98
+ puts ""
99
+ end
100
+ else
101
+ [$stdin.read]
102
+ end
103
+
104
+
105
+ else
106
+ ARGV.dup
107
+ end
108
+ self
109
+ end
110
+
111
+ def run
112
+ database = @database.empty? ? HEITT::DATABASE : load_custom_database(@database)
113
+
114
+ format = @json || !$stdout.tty? ? :json : :tree
115
+ output = @inputs.map do |input|
116
+ results = HEITT::Scanner.scan(input, database: database, min_entropy: @min_entropy)
117
+ groups = HEITT::Grouper.group(results)
118
+
119
+ case format
120
+ when :json
121
+ HEITT::Formatter.json(groups, extended: @extended, show_regex_match: @show_regex_match)
122
+ else
123
+ HEITT::Formatter.tree(groups, extended: @extended, show_regex_match: @show_regex_match)
124
+ end
125
+ end.join("\n")
126
+
127
+ unless @output.empty?
128
+ File.write(@output, output)
129
+ else
130
+ puts output
131
+ end
132
+ end
133
+
134
+ private
135
+ def header(text)
136
+ terminal_width = IO.console.winsize[1] rescue 70
137
+ text.center(terminal_width)
138
+ end
139
+
140
+
141
+ def load_custom_database(filepath)
142
+ unless File.exist?(filepath)
143
+ puts HEITT::Color.colorize("[ERROR] Database file #{filepath} does not exists", :bold, :red)
144
+ exit(1)
145
+ end
146
+ begin
147
+ custom_database = JSON.parse(File.read(filepath), symbolize_names: true)
148
+ HEITT::DATABASE + custom_database
149
+ rescue JSON::ParserError => e
150
+ puts "#{HEITT::Color.colorize("[ERROR] Invalid JSON in database file: ", :bold, :red)}#{e.message}"
151
+ exit(1)
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ cli = HEITT::CLI.new.parse!.run #if __FILE__ == $0