aspisec 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/bin-ruby/aspisec +64 -0
- data/lib-ruby/aspisec/clean.rb +152 -0
- data/lib-ruby/aspisec/config.rb +323 -0
- data/lib-ruby/aspisec/logger.rb +29 -0
- data/lib-ruby/aspisec/module.rb +103 -0
- data/lib-ruby/aspisec/modules/crackmapexec.rb +43 -0
- data/lib-ruby/aspisec/modules/hashcat.rb +43 -0
- data/lib-ruby/aspisec/modules/john.rb +38 -0
- data/lib-ruby/aspisec/modules/metasploit.rb +48 -0
- data/lib-ruby/aspisec/modules/netexec.rb +43 -0
- data/lib-ruby/aspisec/modules/sqlmap.rb +58 -0
- data/lib-ruby/aspisec/modules/theharvester.rb +33 -0
- data/lib-ruby/aspisec/modules.rb +32 -0
- data/lib-ruby/aspisec/version.rb +6 -0
- data/lib-ruby/aspisec.rb +8 -0
- metadata +161 -0
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
|
data/lib-ruby/aspisec.rb
ADDED
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: []
|