aspisec 0.0.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: 0774d430a48a87fa3e42c12c3128539d47ad0fd801e0f72d60f6e0bed0d748c7
4
+ data.tar.gz: 00323257ec5e60891045f4be245854334cb66848ef2232ceb894aefe35cb717c
5
+ SHA512:
6
+ metadata.gz: 74d2860157892606ce4113612e489cccece2eeaa138e825cf4be93f8c16f3182e18db4276f2bc6ce420e215c9d36b46bb3cbf9dc86f0ca557c2145795658707e
7
+ data.tar.gz: 64eb61bb34d63920da20059bb040da0890959726bdc407038191036d6d901a224538e7097ccd30e82aae8a33ec262dc26f4536a25699c9a452587e084fc6eaba
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Alexandre ZANNI at ACCEIS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/bin-ruby/aspisec ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Ruby internal
5
+ # Project internal
6
+ require 'aspisec'
7
+ # External
8
+ require 'docopt'
9
+ require 'pastel'
10
+
11
+ paint = Pastel.new(eachline: "\n")
12
+
13
+ doc = <<~DOCOPT
14
+ #{paint.decorate('AspiSec', :red)} v#{paint.decorate(Aspisec::VERSION, :bold)}
15
+
16
+ #{paint.decorate('Usage:', :red)}
17
+ aspisec [options] clean
18
+ aspisec [options] list
19
+ aspisec -h | --help
20
+ aspisec --version
21
+
22
+ #{paint.decorate('Commands:', :red)}
23
+ clean Removes the traces left by offensive security tools
24
+ list List available modules, locations and their status
25
+
26
+ #{paint.decorate('Options:', :red)}
27
+ --debug Display arguments
28
+ -v, --verbose <level> Set verbosity level (see documentation) [default: 2]
29
+ -h, --help Show this screen
30
+ --version Show version
31
+
32
+ #{paint.decorate('Examples:', :red)}
33
+ aspisec clean
34
+
35
+ #{paint.decorate('Project:', :red)}
36
+ #{paint.decorate('source', :underline)} (https://github.com/acceis/aspisec)
37
+ #{paint.decorate('documentation', :underline)} (https://acceis.github.io/aspisec)
38
+ DOCOPT
39
+
40
+ begin
41
+ args = Docopt.docopt(doc, version: Aspisec::VERSION)
42
+ puts args if args['--debug']
43
+ log_level = 2
44
+ log_level = args['--verbose'].to_i if args['--verbose']
45
+ if args['clean']
46
+ logger = Aspisec::Logger.new(log_level).logger
47
+ conf = Aspisec::Config.new(logger).conf
48
+ cl = Aspisec::Clean.new(conf:, logger:)
49
+ cl.clean
50
+ elsif args['list']
51
+ Aspisec::Modules.modules.each do |mod|
52
+ enabled = mod.enabled? ? '✅' : '❌'
53
+ print "#{enabled} "
54
+ puts paint.decorate(mod.name, :red, :on_black)
55
+ mod.locations.each do |loc|
56
+ enabled = loc.enabled? ? '✅' : '❌'
57
+ print " #{enabled} "
58
+ puts paint.decorate(loc.name, :white, :on_black)
59
+ end
60
+ end
61
+ end
62
+ rescue Docopt::Exit => e
63
+ puts e.message
64
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+ require 'tty-prompt'
5
+
6
+ # ActiveSupport minimal load (we only use number_to_human_size)
7
+ require 'active_support'
8
+ require 'active_support/core_ext/numeric/conversions'
9
+
10
+ module Aspisec
11
+ # Manage the cleaning operations
12
+ class Clean
13
+ # @param conf [Aspisec::Config] an instance of the global configuration
14
+ # If none is provided, the default config is loaded.
15
+ # @param logger [TTY::Logger] logger instance. See {Aspisec::Logger}.
16
+ # If none is provided, a default logger with log level 2 is created.
17
+ # See {Aspisec::Logger::LOG_LEVEL}.
18
+ def initialize(conf: nil, logger: nil)
19
+ @logger = logger || Aspisec::Logger.new.logger
20
+ @conf = conf || Aspisec::Config.new(@logger).conf
21
+ @modules = Aspisec::Modules.modules(conf:, logger:).select(&:enabled?)
22
+ @autoclean = @conf.dig('aspisec', 'autoclean').fetch('enabled', false)
23
+ @describe = @conf.dig('aspisec', 'describe').fetch('enabled', true)
24
+ @prompt = TTY::Prompt.new
25
+ @painter = Pastel.new(eachline: "\n")
26
+ end
27
+
28
+ # Display location (file or directory) information and prompt user for deletion
29
+ # It will follow the configuration wether it has to display the description or not.
30
+ # @param location [Aspisec::Module::Location]
31
+ # @return [true|false]
32
+ def prompt_removal(location:)
33
+ puts "——— #{@painter.decorate(location.name, :cyan, :bold)} ———"
34
+ puts_decorated('Path', location.path.to_s)
35
+ puts_decorated('Type', file_type(location.path))
36
+ puts_decorated('Size', type_size(location.path))
37
+ puts_decorated('Description', location.description) if @describe
38
+ @prompt.yes?("Do you want to remove #{location.name}?")
39
+ end
40
+
41
+ # Display decorated key/value
42
+ # @param key [String]
43
+ # @param value [String]
44
+ # @return [nil]
45
+ def puts_decorated(key, value)
46
+ puts @painter.decorate("#{key}: ", :bright_blue, :bold) + value
47
+ end
48
+
49
+ # Type of file
50
+ # @param path [Pathname]
51
+ # @return [String] `file`, `directory` or `other`
52
+ def file_type(path)
53
+ if path.file?
54
+ 'file'
55
+ elsif path.directory?
56
+ 'directory'
57
+ else
58
+ 'other'
59
+ end
60
+ end
61
+
62
+ # Formats number as bytes into a human-friendly representation.
63
+ # @param size [Integer] file size in bytes
64
+ # @return [String] human-friendly size with the most suitable unit
65
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/NumberHelper.html#method-i-number_to_human_size
66
+ def human_size(size)
67
+ ActiveSupport::NumberHelper.number_to_human_size(size)
68
+ end
69
+
70
+ # Calculate directory size (size of all files stored in it).
71
+ # It will be the real size of files not the size on disk.
72
+ # It ignores anything else than files so it could be wrong for symlinks, mounts, etc.
73
+ # It also don't take into consideration the size of the directory itself.
74
+ # @param path [Pathname]
75
+ # @return [Integer] size in bytes
76
+ def directory_size(path)
77
+ Dir[File.join(path, '**', '*')].select { |f| File.file?(f) }.sum { |f| File.size(f) }
78
+ end
79
+
80
+ # Displays the size (in human-friendly format with {human_size}) regardless of whether it is a file or a directory.
81
+ # @param path [Pathname]
82
+ # @return [String] human-friendly size with the most suitable unit, or `empty` is the size is zero
83
+ def type_size(path)
84
+ size = if path.directory?
85
+ directory_size(path)
86
+ else
87
+ path.size
88
+ end
89
+ size.zero? ? 'empty' : human_size(size)
90
+ end
91
+
92
+ # Delete the location regardless of whether it is a file or a directory.
93
+ # @param path [Pathname]
94
+ # @return [nil]
95
+ def type_delete(path)
96
+ if path.directory?
97
+ path.rmtree
98
+ else
99
+ path.delete
100
+ end
101
+ nil
102
+ end
103
+
104
+ # Handles and logs deletion of locations
105
+ # @param path [Pathname]
106
+ # @return [nil]
107
+ def delete_location(path)
108
+ type_delete(path)
109
+ @logger.info("#{path} was removed")
110
+ nil
111
+ end
112
+
113
+ # Handles the deletion mode. It could be automatic or manual cleaning.
114
+ # @param loc [Aspisec::Module::Location]
115
+ def delete_mode(loc)
116
+ return unless loc.enabled? && loc.path.exist?
117
+
118
+ if @autoclean
119
+ delete_location(loc.path)
120
+ else
121
+ manual_delete(loc)
122
+ end
123
+ end
124
+
125
+ # Handles the manual deletion mode.
126
+ # @param loc [Aspisec::Module::Location]
127
+ def manual_delete(loc)
128
+ remove = prompt_removal(location: loc)
129
+ if remove
130
+ delete_location(loc.path)
131
+ else
132
+ @logger.debug("#{loc.path} was left untouched")
133
+ end
134
+ end
135
+
136
+ # Main method, handling the cleaning.
137
+ # Only enabled modules and locations will be removed.
138
+ # Works either with auto-cleaning or ask for manual confirmation.
139
+ # @return [nil]
140
+ def clean
141
+ @modules.each do |mod|
142
+ next unless mod.enabled?
143
+
144
+ puts "━━━━━━━━━━━━ #{@painter.decorate(mod.name, :white, :bold, :on_blue)} ━━━━━━━━━━━━"
145
+ mod.locations.each do |loc|
146
+ delete_mode(loc)
147
+ end
148
+ end
149
+ nil
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ # stdlib
4
+ require 'yaml'
5
+ # third-party
6
+ require 'xdg'
7
+ require 'tty-logger'
8
+
9
+ module Aspisec
10
+ # Managing the configuration file (location, creation, parsing)
11
+ class Config
12
+ CONFIG_FILENAME = 'aspisec.config.yaml'
13
+ DEFAULT_CONFIG = {
14
+ 'aspisec' => {
15
+ # Auto clean, remove files without asking confirmation
16
+ 'autoclean' => {
17
+ 'enabled' => false
18
+ },
19
+ # Display the description of each location to explain what the file /
20
+ # directory is storing
21
+ 'describe' => {
22
+ 'enabled' => true
23
+ }
24
+ },
25
+ 'tools' => {
26
+ # Example of a tool configuration
27
+ 'example' => {
28
+ # Putting this value to false allow to disable the check for this module only
29
+ 'enabled' => false,
30
+ 'location' => {
31
+ # The base location where the tool stores the confidential stuff to clean
32
+ # $XDG_DATA_HOME is evaluated with a XDG library so even if the environment
33
+ # variable doesn't exist it will be replaced with the default standard value
34
+ 'base' => '$XDG_DATA_HOME/tools/ex',
35
+ 'logs' => {
36
+ # Path to the confidential file n°1
37
+ # <base> will be replaced by location.base value
38
+ 'path' => '<base>/output',
39
+ # Each file check can be individually turned off rather than disabling the whole module
40
+ 'enaled' => false,
41
+ # The description explain which client-related data is stored there and how it is sensitive.
42
+ # It generally says if it's a file or directory.
43
+ 'description' => 'The directory containing log files. Logs contain IP addresses and hostnames.'
44
+ }
45
+ }
46
+ },
47
+ 'sqlmap' => {
48
+ 'enabled' => true,
49
+ 'location' => {
50
+ 'base' => '$XDG_DATA_HOME/sqlmap', # ~/.local/share/sqlmap
51
+ 'history' => {
52
+ 'path' => '<base>/history',
53
+ 'description' => "Directory containing history files.\n" \
54
+ "os.hst stores system commands entered when using --os-pwn option.\n" \
55
+ 'sql.hst stores SQL quries entered when using --os-shell option.'
56
+ },
57
+ 'logs' => {
58
+ 'path' => '<base>/output',
59
+ 'description' => "Directory containing a folder per target.\n" \
60
+ "<target>/log contains all successful injection vectors.\n" \
61
+ "<target>/session.sqlite contains retrieved data.\n" \
62
+ '<target>/target.txt contains target URL + command used.'
63
+ }
64
+ }
65
+ },
66
+ 'crackmapexec' => {
67
+ 'enabled' => true,
68
+ 'location' => {
69
+ 'base' => '$HOME/.cme', # ~/.cme
70
+ 'logs' => {
71
+ 'path' => '<base>/logs',
72
+ 'description' => 'Directory containing log files, secrets, hashes, cleartext passwords etc.'
73
+ },
74
+ 'screenshots' => {
75
+ 'path' => '<base>/screenshots',
76
+ 'description' => 'Directory where are stored all screenshots taken with the --screenshot option.'
77
+ },
78
+ 'workspaces' => {
79
+ 'path' => '<base>/workspaces',
80
+ 'description' => "Directory containing workspaces.\n" \
81
+ 'Workspaces contain SQLite databases including users (domain, usernames, password), ' \
82
+ 'shares, hosts, dpapi secrets, etc.'
83
+ }
84
+ }
85
+ },
86
+ 'netexec' => {
87
+ 'enabled' => true,
88
+ 'location' => {
89
+ 'base' => '$HOME/.nxc', # ~/.nxc
90
+ 'logs' => {
91
+ 'path' => '<base>/logs',
92
+ 'description' => 'Directory containing log files, secrets, hashes, cleartext password etc.'
93
+ },
94
+ 'screenshots' => {
95
+ 'path' => '<base>/screenshots',
96
+ 'description' => 'Directory where are stored all screenshots taken with the --screenshot option.'
97
+ },
98
+ 'workspaces' => {
99
+ 'path' => '<base>/workspaces',
100
+ 'description' => "Directory containing workspaces.\n" \
101
+ 'Workspaces contain SQLite databases including users (domain, usernames, password), ' \
102
+ 'shares, hosts, dpapi secrets, etc.'
103
+ }
104
+ }
105
+ },
106
+ 'hashcat' => {
107
+ 'enabled' => true,
108
+ 'location' => {
109
+ 'base' => '$XDG_DATA_HOME/hashcat', # ~/.local/share/hashcat
110
+ #
111
+ #
112
+ #
113
+ 'sessions' => {
114
+ 'path' => '<base>/sessions',
115
+ 'enaled' => false,
116
+ 'description' => "Directory containing session related data.\n" \
117
+ 'hashcat.log should not contain any sensible data unless the file name ' \
118
+ "of a target file is sensible.\n" \
119
+ 'show.log should not contain any sensible data unless the folder name is sensible.'
120
+ },
121
+ 'potfile' => {
122
+ 'path' => '<base>/hashcat.potfile',
123
+ 'description' => "File containing all cracked hashes.\n" \
124
+ 'Passwords may include enterprize related content or may be easily recognizable.'
125
+ },
126
+ 'dict_cache' => {
127
+ 'path' => '<base>/hashcat.dictstat2',
128
+ 'enabled' => false,
129
+ 'description' => "File is a cache for dictionaries.\n" \
130
+ 'It should not be sensible unless dict. contain confidential data.'
131
+ }
132
+ }
133
+ },
134
+ 'theharvester' => {
135
+ 'enabled' => true,
136
+ 'location' => {
137
+ 'base' => '$XDG_DATA_HOME/theHarvester', # ~/.local/share/theHarvester
138
+ #
139
+ 'stash' => {
140
+ 'path' => '<base>/stash.sqlite',
141
+ 'description' => 'File (SQLite DB) containing all the harvested addresses.'
142
+ }
143
+ }
144
+ },
145
+ 'john' => {
146
+ 'enabled' => true,
147
+ 'location' => {
148
+ 'base' => '$HOME/.john', # ~/.john
149
+ #
150
+ #
151
+ 'logs' => {
152
+ 'path' => '<base>/john.log',
153
+ 'description' => "File containing the logs of the commands launched.\n" \
154
+ 'Does not contain hashes or passwords but usernames and whole command lines.'
155
+ },
156
+ 'potfile' => {
157
+ 'path' => '<base>/john.pot',
158
+ 'description' => "File containing all cracked hashes.\n" \
159
+ 'Passwords may include enterprize related content or may be easily recognizable.'
160
+ }
161
+ }
162
+ },
163
+ 'metasploit' => {
164
+ 'enabled' => true,
165
+ 'location' => {
166
+ 'base' => '$HOME/.msf4', # ~/.msf4
167
+ #
168
+ #
169
+ 'history' => {
170
+ 'path' => '<base>/history',
171
+ 'description' => "File containing the history of commands used in msf shell.\n" \
172
+ 'It certainly contains username, passwords, hostnames, etc.'
173
+ },
174
+ 'logs' => {
175
+ 'path' => '<base>/logs',
176
+ 'description' => "Directory containing log files.\n" \
177
+ "framework.log may contain stacktraces that contain payloads.\n" \
178
+ "production.log and sessions/ ? (I don't know, empty for me)"
179
+ },
180
+ 'loot' => {
181
+ 'path' => '<base>/loot',
182
+ 'description' => "Directory containing looted files.\n" \
183
+ 'Those are retrieved clients files.'
184
+ },
185
+ 'meterpreter' => {
186
+ 'path' => '<base>/meterpreter_history',
187
+ 'description' => "File containing the history of commands used in meterpreter sessions.\n" \
188
+ "Less sensible than msf shell history but could still contains some file paths, \n" \
189
+ 'for example.'
190
+ }
191
+ }
192
+ }
193
+ },
194
+ 'audit' => {
195
+ 'enabled' => false,
196
+ 'location' => {
197
+ 'base' => '$HOME/Projets'
198
+ }
199
+ }
200
+ }.freeze
201
+
202
+ # The parsed Aspisec configuration
203
+ # @return [Hash] the Aspisec configuration object
204
+ attr_reader :conf
205
+
206
+ # Load config. or create a default config. file if not existing.
207
+ # Also parse and interprete custom values.
208
+ # @param logger [TTY::Logger] logger instance. See {Aspisec::Logger}.
209
+ # If none is provided, a default logger with log level 2 is created.
210
+ # See {Aspisec::Logger::LOG_LEVEL}.
211
+ # @example
212
+ # # With default logger
213
+ # cnf = Aspisec::Config.new
214
+ # cnf.conf
215
+ # # With custom logger
216
+ # logger = Aspisec::Logger.new(0).logger
217
+ # cnf = Aspisec::Config.new(logger)
218
+ # cnf.conf
219
+ def initialize(logger = nil)
220
+ # Set log level
221
+ @logger = logger || Aspisec::Logger.new.logger
222
+ # Create the configuration file if it doesn't exist
223
+ create_config unless config_exist?
224
+ # Else load it
225
+ @conf = load_config
226
+ # Replace the path variables / plaholders with real values
227
+ expand_path_conf!
228
+ end
229
+
230
+ # Read and parse (YAML ➡️ Ruby Hash) the config. file
231
+ # @return [Hash|nil] the corresponding Ruby object parsed from the YAML file
232
+ # or `nil` if the configuration file doesn't exist
233
+ def load_config
234
+ if config_exist?
235
+ @logger.debug("Loading configuration from #{config_filepath}")
236
+ YAML.load_file(config_filepath, symbolize_names: false)
237
+ else
238
+ @logger.warn('Configuration not loaded')
239
+ nil
240
+ end
241
+ end
242
+
243
+ # Create the configuration file with default value at default location if it doesn't already exist
244
+ def create_config
245
+ return if config_exist?
246
+
247
+ parent_dir = File.dirname(config_filepath)
248
+ # create parent folder recursively if it doesn't already exist
249
+ FileUtils.mkpath(parent_dir)
250
+ @logger.info("Creating configuration file: #{config_filepath}")
251
+ File.write(config_filepath, YAML.dump(DEFAULT_CONFIG))
252
+ end
253
+
254
+ # Get the Aspisec configuration file path
255
+ # @return [String] absolute file path
256
+ def config_filepath
257
+ xdg = XDG.new
258
+ # Logging this floods debug info and is not meaningful
259
+ # path = xdg.config_home + 'aspisec' + CONFIG_FILENAME
260
+ # @logger.debug("The default configuration file path should be: #{path}")
261
+ # path
262
+ # https://github.com/rubocop/rubocop/issues/11757
263
+ # rubocop:disable Style/StringConcatenation
264
+ xdg.config_home + 'aspisec' + CONFIG_FILENAME # /home/noraj/.config/aspisec/aspisec.config.yaml
265
+ # rubocop:enable Style/StringConcatenation
266
+ end
267
+
268
+ # Check if the Aspisec configuration file exists or not
269
+ # @return [true|false]
270
+ def config_exist?
271
+ # Logging this floods debug info and is not meaningful
272
+ # exist = File.exist?(config_filepath)
273
+ # neg = exist ? '' : 'does not'
274
+ # @logger.debug("The configuration file #{config_filepath} #{neg} exist")
275
+ # exist
276
+ File.exist?(config_filepath)
277
+ end
278
+
279
+ # @note see {Aspisec::Config.expand_path_variables}
280
+ def expand_path_variables(path)
281
+ Config.expand_path_variables(path)
282
+ end
283
+
284
+ # Evaluate XDG variables and $HOME in file path
285
+ # @param path [String] path with variables
286
+ # @return the absolute version of the evaluated path
287
+ # @note Arguments other than Strings are returned untouched, useful to iterate over configuration values
288
+ # @example
289
+ # conf.expand_path_variables('$XDG_DATA_HOME/sqlmap')
290
+ # # => "/home/noraj/.local/share/sqlmap"
291
+ def self.expand_path_variables(path)
292
+ return path unless path.is_a?(String) # not a path, let untouched
293
+
294
+ xdg = XDG.new
295
+ case path
296
+ when /\$XDG_CONFIG_HOME/
297
+ path.sub!('$XDG_CONFIG_HOME', xdg.config_home.to_s)
298
+ when /\$XDG_DATA_HOME/
299
+ path.sub!('$XDG_DATA_HOME', xdg.data_home.to_s)
300
+ when /\$HOME/
301
+ path.sub!('$HOME', Dir.home)
302
+ end
303
+ File.expand_path(path)
304
+ end
305
+
306
+ # Expand all base location with {Aspisec::Config.expand_path_variables} in the configuration
307
+ # + expand the custom `<base>` tags
308
+ def expand_path_conf!
309
+ @conf['tools'].each_key do |tool|
310
+ base_path = @conf.dig('tools', tool, 'location', 'base')
311
+ @conf['tools'][tool]['location']['base'] = expand_path_variables(base_path)
312
+ @conf['tools'][tool]['location'].each_key do |k|
313
+ unless k == 'base'
314
+ feature_path = @conf.dig('tools', tool, 'location', k, 'path')
315
+ @conf['tools'][tool]['location'][k]['path'] = feature_path.sub('<base>', base_path) if feature_path
316
+ end
317
+ end
318
+ end
319
+ @conf['audit']['location']['base'] = expand_path_variables(@conf.dig('audit', 'location', 'base'))
320
+ @conf
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-logger'
4
+
5
+ module Aspisec
6
+ # CLI / Terminal / console information logging
7
+ class Logger
8
+ # Mapping integers to log levels from tty-logger (https://github.com/piotrmurach/tty-logger?tab=readme-ov-file#22-levels)
9
+ LOG_LEVEL = {
10
+ 0 => :debug,
11
+ 1 => :info,
12
+ 2 => :warn,
13
+ 3 => :error,
14
+ 4 => :fatal
15
+ }.freeze
16
+
17
+ # The configuration of the logger
18
+ # @return [TTY::Logger]
19
+ attr_reader :logger
20
+
21
+ # @param log_level [Integer] Default is 2. See {LOG_LEVEL}.
22
+ # @example
23
+ # logger = Aspisec::Logger.new(0).logger
24
+ # conf = Aspisec::Config.new(logger)
25
+ def initialize(log_level = 2)
26
+ @logger = TTY::Logger.new { |config| config.level = LOG_LEVEL[log_level] }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # stdlib
4
+ require 'pathname'
5
+ # third-party
6
+ require 'tty-logger'
7
+
8
+ module Aspisec
9
+ # Generic module class that will be inherited in all modules instances
10
+ class Module
11
+ # The configuration for the tool.
12
+ # Sub-tree under `tools > tool_name` of {Aspisec::Config#conf}.
13
+ # @return [Hash]
14
+ attr_reader :conf
15
+
16
+ # The name of the tool.
17
+ # @return [String] tool name
18
+ attr_reader :name
19
+
20
+ # List of locations (name).
21
+ # Returns something only on module instances like {Aspisec::Modules::Sqlmap}.
22
+ # Will be empty for {Aspisec::Module}.
23
+ # For a list of objects, rather use {locations}.
24
+ # @return [Array<String>]
25
+ attr_reader :locations_list
26
+
27
+ # The base location (directory) where the tool data is stored.
28
+ # @return [Pathname] file path
29
+ attr_reader :base
30
+
31
+ # Not meant to be used directly but to be inherited in modules instead
32
+ # @param conf [Aspisec::Config] an instance of the global configuration
33
+ # @param tool_name [String] The name of the tool. It must match the configuration key.
34
+ # @param logger [TTY::Logger] logger instance. See {Aspisec::Logger}.
35
+ # If none is provided, a default logger with log level 2 is created.
36
+ # See {Aspisec::Logger::LOG_LEVEL}.
37
+ # @example
38
+ # conf = Aspisec::Config.new.conf
39
+ # # you should never do that as you'll get incomplete data and features
40
+ # sqlmap = Aspisec::Module.new(conf, 'sqlmap')
41
+ # # rather call the sqlmap module that will inherit this class
42
+ # sqlmap = Aspisec::Modules::Sqlmap.new(conf)
43
+ def initialize(conf, tool_name, logger: nil)
44
+ @logger = logger || Aspisec::Logger.new.logger
45
+ @name = tool_name
46
+ @logger.debug("Module #{@name} was loaded", app: @name)
47
+ @conf = conf['tools'][tool_name]
48
+ @base = Pathname.new(@conf.dig('location', 'base'))
49
+ @enabled = @conf.fetch('enabled', true)
50
+ @locations_list = []
51
+ end
52
+
53
+ # Is this module enabled?
54
+ # @return [true|false]
55
+ def enabled?
56
+ @enabled
57
+ end
58
+
59
+ # Returns all locations available for the tool.
60
+ # It returns a list {Location} objects unline {locations_list} that returns
61
+ # only strings (location names).
62
+ # @return [Array<Location>]
63
+ def locations
64
+ # Re-compute what's already cumputed and stored in properties
65
+ # @locations_list.map { |loc| Location.new(@conf, loc) }
66
+ # Access properties rather than re-computing
67
+ # Using send() is safe here because the input is a hadrcaoded whitelist
68
+ @locations_list.map { |loc| send(loc) }
69
+ end
70
+
71
+ # Object easing the manipulation of locations.
72
+ # Helpers to get the path, check if this feature/file/directory is enabled, etc.
73
+ class Location
74
+ # Name of the feature, file or directory of the tool.
75
+ # @return [String]
76
+ attr_reader :name
77
+
78
+ # File path of the file or directory location to clean.
79
+ # @return [Pathname] absolute path
80
+ attr_reader :path
81
+
82
+ # Explanation of what the location (file / directory) is containing, to give an idea of how sensitive it is.
83
+ # @return [String] description
84
+ attr_reader :description
85
+
86
+ # @param tool_conf [Hash] Tool configuration as returned by {Aspisec::Module#conf}.
87
+ # @param feature_name [String] Name of the feature/file/directory to clean.
88
+ # Must be equal to the configuration key.
89
+ def initialize(tool_conf, feature_name)
90
+ @name = feature_name
91
+ @path = Pathname.new(tool_conf.dig('location', @name, 'path'))
92
+ @enabled = tool_conf.dig('location', @name).fetch('enabled', true)
93
+ @description = tool_conf.dig('location', @name).fetch('description', '')
94
+ end
95
+
96
+ # Is this location enabled?
97
+ # @return [true|false]
98
+ def enabled?
99
+ @enabled
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # CrackMapExec module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/byt3bl33d3r/CrackMapExec
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a Crackmapexec module instance
15
+ # cme = Aspisec::Modules::Crackmapexec.new(conf)
16
+ # # Locations available
17
+ # cme.locations_list # => ["logs", "screenshots", "workspaces"]
18
+ class Crackmapexec < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :logs
22
+
23
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
24
+ # @return [Location]
25
+ attr_reader :screenshots
26
+
27
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
28
+ # @return [Location]
29
+ attr_reader :workspaces
30
+
31
+ # Inherits from {Aspisec::Module} but without the `tool_name` argument
32
+ # because it is hardcoded for each module.
33
+ # @param conf [Aspisec::Config] an instance of the global configuration
34
+ def initialize(conf, logger: nil)
35
+ super(conf, 'crackmapexec', logger:)
36
+ @logs = Location.new(@conf, 'logs')
37
+ @screenshots = Location.new(@conf, 'screenshots')
38
+ @workspaces = Location.new(@conf, 'workspaces')
39
+ @locations_list = %w[logs screenshots workspaces]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # HashCat module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/hashcat/hashcat
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a Hashcat module instance
15
+ # hc = Aspisec::Modules::Hashcat.new(conf)
16
+ # # Locations available
17
+ # hc.locations_list # => ["sessions", "potfile", "dict_cache"]
18
+ class Hashcat < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :sessions
22
+
23
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
24
+ # @return [Location]
25
+ attr_reader :potfile
26
+
27
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
28
+ # @return [Location]
29
+ attr_reader :dict_cache
30
+
31
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
32
+ # `tool_name` is hardcoded for each module.
33
+ # @param conf [Aspisec::Config] an instance of the global configuration
34
+ def initialize(conf, logger: nil)
35
+ super(conf, 'hashcat', logger:)
36
+ @sessions = Location.new(@conf, 'sessions')
37
+ @potfile = Location.new(@conf, 'potfile')
38
+ @dict_cache = Location.new(@conf, 'dict_cache')
39
+ @locations_list = %w[sessions potfile dict_cache]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # John (the Ripper) module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/openwall/john
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a John module instance
15
+ # john = Aspisec::Modules::John.new(conf)
16
+ # # Locations available
17
+ # john.locations_list # => ["logs", "potfile"]
18
+ class John < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :logs
22
+
23
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
24
+ # @return [Location]
25
+ attr_reader :potfile
26
+
27
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
28
+ # `tool_name` is hardcoded for each module.
29
+ # @param conf [Aspisec::Config] an instance of the global configuration
30
+ def initialize(conf, logger: nil)
31
+ super(conf, 'john', logger:)
32
+ @logs = Location.new(@conf, 'logs')
33
+ @potfile = Location.new(@conf, 'potfile')
34
+ @locations_list = %w[logs potfile]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # Metasploit module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/rapid7/metasploit-framework
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a Metasploit module instance
15
+ # msf = Aspisec::Modules::Metasploit.new(conf)
16
+ # # Locations available
17
+ # msf.locations_list # => ["history", "logs", "loot", "meterpreter"]
18
+ class Metasploit < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :history
22
+
23
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
24
+ # @return [Location]
25
+ attr_reader :logs
26
+
27
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
28
+ # @return [Location]
29
+ attr_reader :loot
30
+
31
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
32
+ # @return [Location]
33
+ attr_reader :meterpreter
34
+
35
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
36
+ # `tool_name` is hardcoded for each module.
37
+ # @param conf [Aspisec::Config] an instance of the global configuration
38
+ def initialize(conf, logger: nil)
39
+ super(conf, 'metasploit', logger:)
40
+ @history = Location.new(@conf, 'history')
41
+ @logs = Location.new(@conf, 'logs')
42
+ @loot = Location.new(@conf, 'loot')
43
+ @meterpreter = Location.new(@conf, 'meterpreter')
44
+ @locations_list = %w[history logs loot meterpreter]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # NetExec module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/Pennyw0rth/NetExec
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a Netexec module instance
15
+ # nxc = Aspisec::Modules::Netexec.new(conf)
16
+ # # Locations available
17
+ # nxc.locations_list # => ["logs", "screenshots", "workspaces"]
18
+ class Netexec < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :logs
22
+
23
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
24
+ # @return [Location]
25
+ attr_reader :screenshots
26
+
27
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
28
+ # @return [Location]
29
+ attr_reader :workspaces
30
+
31
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
32
+ # `tool_name` is hardcoded for each module.
33
+ # @param conf [Aspisec::Config] an instance of the global configuration
34
+ def initialize(conf, logger: nil)
35
+ super(conf, 'netexec', logger:)
36
+ @logs = Location.new(@conf, 'logs')
37
+ @screenshots = Location.new(@conf, 'screenshots')
38
+ @workspaces = Location.new(@conf, 'workspaces')
39
+ @locations_list = %w[logs screenshots workspaces]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # Sqlmap module.
8
+ # Inherits {Aspisec::Module}.
9
+ # @see https://github.com/sqlmapproject/sqlmap
10
+ # @example
11
+ # # Get the global config
12
+ # conf = Aspisec::Config.new.conf
13
+ # # Create a Sqlmap module instance
14
+ # sqlmap = Aspisec::Modules::Sqlmap.new(conf)
15
+ # # Generic methods
16
+ # sqlmap.name # => "sqlmap"
17
+ # sqlmap.conf # => {…}
18
+ # sqlmap.enabled? # => true
19
+ # sqlmap.base # => #<Pathname:/home/noraj/.local/share/sqlmap>
20
+ # sqlmap.locations_list # => ["history", "logs"]
21
+ # sqlmap.locations # => [#<Aspisec::Module::Location …>, … ]
22
+ # # Custom methods for the feature/file/directory location of Sqlmap to clean
23
+ # sqlmap.history # => #<Aspisec::Module::Location …>
24
+ # sqlmap.logs # => #<Aspisec::Module::Location …>
25
+ # # But those custom locations benefits of generic methods
26
+ # sqlmap.history.enabled? # => true
27
+ # sqlmap.history.name # => => "history"
28
+ # sqlmap.history.description # => "Directory containing…"
29
+ # sqlmap.history.path => #<Pathname:/home/noraj/.local/share/sqlmap/history>
30
+ # # Since `.path` returns a {Pathname} object, we can use generic {File},
31
+ # # {FileTest} methods and some from {Dir} and {FileUtils} as well.
32
+ # sqlmap.history.path.exist? # => true
33
+ # sqlmap.history.path.file? # => false
34
+ # sqlmap.history.path.directory? # => true
35
+ # sqlmap.history.path.readable? # => true
36
+ # sqlmap.history.path.children # => [#<Pathname:/home/noraj/.local/share/sqlmap/history/os.hst>, #<Pathname:…>]
37
+ # sqlmap.history.path.children.first.size # => 10
38
+ class Sqlmap < Aspisec::Module
39
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
40
+ # @return [Location]
41
+ attr_reader :history
42
+
43
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
44
+ # @return [Location]
45
+ attr_reader :logs
46
+
47
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
48
+ # `tool_name` is hardcoded for each module.
49
+ # @param conf [Aspisec::Config] an instance of the global configuration
50
+ def initialize(conf, logger: nil)
51
+ super(conf, 'sqlmap', logger:)
52
+ @history = Location.new(@conf, 'history')
53
+ @logs = Location.new(@conf, 'logs')
54
+ @locations_list = %w[history logs]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/module'
4
+
5
+ module Aspisec
6
+ module Modules
7
+ # theHarvester module.
8
+ # Inherits {Aspisec::Module}.
9
+ # For more examples of methods, see {Aspisec::Modules::Sqlmap}.
10
+ # @see https://github.com/laramies/theHarvester
11
+ # @example
12
+ # # Get the global config
13
+ # conf = Aspisec::Config.new.conf
14
+ # # Create a Theharvester module instance
15
+ # th = Aspisec::Modules::Theharvester.new(conf)
16
+ # # Locations available
17
+ # th.locations_list # => ["stash"]
18
+ class Theharvester < Aspisec::Module
19
+ # see {Aspisec::Config::DEFAULT_CONFIG} or call {Aspisec::Module::Location#description}.
20
+ # @return [Location]
21
+ attr_reader :stash
22
+
23
+ # Inherits from {Aspisec::Module} but has only the `conf` argument,
24
+ # `tool_name` is hardcoded for each module.
25
+ # @param conf [Aspisec::Config] an instance of the global configuration
26
+ def initialize(conf, logger: nil)
27
+ super(conf, 'theharvester', logger:)
28
+ @stash = Location.new(@conf, 'stash')
29
+ @locations_list = %w[stash]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require all modules
4
+ Dir[File.join(__dir__, 'modules', '*.rb')].each { |f| require(f) }
5
+
6
+ module Aspisec
7
+ # This Ruby module contains all Aspisec modules.
8
+ # Each Aspisec module is a Ruby class inherinting the Aspisec::Module base class.
9
+ # The "Modules" module also contains methods to manage all Aspisec modules.
10
+ module Modules
11
+ # List all available Aspisec modules
12
+ # @return [Array<Symbol>] list of symbolized module names
13
+ # @example
14
+ # Aspisec::Modules.list_modules
15
+ # # => [:Netexec, :Sqlmap, :Hashcat, :Theharvester, :Crackmapexec, :John, :Metasploit]
16
+ def self.list_modules
17
+ Aspisec::Modules.constants.select { |c| Aspisec::Modules.const_get(c) <= Aspisec::Module }
18
+ end
19
+
20
+ # Intanciates all Aspisec modules
21
+ # @param conf [Aspisec::Config] an instance of the global configuration
22
+ # If none is provided, the default config is loaded.
23
+ # @param logger [TTY::Logger] logger instance. See {Aspisec::Logger}.
24
+ # If none is provided, a default logger with log level 2 is created.
25
+ # See {Aspisec::Logger::LOG_LEVEL}.
26
+ # @return [Array<Aspisec::Module>]
27
+ def self.modules(conf: nil, logger: nil)
28
+ cnf = conf || Aspisec::Config.new(logger).conf
29
+ list_modules.map { |c| Aspisec::Modules.const_get(c).new(cnf, logger:) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspisec
4
+ # Version of aspisec library and app
5
+ VERSION = '0.0.1'
6
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspisec/version'
4
+
5
+ require 'aspisec/clean'
6
+ require 'aspisec/config'
7
+ require 'aspisec/logger'
8
+ require 'aspisec/modules'
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aspisec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandre ZANNI
8
+ autorequire:
9
+ bindir: bin-ruby
10
+ cert_chain: []
11
+ date: 2024-04-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.1.3.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.1.3.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: docopt
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.6'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.6'
47
+ - !ruby/object:Gem::Dependency
48
+ name: pastel
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.8'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.8'
61
+ - !ruby/object:Gem::Dependency
62
+ name: tty-logger
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.6'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.6'
75
+ - !ruby/object:Gem::Dependency
76
+ name: tty-prompt
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.23'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.23'
89
+ - !ruby/object:Gem::Dependency
90
+ name: xdg
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '8.0'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '8.0'
103
+ description: Vacuuming out the remnants of offensive tools. AspiSec is responsible
104
+ for removing the traces and confidential information left by offensive security
105
+ tools on an auditor's computer in various cache and log files.
106
+ email: alexandre.zanni@europe.com
107
+ executables:
108
+ - aspisec
109
+ extensions: []
110
+ extra_rdoc_files: []
111
+ files:
112
+ - LICENSE
113
+ - bin-ruby/aspisec
114
+ - lib-ruby/aspisec.rb
115
+ - lib-ruby/aspisec/clean.rb
116
+ - lib-ruby/aspisec/config.rb
117
+ - lib-ruby/aspisec/logger.rb
118
+ - lib-ruby/aspisec/module.rb
119
+ - lib-ruby/aspisec/modules.rb
120
+ - lib-ruby/aspisec/modules/crackmapexec.rb
121
+ - lib-ruby/aspisec/modules/hashcat.rb
122
+ - lib-ruby/aspisec/modules/john.rb
123
+ - lib-ruby/aspisec/modules/metasploit.rb
124
+ - lib-ruby/aspisec/modules/netexec.rb
125
+ - lib-ruby/aspisec/modules/sqlmap.rb
126
+ - lib-ruby/aspisec/modules/theharvester.rb
127
+ - lib-ruby/aspisec/version.rb
128
+ homepage: https://acceis.github.io/aspisec/
129
+ licenses:
130
+ - MIT
131
+ metadata:
132
+ yard.run: yard
133
+ bug_tracker_uri: https://github.com/acceis/aspisec/issues
134
+ changelog_uri: https://github.com/acceis/aspisec/blob/master/docs/CHANGELOG.md
135
+ documentation_uri: https://acceis.github.io/aspisec/
136
+ homepage_uri: https://acceis.github.io/aspisec/
137
+ source_code_uri: https://github.com/acceis/aspisec/
138
+ rubygems_mfa_required: 'true'
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib-ruby
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 3.1.0
148
+ - - "<"
149
+ - !ruby/object:Gem::Version
150
+ version: '4.0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 3.5.3
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Removes the traces left by offensive security tools.
161
+ test_files: []