Skip to content

Commit 292c160

Browse files
Land #17637, Add module information to docs site
2 parents 71cecfb + bf57918 commit 292c160

File tree

6 files changed

+343
-32
lines changed

6 files changed

+343
-32
lines changed

docs/_includes/header_custom.html

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,2 @@
1-
<style>
2-
#main-content p {
3-
text-align: justify;
4-
}
1+
<link rel="stylesheet" href="{% link assets/css/main.css %}">
52

6-
.language-mermaid .label {
7-
text-transform: inherit;
8-
}
9-
10-
.language-msf .zp {
11-
text-decoration: underline;
12-
}
13-
14-
.language-msf .ze {
15-
color: #960050;
16-
}
17-
18-
.language-msf .zg {
19-
color: #859900;
20-
}
21-
22-
.language-msf .zs {
23-
color: #268bd2;
24-
}
25-
26-
.language-msf .zw {
27-
color: orange;
28-
}
29-
</style>

docs/_includes/js/custom.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Handle opening/closing module overview list items
2+
jtd.onReady(function(ready) {
3+
var moduleStructures = document.querySelectorAll('.module-structure');
4+
for (var i = 0; i < moduleStructures.length; i++) {
5+
jtd.addEvent(moduleStructures[i], 'click', function (e) {
6+
var originalTarget = e.target || e.srcElement || e.originalTarget;
7+
if (originalTarget.tagName !== 'A') { return; }
8+
9+
var parentListItem = originalTarget.closest('li');
10+
if (parentListItem.className.indexOf('folder') === -1) { return; }
11+
12+
var childList = parentListItem.querySelector('ul');
13+
if (childList) {
14+
childList.classList.toggle('open');
15+
}
16+
e.preventDefault();
17+
});
18+
}
19+
});

