From 8c6a9d56223afb340d7e07e0991533625232fa63 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Thu, 28 Mar 2013 15:39:07 -0500 Subject: [PATCH 1/9] Turn shared prefixes into directories [#46491831] Change the shared msf_modules_* prefixes in spec/support/shared/* into directories. --- .../modules/error_attributes.rb} | 0 .../{msf_modules_loader_base.rb => msf/modules/loader_base.rb} | 0 .../modules/error_subclass_initialize.rb} | 0 .../modules/loader_archive_read_module_content.rb} | 0 .../modules/version_compatibility_error.rb} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename spec/support/shared/contexts/{msf_modules_error_attributes.rb => msf/modules/error_attributes.rb} (100%) rename spec/support/shared/contexts/{msf_modules_loader_base.rb => msf/modules/loader_base.rb} (100%) rename spec/support/shared/examples/{msf_modules_error_subclass_initialize.rb => msf/modules/error_subclass_initialize.rb} (100%) rename spec/support/shared/examples/{msf_modules_loader_archive_read_module_content.rb => msf/modules/loader_archive_read_module_content.rb} (100%) rename spec/support/shared/examples/{msf_modules_version_compatibility_error.rb => msf/modules/version_compatibility_error.rb} (100%) diff --git a/spec/support/shared/contexts/msf_modules_error_attributes.rb b/spec/support/shared/contexts/msf/modules/error_attributes.rb similarity index 100% rename from spec/support/shared/contexts/msf_modules_error_attributes.rb rename to spec/support/shared/contexts/msf/modules/error_attributes.rb diff --git a/spec/support/shared/contexts/msf_modules_loader_base.rb b/spec/support/shared/contexts/msf/modules/loader_base.rb similarity index 100% rename from spec/support/shared/contexts/msf_modules_loader_base.rb rename to spec/support/shared/contexts/msf/modules/loader_base.rb diff --git a/spec/support/shared/examples/msf_modules_error_subclass_initialize.rb b/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb similarity index 100% rename from spec/support/shared/examples/msf_modules_error_subclass_initialize.rb rename to spec/support/shared/examples/msf/modules/error_subclass_initialize.rb diff --git a/spec/support/shared/examples/msf_modules_loader_archive_read_module_content.rb b/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb similarity index 100% rename from spec/support/shared/examples/msf_modules_loader_archive_read_module_content.rb rename to spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb diff --git a/spec/support/shared/examples/msf_modules_version_compatibility_error.rb b/spec/support/shared/examples/msf/modules/version_compatibility_error.rb similarity index 100% rename from spec/support/shared/examples/msf_modules_version_compatibility_error.rb rename to spec/support/shared/examples/msf/modules/version_compatibility_error.rb From c210260845cf1bfbc9cddaeccc247afd2f77a248 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sat, 30 Mar 2013 15:32:38 -0500 Subject: [PATCH 2/9] Fix Undocumentable method, missing name YARD warning [#46491831] Comments at the start of the file with ## caused YARD to think the comment was documenting the require call. By removing the ##, the warning disappeared. I did not determine what is special about ## in file comments. --- lib/msf/base.rb | 3 --- lib/msf/base/sessions/meterpreter.rb | 2 -- lib/msf/base/sessions/meterpreter_java.rb | 2 -- lib/msf/base/sessions/meterpreter_options.rb | 2 -- lib/msf/base/sessions/meterpreter_php.rb | 2 -- lib/msf/base/sessions/meterpreter_x64_win.rb | 3 --- lib/msf/base/sessions/meterpreter_x86_bsd.rb | 2 -- lib/msf/base/sessions/meterpreter_x86_linux.rb | 2 -- lib/msf/base/sessions/meterpreter_x86_win.rb | 2 -- lib/msf/base/sessions/tty.rb | 2 -- lib/msf/base/simple/buffer.rb | 2 -- lib/msf/base/simple/payload.rb | 2 -- lib/msf/core/auxiliary/commandshell.rb | 2 -- lib/msf/core/auxiliary/web/form.rb | 2 -- lib/msf/core/auxiliary/web/fuzzable.rb | 2 -- lib/msf/core/auxiliary/web/path.rb | 2 -- lib/msf/core/auxiliary/web/target.rb | 2 -- lib/msf/core/exploit/browser_autopwn.rb | 6 +----- lib/msf/core/exploit/cmdstager.rb | 2 -- lib/msf/core/exploit/cmdstager_debug_asm.rb | 2 -- lib/msf/core/exploit/cmdstager_debug_write.rb | 2 -- lib/msf/core/exploit/cmdstager_tftp.rb | 2 -- lib/msf/core/exploit/cmdstager_vbs.rb | 2 -- lib/msf/core/exploit/cmdstager_vbs_adodb.rb | 2 -- lib/msf/core/exploit/dhcp.rb | 2 -- lib/msf/core/exploit/tftp.rb | 2 -- lib/msf/ui/console/command_dispatcher/payload.rb | 2 -- lib/rex/exploitation/cmdstager.rb | 2 -- lib/rex/exploitation/cmdstager/debug_asm.rb | 3 --- lib/rex/exploitation/cmdstager/debug_write.rb | 3 --- lib/rex/exploitation/cmdstager/tftp.rb | 2 -- lib/rex/exploitation/cmdstager/vbs.rb | 3 --- lib/rex/exploitation/jsobfu.rb | 2 -- lib/rex/proto/natpmp.rb | 6 +----- lib/rex/proto/ntlm/base.rb | 6 +----- lib/rex/proto/rfb.rb | 8 +------- lib/rex/proto/rfb.rb.ut.rb | 13 ++++--------- 37 files changed, 8 insertions(+), 100 deletions(-) diff --git a/lib/msf/base.rb b/lib/msf/base.rb index 5982de14cc..3a4f57225c 100644 --- a/lib/msf/base.rb +++ b/lib/msf/base.rb @@ -1,5 +1,4 @@ # -*- coding: binary -*- -### # # framework-base # -------------- @@ -13,8 +12,6 @@ # Beyond providing the default sessions, framework-base also provides # a wrapper interface to framework-core that makes some of the tasks, # such as exploitation, into easier to manage functions. -# -### # framework-base depends on framework-core require 'msf/core' diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 211e52b4e1..2f8b79580a 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base' require 'msf/base/sessions/scriptable' diff --git a/lib/msf/base/sessions/meterpreter_java.rb b/lib/msf/base/sessions/meterpreter_java.rb index 7f298bf408..c92e58e782 100644 --- a/lib/msf/base/sessions/meterpreter_java.rb +++ b/lib/msf/base/sessions/meterpreter_java.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/meterpreter' diff --git a/lib/msf/base/sessions/meterpreter_options.rb b/lib/msf/base/sessions/meterpreter_options.rb index fb467f39f4..c44b8c6f5e 100644 --- a/lib/msf/base/sessions/meterpreter_options.rb +++ b/lib/msf/base/sessions/meterpreter_options.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'shellwords' diff --git a/lib/msf/base/sessions/meterpreter_php.rb b/lib/msf/base/sessions/meterpreter_php.rb index 90cd75b2e7..2ba47c2c03 100644 --- a/lib/msf/base/sessions/meterpreter_php.rb +++ b/lib/msf/base/sessions/meterpreter_php.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/meterpreter' diff --git a/lib/msf/base/sessions/meterpreter_x64_win.rb b/lib/msf/base/sessions/meterpreter_x64_win.rb index 84b56c9e69..450e26bf8d 100644 --- a/lib/msf/base/sessions/meterpreter_x64_win.rb +++ b/lib/msf/base/sessions/meterpreter_x64_win.rb @@ -1,7 +1,4 @@ # -*- coding: binary -*- -## -# $Id: meterpreter_options.rb 10595 2010-10-08 04:11:47Z hdm $ -## require 'msf/base/sessions/meterpreter' require 'msf/windows_error' diff --git a/lib/msf/base/sessions/meterpreter_x86_bsd.rb b/lib/msf/base/sessions/meterpreter_x86_bsd.rb index fcb55d3788..1f851e270c 100644 --- a/lib/msf/base/sessions/meterpreter_x86_bsd.rb +++ b/lib/msf/base/sessions/meterpreter_x86_bsd.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/meterpreter' diff --git a/lib/msf/base/sessions/meterpreter_x86_linux.rb b/lib/msf/base/sessions/meterpreter_x86_linux.rb index df6029cf60..33e72d1be9 100644 --- a/lib/msf/base/sessions/meterpreter_x86_linux.rb +++ b/lib/msf/base/sessions/meterpreter_x86_linux.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/meterpreter' diff --git a/lib/msf/base/sessions/meterpreter_x86_win.rb b/lib/msf/base/sessions/meterpreter_x86_win.rb index 12a259a892..2f44f0e640 100644 --- a/lib/msf/base/sessions/meterpreter_x86_win.rb +++ b/lib/msf/base/sessions/meterpreter_x86_win.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/meterpreter' require 'msf/windows_error' diff --git a/lib/msf/base/sessions/tty.rb b/lib/msf/base/sessions/tty.rb index cf8dddf2d6..acaad4629d 100644 --- a/lib/msf/base/sessions/tty.rb +++ b/lib/msf/base/sessions/tty.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base' diff --git a/lib/msf/base/simple/buffer.rb b/lib/msf/base/simple/buffer.rb index 3f4992c071..13d26ab7c6 100644 --- a/lib/msf/base/simple/buffer.rb +++ b/lib/msf/base/simple/buffer.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base' diff --git a/lib/msf/base/simple/payload.rb b/lib/msf/base/simple/payload.rb index 7f6cae11dd..59357b1255 100644 --- a/lib/msf/base/simple/payload.rb +++ b/lib/msf/base/simple/payload.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base' diff --git a/lib/msf/core/auxiliary/commandshell.rb b/lib/msf/core/auxiliary/commandshell.rb index d4c91a454d..ae145c81be 100644 --- a/lib/msf/core/auxiliary/commandshell.rb +++ b/lib/msf/core/auxiliary/commandshell.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/base/sessions/command_shell_options' diff --git a/lib/msf/core/auxiliary/web/form.rb b/lib/msf/core/auxiliary/web/form.rb index b899be34fd..6636db9414 100644 --- a/lib/msf/core/auxiliary/web/form.rb +++ b/lib/msf/core/auxiliary/web/form.rb @@ -1,9 +1,7 @@ -## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ -## require 'net/https' require 'net/http' diff --git a/lib/msf/core/auxiliary/web/fuzzable.rb b/lib/msf/core/auxiliary/web/fuzzable.rb index e3daebb393..d4e0e91c88 100644 --- a/lib/msf/core/auxiliary/web/fuzzable.rb +++ b/lib/msf/core/auxiliary/web/fuzzable.rb @@ -1,9 +1,7 @@ -## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ -## require 'net/https' require 'net/http' diff --git a/lib/msf/core/auxiliary/web/path.rb b/lib/msf/core/auxiliary/web/path.rb index c8ee856032..2344aa8c6c 100644 --- a/lib/msf/core/auxiliary/web/path.rb +++ b/lib/msf/core/auxiliary/web/path.rb @@ -1,9 +1,7 @@ -## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ -## require 'net/https' require 'net/http' diff --git a/lib/msf/core/auxiliary/web/target.rb b/lib/msf/core/auxiliary/web/target.rb index ea892fda81..09578c56a2 100644 --- a/lib/msf/core/auxiliary/web/target.rb +++ b/lib/msf/core/auxiliary/web/target.rb @@ -1,9 +1,7 @@ -## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ -## require 'net/https' require 'net/http' diff --git a/lib/msf/core/exploit/browser_autopwn.rb b/lib/msf/core/exploit/browser_autopwn.rb index a792abcc1e..f4b63f8251 100644 --- a/lib/msf/core/exploit/browser_autopwn.rb +++ b/lib/msf/core/exploit/browser_autopwn.rb @@ -1,14 +1,10 @@ # -*- coding: binary -*- -## # $Id$ -## - -## +# # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ -## require 'msf/core/auxiliary' diff --git a/lib/msf/core/exploit/cmdstager.rb b/lib/msf/core/exploit/cmdstager.rb index 5e2e551a54..c99d29064f 100644 --- a/lib/msf/core/exploit/cmdstager.rb +++ b/lib/msf/core/exploit/cmdstager.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/exploitation/cmdstager' require 'msf/core/exploit/exe' diff --git a/lib/msf/core/exploit/cmdstager_debug_asm.rb b/lib/msf/core/exploit/cmdstager_debug_asm.rb index 979edb6bec..508307a25d 100644 --- a/lib/msf/core/exploit/cmdstager_debug_asm.rb +++ b/lib/msf/core/exploit/cmdstager_debug_asm.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/core/exploit/cmdstager' diff --git a/lib/msf/core/exploit/cmdstager_debug_write.rb b/lib/msf/core/exploit/cmdstager_debug_write.rb index 707369d2f7..8b73ac8adf 100644 --- a/lib/msf/core/exploit/cmdstager_debug_write.rb +++ b/lib/msf/core/exploit/cmdstager_debug_write.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/core/exploit/cmdstager' diff --git a/lib/msf/core/exploit/cmdstager_tftp.rb b/lib/msf/core/exploit/cmdstager_tftp.rb index 3b11683878..5549a687bd 100644 --- a/lib/msf/core/exploit/cmdstager_tftp.rb +++ b/lib/msf/core/exploit/cmdstager_tftp.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/text' require 'msf/core/exploit/tftp' diff --git a/lib/msf/core/exploit/cmdstager_vbs.rb b/lib/msf/core/exploit/cmdstager_vbs.rb index 0f75595916..1247d118a7 100644 --- a/lib/msf/core/exploit/cmdstager_vbs.rb +++ b/lib/msf/core/exploit/cmdstager_vbs.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'msf/core/exploit/cmdstager' diff --git a/lib/msf/core/exploit/cmdstager_vbs_adodb.rb b/lib/msf/core/exploit/cmdstager_vbs_adodb.rb index b723abbc4a..b6a082a2a1 100644 --- a/lib/msf/core/exploit/cmdstager_vbs_adodb.rb +++ b/lib/msf/core/exploit/cmdstager_vbs_adodb.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id: $ -## require 'msf/core/exploit/cmdstager' diff --git a/lib/msf/core/exploit/dhcp.rb b/lib/msf/core/exploit/dhcp.rb index 655e70877f..455c204d84 100644 --- a/lib/msf/core/exploit/dhcp.rb +++ b/lib/msf/core/exploit/dhcp.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/proto/dhcp' diff --git a/lib/msf/core/exploit/tftp.rb b/lib/msf/core/exploit/tftp.rb index feb197e9ec..c20250f5d8 100644 --- a/lib/msf/core/exploit/tftp.rb +++ b/lib/msf/core/exploit/tftp.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/proto/tftp' diff --git a/lib/msf/ui/console/command_dispatcher/payload.rb b/lib/msf/ui/console/command_dispatcher/payload.rb index c3449fc484..8b6a248384 100644 --- a/lib/msf/ui/console/command_dispatcher/payload.rb +++ b/lib/msf/ui/console/command_dispatcher/payload.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/parser/arguments' diff --git a/lib/rex/exploitation/cmdstager.rb b/lib/rex/exploitation/cmdstager.rb index 298976ec9e..89c04de2ba 100644 --- a/lib/rex/exploitation/cmdstager.rb +++ b/lib/rex/exploitation/cmdstager.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/exploitation/cmdstager/base' require 'rex/exploitation/cmdstager/vbs' diff --git a/lib/rex/exploitation/cmdstager/debug_asm.rb b/lib/rex/exploitation/cmdstager/debug_asm.rb index 92d1aae809..a2eff63d21 100644 --- a/lib/rex/exploitation/cmdstager/debug_asm.rb +++ b/lib/rex/exploitation/cmdstager/debug_asm.rb @@ -1,7 +1,4 @@ # -*- coding: binary -*- -## -# $Id: debug_asm.rb 12595 2011-05-12 18:33:49Z jduck $ -## require 'rex/text' require 'rex/arch' diff --git a/lib/rex/exploitation/cmdstager/debug_write.rb b/lib/rex/exploitation/cmdstager/debug_write.rb index 1d0fdbc8d7..d795439890 100644 --- a/lib/rex/exploitation/cmdstager/debug_write.rb +++ b/lib/rex/exploitation/cmdstager/debug_write.rb @@ -1,7 +1,4 @@ # -*- coding: binary -*- -## -# $Id: debug_write.rb 12595 2011-05-12 18:33:49Z jduck $ -## require 'rex/text' require 'rex/arch' diff --git a/lib/rex/exploitation/cmdstager/tftp.rb b/lib/rex/exploitation/cmdstager/tftp.rb index 130ad91999..3607fb4ea7 100644 --- a/lib/rex/exploitation/cmdstager/tftp.rb +++ b/lib/rex/exploitation/cmdstager/tftp.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/text' require 'rex/arch' diff --git a/lib/rex/exploitation/cmdstager/vbs.rb b/lib/rex/exploitation/cmdstager/vbs.rb index a699ad3820..c50e1b5350 100644 --- a/lib/rex/exploitation/cmdstager/vbs.rb +++ b/lib/rex/exploitation/cmdstager/vbs.rb @@ -1,7 +1,4 @@ # -*- coding: binary -*- -## -# $Id: vbs.rb 12595 2011-05-12 18:33:49Z jduck $ -## require 'rex/text' require 'rex/arch' diff --git a/lib/rex/exploitation/jsobfu.rb b/lib/rex/exploitation/jsobfu.rb index 844d9f8e64..2c8d51299a 100644 --- a/lib/rex/exploitation/jsobfu.rb +++ b/lib/rex/exploitation/jsobfu.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -## # $Id$ -## require 'rex/text' require 'rkelly' diff --git a/lib/rex/proto/natpmp.rb b/lib/rex/proto/natpmp.rb index cf41512b54..e4aab9e7cc 100644 --- a/lib/rex/proto/natpmp.rb +++ b/lib/rex/proto/natpmp.rb @@ -1,11 +1,7 @@ # -*- coding: binary -*- -## -# # NAT-PMP protocol support # -# by Jon Hart -# -## +# @author Jon Hart require 'rex/proto/natpmp/constants' require 'rex/proto/natpmp/packet' diff --git a/lib/rex/proto/ntlm/base.rb b/lib/rex/proto/ntlm/base.rb index 94c027c91b..7db9bc5696 100644 --- a/lib/rex/proto/ntlm/base.rb +++ b/lib/rex/proto/ntlm/base.rb @@ -40,17 +40,13 @@ # The latter has a minor bug in its separate_keys function. # The third key has to begin from the 14th character of the # input string instead of 13th:) -#-- -# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $ -#++ - -#this class defines the base type needed for other modules like message and crypt require 'rex/proto/ntlm/constants' module Rex module Proto module NTLM +# The base type needed for other modules like message and crypt class Base CONST = Rex::Proto::NTLM::Constants diff --git a/lib/rex/proto/rfb.rb b/lib/rex/proto/rfb.rb index 53e493e4df..378bc1428b 100644 --- a/lib/rex/proto/rfb.rb +++ b/lib/rex/proto/rfb.rb @@ -1,19 +1,13 @@ # -*- coding: binary -*- -## # $Id: $ -## - -## # # RFB protocol support # -# by Joshua J. Drake +# @author Joshua J. Drake # # Based on: # vnc_auth_none contributed by Matteo Cantoni # vnc_auth_login contributed by carstein -# -## require 'rex/proto/rfb/constants' require 'rex/proto/rfb/cipher' diff --git a/lib/rex/proto/rfb.rb.ut.rb b/lib/rex/proto/rfb.rb.ut.rb index d9771e7516..d06de6c4d2 100644 --- a/lib/rex/proto/rfb.rb.ut.rb +++ b/lib/rex/proto/rfb.rb.ut.rb @@ -1,23 +1,18 @@ #!/usr/bin/env ruby # -*- coding: binary -*- - -$:.unshift(File.join(File.dirname(__FILE__), '..', '..')) - -## +# # $Id: $ -## - -## # # RFB protocol support # -# by Joshua J. Drake +# @author Joshua J. Drake # # Based on: # vnc_auth_none contributed by Matteo Cantoni # vnc_auth_login contributed by carstein # -## + +$:.unshift(File.join(File.dirname(__FILE__), '..', '..')) require 'rex/socket' require 'rex/proto/rfb' From bc4b87ebd925f60b6da74e53eaf52542cc7a9051 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sat, 30 Mar 2013 16:05:12 -0500 Subject: [PATCH 3/9] Fix Undocumentable method defined on object instance YARD warnings [#46491831] Change code to use format that YARD can document without changing semantics. --- lib/msf/ui/console/command_dispatcher/core.rb | 20 ++++++++++++---- lib/rex/proto/ntlm/base.rb | 20 +++++++++------- lib/rex/ui/text/output/buffer/stdout.rb | 24 +++++++++++++++++++ 3 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 lib/rex/ui/text/output/buffer/stdout.rb diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 58d21445a7..b5742731e1 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -1,4 +1,15 @@ # -*- coding: binary -*- + +# +# Rex +# + +require 'rex/ui/text/output/buffer/stdout' + +# +# Project +# + require 'msf/ui/console/command_dispatcher/encoder' require 'msf/ui/console/command_dispatcher/exploit' require 'msf/ui/console/command_dispatcher/nop' @@ -2532,11 +2543,12 @@ class Core # redirect output after saving the old ones and getting a new output buffer to use for redirect orig_driver_output = orig_driver.output orig_driver_input = orig_driver.input - # we use a rex buffer but add a write method to the instance, which is required in order to be valid $stdout + + # we use a rex buffer but add a write method to the instance, which is + # required in order to be valid $stdout temp_output = Rex::Ui::Text::Output::Buffer.new - def temp_output.write(msg = '') - self.print_raw(msg) - end + temp_output.extend Rex::Ui::Text::Output::Buffer::Stdout + orig_driver.init_ui(orig_driver_input,temp_output) # run the desired command to be grepped orig_driver.run_single(cmd) diff --git a/lib/rex/proto/ntlm/base.rb b/lib/rex/proto/ntlm/base.rb index 7db9bc5696..fd36bba1e4 100644 --- a/lib/rex/proto/ntlm/base.rb +++ b/lib/rex/proto/ntlm/base.rb @@ -160,15 +160,19 @@ CONST = Rex::Proto::NTLM::Constants class FieldSet class << FieldSet def define(&block) - c = Class.new(self) - def c.inherited(subclass) - proto = @proto - subclass.instance_eval { - @proto = proto - } + klass = Class.new(self) do + def self.inherited(subclass) + proto = @proto + + subclass.instance_eval do + @proto = proto + end + end end - c.module_eval(&block) - c + + klass.module_eval(&block) + + klass end def string(name, opts) diff --git a/lib/rex/ui/text/output/buffer/stdout.rb b/lib/rex/ui/text/output/buffer/stdout.rb new file mode 100644 index 0000000000..a00426942c --- /dev/null +++ b/lib/rex/ui/text/output/buffer/stdout.rb @@ -0,0 +1,24 @@ +# make sure the class is define before opening it to define submodule +require 'rex/ui/text/output/buffer' + +module Rex + module Ui + module Text + module Output + class Buffer + # Adds {#write} method to {Rex::Ui::Text::Output::Buffer} so it can + # function as a stand-in for `$stdout` + module Stdout + # Prints raw message. + # + # @param (see Rex::Ui::Text::Output::Buffer#write) + # @return (see Rex::Ui::Text::Output::Buffer#write) + def write(msg = '') + print_raw(msg) + end + end + end + end + end + end +end \ No newline at end of file From 7ed2812ec38b66a2f4f2c5975a20cf077c538849 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sat, 30 Mar 2013 16:58:49 -0500 Subject: [PATCH 4/9] Fix Cannot resolve link YARD warnings [#46491831] --- lib/msf/core/modules/loader/base.rb | 7 ++++--- lib/rex/peparsey/section.rb | 6 ------ lib/rex/text.rb | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/modules/loader/base.rb b/lib/msf/core/modules/loader/base.rb index c2c4e49bbe..8e3930d45e 100644 --- a/lib/msf/core/modules/loader/base.rb +++ b/lib/msf/core/modules/loader/base.rb @@ -58,7 +58,8 @@ class Msf::Modules::Loader::Base MODULE_EXTENSION = '.rb' # String used to separate module names in a qualified module name. MODULE_SEPARATOR = '::' - # The base namespace name under which {#namespace_module #namespace_modules} are created. + # The base namespace name under which {#create_namespace_module + # namespace modules are created}. NAMESPACE_MODULE_NAMES = ['Msf', 'Modules'] # Regex that can distinguish regular ruby source from unit test source. UNIT_TEST_REGEX = /rb\.(ut|ts)\.rb$/ @@ -340,7 +341,7 @@ class Msf::Modules::Loader::Base # module's classes. The wrapper module must be named so that active_support's autoloading code doesn't break when # searching constants from inside the Metasploit(1|2|3) class. # - # @param [String] namespace_module_names (see #{namespace_module_names}) + # @param [Array] {NAMESPACE_MODULE_NAMES} + # @return [Module] module that can wrap the module content from {#read_module_content} using # module_eval_with_lexical_scope. # @@ -375,7 +376,7 @@ class Msf::Modules::Loader::Base namespace_module end - # Returns the module with {#module_names} if it exists. + # Returns the module with `module_names` if it exists. # # @param [Array] module_names a list of module names to resolve from Object downward. # @return [Module] module that wraps the previously loaded content from {#read_module_content}. diff --git a/lib/rex/peparsey/section.rb b/lib/rex/peparsey/section.rb index 3036028929..392b42f067 100644 --- a/lib/rex/peparsey/section.rb +++ b/lib/rex/peparsey/section.rb @@ -100,12 +100,6 @@ class Section def file_offset_to_rva(foffset) return offset_to_rva(foffset - file_offset) end -# if offset < 0 || offset < file_offset || offset >= file_offset+size -# raise BoundsError, "File offset #{offset} outside of section", caller -# end -# -# return (offset - file_offset) + base_rva -# end def rva_to_offset(rva) offset = rva - base_rva diff --git a/lib/rex/text.rb b/lib/rex/text.rb index e9a46035ff..6cc2f00a69 100644 --- a/lib/rex/text.rb +++ b/lib/rex/text.rb @@ -1020,7 +1020,7 @@ module Text rand_base(len, bad, *foo ) end - # Generate a random GUID, of the form {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + # Generate a random GUID, of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. def self.rand_guid "{#{[8,4,4,4,12].map {|a| rand_text_hex(a) }.join("-")}}" end From 2317e9ccede5cb48521def9557b626259aee33b4 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sat, 30 Mar 2013 17:13:12 -0500 Subject: [PATCH 5/9] Fix yard tag warnings [#46491831] --- lib/msf/core/modules/loader/base.rb | 20 +++++++++++--------- lib/rex/poly/block.rb | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/modules/loader/base.rb b/lib/msf/core/modules/loader/base.rb index 8e3930d45e..b6daec5da1 100644 --- a/lib/msf/core/modules/loader/base.rb +++ b/lib/msf/core/modules/loader/base.rb @@ -341,7 +341,8 @@ class Msf::Modules::Loader::Base # module's classes. The wrapper module must be named so that active_support's autoloading code doesn't break when # searching constants from inside the Metasploit(1|2|3) class. # - # @param [Array] {NAMESPACE_MODULE_NAMES} + + # @param namespace_module_names [Array] + # {NAMESPACE_MODULE_NAMES} + # @return [Module] module that can wrap the module content from {#read_module_content} using # module_eval_with_lexical_scope. # @@ -485,13 +486,14 @@ class Msf::Modules::Loader::Base # Returns the fully-qualified name to the {#create_namespace_module} that wraps the module with the given module # reference name. # - # @param [String] module_reference_name The canonical name for referring to the module. + # @param [String] module_full_name The canonical name for referring to the + # module. # @return [String] name of module. # # @see MODULE_SEPARATOR # @see #namespace_module_names - def namespace_module_name(uniq_module_reference_name) - namespace_module_names = self.namespace_module_names(uniq_module_reference_name) + def namespace_module_name(module_full_name) + namespace_module_names = self.namespace_module_names(module_full_name) namespace_module_name = namespace_module_names.join(MODULE_SEPARATOR) namespace_module_name @@ -503,20 +505,20 @@ class Msf::Modules::Loader::Base # escaped by using 'H*' unpacking and prefixing each code with X so # the code remains a valid module name when it starts with a digit. # - # @param [String] uniq_module_reference_name The unique canonical name + # @param [String] module_full_name The unique canonical name # for the module including type. # @return [Array] {NAMESPACE_MODULE_NAMES} + # # @see namespace_module - def namespace_module_names(uniq_module_reference_name) - NAMESPACE_MODULE_NAMES + [ "Mod" + uniq_module_reference_name.unpack("H*").first.downcase ] + def namespace_module_names(module_full_name) + NAMESPACE_MODULE_NAMES + [ "Mod" + module_full_name.unpack("H*").first.downcase ] end - def namespace_module_transaction(uniq_module_reference_name, options={}, &block) + def namespace_module_transaction(module_full_name, options={}, &block) options.assert_valid_keys(:reload) reload = options[:reload] || false - namespace_module_names = self.namespace_module_names(uniq_module_reference_name) + namespace_module_names = self.namespace_module_names(module_full_name) previous_namespace_module = current_module(namespace_module_names) diff --git a/lib/rex/poly/block.rb b/lib/rex/poly/block.rb index 1b67290465..89a07ade9a 100644 --- a/lib/rex/poly/block.rb +++ b/lib/rex/poly/block.rb @@ -131,7 +131,7 @@ class LogicalBlock # # Increments the number of blocks that have completed their dependency # pass on this block. This number should never become higher than the - # @references attribute. + # `@references` attribute. # def deref @used_references += 1 From 0bb79ba8904e0c0ecd17c1aba55ce2da118de6bf Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 1 Apr 2013 16:06:40 -0500 Subject: [PATCH 6/9] Msf::DBManager#import_msf_xml refactor [#46491831] Move Msf::DBManager#import_msf_xml into Msf::DBManager::ImportMsfXml#import_msf_xml and include Msf::DBManager::ImportMsfXml to cut down size of the infamous db.rb. Break up #import_msf_xml to have separate methods for parsing web_forms, web_pages, and web_vulns. The method for web_vulns, #import_msf_web_vuln_element is needed so that it can be overridden in Pro to handle the Pro-only changes to Mdm::WebVuln. --- Gemfile | 8 + Gemfile.lock | 5 + lib/metasploit/framework/database.rb | 15 + lib/msf/core/db.rb | 395 +----- lib/msf/core/db_manager/import_msf_xml.rb | 580 ++++++++ lib/tasks/database.rake | 16 +- spec/factories/mdm/web_forms.rb | 34 + spec/factories/mdm/web_pages.rb | 64 + spec/factories/mdm/web_vulns.rb | 14 + spec/lib/msf/core/db_spec.rb | 22 + spec/spec_helper.rb | 17 + .../shared/contexts/database_cleaner.rb | 34 + .../shared/contexts/msf/simple/framework.rb | 39 + .../examples/msf/db_manager/import_msf_xml.rb | 1182 +++++++++++++++++ .../check_msf_xml_version_with_root_tag.rb | 24 + .../import_msf_web_element_specialization.rb | 41 + 16 files changed, 2130 insertions(+), 360 deletions(-) create mode 100644 lib/metasploit/framework/database.rb create mode 100644 lib/msf/core/db_manager/import_msf_xml.rb create mode 100644 spec/factories/mdm/web_forms.rb create mode 100644 spec/factories/mdm/web_pages.rb create mode 100644 spec/factories/mdm/web_vulns.rb create mode 100644 spec/lib/msf/core/db_spec.rb create mode 100644 spec/support/shared/contexts/database_cleaner.rb create mode 100644 spec/support/shared/contexts/msf/simple/framework.rb create mode 100644 spec/support/shared/examples/msf/db_manager/import_msf_xml.rb create mode 100644 spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb create mode 100644 spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb diff --git a/Gemfile b/Gemfile index 5b75bf461b..ebf4176c7b 100755 --- a/Gemfile +++ b/Gemfile @@ -27,11 +27,19 @@ group :development do end group :development, :test do + # supplies factories for producing model instance for specs + # Version 4.1.0 or newer is needed to support generate calls without the + # 'FactoryGirl.' in factory definitions syntax. + gem 'factory_girl', '>= 4.1.0' # running documentation generation tasks and rspec tasks gem 'rake' end group :test do + # Removes records from database created during tests. Can't use rspec-rails' + # transactional fixtures because multiple connections are in use so + # transactions won't work. + gem 'database_cleaner' # testing framework gem 'rspec', '>= 2.12' # code coverage for tests diff --git a/Gemfile.lock b/Gemfile.lock index f789bbb3fd..56e95a9393 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,7 +26,10 @@ GEM arel (3.0.2) builder (3.0.4) coderay (1.0.9) + database_cleaner (0.9.1) diff-lcs (1.1.3) + factory_girl (4.2.0) + activesupport (>= 3.0.0) i18n (0.6.1) json (1.7.7) method_source (0.8.1) @@ -64,6 +67,8 @@ PLATFORMS DEPENDENCIES activerecord activesupport (>= 3.0.0) + database_cleaner + factory_girl (>= 4.1.0) json metasploit_data_models! msgpack diff --git a/lib/metasploit/framework/database.rb b/lib/metasploit/framework/database.rb new file mode 100644 index 0000000000..51cb4bcf26 --- /dev/null +++ b/lib/metasploit/framework/database.rb @@ -0,0 +1,15 @@ +require 'metasploit/framework' + +module Metasploit + module Framework + module Database + def self.configurations + YAML.load_file(configurations_pathname) + end + + def self.configurations_pathname + Metasploit::Framework.root.join('config', 'database.yml') + end + end + end +end diff --git a/lib/msf/core/db.rb b/lib/msf/core/db.rb index bd6d0e4d81..6a4985be05 100644 --- a/lib/msf/core/db.rb +++ b/lib/msf/core/db.rb @@ -1,35 +1,61 @@ # -*- coding: binary -*- + +# +# Standard Library +# + +require 'csv' +require 'tmpdir' +require 'uri' +require 'zip' + +# +# +# Included Libraries +# +# + +# +# PacketFu +# + +require 'packetfu' + +# +# Rex +# + + +require 'rex/socket' + # Check Rex::Parser.nokogiri_loaded for status of the Nokogiri parsers -require 'rex/parser/nmap_nokogiri' -require 'rex/parser/nexpose_simple_nokogiri' -require 'rex/parser/nexpose_raw_nokogiri' -require 'rex/parser/foundstone_nokogiri' -require 'rex/parser/mbsa_nokogiri' require 'rex/parser/acunetix_nokogiri' require 'rex/parser/appscan_nokogiri' require 'rex/parser/burp_session_nokogiri' require 'rex/parser/ci_nokogiri' -require 'rex/parser/wapiti_nokogiri' -require 'rex/parser/openvas_nokogiri' +require 'rex/parser/foundstone_nokogiri' require 'rex/parser/fusionvm_nokogiri' +require 'rex/parser/mbsa_nokogiri' +require 'rex/parser/nexpose_raw_nokogiri' +require 'rex/parser/nexpose_simple_nokogiri' +require 'rex/parser/nmap_nokogiri' +require 'rex/parser/openvas_nokogiri' +require 'rex/parser/wapiti_nokogiri' # Legacy XML parsers -- these will be converted some day - -require 'rex/parser/nmap_xml' -require 'rex/parser/nexpose_xml' -require 'rex/parser/retina_xml' -require 'rex/parser/netsparker_xml' -require 'rex/parser/nessus_xml' -require 'rex/parser/ip360_xml' require 'rex/parser/ip360_aspl_xml' +require 'rex/parser/ip360_xml' +require 'rex/parser/nessus_xml' +require 'rex/parser/netsparker_xml' +require 'rex/parser/nexpose_xml' +require 'rex/parser/nmap_xml' +require 'rex/parser/retina_xml' -require 'rex/socket' -require 'zip' -require 'packetfu' -require 'uri' -require 'tmpdir' -require 'csv' +# +# Project +# +require 'msf/core/db_manager/import_msf_xml' module Msf @@ -129,6 +155,7 @@ end # ### class DBManager + include Msf::DBManager::ImportMsfXml def rfc3330_reserved(ip) case ip.class.to_s @@ -3669,334 +3696,6 @@ class DBManager end - # For each host, step through services, notes, and vulns, and import - # them. - # TODO: loot, tasks, and reports - def import_msf_xml(args={}, &block) - data = args[:data] - wspace = args[:wspace] || workspace - bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : [] - - allow_yaml = false - btag = nil - - doc = rexmlify(data) - if doc.elements["MetasploitExpressV1"] - m_ver = 1 - allow_yaml = true - btag = "MetasploitExpressV1" - elsif doc.elements["MetasploitExpressV2"] - m_ver = 2 - allow_yaml = true - btag = "MetasploitExpressV2" - elsif doc.elements["MetasploitExpressV3"] - m_ver = 3 - btag = "MetasploitExpressV3" - elsif doc.elements["MetasploitExpressV4"] - m_ver = 4 - btag = "MetasploitExpressV4" - elsif doc.elements["MetasploitV4"] - m_ver = 4 - btag = "MetasploitV4" - else - m_ver = nil - end - unless m_ver and btag - raise DBImportError.new("Unsupported Metasploit XML document format") - end - - doc.elements.each("/#{btag}/hosts/host") do |host| - host_data = {} - host_data[:workspace] = wspace - host_data[:host] = nils_for_nulls(host.elements["address"].text.to_s.strip) - if bl.include? host_data[:host] - next - else - yield(:address,host_data[:host]) if block - end - host_data[:mac] = nils_for_nulls(host.elements["mac"].text.to_s.strip) - if host.elements["comm"].text - host_data[:comm] = nils_for_nulls(host.elements["comm"].text.to_s.strip) - end - %W{created-at updated-at name state os-flavor os-lang os-name os-sp purpose}.each { |datum| - if host.elements[datum].text - host_data[datum.gsub('-','_')] = nils_for_nulls(host.elements[datum].text.to_s.strip) - end - } - host_address = host_data[:host].dup # Preserve after report_host() deletes - hobj = report_host(host_data) - - host.elements.each("host_details/host_detail") do |hdet| - hdet_data = {} - hdet.elements.each do |det| - next if ["id", "host-id"].include?(det.name) - if det.text - hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) - end - end - report_host_details(hobj, hdet_data) - end - - host.elements.each("exploit_attempts/exploit_attempt") do |hdet| - hdet_data = {} - hdet.elements.each do |det| - next if ["id", "host-id", "session-id", "vuln-id", "service-id", "loot-id"].include?(det.name) - if det.text - hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) - end - end - report_exploit_attempt(hobj, hdet_data) - end - - host.elements.each('services/service') do |service| - service_data = {} - service_data[:workspace] = wspace - service_data[:host] = hobj - service_data[:port] = nils_for_nulls(service.elements["port"].text.to_s.strip).to_i - service_data[:proto] = nils_for_nulls(service.elements["proto"].text.to_s.strip) - %W{created-at updated-at name state info}.each { |datum| - if service.elements[datum].text - if datum == "info" - service_data["info"] = nils_for_nulls(unserialize_object(service.elements[datum], false)) - else - service_data[datum.gsub("-","_")] = nils_for_nulls(service.elements[datum].text.to_s.strip) - end - end - } - report_service(service_data) - end - - host.elements.each('notes/note') do |note| - note_data = {} - note_data[:workspace] = wspace - note_data[:host] = hobj - note_data[:type] = nils_for_nulls(note.elements["ntype"].text.to_s.strip) - note_data[:data] = nils_for_nulls(unserialize_object(note.elements["data"], allow_yaml)) - - if note.elements["critical"].text - note_data[:critical] = true unless note.elements["critical"].text.to_s.strip == "NULL" - end - if note.elements["seen"].text - note_data[:seen] = true unless note.elements["critical"].text.to_s.strip == "NULL" - end - %W{created-at updated-at}.each { |datum| - if note.elements[datum].text - note_data[datum.gsub("-","_")] = nils_for_nulls(note.elements[datum].text.to_s.strip) - end - } - report_note(note_data) - end - - host.elements.each('tags/tag') do |tag| - tag_data = {} - tag_data[:addr] = host_address - tag_data[:wspace] = wspace - tag_data[:name] = tag.elements["name"].text.to_s.strip - tag_data[:desc] = tag.elements["desc"].text.to_s.strip - if tag.elements["report-summary"].text - tag_data[:summary] = tag.elements["report-summary"].text.to_s.strip - end - if tag.elements["report-detail"].text - tag_data[:detail] = tag.elements["report-detail"].text.to_s.strip - end - if tag.elements["critical"].text - tag_data[:crit] = true unless tag.elements["critical"].text.to_s.strip == "NULL" - end - report_host_tag(tag_data) - end - - host.elements.each('vulns/vuln') do |vuln| - vuln_data = {} - vuln_data[:workspace] = wspace - vuln_data[:host] = hobj - vuln_data[:info] = nils_for_nulls(unserialize_object(vuln.elements["info"], allow_yaml)) - vuln_data[:name] = nils_for_nulls(vuln.elements["name"].text.to_s.strip) - %W{created-at updated-at exploited-at}.each { |datum| - if vuln.elements[datum] and vuln.elements[datum].text - vuln_data[datum.gsub("-","_")] = nils_for_nulls(vuln.elements[datum].text.to_s.strip) - end - } - if vuln.elements["refs"] - vuln_data[:refs] = [] - vuln.elements.each("refs/ref") do |ref| - vuln_data[:refs] << nils_for_nulls(ref.text.to_s.strip) - end - end - - vobj = report_vuln(vuln_data) - - vuln.elements.each("vuln_details/vuln_detail") do |vdet| - vdet_data = {} - vdet.elements.each do |det| - next if ["id", "vuln-id"].include?(det.name) - if det.text - vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) - end - end - report_vuln_details(vobj, vdet_data) - end - - vuln.elements.each("vuln_attempts/vuln_attempt") do |vdet| - vdet_data = {} - vdet.elements.each do |det| - next if ["id", "vuln-id", "loot-id", "session-id"].include?(det.name) - if det.text - vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) - end - end - report_vuln_attempt(vobj, vdet_data) - end - end - - host.elements.each('creds/cred') do |cred| - cred_data = {} - cred_data[:workspace] = wspace - cred_data[:host] = hobj - %W{port ptype sname proto proof active user pass}.each {|datum| - if cred.elements[datum].respond_to? :text - cred_data[datum.intern] = nils_for_nulls(cred.elements[datum].text.to_s.strip) - end - } - %W{created-at updated-at}.each { |datum| - if cred.elements[datum].respond_to? :text - cred_data[datum.gsub("-","_")] = nils_for_nulls(cred.elements[datum].text.to_s.strip) - end - } - %W{source-type source-id}.each { |datum| - if cred.elements[datum].respond_to? :text - cred_data[datum.gsub("-","_").intern] = nils_for_nulls(cred.elements[datum].text.to_s.strip) - end - } - if cred_data[:pass] == "" - cred_data[:pass] = "" - cred_data[:active] = false - elsif cred_data[:pass] == "*BLANK PASSWORD*" - cred_data[:pass] = "" - end - report_cred(cred_data) - end - - host.elements.each('sessions/session') do |sess| - sess_id = nils_for_nulls(sess.elements["id"].text.to_s.strip.to_i) - sess_data = {} - sess_data[:host] = hobj - %W{desc platform port stype}.each {|datum| - if sess.elements[datum].respond_to? :text - sess_data[datum.intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip) - end - } - %W{opened-at close-reason closed-at via-exploit via-payload}.each {|datum| - if sess.elements[datum].respond_to? :text - sess_data[datum.gsub("-","_").intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip) - end - } - sess_data[:datastore] = nils_for_nulls(unserialize_object(sess.elements["datastore"], allow_yaml)) - if sess.elements["routes"] - sess_data[:routes] = nils_for_nulls(unserialize_object(sess.elements["routes"], allow_yaml)) || [] - end - if not sess_data[:closed_at] # Fake a close if we don't already have one - sess_data[:closed_at] = Time.now.utc - sess_data[:close_reason] = "Imported at #{Time.now.utc}" - end - - existing_session = get_session( - :workspace => sess_data[:host].workspace, - :addr => sess_data[:host].address, - :time => sess_data[:opened_at] - ) - this_session = existing_session || report_session(sess_data) - next if existing_session - sess.elements.each('events/event') do |sess_event| - sess_event_data = {} - sess_event_data[:session] = this_session - %W{created-at etype local-path remote-path}.each {|datum| - if sess_event.elements[datum].respond_to? :text - sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(sess_event.elements[datum].text.to_s.strip) - end - } - %W{command output}.each {|datum| - if sess_event.elements[datum].respond_to? :text - sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(unserialize_object(sess_event.elements[datum], allow_yaml)) - end - } - report_session_event(sess_event_data) - end - end - end - - # Import web sites - doc.elements.each("/#{btag}/web_sites/web_site") do |web| - info = {} - info[:workspace] = wspace - - %W{host port vhost ssl comments}.each do |datum| - if web.elements[datum].respond_to? :text - info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - end - - info[:options] = nils_for_nulls(unserialize_object(web.elements["options"], allow_yaml)) if web.elements["options"].respond_to?(:text) - info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false - - %W{created-at updated-at}.each { |datum| - if web.elements[datum].text - info[datum.gsub("-","_")] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - } - - report_web_site(info) - yield(:web_site, "#{info[:host]}:#{info[:port]} (#{info[:vhost]})") if block - end - - %W{page form vuln}.each do |wtype| - doc.elements.each("/#{btag}/web_#{wtype}s/web_#{wtype}") do |web| - info = {} - info[:workspace] = wspace - info[:host] = nils_for_nulls(web.elements["host"].text.to_s.strip) if web.elements["host"].respond_to?(:text) - info[:port] = nils_for_nulls(web.elements["port"].text.to_s.strip) if web.elements["port"].respond_to?(:text) - info[:ssl] = nils_for_nulls(web.elements["ssl"].text.to_s.strip) if web.elements["ssl"].respond_to?(:text) - info[:vhost] = nils_for_nulls(web.elements["vhost"].text.to_s.strip) if web.elements["vhost"].respond_to?(:text) - - info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false - - case wtype - when "page" - %W{path code body query cookie auth ctype mtime location}.each do |datum| - if web.elements[datum].respond_to? :text - info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - end - info[:headers] = nils_for_nulls(unserialize_object(web.elements["headers"], allow_yaml)) - when "form" - %W{path query method}.each do |datum| - if web.elements[datum].respond_to? :text - info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - end - info[:params] = nils_for_nulls(unserialize_object(web.elements["params"], allow_yaml)) - when "vuln" - %W{path query method pname proof risk name blame description category confidence}.each do |datum| - if web.elements[datum].respond_to? :text - info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - end - info[:params] = nils_for_nulls(unserialize_object(web.elements["params"], allow_yaml)) - info[:risk] = info[:risk].to_i - info[:confidence] = info[:confidence].to_i - end - - %W{created-at updated-at}.each { |datum| - if web.elements[datum].text - info[datum.gsub("-","_")] = nils_for_nulls(web.elements[datum].text.to_s.strip) - end - } - self.send("report_web_#{wtype}", info) - - yield("web_#{wtype}".intern, info[:path]) if block - end - end - end - # Convert the string "NULL" to actual nil def nils_for_nulls(str) str == "NULL" ? nil : str diff --git a/lib/msf/core/db_manager/import_msf_xml.rb b/lib/msf/core/db_manager/import_msf_xml.rb new file mode 100644 index 0000000000..face86348b --- /dev/null +++ b/lib/msf/core/db_manager/import_msf_xml.rb @@ -0,0 +1,580 @@ +module Msf + class DBManager + # Handles importing of the xml format exported by Pro. The methods are in a + # module because (1) it's just good code layout and (2) it allows the + # methods to be overridden in Pro without using alias_method_chain as + # methods defined in a class cannot be overridden by including a module + # (unless you're running Ruby 2.0 and can use prepend) + module ImportMsfXml + # + # CONSTANTS + # + + # Elements that can be treated as text (i.e. do not need to be + # deserialized) in {#import_msf_web_page_element} + MSF_WEB_PAGE_TEXT_ELEMENT_NAMES = [ + 'auth', + 'body', + 'code', + 'cookie', + 'ctype', + 'location', + 'mtime' + ] + + # Elements that can be treated as text (i.e. do not need to be + # deserialized) in {#import_msf_web_element}. + MSF_WEB_TEXT_ELEMENT_NAMES = [ + 'created-at', + 'host', + 'path', + 'port', + 'query', + 'ssl', + 'updated-at', + 'vhost' + ] + + # Elements that can be treated as text (i.e. do not need to be + # deserialized) in {#import_msf_web_vuln_element}. + MSF_WEB_VULN_TEXT_ELEMENT_NAMES = [ + 'blame', + 'category', + 'confidence', + 'description', + 'method', + 'name', + 'pname', + 'proof', + 'risk' + ] + + # + # Instance Methods + # + + # Imports web_form element using {Msf::DBManager#report_web_form}. + # + # @param element [REXML::Element] web_form element. + # @param options [Hash{Symbol => Object}] options + # @option options [Boolean] :allow_yaml (false) Whether to allow YAML when + # deserializing params. + # @option options [Mdm::Workspace, nil] :workspace + # (Msf::DBManager#workspace) workspace under which to report the + # Mdm::WebForm + # @yield [event, data] + # @yieldparam event [:web_page] The event name + # @yieldparam data [String] path + # @yieldreturn [void] + # @return [void] + def import_msf_web_form_element(element, options={}, ¬ifier) + options.assert_valid_keys(:allow_yaml, :workspace) + + import_msf_web_element(element, + :allow_yaml => options[:allow_yaml], + :notifier => notifier, + :type => :form, + :workspace => options[:workspace]) do |element, options| + info = import_msf_text_element(element, 'method') + + # FIXME https://www.pivotaltracker.com/story/show/46578647 + # FIXME https://www.pivotaltracker.com/story/show/47128407 + unserialized_params = unserialize_object( + element.elements['params'], + options[:allow_yaml] + ) + info[:params] = nils_for_nulls(unserialized_params) + + info + end + end + + # Imports web_page element using {Msf::DBManager#report_web_page}. + # + # @param element [REXML::Element] web_page element. + # @param options [Hash{Symbol => Object}] options + # @option options [Boolean] :allow_yaml (false) Whether to allow YAML when + # deserializing headers. + # @option options [Mdm::Workspace, nil] :workspace + # (Msf::DBManager#workspace) workspace under which to report the + # Mdm::WebPage. + # @yield [event, data] + # @yieldparam event [:web_page] The event name + # @yieldparam data [String] path + # @yieldreturn [void] + # @return [void] + def import_msf_web_page_element(element, options={}, ¬ifier) + options.assert_valid_keys(:allow_yaml, :workspace) + + import_msf_web_element(element, + :allow_yaml => options[:allow_yaml], + :notifier => notifier, + :type => :page, + :workspace => options[:workspace]) do |element, options| + info = {} + + MSF_WEB_PAGE_TEXT_ELEMENT_NAMES.each do |name| + element_info = import_msf_text_element(element, name) + info.merge!(element_info) + end + + code = info[:code] + + if code + info[:code] = code.to_i + end + + # FIXME https://www.pivotaltracker.com/story/show/46578647 + # FIXME https://www.pivotaltracker.com/story/show/47128407 + unserialized_headers = unserialize_object( + element.elements['headers'], + options[:allow_yaml] + ) + info[:headers] = nils_for_nulls(unserialized_headers) + + info + end + end + + # Imports web_vuln element using {Msf::DBManager#report_web_vuln}. + # + # @param element [REXML::Element] web_vuln element. + # @param options [Hash{Symbol => Object}] options + # @option options [Boolean] :allow_yaml (false) Whether to allow YAML when + # deserializing headers. + # @option options [Mdm::Workspace, nil] :workspace + # (Msf::DBManager#workspace) workspace under which to report the + # Mdm::WebPage. + # @yield [event, data] + # @yieldparam event [:web_page] The event name + # @yieldparam data [String] path + # @yieldreturn [void] + # @return [void] + def import_msf_web_vuln_element(element, options={}, ¬ifier) + options.assert_valid_keys(:allow_yaml, :workspace) + + import_msf_web_element(element, + :allow_yaml => options[:allow_yaml], + :notifier => notifier, + :workspace => options[:workspace], + :type => :vuln) do |element, options| + info = {} + + MSF_WEB_VULN_TEXT_ELEMENT_NAMES.each do |name| + element_info = import_msf_text_element(element, name) + info.merge!(element_info) + end + + confidence = info[:confidence] + + if confidence + info[:confidence] = confidence.to_i + end + + # FIXME https://www.pivotaltracker.com/story/show/46578647 + # FIXME https://www.pivotaltracker.com/story/show/47128407 + unserialized_params = unserialize_object( + element.elements['params'], + options[:allow_yaml] + ) + info[:params] = nils_for_nulls(unserialized_params) + + risk = info[:risk] + + if risk + info[:risk] = risk.to_i + end + + info + end + end + + # For each host, step through services, notes, and vulns, and import + # them. + # TODO: loot, tasks, and reports + def import_msf_xml(args={}, &block) + data = args[:data] + wspace = args[:wspace] || workspace + bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : [] + + doc = rexmlify(data) + metadata = check_msf_xml_version!(doc) + allow_yaml = metadata[:allow_yaml] + btag = metadata[:root_tag] + + doc.elements.each("/#{btag}/hosts/host") do |host| + host_data = {} + host_data[:workspace] = wspace + host_data[:host] = nils_for_nulls(host.elements["address"].text.to_s.strip) + if bl.include? host_data[:host] + next + else + yield(:address,host_data[:host]) if block + end + host_data[:mac] = nils_for_nulls(host.elements["mac"].text.to_s.strip) + if host.elements["comm"].text + host_data[:comm] = nils_for_nulls(host.elements["comm"].text.to_s.strip) + end + %W{created-at updated-at name state os-flavor os-lang os-name os-sp purpose}.each { |datum| + if host.elements[datum].text + host_data[datum.gsub('-','_')] = nils_for_nulls(host.elements[datum].text.to_s.strip) + end + } + host_address = host_data[:host].dup # Preserve after report_host() deletes + hobj = report_host(host_data) + + host.elements.each("host_details/host_detail") do |hdet| + hdet_data = {} + hdet.elements.each do |det| + next if ["id", "host-id"].include?(det.name) + if det.text + hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) + end + end + report_host_details(hobj, hdet_data) + end + + host.elements.each("exploit_attempts/exploit_attempt") do |hdet| + hdet_data = {} + hdet.elements.each do |det| + next if ["id", "host-id", "session-id", "vuln-id", "service-id", "loot-id"].include?(det.name) + if det.text + hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) + end + end + report_exploit_attempt(hobj, hdet_data) + end + + host.elements.each('services/service') do |service| + service_data = {} + service_data[:workspace] = wspace + service_data[:host] = hobj + service_data[:port] = nils_for_nulls(service.elements["port"].text.to_s.strip).to_i + service_data[:proto] = nils_for_nulls(service.elements["proto"].text.to_s.strip) + %W{created-at updated-at name state info}.each { |datum| + if service.elements[datum].text + if datum == "info" + service_data["info"] = nils_for_nulls(unserialize_object(service.elements[datum], false)) + else + service_data[datum.gsub("-","_")] = nils_for_nulls(service.elements[datum].text.to_s.strip) + end + end + } + report_service(service_data) + end + + host.elements.each('notes/note') do |note| + note_data = {} + note_data[:workspace] = wspace + note_data[:host] = hobj + note_data[:type] = nils_for_nulls(note.elements["ntype"].text.to_s.strip) + note_data[:data] = nils_for_nulls(unserialize_object(note.elements["data"], allow_yaml)) + + if note.elements["critical"].text + note_data[:critical] = true unless note.elements["critical"].text.to_s.strip == "NULL" + end + if note.elements["seen"].text + note_data[:seen] = true unless note.elements["critical"].text.to_s.strip == "NULL" + end + %W{created-at updated-at}.each { |datum| + if note.elements[datum].text + note_data[datum.gsub("-","_")] = nils_for_nulls(note.elements[datum].text.to_s.strip) + end + } + report_note(note_data) + end + + host.elements.each('tags/tag') do |tag| + tag_data = {} + tag_data[:addr] = host_address + tag_data[:wspace] = wspace + tag_data[:name] = tag.elements["name"].text.to_s.strip + tag_data[:desc] = tag.elements["desc"].text.to_s.strip + if tag.elements["report-summary"].text + tag_data[:summary] = tag.elements["report-summary"].text.to_s.strip + end + if tag.elements["report-detail"].text + tag_data[:detail] = tag.elements["report-detail"].text.to_s.strip + end + if tag.elements["critical"].text + tag_data[:crit] = true unless tag.elements["critical"].text.to_s.strip == "NULL" + end + report_host_tag(tag_data) + end + + host.elements.each('vulns/vuln') do |vuln| + vuln_data = {} + vuln_data[:workspace] = wspace + vuln_data[:host] = hobj + vuln_data[:info] = nils_for_nulls(unserialize_object(vuln.elements["info"], allow_yaml)) + vuln_data[:name] = nils_for_nulls(vuln.elements["name"].text.to_s.strip) + %W{created-at updated-at exploited-at}.each { |datum| + if vuln.elements[datum] and vuln.elements[datum].text + vuln_data[datum.gsub("-","_")] = nils_for_nulls(vuln.elements[datum].text.to_s.strip) + end + } + if vuln.elements["refs"] + vuln_data[:refs] = [] + vuln.elements.each("refs/ref") do |ref| + vuln_data[:refs] << nils_for_nulls(ref.text.to_s.strip) + end + end + + vobj = report_vuln(vuln_data) + + vuln.elements.each("vuln_details/vuln_detail") do |vdet| + vdet_data = {} + vdet.elements.each do |det| + next if ["id", "vuln-id"].include?(det.name) + if det.text + vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) + end + end + report_vuln_details(vobj, vdet_data) + end + + vuln.elements.each("vuln_attempts/vuln_attempt") do |vdet| + vdet_data = {} + vdet.elements.each do |det| + next if ["id", "vuln-id", "loot-id", "session-id"].include?(det.name) + if det.text + vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip) + end + end + report_vuln_attempt(vobj, vdet_data) + end + end + + host.elements.each('creds/cred') do |cred| + cred_data = {} + cred_data[:workspace] = wspace + cred_data[:host] = hobj + %W{port ptype sname proto proof active user pass}.each {|datum| + if cred.elements[datum].respond_to? :text + cred_data[datum.intern] = nils_for_nulls(cred.elements[datum].text.to_s.strip) + end + } + %W{created-at updated-at}.each { |datum| + if cred.elements[datum].respond_to? :text + cred_data[datum.gsub("-","_")] = nils_for_nulls(cred.elements[datum].text.to_s.strip) + end + } + %W{source-type source-id}.each { |datum| + if cred.elements[datum].respond_to? :text + cred_data[datum.gsub("-","_").intern] = nils_for_nulls(cred.elements[datum].text.to_s.strip) + end + } + if cred_data[:pass] == "" + cred_data[:pass] = "" + cred_data[:active] = false + elsif cred_data[:pass] == "*BLANK PASSWORD*" + cred_data[:pass] = "" + end + report_cred(cred_data) + end + + host.elements.each('sessions/session') do |sess| + sess_id = nils_for_nulls(sess.elements["id"].text.to_s.strip.to_i) + sess_data = {} + sess_data[:host] = hobj + %W{desc platform port stype}.each {|datum| + if sess.elements[datum].respond_to? :text + sess_data[datum.intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip) + end + } + %W{opened-at close-reason closed-at via-exploit via-payload}.each {|datum| + if sess.elements[datum].respond_to? :text + sess_data[datum.gsub("-","_").intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip) + end + } + sess_data[:datastore] = nils_for_nulls(unserialize_object(sess.elements["datastore"], allow_yaml)) + if sess.elements["routes"] + sess_data[:routes] = nils_for_nulls(unserialize_object(sess.elements["routes"], allow_yaml)) || [] + end + if not sess_data[:closed_at] # Fake a close if we don't already have one + sess_data[:closed_at] = Time.now.utc + sess_data[:close_reason] = "Imported at #{Time.now.utc}" + end + + existing_session = get_session( + :workspace => sess_data[:host].workspace, + :addr => sess_data[:host].address, + :time => sess_data[:opened_at] + ) + this_session = existing_session || report_session(sess_data) + next if existing_session + sess.elements.each('events/event') do |sess_event| + sess_event_data = {} + sess_event_data[:session] = this_session + %W{created-at etype local-path remote-path}.each {|datum| + if sess_event.elements[datum].respond_to? :text + sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(sess_event.elements[datum].text.to_s.strip) + end + } + %W{command output}.each {|datum| + if sess_event.elements[datum].respond_to? :text + sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(unserialize_object(sess_event.elements[datum], allow_yaml)) + end + } + report_session_event(sess_event_data) + end + end + end + + # Import web sites + doc.elements.each("/#{btag}/web_sites/web_site") do |web| + info = {} + info[:workspace] = wspace + + %W{host port vhost ssl comments}.each do |datum| + if web.elements[datum].respond_to? :text + info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip) + end + end + + info[:options] = nils_for_nulls(unserialize_object(web.elements["options"], allow_yaml)) if web.elements["options"].respond_to?(:text) + info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false + + %W{created-at updated-at}.each { |datum| + if web.elements[datum].text + info[datum.gsub("-","_")] = nils_for_nulls(web.elements[datum].text.to_s.strip) + end + } + + report_web_site(info) + yield(:web_site, "#{info[:host]}:#{info[:port]} (#{info[:vhost]})") if block + end + + %W{page form vuln}.each do |wtype| + doc.elements.each("/#{btag}/web_#{wtype}s/web_#{wtype}") do |element| + send( + "import_msf_web_#{wtype}_element", + element, + :allow_yaml => allow_yaml, + :workspace => wspace, + &block + ) + end + end + end + + private + + # Checks if the XML document has a format version that the importer + # understands. + # + # @param document [REXML::Document] a REXML::Document produced by + # {Msf::DBManager#rexmlify}. + # @return [Hash{Symbol => Object}] `:allow_yaml` is true if the format + # requires YAML loading when calling + # {Msf::DBManager#unserialize_object}. `:root_tag` the tag name of the + # root element for MSF XML. + # @raise [Msf::DBImportError] if unsupported format + def check_msf_xml_version!(document) + metadata = { + # FIXME https://www.pivotaltracker.com/story/show/47128407 + :allow_yaml => false, + :root_tag => nil + } + + if document.elements['MetasploitExpressV1'] + # FIXME https://www.pivotaltracker.com/story/show/47128407 + metadata[:allow_yaml] = true + metadata[:root_tag] = 'MetasploitExpressV1' + elsif document.elements['MetasploitExpressV2'] + # FIXME https://www.pivotaltracker.com/story/show/47128407 + metadata[:allow_yaml] = true + metadata[:root_tag] = 'MetasploitExpressV2' + elsif document.elements['MetasploitExpressV3'] + metadata[:root_tag] = 'MetasploitExpressV3' + elsif document.elements['MetasploitExpressV4'] + metadata[:root_tag] = 'MetasploitExpressV4' + elsif document.elements['MetasploitV4'] + metadata[:root_tag] = 'MetasploitV4' + end + + unless metadata[:root_tag] + raise Msf::DBImportError, + 'Unsupported Metasploit XML document format' + end + + metadata + end + + # Retrieves text of element if it exists. + # + # @param parent_element [REXML::Element] element under which element with + # `child_name` exists. + # @param child_name [String] the name of the element under + # `parent_element` whose text should be returned + # @return [{}] if element with child_name does not exist or does not have + # text. + # @return [Hash{Symbol => String}] Maps child_name symbol to text. Text is + # stripped and any NULLs are converted to `nil`. + # @return [nil] if element with `child_name` does not exist under + # `parent_element`. + def import_msf_text_element(parent_element, child_name) + child_element = parent_element.elements[child_name] + info = {} + + if child_element + stripped = child_element.text.to_s.strip + attribute_name = child_name.underscore.to_sym + info[attribute_name] = nils_for_nulls(stripped) + end + + info + end + + # Imports web_form, web_page, or web_vuln element using + # {Msf::DBManager#report_web_form}, {Msf::DBManager#report_web_page}, and + # {Msf::DBManager#report_web_vuln}, respectively. + # + # @param element [REXML::Element] the web_form, web_page, or web_vuln + # element. + # @param options [Hash{Symbol => Object}] options + # @option options [Boolean] :allow_yaml (false) Whether to allow YAML when + # deserializing elements. + # @option options [Proc] :notifier Block called with web_* event and path + # @option options [Symbol] :type the type of web element, such as :form, + # :page, or :vuln. Must correspond to a report_web_ method on + # {Msf::DBManager}. + # @option options [Mdm::Workspace, nil] :workspace + # (Msf::DBManager#workspace) workspace under which to report the + # imported record. + # @yield [element, options] + # @yieldparam element [REXML::Element] the web_form, web_page, or + # web_vuln element passed to {#import_msf_web_element}. + # @yieldparam options [Hash{Symbol => Object}] options for parsing + # @yieldreturn [Hash{Symbol => Object}] info + # @return [void] + # @raise [KeyError] if `:type` is not given + def import_msf_web_element(element, options={}, &specialization) + options.assert_valid_keys(:allow_yaml, :notifier, :type, :workspace) + type = options.fetch(:type) + + info = {} + info[:workspace] = options[:workspace] || self.workspace + + MSF_WEB_TEXT_ELEMENT_NAMES.each do |name| + element_info = import_msf_text_element(element, name) + info.merge!(element_info) + end + + info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false + + specialized_info = specialization.call(element, options) + info.merge!(specialized_info) + + self.send("report_web_#{type}", info) + + notifier = options[:notifier] + + if notifier + event = "web_#{type}".to_sym + notifier.call(event, info[:path]) + end + end + end + end +end diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake index 3f9951d7da..54847d3172 100644 --- a/lib/tasks/database.rake +++ b/lib/tasks/database.rake @@ -1,6 +1,7 @@ load 'active_record/railties/databases.rake' require 'metasploit/framework' +require 'metasploit/framework/database' # A modification to remove dependency on Rails.env # @@ -21,19 +22,10 @@ def configs_for_environment valid_environment_configurations end -# This would normally use Rails.application.config.database_configuration -def database_configurations - YAML.load_file(database_configurations_pathname) -end - -def database_configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') -end - # emulate initializer "active_record.initialize_database" from active_record/railtie ActiveSupport.on_load(:active_record) do - self.configurations = database_configurations - puts "Connecting to database specified by #{database_configurations_pathname}" + self.configurations = Metasploit::Framework::Database.configurations + puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" spec = configurations[Metasploit::Framework.env] establish_connection(spec) @@ -58,7 +50,7 @@ Rake::Task['db:seed'].clear db_namespace = namespace :db do task :load_config do - ActiveRecord::Base.configurations = database_configurations + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations ActiveRecord::Migrator.migrations_paths = [ # rails isn't in Gemfile, so can't use the more appropriate diff --git a/spec/factories/mdm/web_forms.rb b/spec/factories/mdm/web_forms.rb new file mode 100644 index 0000000000..608a36cce6 --- /dev/null +++ b/spec/factories/mdm/web_forms.rb @@ -0,0 +1,34 @@ +FactoryGirl.define do + factory :mdm_web_form, :class => Mdm::WebForm do + # + # Associations + # + association :web_site, :factory => :mdm_web_site + + # attributes that would be in web_form element from Pro export + trait :exported do + method { generate :mdm_web_form_method } + params { generate :mdm_web_form_params } + path { generate :mdm_web_form_path } + end + end + + methods = ['GET', 'POST'] + + sequence :mdm_web_form_method do |n| + methods[n % methods.length] + end + + sequence :mdm_web_form_params do |n| + [ + [ + "name#{n}", + "value#{n}" + ] + ] + end + + sequence :mdm_web_form_path do |n| + "path/to/web/form/#{n}" + end +end \ No newline at end of file diff --git a/spec/factories/mdm/web_pages.rb b/spec/factories/mdm/web_pages.rb new file mode 100644 index 0000000000..67e9662885 --- /dev/null +++ b/spec/factories/mdm/web_pages.rb @@ -0,0 +1,64 @@ +FactoryGirl.define do + factory :mdm_web_page, :class => Mdm::WebPage do + auth { generate :mdm_web_page_auth } + body { generate :mdm_web_page_body } + code { generate :mdm_web_page_code } + cookie { generate :mdm_web_page_cookie } + ctype { generate :mdm_web_page_ctype } + headers { generate :mdm_web_page_headers } + location { generate :mdm_web_page_location } + mtime { generate :mdm_web_page_mtime } + query { generate :mdm_web_page_query } + + # + # Associations + # + association :web_site, :factory => :mdm_web_site + end + + sequence :mdm_web_page_auth do |n| + "Authorization: #{n}" + end + + sequence :mdm_web_page_body do |n| + xml = Builder::XmlMarkup.new(:indent => 2) + + xml.html + + xml.target!.strip + end + + sequence :mdm_web_page_code do |n| + n + end + + sequence :mdm_web_page_cookie do |n| + "name#{n}=value#{n}" + end + + sequence :mdm_web_page_ctype do |n| + "application/x-#{n}" + end + + sequence :mdm_web_page_headers do |n| + [ + [ + "Header#{n}", + "Value#{n}" + ] + ] + end + + sequence :mdm_web_page_location do |n| + "http://example.com/location/#{n}" + end + + sequence :mdm_web_page_mtime do |n| + past = Time.now - n + past.utc.strftime('%a, %d %b %Y %H:%M:%S %Z') + end + + sequence :mdm_web_page_query do |n| + "param#{n}=value#{n}" + end +end \ No newline at end of file diff --git a/spec/factories/mdm/web_vulns.rb b/spec/factories/mdm/web_vulns.rb new file mode 100644 index 0000000000..55ad4de79e --- /dev/null +++ b/spec/factories/mdm/web_vulns.rb @@ -0,0 +1,14 @@ +FactoryGirl.define do + factory :exported_web_vuln, :parent => :mdm_web_vuln do + blame { generate :mdm_web_vuln_blame } + description { generate :mdm_web_vuln_description } + end + + sequence :mdm_web_vuln_blame do |n| + "Blame employee ##{n}" + end + + sequence :mdm_web_vuln_description do |n| + "Mdm::WebVuln#description #{n}" + end +end \ No newline at end of file diff --git a/spec/lib/msf/core/db_spec.rb b/spec/lib/msf/core/db_spec.rb new file mode 100644 index 0000000000..f73e129a18 --- /dev/null +++ b/spec/lib/msf/core/db_spec.rb @@ -0,0 +1,22 @@ +# +# Specs +# + +require 'spec_helper' + +# +# Project +# + +require 'metasploit/framework/database' +require 'msf/core' + +describe Msf::DBManager do + include_context 'Msf::Simple::Framework' + + subject(:db_manager) do + framework.db + end + + it_should_behave_like 'Msf::DBManager::ImportMsfXml' +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index de1281e047..8f58fa6d0f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,5 +25,22 @@ end RSpec.configure do |config| config.mock_with :rspec + + # Can't use factory_girl_rails since not using rails, so emulate + # factory_girl.set_factory_paths initializer and after_initialize for + # FactoryGirl::Railtie + config.before(:suite) do + # Need to load Mdm models first so factories can use them + MetasploitDataModels.require_models + + FactoryGirl.definition_file_paths = [ + MetasploitDataModels.root.join('spec', 'factories'), + # Have metasploit-framework's definition file path last so it can + # modify gem factories. + Metasploit::Framework.root.join('spec', 'factories') + ] + + FactoryGirl.find_definitions + end end diff --git a/spec/support/shared/contexts/database_cleaner.rb b/spec/support/shared/contexts/database_cleaner.rb new file mode 100644 index 0000000000..0c03ae6397 --- /dev/null +++ b/spec/support/shared/contexts/database_cleaner.rb @@ -0,0 +1,34 @@ +shared_context 'DatabaseCleaner' do + def with_established_connection + begin + ActiveRecord::Base.connection_pool.with_connection do + yield + end + rescue ActiveRecord::ConnectionNotEstablished + # if there isn't a connection established, then established one and try + # again + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations + spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + ActiveRecord::Base.establish_connection(spec) + + retry + end + end + + # clean before all in case last test run was interrupted before + # after(:each) could clean up + before(:all) do + with_established_connection do + DatabaseCleaner.clean_with(:truncation) + end + end + + # Clean up after each test + after(:each) do + with_established_connection do + # Testing using both :truncation and :deletion; :truncation took long + # for testing. + DatabaseCleaner.clean_with(:deletion) + end + end +end \ No newline at end of file diff --git a/spec/support/shared/contexts/msf/simple/framework.rb b/spec/support/shared/contexts/msf/simple/framework.rb new file mode 100644 index 0000000000..422c51144e --- /dev/null +++ b/spec/support/shared/contexts/msf/simple/framework.rb @@ -0,0 +1,39 @@ +require 'msf/base/simple/framework' +require 'metasploit/framework' + +shared_context 'Msf::Simple::Framework' do + let(:dummy_pathname) do + Metasploit::Framework.root.join('spec', 'dummy') + end + + let(:framework) do + Msf::Simple::Framework.create( + 'ConfigDirectory' => framework_config_pathname.to_s, + # don't load any module paths so we can just load the module under test and save time + 'DeferModuleLoads' => true + ) + end + + let(:framework_config_pathname) do + dummy_pathname.join('framework', 'config') + end + + before(:each) do + framework_config_pathname.mkpath + end + + after(:each) do + dummy_pathname.rmtree + end + + after(:each) do + # explicitly kill threads so that they don't exhaust connection pool + thread_manager = framework.threads + + thread_manager.each do |thread| + thread.kill + end + + thread_manager.monitor.kill + end +end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb new file mode 100644 index 0000000000..5f432d2339 --- /dev/null +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -0,0 +1,1182 @@ +require 'builder' + +shared_examples_for 'Msf::DBManager::ImportMsfXml' do + # Serialized format from pro/modules/auxiliary/pro/report.rb + def serialize(object) + # FIXME https://www.pivotaltracker.com/story/show/46578647 + marshalled = Marshal.dump(object) + base64_encoded = [marshalled].pack('m') + compact = base64_encoded.gsub(/\s+/, '') + + compact + end + + def with_info + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + yield info + end + + subject + end + + let(:allow_yaml) do + false + end + + let(:document) do + REXML::Document.new(source) + end + + let(:element) do + nil + end + + let(:host_attributes) do + FactoryGirl.attributes_for(:mdm_host) + end + + let(:msf_web_text_element_names) do + [ + 'created-at', + 'host', + 'path', + 'port', + 'query', + 'ssl', + 'updated-at', + 'vhost' + ] + end + + let(:notifier) do + lambda do |event, data| + + end + end + + let(:options) do + { + :allow_yaml => allow_yaml, + :workspace => workspace + } + end + + let(:service_attributes) do + FactoryGirl.attributes_for(:web_service) + end + + let(:web_form_attributes) do + FactoryGirl.attributes_for(:mdm_web_form, :exported) + end + + let(:web_page_attributes) do + FactoryGirl.attributes_for(:mdm_web_page) + end + + let(:workspace) do + nil + end + + let(:xml) do + Builder::XmlMarkup.new(:indent => 2) + end + + before(:each) do + configurations = Metasploit::Framework::Database.configurations + spec = configurations[Metasploit::Framework.env] + + # Need to connect or Msf::DBManager#active will be false and + # Msf::DBManager#report_* methods won't create any records. + db_manager.connect(spec) + end + + it 'should include methods from module so method can be overridden easier in pro' do + db_manager.should be_a Msf::DBManager::ImportMsfXml + end + + context 'CONSTANTS' do + it 'should define MSF_WEB_PAGE_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_PAGE_TEXT_ELEMENT_NAMES =~ [ + 'auth', + 'body', + 'code', + 'cookie', + 'ctype', + 'location', + 'mtime' + ] + end + + it 'should define MSF_WEB_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_TEXT_ELEMENT_NAMES =~ msf_web_text_element_names + end + + it 'should define MSF_WEB_VULN_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_VULN_TEXT_ELEMENT_NAMES =~ [ + 'blame', + 'category', + 'confidence', + 'description', + 'method', + 'name', + 'pname', + 'proof', + 'risk' + ] + end + end + + context '#check_msf_xml_version!' do + let(:root_tag) do + 'root' + end + + let(:source) do + xml.tag!(root_tag) + + xml.target! + end + + subject(:metadata) do + db_manager.send(:check_msf_xml_version!, document) + end + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV1', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV2', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV3', + :allow_yaml => false + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV4', + :allow_yaml => false + ) + + context 'with other' do + it 'should raise DBImportError' do + expect { + metadata + }.to raise_error( + Msf::DBImportError, + 'Unsupported Metasploit XML document format' + ) + end + end + end + + context '#import_msf_text_element' do + let(:parent_element) do + document.root + end + + let(:child_name) do + 'child' + end + + let(:child_sym) do + child_name.to_sym + end + + subject(:info) do + db_manager.send(:import_msf_text_element, parent_element, child_name) + end + + context 'with child element' do + let(:source) do + xml.parent do + xml.tag!(child_name, text) + end + + xml.target! + end + + context 'with padded text' do + let(:stripped) do + 'stripped' + end + + let(:text) do + " #{stripped} " + end + + it 'should strip text' do + info[:child].should == stripped + end + end + + context 'with NULL text' do + let(:text) do + 'NULL' + end + + it 'should have nil for child name in info' do + # use have_key to verify info isn't just returning hash default of + # `nil`. + info.should have_key(child_sym) + info[child_sym].should be_nil + end + end + + context 'without NULL text' do + let(:text) do + 'some text' + end + + it 'should have text for child name in info' do + info[child_sym].should == text + end + end + end + + context 'without child element' do + let(:source) do + xml.parent + + xml.target! + end + + it 'should return an empty Hash' do + info.should == {} + end + end + end + + context 'import_msf_web_element' do + let(:element) do + document.root + end + + let(:options) do + {} + end + + let(:specialization) do + lambda { |element, options| + {} + } + end + + subject(:import_msf_web_element) do + db_manager.send( + :import_msf_web_element, + element, + options, + &specialization + ) + end + + context 'with :type' do + include_context 'DatabaseCleaner' + + let(:source) do + xml.tag!("web_#{type}") do + web_site = web_vuln.web_site + service = web_site.service + + xml.host(service.host.address) + xml.path(web_vuln.path) + xml.port(service.port) + xml.query(web_vuln.query) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + + xml.vhost(web_site.vhost) + end + + xml.target! + end + + let(:type) do + :vuln + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + before(:each) do + db_manager.stub( + :report_web_vuln + ).with( + an_instance_of(Hash) + ) + + options[:type] = type + end + + context 'with :workspace' do + let(:workspace) do + mock(':workspace') + end + + before(:each) do + options[:workspace] = workspace + end + + it 'should not call Msf::DBManager#workspace' do + db_manager.should_not_receive(:workspace) + + import_msf_web_element + end + + it 'should pass :workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + context 'without :workspace' do + let(:workspace) do + FactoryGirl.create(:mdm_workspace) + end + + before(:each) do + db_manager.workspace = workspace + end + + it 'should call Msf::DBManager#workspace' do + db_manager.should_receive(:workspace).and_call_original + + import_msf_web_element + end + + it 'should pass Msf::DBManager#workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + it 'should import all elements in MSF_WEB_TEXT_ELEMENT_NAMES with #import_msf_text_element' do + msf_web_text_element_names.each do |name| + db_manager.should_receive( + :import_msf_text_element + ).with( + element, + name + ).and_call_original + end + + import_msf_web_element + end + + context 'with non-empty Hash from #import_msf_text_element' do + let(:returned_hash) do + { + :host => '192.168.0.1' + } + end + + before(:each) do + db_manager.stub(:import_msf_text_element).and_return(returned_hash) + end + + it 'should pass returned Hash as part of Hash passed to report_web_<:type' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'ssl element' do + context 'without element' do + let(:source) do + xml.tag!("web_#{type}") + + xml.target! + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + + context 'with element' do + let(:source) do + xml.tag!("web_#{type}") do + xml.ssl(ssl) + end + + xml.target! + end + + context "with 'true' text" do + let(:ssl) do + true + end + + it 'should pass true for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => true) + ) + + import_msf_web_element + end + end + + context "without 'true' text" do + let(:ssl) do + false + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + end + end + + context 'specialization block' do + let(:returned_hash) do + { + :specialized => mock('Value') + } + end + + let(:specialization) do + lambda { |element, option| + returned_hash + } + end + + it 'should be called with element and options' do + actual_args = [] + + db_manager.send( + :import_msf_web_element, + element, + options) do |*args| + actual_args = args + + returned_hash + end + + actual_args.should == [element, options] + end + + it 'should pass return Hash to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'notifier' do + context 'with :notifier' do + let(:event) do + "web_#{type}".to_sym + end + + let(:notifier) do + lambda do |*args| + successive_args << args + end + end + + let(:successive_args) do + [] + end + + before(:each) do + options[:notifier] = notifier + end + + it 'should call :notifier with event and path' do + import_msf_web_element + + successive_args.length.should == 1 + + args = successive_args[0] + + args.length.should == 2 + args[0].should == event + args[1].should == web_vuln.path + end + end + + context 'without :notifier' do + it 'should not raise an error' do + expect { + import_msf_web_element + }.to_not raise_error + end + end + end + end + + context 'without :type' do + let(:element) do + nil + end + + it 'should raise KeyError' do + expect { + import_msf_web_element + }.to raise_error(KeyError, 'key not found: :type') + end + end + end + + context '#import_msf_web_form_element' do + let(:type) do + :form + end + + subject(:import_msf_web_form_element) do + db_manager.import_msf_web_form_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.method( + web_form_attributes.fetch(:method) + ) + + serialized_params = serialize( + web_form_attributes.fetch(:params) + ) + xml.params(serialized_params) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_form_attributes[:method] + end + end + + it 'should include :params' do + with_info do |info| + info[:params].should == web_form_attributes[:params] + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebForm' do + expect { + import_msf_web_form_element + }.to change(Mdm::WebForm, :count).by(1) + end + end + end + + context '#import_msf_web_page_element' do + let(:type) do + :page + end + + subject(:import_msf_web_page_element) do + db_manager.import_msf_web_page_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.auth( + web_page_attributes.fetch(:auth) + ) + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + xml.cookie( + web_page_attributes.fetch(:cookie) + ) + xml.ctype( + web_page_attributes.fetch(:ctype) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.location( + web_page_attributes.fetch(:location) + ) + xml.mtime( + web_page_attributes.fetch(:mtime) + ) + end + + xml.target! + end + + it 'should be a Hash' do + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + info.should be_a Hash + end + + import_msf_web_page_element + end + + it 'should include :auth' do + with_info do |info| + info[:auth].should == web_page_attributes.fetch(:auth) + end + end + + it 'should include :body' do + with_info do |info| + info[:body].should == web_page_attributes.fetch(:body) + end + end + + it 'should include :code' do + with_info do |info| + info[:code].should == web_page_attributes.fetch(:code) + end + end + + it 'should include :cookie' do + with_info do |info| + info[:cookie].should == web_page_attributes.fetch(:cookie) + end + end + + it 'should include :ctype' do + with_info do |info| + info[:ctype].should == web_page_attributes.fetch(:ctype) + end + end + + it 'should include :headers' do + with_info do |info| + info[:headers].should == web_page_attributes.fetch(:headers) + end + end + + it 'should include :location' do + with_info do |info| + info[:location].should == web_page_attributes.fetch(:location) + end + end + + it 'should include :mtime' do + with_info do |info| + info[:mtime].should == web_page_attributes.fetch(:mtime) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebPage' do + expect { + import_msf_web_page_element + }.to change(Mdm::WebPage, :count).by(1) + end + end + end + + context '#import_msf_web_vuln_element' do + let(:type) do + :vuln + end + + let(:web_vuln_attributes) do + FactoryGirl.attributes_for(:exported_web_vuln) + end + + subject(:import_msf_web_vuln_element) do + db_manager.import_msf_web_vuln_element( + element, + options, + ¬ifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_vuln do + xml.blame( + web_vuln_attributes.fetch(:blame) + ) + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.confidence( + web_vuln_attributes.fetch(:confidence) + ) + xml.description( + web_vuln_attributes.fetch(:description) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + + import_msf_web_vuln_element + end + + it 'should include :blame' do + with_info do |info| + info[:blame].should == web_vuln_attributes.fetch(:blame) + end + end + + it 'should include :category' do + with_info do |info| + info[:category].should == web_vuln_attributes.fetch(:category) + end + end + + it 'should include :confidence' do + with_info do |info| + info[:confidence].should == web_vuln_attributes.fetch(:confidence) + end + end + + it 'should include :description' do + with_info do |info| + info[:description].should == web_vuln_attributes.fetch(:description) + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_vuln_attributes.fetch(:method) + end + end + + it 'should include :name' do + with_info do |info| + info[:name].should == web_vuln_attributes.fetch(:name) + end + end + + it 'should include :pname' do + with_info do |info| + info[:pname].should == web_vuln_attributes.fetch(:pname) + end + end + + it 'should include :proof' do + with_info do |info| + info[:proof].should == web_vuln_attributes.fetch(:proof) + end + end + + it 'should include :risk' do + with_info do |info| + info[:risk].should == web_vuln_attributes.fetch(:risk) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + + serialized_params = serialize( + web_vuln_attributes.fetch(:params) + ) + xml.params(serialized_params) + + xml.path( + web_vuln_attributes.fetch(:path) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebVuln' do + expect { + import_msf_web_vuln_element + }.to change(Mdm::WebVuln, :count).by(1) + end + end + end + + context '#import_msf_xml' do + let(:data) do + '' + end + + subject(:import_msf_xml) do + db_manager.import_msf_xml(:data => data) + end + + it 'should call #check_msf_xml_version!' do + db_manager.should_receive(:check_msf_xml_version!).and_call_original + + import_msf_xml + end + + context 'with web_forms/web_form elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_forms do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_form_element' do + db_manager.should_receive(:import_msf_web_form_element).and_call_original + + import_msf_xml + end + end + + context 'with web_pages/web_page elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_pages do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_page_element' do + db_manager.should_receive(:import_msf_web_page_element).and_call_original + + import_msf_xml + end + end + + context 'with web_vulns/web_vuln elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_vulns do + xml.web_vuln do + xml.category(web_vuln.category) + + service = web_vuln.web_site.service + xml.host(service.host.address) + + xml.method(web_vuln.method) + xml.name(web_vuln.name) + + serialized_params = serialize(web_vuln.params) + xml.params(serialized_params) + + xml.path(web_vuln.path) + xml.pname(web_vuln.pname) + xml.port(service.port) + xml.proof(web_vuln.proof) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + it 'should call #import_msf_web_vuln_element' do + db_manager.should_receive(:import_msf_web_vuln_element).and_call_original + + import_msf_xml + end + end + end +end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb new file mode 100644 index 0000000000..13ef74ecfa --- /dev/null +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb @@ -0,0 +1,24 @@ +shared_examples_for 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag' do |root_tag, options={}| + options.assert_valid_keys(:allow_yaml) + allow_yaml = options.fetch(:allow_yaml) + + context "with #{root_tag}" do + let(:root_tag) do + root_tag + end + + should_label_by_allow_yaml = { + true => 'should', + false => 'should not' + } + should_label = should_label_by_allow_yaml[allow_yaml] + + it "#{should_label} allow YAML" do + expect(metadata[:allow_yaml]).to eq(allow_yaml) + end + + it "should have #{root_tag} as root tag" do + metadata[:root_tag].should == root_tag + end + end +end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb new file mode 100644 index 0000000000..699b9a75d9 --- /dev/null +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb @@ -0,0 +1,41 @@ +shared_examples_for 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' do + it 'should call #import_msf_web_element with element' do + db_manager.should_receive(:import_msf_web_element).with(element, anything) + + subject + end + + it 'should call #import_msf_web_element with :allow_yaml and :workspace' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :allow_yaml => allow_yaml, + :workspace => workspace + ) + ) + + subject + end + + it 'should call #import_msf_web_element with :type' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :type => type + ) + ) + + subject + end + + it 'should pass block to #import_msf_web_element as :notifier' do + db_manager.should_receive( + :import_msf_web_element + ).with( + anything, + hash_including(:notifier => notifier) + ) + + subject + end +end From 47842aa6a237c1c0117c5b896e08971db89676c1 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 1 Apr 2013 20:16:28 -0500 Subject: [PATCH 7/9] Fix 'Output is not a module' [#46491831] I missed that Rex::Ui::Text::Output was a class and not a module, so starting up prosvc fell over when it loaded rex/ui/text/output/buffer/stdout, which also would screw up msf/ui/console/command_dispatcher/core.rb where I original added Rex::Ui::Text::Output::Buffer::Stdout. --- lib/rex/ui/text/output/buffer/stdout.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rex/ui/text/output/buffer/stdout.rb b/lib/rex/ui/text/output/buffer/stdout.rb index a00426942c..4e8c86b776 100644 --- a/lib/rex/ui/text/output/buffer/stdout.rb +++ b/lib/rex/ui/text/output/buffer/stdout.rb @@ -1,10 +1,11 @@ -# make sure the class is define before opening it to define submodule +# make sure the classes are defined before opening it to define submodule +require 'rex/ui/text/output' require 'rex/ui/text/output/buffer' module Rex module Ui module Text - module Output + class Output class Buffer # Adds {#write} method to {Rex::Ui::Text::Output::Buffer} so it can # function as a stand-in for `$stdout` From 7d5f010e4e97494f4786b782042540b971d0826d Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 2 Apr 2013 09:11:43 -0500 Subject: [PATCH 8/9] Fix typo in spec let [#46491831] The root element was web_page in the source for example that tests that import_msf_web_vuln_element creates an Mdm::WebVuln. The root element name did not actually matter for the example, but it looked like an error and was confusing to read the setup that root element was web_page instead of the correct web_vuln. --- spec/support/shared/examples/msf/db_manager/import_msf_xml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb index 5f432d2339..ea63101114 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -967,7 +967,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end let(:source) do - xml.web_page do + xml.web_vuln do xml.category( web_vuln_attributes.fetch(:category) ) From e4d901d12cfabe49ff7df209b101558bc39ec86f Mon Sep 17 00:00:00 2001 From: Tod Beardsley Date: Wed, 3 Apr 2013 09:20:01 -0500 Subject: [PATCH 9/9] Space at EOL (msftidy) --- modules/exploits/multi/http/stunshell_eval.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/stunshell_eval.rb b/modules/exploits/multi/http/stunshell_eval.rb index 61ec4052ef..d1959e6e4f 100644 --- a/modules/exploits/multi/http/stunshell_eval.rb +++ b/modules/exploits/multi/http/stunshell_eval.rb @@ -39,7 +39,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Platform' => ['php'], 'Arch' => ARCH_PHP, - 'Targets' => + 'Targets' => [ ['stunshell', {}] ],