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 +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: []
|