docs/_plugins/metasploit_stats.rb

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
require 'jekyll'
2+
require 'json'
3+
require 'pathname'
4+
5+
#
6+
# Helper class for extracting information related to Metasploit framework's stats
7+
#
8+
class MetasploitStats
9+
# @return [Hash<String, Integer>] A map of module type to the amount of modules
10+
def module_counts
11+
module_counts_by_type = modules.group_by { |mod| mod['type'].to_s }.transform_values { |mods| mods.count }.sort_by(&:first).to_h
12+
module_counts_by_type
13+
end
14+
15+
# @return [Array<Hash<String, Hash>>] A nested array of module metadata, containing at least the keys :name, :total, :children
16+
def nested_module_counts
17+
create_nested_module_counts(modules)
18+
end
19+
20+
protected
21+
22+
# @param [Array<Hash>] modules
23+
# @param [String] parent_path The parent path to track the nesting depth when called recursively
24+
# i.e. auxiliary, then auxiliary/admin, then auxiliary/admin/foo, etc
25+
def create_nested_module_counts(modules, parent_path = '')
26+
# Group the modules by their prefix, i.e. auxiliary/payload/encoder/etc
27+
top_level_buckets = modules.select { |mod| mod['fullname'].start_with?(parent_path) }.group_by do |mod|
28+
remaining_paths = mod['fullname'].gsub(parent_path.empty? ? '' : %r{^#{parent_path}/}, '').split('/')
29+
remaining_paths[0]
30+
end.sort.to_h
31+
32+
top_level_buckets.map do |(prefix, children)|
33+
current_path = parent_path.empty? ? prefix : "#{parent_path}/#{prefix}"
34+
mod = modules_by_fullname[current_path]
35+
{
36+
name: prefix,
37+
total: children.count,
38+
module_fullname: mod ? mod['fullname'] : nil,
39+
module_path: mod ? mod['path'] : nil,
40+
children: mod.nil? ? create_nested_module_counts(children, current_path) : []
41+
}
42+
end
43+
end
44+
45+
# @return [Array<Hash>] An array of Hashes containing each Metasploit module's metadata
46+
def modules
47+
return @modules if @modules
48+
49+
module_metadata_path = '../db/modules_metadata_base.json'
50+
unless File.exist?(module_metadata_path)
51+
raise "Unable to find Metasploit module data, expected it to be at #{module_metadata_path}"
52+
end
53+
54+
@modules = JSON.parse(File.binread(module_metadata_path)).values
55+
@modules
56+
end
57+
58+
# @return [Hash<String, Hash>] A mapping of module name to Metasploit module metadata
59+
def modules_by_fullname
60+
@modules_by_fullname ||= @modules.each_with_object({}) do |mod, hash|
61+
fullname = mod['fullname']
62+
hash[fullname] = mod
63+
end
64+
end
65+
end
66+
67+
# Custom liquid filter implementation for visualizing nested Metasploit module metadata
68+
#
69+
# Intended usage:
70+
# {{ site.metasploit_nested_module_counts | module_tree }}
71+
module ModuleFilter
72+
# @param [Array<Hash>] modules The array of Metasploit cache information
73+
# @return [String] The module tree HTML representation of the given modules
74+
def module_tree(modules)
75+
rendered_children = render_modules(modules)
76+
77+
<<~EOF
78+
<ul class="module-structure">#{rendered_children}</ul>
79+
EOF
80+
end
81+
82+
module_function
83+
84+
# @param [Array<Hash>] modules The array of Metasploit cache information
85+
# @return [String] The rendered tree HTML representation of the given modules
86+
def render_modules(modules)
87+
modules.map do |mod|
88+
result = "<li#{render_child_modules?(mod) ? ' class="folder"' : ''}>#{heading_for_mod(mod)}"
89+
if render_child_modules?(mod)
90+
result += "\n<ul>#{render_modules(mod[:children].sort_by { |mod| "#{render_child_modules?(mod) ? 0 : 1}-#{mod[:name]}" })}</ul>\n"
91+
end
92+
result += "</li>"
93+
result
94+
end.join("\n")
95+
end
96+
97+
# @param [Hash] mod The module metadata object
98+
# @return [String] Human readable string for a module list such as `- <a>Auxiliary (1234)</a>` or `- Other (50)`
99+
def heading_for_mod(mod)
100+
if render_child_modules?(mod)
101+
"<a href=\"#\"><div class=\"target\">#{mod[:name]} (#{mod[:total]})</div></a>"
102+
else
103+
config = Jekyll.sites.first.config
104+
# Preference linking to module documentation over the module implementation
105+
module_docs_path = Pathname.new("documentation").join(mod[:module_path].gsub(/^\//, '')).sub_ext(".md")
106+
link_path = File.exist?(File.join('..', module_docs_path)) ? "/#{module_docs_path}" : mod[:module_path]
107+
docs_link = "#{config['gh_edit_repository']}/#{config['gh_edit_view_mode']}/#{config['gh_edit_branch']}#{link_path}"
108+
"<a href=\"#{docs_link}\" target=\"_blank\"><div class=\"target\">#{mod[:module_fullname]}</div></a>"
109+
end
110+
end
111+
112+
# @param [Hash] mod The module metadata object
113+
# @return [TrueClass, FalseClass]
114+
def render_child_modules?(mod)
115+
mod[:children].length >= 1 && mod[:module_path].nil?
116+
end
117+
end
118+
119+
# Register the Liquid filter so any Jekyll page can render module information
120+
Liquid::Template.register_filter(ModuleFilter)
121+
122+
# Register the site initialization hook to populate global site information so any Jekyll page can access Metasploit stats information
123+
Jekyll::Hooks.register :site, :after_init do |site|
124+
begin
125+
Jekyll.logger.info 'Calculating module stats'
126+
127+
metasploit_stats = MetasploitStats.new
128+
129+
site.config['metasploit_total_module_count'] = metasploit_stats.module_counts.sum { |_type, count| count }
130+
site.config['metasploit_module_counts'] = metasploit_stats.module_counts
131+
site.config['metasploit_nested_module_counts'] = metasploit_stats.nested_module_counts
132+
133+
Jekyll.logger.info 'Finished calculating module stats'
134+
rescue
135+
Jekyll.logger.error "Unable to to extractMetasploit stats"
136+
raise
137+
end
138+
end

docs/assets/css/main.css

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
---
3+
4+
#main-content p {
5+
text-align: justify;
6+
}
7+
8+
/* Color highlighting for msf console text */
9+
.language-mermaid .label {
10+
text-transform: inherit;
11+
}
12+
13+
.language-msf .zp {
14+
text-decoration: underline;
15+
}
16+
17+
.language-msf .ze {
18+
color: #960050;
19+
}
20+
21+
.language-msf .zg {
22+
color: #859900;
23+
}
24+
25+
.language-msf .zs {
26+
color: #268bd2;
27+
}
28+
29+
.language-msf .zw {
30+
color: orange;
31+
}
32+
33+
/* Module overview styles */
34+
35+
.module-structure li::before {
36+
content: ' ' !important;
37+
}
38+
39+
.module-structure a {
40+
height: 100%;
41+
padding: 0.2rem;
42+
background-image: none;
43+
overflow: initial;
44+
display: inline-block;
45+
width: 90%;
46+
}
47+
48+
.module-structure a, .module-structure a:hover {
49+
background-image: none;
50+
}
51+
52+
.module-structure a:hover .target {
53+
pointer-events: none;
54+
display: inline-block;
55+
text-decoration: none;
56+
background-image: linear-gradient(rgba(114, 83, 237, 0.45) 0%, rgba(114, 83, 237, 0.45) 100%);
57+
background-repeat: repeat-x;
58+
background-position: 0 100%;
59+
background-size: 1px 1px;
60+
}
61+
62+
.module-structure {
63+
line-height: 2rem;
64+
}
65+
66+
/* visual indentation lines */
67+
.module-structure ul {
68+
margin-left: 7px !important;
69+
padding-left: 20px !important;
70+
border-left: 1px dashed #d1d7de;
71+
}
72+
73+
.module-structure li p {
74+
margin: 0;
75+
}
76+
77+
.module-structure li {
78+
margin: 0;
79+
list-style: none;
80+
}
81+
82+
.module-structure ul {
83+
display: none;
84+
margin: 0;
85+
}
86+
87+
.module-structure ul.open {
88+
display: block;
89+
}
90+
91+
/* Default li style - files */
92+
.module-structure li::before {
93+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='%234158bf' viewBox='0 0 512 512'><path d='M320 464c8.8 0 16-7.2 16-16V160H256c-17.7 0-32-14.3-32-32V48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320zM0 64C0 28.7 28.7 0 64 0H229.5c17 0 33.3 6.7 45.3 18.7l90.5 90.5c12 12 18.7 28.3 18.7 45.3V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64z'/></svg>");
94+
background-repeat: no-repeat;
95+
width: 1rem;
96+
height: 1rem;
97+
background-position: center top;
98+
background-size: 90% auto;
99+
margin-top: 0;
100+
vertical-align: middle;
101+
margin-left: initial !important;
102+
margin-right: 0.5rem !important;
103+
display: inline-block !important;
104+
position: initial !important;
105+
}
106+
107+
/* li style - folders */
108+
.module-structure li.folder::before {
109+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='%234158bf' viewBox='0 0 512 512'><path d='M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z'/></svg>");
110+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
## Metasploit modules
2+
3+
There are currently {{ site.metasploit_total_module_count }} Metasploit modules:
4+
5+
{{ site.metasploit_nested_module_counts | module_tree }}
6+
7+
## Module types
8+
9+
### Auxiliary modules ({{ site.metasploit_module_counts["auxiliary"] }})
10+
11+
Auxiliary modules do not exploit a target, but can perform useful tasks such as:
12+
13+
- Administration - Modify, operate, or manipulate something on target machine
14+
- Analyzing - Tools that perform analysis, mostly password cracking
15+
- Gathering - Gather, collect, or enumerate data from a single target
16+
- Denial of Service - Crash or slow a target machine or service
17+
- Scanning - Scan targets for known vulnerabilities
18+
- Server Support - Run Servers for common protocols such as SMB, FTP, etc
19+
20+
### Encoder modules ({{ site.metasploit_module_counts["encoder"] }})
21+
22+
Encoders take the raw bytes of a payload and run some sort of encoding algorithm, like bitwise XOR. These modules are useful for encoding
23+
bad characters such as null bytes.
24+
25+
### Evasion modules ({{ site.metasploit_module_counts["evasion"] }})
26+
27+
Evasion modules give Framework users the ability to generate evasive payloads that aim to evade AntiVirus, such as Windows Defender,
28+
without having to install external tools.
29+
30+
### Exploit modules ({{ site.metasploit_module_counts["exploit"] }})
31+
32+
Exploit modules are used to leverage vulnerabilities in a manner that allows the framework to execute arbitrary code.
33+
The arbitrary code that is executed is referred to as the payload.
34+
35+
### Nop modules ({{ site.metasploit_module_counts["nop"] }})
36+
37+
Nop modules, short for 'No Operation', generate a sequence of 'No Operation' instructions that perform no side-effects.
38+
NOPs are often used in conjunction with stack buffer overflows.
39+
40+
### Payloads modules ({{ site.metasploit_module_counts["payload"] }})
41+
42+
In the context of Metasploit exploit modules, payload modules encapsulate the arbitrary code (shellcode) that is executed
43+
as the result of an exploit succeeding. This normally involves the creation of a Metasploit session, but may instead
44+
execute code such as adding user accounts, or executing a simple pingback command that verifies that code execution was successful against a vulnerable target.
45+
46+
Payload modules can also be used individually to generate standalone executables, or shellcode for use within exploits:
47+
48+
```msf
49+
msf6 payload(linux/x86/shell_reverse_tcp) > back
50+
msf6 > use payload/linux/x86/shell_reverse_tcp
51+
msf6 payload(linux/x86/shell_reverse_tcp) > set lhost 127.0.0.1
52+
lhost => 127.0.0.1
53+
msf6 payload(linux/x86/shell_reverse_tcp) > set lport 4444
54+
lport => 4444
55+
56+
# Generate a payload for use within C
57+
msf6 payload(linux/x86/shell_reverse_tcp) > generate -f c
58+
59+
# Generate an ELF file for execution on Linux environments
60+
msf6 payload(linux/x86/shell_reverse_tcp) > generate -f elf -o linux_shell
61+
```
62+
63+
### Post modules ({{ site.metasploit_module_counts["post"] }})
64+
65+
These modules are useful after a machine has been compromised and a Metasploit session has been opened. They perform useful
66+
tasks such as gathering, collecting, or enumerating data from a session.

0 commit comments

Comments
 (0)