librex 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +4 -0
- data/lib/rex.rb +101 -0
- data/lib/rex.rb.ts.rb +70 -0
- data/lib/rex/LICENSE +29 -0
- data/lib/rex/arch.rb +103 -0
- data/lib/rex/arch/sparc.rb +75 -0
- data/lib/rex/arch/sparc.rb.ut.rb +18 -0
- data/lib/rex/arch/x86.rb +513 -0
- data/lib/rex/arch/x86.rb.ut.rb +93 -0
- data/lib/rex/assembly/nasm.rb +100 -0
- data/lib/rex/assembly/nasm.rb.ut.rb +22 -0
- data/lib/rex/codepage.map +104 -0
- data/lib/rex/compat.rb +281 -0
- data/lib/rex/constants.rb +113 -0
- data/lib/rex/elfparsey.rb +11 -0
- data/lib/rex/elfparsey/elf.rb +123 -0
- data/lib/rex/elfparsey/elfbase.rb +260 -0
- data/lib/rex/elfparsey/exceptions.rb +27 -0
- data/lib/rex/elfscan.rb +12 -0
- data/lib/rex/elfscan/scanner.rb +207 -0
- data/lib/rex/elfscan/search.rb +46 -0
- data/lib/rex/encoder/alpha2.rb +31 -0
- data/lib/rex/encoder/alpha2/alpha_mixed.rb +68 -0
- data/lib/rex/encoder/alpha2/alpha_upper.rb +79 -0
- data/lib/rex/encoder/alpha2/generic.rb +113 -0
- data/lib/rex/encoder/alpha2/unicode_mixed.rb +117 -0
- data/lib/rex/encoder/alpha2/unicode_upper.rb +129 -0
- data/lib/rex/encoder/ndr.rb +89 -0
- data/lib/rex/encoder/ndr.rb.ut.rb +44 -0
- data/lib/rex/encoder/nonalpha.rb +61 -0
- data/lib/rex/encoder/nonupper.rb +64 -0
- data/lib/rex/encoder/xdr.rb +106 -0
- data/lib/rex/encoder/xdr.rb.ut.rb +29 -0
- data/lib/rex/encoder/xor.rb +69 -0
- data/lib/rex/encoder/xor/dword.rb +13 -0
- data/lib/rex/encoder/xor/dword_additive.rb +13 -0
- data/lib/rex/encoders/xor_dword.rb +35 -0
- data/lib/rex/encoders/xor_dword_additive.rb +53 -0
- data/lib/rex/encoders/xor_dword_additive.rb.ut.rb +12 -0
- data/lib/rex/encoding/xor.rb +20 -0
- data/lib/rex/encoding/xor.rb.ts.rb +14 -0
- data/lib/rex/encoding/xor/byte.rb +15 -0
- data/lib/rex/encoding/xor/byte.rb.ut.rb +21 -0
- data/lib/rex/encoding/xor/dword.rb +21 -0
- data/lib/rex/encoding/xor/dword.rb.ut.rb +15 -0
- data/lib/rex/encoding/xor/dword_additive.rb +92 -0
- data/lib/rex/encoding/xor/dword_additive.rb.ut.rb +15 -0
- data/lib/rex/encoding/xor/exceptions.rb +17 -0
- data/lib/rex/encoding/xor/generic.rb +146 -0
- data/lib/rex/encoding/xor/generic.rb.ut.rb +120 -0
- data/lib/rex/encoding/xor/qword.rb +15 -0
- data/lib/rex/encoding/xor/word.rb +21 -0
- data/lib/rex/encoding/xor/word.rb.ut.rb +13 -0
- data/lib/rex/exceptions.rb +275 -0
- data/lib/rex/exceptions.rb.ut.rb +44 -0
- data/lib/rex/exploitation/cmdstager.rb +133 -0
- data/lib/rex/exploitation/egghunter.rb +143 -0
- data/lib/rex/exploitation/egghunter.rb.ut.rb +25 -0
- data/lib/rex/exploitation/encryptjs.rb +77 -0
- data/lib/rex/exploitation/heaplib.js.b64 +331 -0
- data/lib/rex/exploitation/heaplib.rb +94 -0
- data/lib/rex/exploitation/javascriptosdetect.rb +735 -0
- data/lib/rex/exploitation/obfuscatejs.rb +335 -0
- data/lib/rex/exploitation/opcodedb.rb +818 -0
- data/lib/rex/exploitation/opcodedb.rb.ut.rb +279 -0
- data/lib/rex/exploitation/seh.rb +92 -0
- data/lib/rex/exploitation/seh.rb.ut.rb +19 -0
- data/lib/rex/file.rb +84 -0
- data/lib/rex/file.rb.ut.rb +16 -0
- data/lib/rex/image_source.rb +12 -0
- data/lib/rex/image_source/disk.rb +60 -0
- data/lib/rex/image_source/image_source.rb +46 -0
- data/lib/rex/image_source/memory.rb +37 -0
- data/lib/rex/io/bidirectional_pipe.rb +157 -0
- data/lib/rex/io/datagram_abstraction.rb +35 -0
- data/lib/rex/io/stream.rb +313 -0
- data/lib/rex/io/stream_abstraction.rb +186 -0
- data/lib/rex/io/stream_server.rb +211 -0
- data/lib/rex/job_container.rb +202 -0
- data/lib/rex/logging.rb +4 -0
- data/lib/rex/logging/log_dispatcher.rb +179 -0
- data/lib/rex/logging/log_sink.rb +42 -0
- data/lib/rex/logging/sinks/flatfile.rb +55 -0
- data/lib/rex/logging/sinks/stderr.rb +43 -0
- data/lib/rex/machparsey.rb +9 -0
- data/lib/rex/machparsey/exceptions.rb +34 -0
- data/lib/rex/machparsey/mach.rb +209 -0
- data/lib/rex/machparsey/machbase.rb +408 -0
- data/lib/rex/machscan.rb +9 -0
- data/lib/rex/machscan/scanner.rb +217 -0
- data/lib/rex/mime.rb +9 -0
- data/lib/rex/mime/header.rb +75 -0
- data/lib/rex/mime/message.rb +112 -0
- data/lib/rex/mime/part.rb +20 -0
- data/lib/rex/nop/opty2.rb +108 -0
- data/lib/rex/nop/opty2.rb.ut.rb +23 -0
- data/lib/rex/nop/opty2_tables.rb +300 -0
- data/lib/rex/ole.rb +128 -0
- data/lib/rex/ole/clsid.rb +47 -0
- data/lib/rex/ole/difat.rb +141 -0
- data/lib/rex/ole/directory.rb +230 -0
- data/lib/rex/ole/direntry.rb +240 -0
- data/lib/rex/ole/fat.rb +99 -0
- data/lib/rex/ole/header.rb +204 -0
- data/lib/rex/ole/minifat.rb +77 -0
- data/lib/rex/ole/samples/create_ole.rb +27 -0
- data/lib/rex/ole/samples/dir.rb +35 -0
- data/lib/rex/ole/samples/dump_stream.rb +34 -0
- data/lib/rex/ole/samples/ole_info.rb +23 -0
- data/lib/rex/ole/storage.rb +395 -0
- data/lib/rex/ole/stream.rb +53 -0
- data/lib/rex/ole/substorage.rb +49 -0
- data/lib/rex/ole/util.rb +157 -0
- data/lib/rex/parser/arguments.rb +97 -0
- data/lib/rex/parser/arguments.rb.ut.rb +67 -0
- data/lib/rex/parser/ini.rb +185 -0
- data/lib/rex/parser/ini.rb.ut.rb +29 -0
- data/lib/rex/parser/nmap_xml.rb +111 -0
- data/lib/rex/payloads.rb +1 -0
- data/lib/rex/payloads/win32.rb +2 -0
- data/lib/rex/payloads/win32/common.rb +26 -0
- data/lib/rex/payloads/win32/kernel.rb +53 -0
- data/lib/rex/payloads/win32/kernel/common.rb +54 -0
- data/lib/rex/payloads/win32/kernel/migration.rb +12 -0
- data/lib/rex/payloads/win32/kernel/recovery.rb +50 -0
- data/lib/rex/payloads/win32/kernel/stager.rb +171 -0
- data/lib/rex/peparsey.rb +12 -0
- data/lib/rex/peparsey/exceptions.rb +32 -0
- data/lib/rex/peparsey/pe.rb +188 -0
- data/lib/rex/peparsey/pe_memdump.rb +63 -0
- data/lib/rex/peparsey/pebase.rb +1655 -0
- data/lib/rex/peparsey/section.rb +136 -0
- data/lib/rex/pescan.rb +13 -0
- data/lib/rex/pescan/analyze.rb +309 -0
- data/lib/rex/pescan/scanner.rb +206 -0
- data/lib/rex/pescan/search.rb +56 -0
- data/lib/rex/platforms.rb +1 -0
- data/lib/rex/platforms/windows.rb +51 -0
- data/lib/rex/poly.rb +132 -0
- data/lib/rex/poly/block.rb +468 -0
- data/lib/rex/poly/register.rb +100 -0
- data/lib/rex/poly/register/x86.rb +40 -0
- data/lib/rex/post.rb +8 -0
- data/lib/rex/post/dir.rb +51 -0
- data/lib/rex/post/file.rb +172 -0
- data/lib/rex/post/file_stat.rb +220 -0
- data/lib/rex/post/gen.pl +13 -0
- data/lib/rex/post/io.rb +182 -0
- data/lib/rex/post/meterpreter.rb +4 -0
- data/lib/rex/post/meterpreter/channel.rb +438 -0
- data/lib/rex/post/meterpreter/channel_container.rb +54 -0
- data/lib/rex/post/meterpreter/channels/pool.rb +160 -0
- data/lib/rex/post/meterpreter/channels/pools/file.rb +62 -0
- data/lib/rex/post/meterpreter/channels/pools/stream_pool.rb +103 -0
- data/lib/rex/post/meterpreter/channels/stream.rb +87 -0
- data/lib/rex/post/meterpreter/client.rb +335 -0
- data/lib/rex/post/meterpreter/client_core.rb +274 -0
- data/lib/rex/post/meterpreter/dependencies.rb +3 -0
- data/lib/rex/post/meterpreter/extension.rb +32 -0
- data/lib/rex/post/meterpreter/extensions/espia/espia.rb +58 -0
- data/lib/rex/post/meterpreter/extensions/espia/tlv.rb +16 -0
- data/lib/rex/post/meterpreter/extensions/incognito/incognito.rb +94 -0
- data/lib/rex/post/meterpreter/extensions/incognito/tlv.rb +21 -0
- data/lib/rex/post/meterpreter/extensions/priv/fs.rb +118 -0
- data/lib/rex/post/meterpreter/extensions/priv/passwd.rb +61 -0
- data/lib/rex/post/meterpreter/extensions/priv/priv.rb +104 -0
- data/lib/rex/post/meterpreter/extensions/priv/tlv.rb +28 -0
- data/lib/rex/post/meterpreter/extensions/sniffer/sniffer.rb +100 -0
- data/lib/rex/post/meterpreter/extensions/sniffer/tlv.rb +24 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/constants.rb +333 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +273 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +235 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file_stat.rb +103 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/io.rb +48 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/config.rb +144 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/interface.rb +73 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/route.rb +56 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/socket.rb +137 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_client_channel.rb +167 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb +167 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb +192 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/stdapi.rb +139 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +97 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/event_log.rb +184 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/event_log_subsystem/event_record.rb +41 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/power.rb +61 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +361 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/image.rb +129 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/io.rb +55 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/memory.rb +336 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/thread.rb +141 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +279 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key.rb +182 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_value.rb +102 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/thread.rb +174 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +185 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +227 -0
- data/lib/rex/post/meterpreter/inbound_packet_handler.rb +30 -0
- data/lib/rex/post/meterpreter/object_aliases.rb +83 -0
- data/lib/rex/post/meterpreter/packet.rb +596 -0
- data/lib/rex/post/meterpreter/packet_dispatcher.rb +409 -0
- data/lib/rex/post/meterpreter/packet_parser.rb +94 -0
- data/lib/rex/post/meterpreter/packet_response_waiter.rb +83 -0
- data/lib/rex/post/meterpreter/ui/console.rb +135 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb +62 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +595 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/espia.rb +108 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/incognito.rb +241 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv.rb +61 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb +98 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/passwd.rb +51 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/timestomp.rb +132 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/sniffer.rb +187 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi.rb +63 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +376 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +270 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +484 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/ui.rb +315 -0
- data/lib/rex/post/meterpreter/ui/console/interactive_channel.rb +95 -0
- data/lib/rex/post/permission.rb +26 -0
- data/lib/rex/post/process.rb +57 -0
- data/lib/rex/post/thread.rb +57 -0
- data/lib/rex/post/ui.rb +52 -0
- data/lib/rex/proto.rb +12 -0
- data/lib/rex/proto.rb.ts.rb +8 -0
- data/lib/rex/proto/dcerpc.rb +6 -0
- data/lib/rex/proto/dcerpc.rb.ts.rb +9 -0
- data/lib/rex/proto/dcerpc/client.rb +358 -0
- data/lib/rex/proto/dcerpc/client.rb.ut.rb +491 -0
- data/lib/rex/proto/dcerpc/exceptions.rb +150 -0
- data/lib/rex/proto/dcerpc/handle.rb +47 -0
- data/lib/rex/proto/dcerpc/handle.rb.ut.rb +85 -0
- data/lib/rex/proto/dcerpc/ndr.rb +72 -0
- data/lib/rex/proto/dcerpc/ndr.rb.ut.rb +41 -0
- data/lib/rex/proto/dcerpc/packet.rb +253 -0
- data/lib/rex/proto/dcerpc/packet.rb.ut.rb +56 -0
- data/lib/rex/proto/dcerpc/response.rb +186 -0
- data/lib/rex/proto/dcerpc/response.rb.ut.rb +15 -0
- data/lib/rex/proto/dcerpc/uuid.rb +84 -0
- data/lib/rex/proto/dcerpc/uuid.rb.ut.rb +46 -0
- data/lib/rex/proto/drda.rb +5 -0
- data/lib/rex/proto/drda.rb.ts.rb +17 -0
- data/lib/rex/proto/drda/constants.rb +49 -0
- data/lib/rex/proto/drda/constants.rb.ut.rb +23 -0
- data/lib/rex/proto/drda/packet.rb +252 -0
- data/lib/rex/proto/drda/packet.rb.ut.rb +109 -0
- data/lib/rex/proto/drda/utils.rb +123 -0
- data/lib/rex/proto/drda/utils.rb.ut.rb +84 -0
- data/lib/rex/proto/http.rb +5 -0
- data/lib/rex/proto/http.rb.ts.rb +12 -0
- data/lib/rex/proto/http/client.rb +817 -0
- data/lib/rex/proto/http/client.rb.ut.rb +93 -0
- data/lib/rex/proto/http/handler.rb +46 -0
- data/lib/rex/proto/http/handler/erb.rb +128 -0
- data/lib/rex/proto/http/handler/erb.rb.ut.rb +21 -0
- data/lib/rex/proto/http/handler/erb.rb.ut.rb.rhtml +1 -0
- data/lib/rex/proto/http/handler/proc.rb +54 -0
- data/lib/rex/proto/http/handler/proc.rb.ut.rb +24 -0
- data/lib/rex/proto/http/header.rb +161 -0
- data/lib/rex/proto/http/header.rb.ut.rb +46 -0
- data/lib/rex/proto/http/packet.rb +394 -0
- data/lib/rex/proto/http/packet.rb.ut.rb +165 -0
- data/lib/rex/proto/http/request.rb +356 -0
- data/lib/rex/proto/http/request.rb.ut.rb +214 -0
- data/lib/rex/proto/http/response.rb +85 -0
- data/lib/rex/proto/http/response.rb.ut.rb +149 -0
- data/lib/rex/proto/http/server.rb +367 -0
- data/lib/rex/proto/http/server.rb.ut.rb +79 -0
- data/lib/rex/proto/smb.rb +7 -0
- data/lib/rex/proto/smb.rb.ts.rb +8 -0
- data/lib/rex/proto/smb/client.rb +1733 -0
- data/lib/rex/proto/smb/client.rb.ut.rb +223 -0
- data/lib/rex/proto/smb/constants.rb +1062 -0
- data/lib/rex/proto/smb/constants.rb.ut.rb +18 -0
- data/lib/rex/proto/smb/crypt.rb +95 -0
- data/lib/rex/proto/smb/crypt.rb.ut.rb +20 -0
- data/lib/rex/proto/smb/evasions.rb +65 -0
- data/lib/rex/proto/smb/exceptions.rb +846 -0
- data/lib/rex/proto/smb/simpleclient.rb +292 -0
- data/lib/rex/proto/smb/simpleclient.rb.ut.rb +128 -0
- data/lib/rex/proto/smb/utils.rb +514 -0
- data/lib/rex/proto/smb/utils.rb.ut.rb +20 -0
- data/lib/rex/proto/sunrpc.rb +1 -0
- data/lib/rex/proto/sunrpc/client.rb +195 -0
- data/lib/rex/script.rb +42 -0
- data/lib/rex/script/base.rb +59 -0
- data/lib/rex/script/meterpreter.rb +9 -0
- data/lib/rex/script/shell.rb +9 -0
- data/lib/rex/service.rb +48 -0
- data/lib/rex/service_manager.rb +141 -0
- data/lib/rex/service_manager.rb.ut.rb +32 -0
- data/lib/rex/services/local_relay.rb +423 -0
- data/lib/rex/socket.rb +586 -0
- data/lib/rex/socket.rb.ut.rb +86 -0
- data/lib/rex/socket/comm.rb +119 -0
- data/lib/rex/socket/comm/local.rb +409 -0
- data/lib/rex/socket/comm/local.rb.ut.rb +75 -0
- data/lib/rex/socket/ip.rb +129 -0
- data/lib/rex/socket/parameters.rb +345 -0
- data/lib/rex/socket/parameters.rb.ut.rb +51 -0
- data/lib/rex/socket/range_walker.rb +295 -0
- data/lib/rex/socket/range_walker.rb.ut.rb +55 -0
- data/lib/rex/socket/ssl_tcp.rb +184 -0
- data/lib/rex/socket/ssl_tcp.rb.ut.rb +39 -0
- data/lib/rex/socket/ssl_tcp_server.rb +122 -0
- data/lib/rex/socket/ssl_tcp_server.rb.ut.rb +51 -0
- data/lib/rex/socket/subnet_walker.rb +75 -0
- data/lib/rex/socket/subnet_walker.rb.ut.rb +28 -0
- data/lib/rex/socket/switch_board.rb +272 -0
- data/lib/rex/socket/switch_board.rb.ut.rb +52 -0
- data/lib/rex/socket/tcp.rb +76 -0
- data/lib/rex/socket/tcp.rb.ut.rb +64 -0
- data/lib/rex/socket/tcp_server.rb +67 -0
- data/lib/rex/socket/tcp_server.rb.ut.rb +44 -0
- data/lib/rex/socket/udp.rb +157 -0
- data/lib/rex/socket/udp.rb.ut.rb +44 -0
- data/lib/rex/struct2.rb +5 -0
- data/lib/rex/struct2/c_struct.rb +181 -0
- data/lib/rex/struct2/c_struct_template.rb +39 -0
- data/lib/rex/struct2/constant.rb +26 -0
- data/lib/rex/struct2/element.rb +44 -0
- data/lib/rex/struct2/generic.rb +73 -0
- data/lib/rex/struct2/restraint.rb +54 -0
- data/lib/rex/struct2/s_string.rb +72 -0
- data/lib/rex/struct2/s_struct.rb +111 -0
- data/lib/rex/sync.rb +6 -0
- data/lib/rex/sync/event.rb +94 -0
- data/lib/rex/sync/read_write_lock.rb +176 -0
- data/lib/rex/sync/ref.rb +57 -0
- data/lib/rex/sync/thread_safe.rb +82 -0
- data/lib/rex/test.rb +35 -0
- data/lib/rex/text.rb +1029 -0
- data/lib/rex/text.rb.ut.rb +168 -0
- data/lib/rex/time.rb +65 -0
- data/lib/rex/transformer.rb +115 -0
- data/lib/rex/transformer.rb.ut.rb +38 -0
- data/lib/rex/ui.rb +21 -0
- data/lib/rex/ui/interactive.rb +252 -0
- data/lib/rex/ui/output.rb +80 -0
- data/lib/rex/ui/output/none.rb +18 -0
- data/lib/rex/ui/progress_tracker.rb +96 -0
- data/lib/rex/ui/subscriber.rb +149 -0
- data/lib/rex/ui/text/color.rb +97 -0
- data/lib/rex/ui/text/color.rb.ut.rb +18 -0
- data/lib/rex/ui/text/dispatcher_shell.rb +382 -0
- data/lib/rex/ui/text/input.rb +117 -0
- data/lib/rex/ui/text/input/buffer.rb +75 -0
- data/lib/rex/ui/text/input/readline.rb +129 -0
- data/lib/rex/ui/text/input/socket.rb +95 -0
- data/lib/rex/ui/text/input/stdio.rb +45 -0
- data/lib/rex/ui/text/irb_shell.rb +55 -0
- data/lib/rex/ui/text/output.rb +80 -0
- data/lib/rex/ui/text/output/buffer.rb +65 -0
- data/lib/rex/ui/text/output/file.rb +37 -0
- data/lib/rex/ui/text/output/socket.rb +43 -0
- data/lib/rex/ui/text/output/stdio.rb +40 -0
- data/lib/rex/ui/text/progress_tracker.rb +56 -0
- data/lib/rex/ui/text/progress_tracker.rb.ut.rb +34 -0
- data/lib/rex/ui/text/shell.rb +321 -0
- data/lib/rex/ui/text/table.rb +254 -0
- data/lib/rex/ui/text/table.rb.ut.rb +55 -0
- data/lib/rex/zip.rb +93 -0
- data/lib/rex/zip/archive.rb +91 -0
- data/lib/rex/zip/blocks.rb +182 -0
- data/lib/rex/zip/entry.rb +95 -0
- data/lib/rex/zip/samples/comment.rb +32 -0
- data/lib/rex/zip/samples/mkwar.rb +138 -0
- data/lib/rex/zip/samples/mkzip.rb +19 -0
- data/lib/rex/zip/samples/recursive.rb +58 -0
- metadata +435 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..'))
|
|
4
|
+
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
require 'rex/proto/http'
|
|
7
|
+
|
|
8
|
+
class Rex::Proto::Http::Server::UnitTest < Test::Unit::TestCase
|
|
9
|
+
|
|
10
|
+
ListenPort = 8090
|
|
11
|
+
ListenHost = '127.0.0.1'
|
|
12
|
+
|
|
13
|
+
SrvKlass = Rex::Proto::Http::Server
|
|
14
|
+
CliKlass = Rex::Proto::Http::Client
|
|
15
|
+
|
|
16
|
+
def test_server
|
|
17
|
+
begin
|
|
18
|
+
s = start_srv
|
|
19
|
+
c = CliKlass.new(ListenHost, ListenPort)
|
|
20
|
+
|
|
21
|
+
1.upto(10) {
|
|
22
|
+
req = c.request_raw('uri' => '/')
|
|
23
|
+
res = c.send_recv(req)
|
|
24
|
+
assert_not_nil(res)
|
|
25
|
+
assert_equal(404, res.code)
|
|
26
|
+
}
|
|
27
|
+
ensure
|
|
28
|
+
stop_srv
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_resource
|
|
33
|
+
begin
|
|
34
|
+
s = start_srv
|
|
35
|
+
c = CliKlass.new(ListenHost, ListenPort)
|
|
36
|
+
|
|
37
|
+
s.add_resource('/foo',
|
|
38
|
+
'Proc' => Proc.new { |cli, req|
|
|
39
|
+
resp = Rex::Proto::Http::Response::OK.new
|
|
40
|
+
|
|
41
|
+
resp.body = "Chickens everywhere"
|
|
42
|
+
|
|
43
|
+
cli.send_response(resp)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
1.upto(10) {
|
|
47
|
+
req = c.request_raw('uri' => '/foo')
|
|
48
|
+
res = c.send_recv(req)
|
|
49
|
+
assert_not_nil(res)
|
|
50
|
+
assert_equal(200, res.code)
|
|
51
|
+
assert_equal("Chickens everywhere", res.body)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
s.remove_resource('/foo')
|
|
55
|
+
|
|
56
|
+
req = c.request_raw('uri' => '/foo')
|
|
57
|
+
res = c.send_recv(req)
|
|
58
|
+
assert_not_nil(res)
|
|
59
|
+
assert_equal(404, res.code)
|
|
60
|
+
ensure
|
|
61
|
+
stop_srv
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
protected
|
|
66
|
+
|
|
67
|
+
def start_srv
|
|
68
|
+
self.srv = SrvKlass.new(ListenPort, ListenHost)
|
|
69
|
+
self.srv.start
|
|
70
|
+
self.srv
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def stop_srv
|
|
74
|
+
self.srv.stop if (self.srv)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_accessor :srv
|
|
78
|
+
|
|
79
|
+
end
|
|
@@ -0,0 +1,1733 @@
|
|
|
1
|
+
module Rex
|
|
2
|
+
module Proto
|
|
3
|
+
module SMB
|
|
4
|
+
class Client
|
|
5
|
+
|
|
6
|
+
require 'rex/text'
|
|
7
|
+
require 'rex/struct2'
|
|
8
|
+
require 'rex/proto/smb/constants'
|
|
9
|
+
require 'rex/proto/smb/exceptions'
|
|
10
|
+
require 'rex/proto/smb/evasions'
|
|
11
|
+
require 'rex/proto/smb/crypt'
|
|
12
|
+
require 'rex/proto/smb/utils'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Some short-hand class aliases
|
|
16
|
+
CONST = Rex::Proto::SMB::Constants
|
|
17
|
+
CRYPT = Rex::Proto::SMB::Crypt
|
|
18
|
+
UTILS = Rex::Proto::SMB::Utils
|
|
19
|
+
XCEPT = Rex::Proto::SMB::Exceptions
|
|
20
|
+
EVADE = Rex::Proto::SMB::Evasions
|
|
21
|
+
|
|
22
|
+
def initialize(socket)
|
|
23
|
+
self.socket = socket
|
|
24
|
+
self.native_os = 'Windows 2000 2195'
|
|
25
|
+
self.native_lm = 'Windows 2000 5.0'
|
|
26
|
+
self.encrypt_passwords = true
|
|
27
|
+
self.extended_security = false
|
|
28
|
+
self.multiplex_id = rand(0xffff)
|
|
29
|
+
self.process_id = rand(0xffff)
|
|
30
|
+
self.read_timeout = 10
|
|
31
|
+
self.evasion_opts = {
|
|
32
|
+
|
|
33
|
+
# Padding is performed between packet headers and data
|
|
34
|
+
'pad_data' => EVADE::EVASION_NONE,
|
|
35
|
+
|
|
36
|
+
# File path padding is performed on all open/create calls
|
|
37
|
+
'pad_file' => EVADE::EVASION_NONE,
|
|
38
|
+
|
|
39
|
+
# Modify the \PIPE\ string in trans_named_pipe calls
|
|
40
|
+
'obscure_trans_pipe' => EVADE::EVASION_NONE,
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Read a SMB packet from the socket
|
|
45
|
+
def smb_recv
|
|
46
|
+
data = socket.get_once(-1, self.read_timeout)
|
|
47
|
+
|
|
48
|
+
if (data.nil? or data.length < 4)
|
|
49
|
+
raise XCEPT::NoReply
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
recv_len = data[2,2].unpack('n')[0]
|
|
53
|
+
if (recv_len == 0)
|
|
54
|
+
return data
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
recv_len += 4
|
|
58
|
+
|
|
59
|
+
while (data.length != recv_len)
|
|
60
|
+
buff = ''
|
|
61
|
+
|
|
62
|
+
begin
|
|
63
|
+
buff << self.socket.timed_read(recv_len - data.length, self.read_timeout)
|
|
64
|
+
rescue Timeout::Error
|
|
65
|
+
rescue
|
|
66
|
+
raise XCEPT::ReadPacket
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if (buff.nil? or buff.length == 0)
|
|
70
|
+
raise XCEPT::ReadPacket
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
data << buff
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return data
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Send a SMB packet down the socket
|
|
80
|
+
def smb_send(data, evasion_level=0)
|
|
81
|
+
|
|
82
|
+
# evasion_level is ignored, since real evasion happens
|
|
83
|
+
# in the actual socket layer
|
|
84
|
+
|
|
85
|
+
size = 0
|
|
86
|
+
wait = 0
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
# Just send the packet and return
|
|
90
|
+
if (size == 0 or size >= data.length)
|
|
91
|
+
return self.socket.put(data)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Break the packet up into chunks and wait between them
|
|
95
|
+
ret = 0
|
|
96
|
+
while ( (chunk = data.slice!(0, size)).length > 0 )
|
|
97
|
+
ret = self.socket.put(chunk)
|
|
98
|
+
if (wait > 0)
|
|
99
|
+
select(nil, nil, nil, wait)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
return ret
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Set the SMB parameters to some reasonable defaults
|
|
107
|
+
def smb_defaults(packet)
|
|
108
|
+
packet.v['MultiplexID'] = self.multiplex_id.to_i
|
|
109
|
+
packet.v['TreeID'] = self.last_tree_id.to_i
|
|
110
|
+
packet.v['UserID'] = self.auth_user_id.to_i
|
|
111
|
+
packet.v['ProcessID'] = self.process_id.to_i
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# The main dispatcher for all incoming SMB packets
|
|
116
|
+
def smb_recv_parse(expected_type, ignore_errors = false)
|
|
117
|
+
|
|
118
|
+
# This will throw an exception if it fails to read the whole packet
|
|
119
|
+
data = self.smb_recv
|
|
120
|
+
|
|
121
|
+
pkt = CONST::SMB_BASE_PKT.make_struct
|
|
122
|
+
pkt.from_s(data)
|
|
123
|
+
res = pkt
|
|
124
|
+
|
|
125
|
+
begin
|
|
126
|
+
case pkt['Payload']['SMB'].v['Command']
|
|
127
|
+
|
|
128
|
+
when CONST::SMB_COM_NEGOTIATE
|
|
129
|
+
res = smb_parse_negotiate(pkt, data)
|
|
130
|
+
|
|
131
|
+
when CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
132
|
+
res = smb_parse_session_setup(pkt, data)
|
|
133
|
+
|
|
134
|
+
when CONST::SMB_COM_TREE_CONNECT_ANDX
|
|
135
|
+
res = smb_parse_tree_connect(pkt, data)
|
|
136
|
+
|
|
137
|
+
when CONST::SMB_COM_TREE_DISCONNECT
|
|
138
|
+
res = smb_parse_tree_disconnect(pkt, data)
|
|
139
|
+
|
|
140
|
+
when CONST::SMB_COM_NT_CREATE_ANDX
|
|
141
|
+
res = smb_parse_create(pkt, data)
|
|
142
|
+
|
|
143
|
+
when CONST::SMB_COM_TRANSACTION, CONST::SMB_COM_TRANSACTION2
|
|
144
|
+
res = smb_parse_trans(pkt, data)
|
|
145
|
+
|
|
146
|
+
when CONST::SMB_COM_NT_TRANSACT
|
|
147
|
+
res = smb_parse_nttrans(pkt, data)
|
|
148
|
+
|
|
149
|
+
when CONST::SMB_COM_NT_TRANSACT_SECONDARY
|
|
150
|
+
res = smb_parse_nttrans(pkt, data)
|
|
151
|
+
|
|
152
|
+
when CONST::SMB_COM_OPEN_ANDX
|
|
153
|
+
res = smb_parse_open(pkt, data)
|
|
154
|
+
|
|
155
|
+
when CONST::SMB_COM_WRITE_ANDX
|
|
156
|
+
res = smb_parse_write(pkt, data)
|
|
157
|
+
|
|
158
|
+
when CONST::SMB_COM_READ_ANDX
|
|
159
|
+
res = smb_parse_read(pkt, data)
|
|
160
|
+
|
|
161
|
+
when CONST::SMB_COM_CLOSE
|
|
162
|
+
res = smb_parse_close(pkt, data)
|
|
163
|
+
|
|
164
|
+
when CONST::SMB_COM_DELETE
|
|
165
|
+
res = smb_parse_delete(pkt, data)
|
|
166
|
+
|
|
167
|
+
else
|
|
168
|
+
raise XCEPT::InvalidCommand
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if (pkt['Payload']['SMB'].v['Command'] != expected_type)
|
|
172
|
+
raise XCEPT::InvalidType
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if (ignore_errors == false and pkt['Payload']['SMB'].v['ErrorClass'] != 0)
|
|
176
|
+
raise XCEPT::ErrorCode
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
rescue XCEPT::InvalidWordCount, XCEPT::InvalidCommand, XCEPT::InvalidType, XCEPT::ErrorCode
|
|
180
|
+
$!.word_count = pkt['Payload']['SMB'].v['WordCount']
|
|
181
|
+
$!.command = pkt['Payload']['SMB'].v['Command']
|
|
182
|
+
$!.error_code = pkt['Payload']['SMB'].v['ErrorClass']
|
|
183
|
+
raise $!
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
return res
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Process incoming SMB_COM_NEGOTIATE packets
|
|
190
|
+
def smb_parse_negotiate(pkt, data)
|
|
191
|
+
#Process NTLM negotiate responses
|
|
192
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 17)
|
|
193
|
+
res = CONST::SMB_NEG_RES_NT_PKT.make_struct
|
|
194
|
+
res.from_s(data)
|
|
195
|
+
return res
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Process LANMAN negotiate responses
|
|
199
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 13)
|
|
200
|
+
res = CONST::SMB_NEG_RES_LM_PKT.make_struct
|
|
201
|
+
res.from_s(data)
|
|
202
|
+
return res
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Process ERROR negotiate responses
|
|
206
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 1)
|
|
207
|
+
res = CONST::SMB_NEG_RES_ERR_PKT.make_struct
|
|
208
|
+
res.from_s(data)
|
|
209
|
+
return res
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Process SMB error responses
|
|
213
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
214
|
+
return pkt
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
raise XCEPT::InvalidWordCount
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Process incoming SMB_COM_SESSION_SETUP_ANDX packets
|
|
221
|
+
def smb_parse_session_setup(pkt, data)
|
|
222
|
+
# Process NTLMv2 negotiate responses
|
|
223
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 4)
|
|
224
|
+
res = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct
|
|
225
|
+
res.from_s(data)
|
|
226
|
+
return res
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Process NTLMv1 and LANMAN responses
|
|
230
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 3)
|
|
231
|
+
res = CONST::SMB_SETUP_RES_PKT.make_struct
|
|
232
|
+
res.from_s(data)
|
|
233
|
+
return res
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Process SMB error responses
|
|
237
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
238
|
+
return pkt
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
raise XCEPT::InvalidWordCount
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Process incoming SMB_COM_TREE_CONNECT_ANDX packets
|
|
245
|
+
def smb_parse_tree_connect(pkt, data)
|
|
246
|
+
|
|
247
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 3)
|
|
248
|
+
res = CONST::SMB_TREE_CONN_RES_PKT.make_struct
|
|
249
|
+
res.from_s(data)
|
|
250
|
+
return res
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Process SMB error responses
|
|
254
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
255
|
+
return pkt
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
raise XCEPT::InvalidWordCount
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Process incoming SMB_COM_TREE_DISCONNECT packets
|
|
262
|
+
def smb_parse_tree_disconnect(pkt, data)
|
|
263
|
+
|
|
264
|
+
# Process SMB responses
|
|
265
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
266
|
+
res = CONST::SMB_TREE_DISCONN_RES_PKT.make_struct
|
|
267
|
+
res.from_s(data)
|
|
268
|
+
return res
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
raise XCEPT::InvalidWordCount
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Process incoming SMB_COM_NT_CREATE_ANDX packets
|
|
275
|
+
def smb_parse_create(pkt, data)
|
|
276
|
+
|
|
277
|
+
# Windows says 42, but Samba says 34, same structure :-/
|
|
278
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 42)
|
|
279
|
+
res = CONST::SMB_CREATE_RES_PKT.make_struct
|
|
280
|
+
res.from_s(data)
|
|
281
|
+
return res
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 34)
|
|
285
|
+
res = CONST::SMB_CREATE_RES_PKT.make_struct
|
|
286
|
+
res.from_s(data)
|
|
287
|
+
return res
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Process SMB error responses
|
|
291
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
292
|
+
return pkt
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
raise XCEPT::InvalidWordCount
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Process incoming SMB_COM_TRANSACTION packets
|
|
299
|
+
def smb_parse_trans(pkt, data)
|
|
300
|
+
|
|
301
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 10)
|
|
302
|
+
res = CONST::SMB_TRANS_RES_PKT.make_struct
|
|
303
|
+
res.from_s(data)
|
|
304
|
+
return res
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Process SMB error responses
|
|
308
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
309
|
+
return pkt
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
raise XCEPT::InvalidWordCount
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Process incoming SMB_COM_NT_TRANSACT packets
|
|
316
|
+
def smb_parse_nttrans(pkt, data)
|
|
317
|
+
|
|
318
|
+
# Process SMB error responses
|
|
319
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
320
|
+
return pkt
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
if (pkt['Payload']['SMB'].v['WordCount'] >= 18)
|
|
324
|
+
res = SMB_NTTRANS_RES_PKT.make_struct
|
|
325
|
+
res.from_s(data)
|
|
326
|
+
return res
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
raise XCEPT::InvalidWordCount
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Process incoming SMB_COM_OPEN_ANDX packets
|
|
333
|
+
def smb_parse_open(pkt, data)
|
|
334
|
+
# Process open responses
|
|
335
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 15)
|
|
336
|
+
res = CONST::SMB_OPEN_RES_PKT.make_struct
|
|
337
|
+
res.from_s(data)
|
|
338
|
+
return res
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Process SMB error responses
|
|
342
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
343
|
+
return pkt
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
raise XCEPT::InvalidWordCount
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Process incoming SMB_COM_WRITE_ANDX packets
|
|
350
|
+
def smb_parse_write(pkt, data)
|
|
351
|
+
|
|
352
|
+
# Process write responses
|
|
353
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 6)
|
|
354
|
+
res = CONST::SMB_WRITE_RES_PKT.make_struct
|
|
355
|
+
res.from_s(data)
|
|
356
|
+
return res
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Process SMB error responses
|
|
360
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
361
|
+
return pkt
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
raise XCEPT::InvalidWordCount
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Process incoming SMB_COM_READ_ANDX packets
|
|
368
|
+
def smb_parse_read(pkt, data)
|
|
369
|
+
|
|
370
|
+
# Process read responses
|
|
371
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 12)
|
|
372
|
+
res = CONST::SMB_READ_RES_PKT.make_struct
|
|
373
|
+
res.from_s(data)
|
|
374
|
+
return res
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Process SMB error responses
|
|
378
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
379
|
+
return pkt
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
raise XCEPT::InvalidWordCount
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Process incoming SMB_COM_CLOSE packets
|
|
386
|
+
def smb_parse_close(pkt, data)
|
|
387
|
+
|
|
388
|
+
# Process SMB error responses
|
|
389
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
390
|
+
return pkt
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
raise XCEPT::InvalidWordCount
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Process incoming SMB_COM_DELETE packets
|
|
397
|
+
def smb_parse_delete(pkt, data)
|
|
398
|
+
|
|
399
|
+
# Process SMB error responses
|
|
400
|
+
if (pkt['Payload']['SMB'].v['WordCount'] == 0)
|
|
401
|
+
res = CONST::SMB_DELETE_RES_PKT.make_struct
|
|
402
|
+
res.from_s(data)
|
|
403
|
+
return res
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
raise XCEPT::InvalidWordCount
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Request a SMB session over NetBIOS
|
|
410
|
+
def session_request(name = '*SMBSERVER')
|
|
411
|
+
|
|
412
|
+
name ||= '*SMBSERVER'
|
|
413
|
+
|
|
414
|
+
data = ''
|
|
415
|
+
data << "\x20" + UTILS.nbname_encode(name) + "\x00"
|
|
416
|
+
data << "\x20" + CONST::NETBIOS_REDIR + "\x00"
|
|
417
|
+
|
|
418
|
+
pkt = CONST::NBRAW_PKT.make_struct
|
|
419
|
+
pkt.v['Type'] = 0x81
|
|
420
|
+
pkt['Payload'].v['Payload'] = data
|
|
421
|
+
|
|
422
|
+
# Most SMB implementations can't handle this being fragmented
|
|
423
|
+
self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
|
|
424
|
+
res = self.smb_recv
|
|
425
|
+
|
|
426
|
+
ack = CONST::NBRAW_PKT.make_struct
|
|
427
|
+
ack.from_s(res)
|
|
428
|
+
|
|
429
|
+
if (ack.v['Type'] != 130)
|
|
430
|
+
raise XCEPT::NetbiosSessionFailed
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
return ack
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Negotiate a SMB dialect
|
|
437
|
+
def negotiate(extended=true)
|
|
438
|
+
|
|
439
|
+
dialects = ['LANMAN1.0', 'LM1.2X002' ]
|
|
440
|
+
|
|
441
|
+
if (self.encrypt_passwords)
|
|
442
|
+
dialects.push('NT LANMAN 1.0', 'NT LM 0.12')
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
|
|
446
|
+
|
|
447
|
+
pkt = CONST::SMB_NEG_PKT.make_struct
|
|
448
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
449
|
+
|
|
450
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
|
|
451
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
452
|
+
|
|
453
|
+
if(extended)
|
|
454
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
|
455
|
+
else
|
|
456
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
pkt['Payload'].v['Payload'] = data
|
|
460
|
+
|
|
461
|
+
self.smb_send(pkt.to_s)
|
|
462
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_NEGOTIATE)
|
|
463
|
+
|
|
464
|
+
idx = ack['Payload'].v['Dialect']
|
|
465
|
+
|
|
466
|
+
# Check for failed dialect selection
|
|
467
|
+
if (idx < 0 or idx >= dialects.length)
|
|
468
|
+
return nil
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Set the selected dialect
|
|
472
|
+
self.dialect = dialects[idx]
|
|
473
|
+
|
|
474
|
+
# Does the server support extended security negotiation?
|
|
475
|
+
if (ack['Payload'].v['Capabilities'] & 0x80000000 != 0)
|
|
476
|
+
self.extended_security = true
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Set the security mode
|
|
480
|
+
self.security_mode = ack['Payload'].v['SecurityMode']
|
|
481
|
+
|
|
482
|
+
# Set the challenge key
|
|
483
|
+
if (ack['Payload'].v['EncryptionKey'] != nil)
|
|
484
|
+
self.challenge_key = ack['Payload'].v['EncryptionKey']
|
|
485
|
+
else
|
|
486
|
+
# Handle Windows NT 4.0 responses
|
|
487
|
+
if (ack['Payload'].v['KeyLength'] > 0)
|
|
488
|
+
self.challenge_key = ack['Payload'].v['Payload'][0, ack['Payload'].v['KeyLength']]
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# Set the session identifier
|
|
493
|
+
if (ack['Payload'].v['SessionKey'] != nil)
|
|
494
|
+
self.session_id = ack['Payload'].v['SessionKey']
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Extract the payload (GUID/SecurityBlob)
|
|
498
|
+
buf = ack['Payload'].v['Payload'] || ''
|
|
499
|
+
|
|
500
|
+
# Set the server GUID
|
|
501
|
+
if (self.extended_security and buf.length >= 16)
|
|
502
|
+
self.server_guid = buf[0,16]
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# Set the server SecurityBlob
|
|
506
|
+
if (self.extended_security and buf.length > 16)
|
|
507
|
+
# buf[16, buf.length - 16]
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
if (ack['Payload'].v['ServerDate'] > 0)
|
|
511
|
+
stamp = UTILS.time_smb_to_unix(ack['Payload'].v['ServerDate'],ack['Payload'].v['ServerTime'])
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
return ack
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
# Authenticate and establish a session
|
|
519
|
+
def session_setup(*args)
|
|
520
|
+
|
|
521
|
+
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
if (self.challenge_key)
|
|
525
|
+
return self.session_setup_ntlmv1(*args)
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
if ( self.extended_security )
|
|
529
|
+
return self.session_setup_ntlmv2(*args)
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
return self.session_setup_clear(*args)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# Authenticate using clear-text passwords
|
|
537
|
+
def session_setup_clear(user = '', pass = '', domain = '')
|
|
538
|
+
|
|
539
|
+
data = [ pass, user, domain, self.native_os, self.native_lm ].collect{ |a| a + "\x00" }.join('');
|
|
540
|
+
|
|
541
|
+
pkt = CONST::SMB_SETUP_LANMAN_PKT.make_struct
|
|
542
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
543
|
+
|
|
544
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
545
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
546
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
547
|
+
pkt['Payload']['SMB'].v['WordCount'] = 10
|
|
548
|
+
pkt['Payload'].v['AndX'] = 255
|
|
549
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
550
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
551
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
552
|
+
pkt['Payload'].v['PasswordLen'] = pass.length + 1
|
|
553
|
+
pkt['Payload'].v['Capabilities'] = 64
|
|
554
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
555
|
+
pkt['Payload'].v['Payload'] = data
|
|
556
|
+
|
|
557
|
+
self.smb_send(pkt.to_s)
|
|
558
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
|
|
559
|
+
|
|
560
|
+
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
|
|
561
|
+
self.auth_user = user
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
|
|
565
|
+
|
|
566
|
+
info = ack['Payload'].v['Payload'].split(/\x00/)
|
|
567
|
+
self.peer_native_os = info[0]
|
|
568
|
+
self.peer_native_lm = info[1]
|
|
569
|
+
self.default_domain = info[2]
|
|
570
|
+
|
|
571
|
+
return ack
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Authenticate using NTLMv1
|
|
575
|
+
def session_setup_ntlmv1(user = '', pass = '', domain = '')
|
|
576
|
+
|
|
577
|
+
raise XCEPT::NTLM1MissingChallenge if not self.challenge_key
|
|
578
|
+
|
|
579
|
+
if (pass.length == 65)
|
|
580
|
+
hash_lm = CRYPT.e_p24( [ pass.upcase()[0,32] ].pack('H42'), self.challenge_key)
|
|
581
|
+
hash_nt = CRYPT.e_p24( [ pass.upcase()[33,65] ].pack('H42'), self.challenge_key)
|
|
582
|
+
else
|
|
583
|
+
hash_lm = pass.length > 0 ? CRYPT.lanman_des(pass, self.challenge_key) : ''
|
|
584
|
+
hash_nt = pass.length > 0 ? CRYPT.ntlm_md4(pass, self.challenge_key) : ''
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
data = ''
|
|
588
|
+
data << hash_lm
|
|
589
|
+
data << hash_nt
|
|
590
|
+
data << user + "\x00"
|
|
591
|
+
data << domain + "\x00"
|
|
592
|
+
data << self.native_os + "\x00"
|
|
593
|
+
data << self.native_lm + "\x00"
|
|
594
|
+
|
|
595
|
+
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
|
|
596
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
597
|
+
|
|
598
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
599
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
600
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
601
|
+
pkt['Payload']['SMB'].v['WordCount'] = 13
|
|
602
|
+
pkt['Payload'].v['AndX'] = 255
|
|
603
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
604
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
605
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
606
|
+
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
|
|
607
|
+
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
|
|
608
|
+
pkt['Payload'].v['Capabilities'] = 64
|
|
609
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
610
|
+
pkt['Payload'].v['Payload'] = data
|
|
611
|
+
|
|
612
|
+
self.smb_send(pkt.to_s)
|
|
613
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
|
|
614
|
+
|
|
615
|
+
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
|
|
616
|
+
self.auth_user = user
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
|
|
620
|
+
|
|
621
|
+
info = ack['Payload'].v['Payload'].split(/\x00/)
|
|
622
|
+
|
|
623
|
+
self.peer_native_os = info[0]
|
|
624
|
+
self.peer_native_lm = info[1]
|
|
625
|
+
self.default_domain = info[2]
|
|
626
|
+
|
|
627
|
+
return ack
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
# Authenticate using NTLMv1 with a precomputed hash pair
|
|
632
|
+
def session_setup_ntlmv1_prehash(user, domain, hash_lm, hash_nt)
|
|
633
|
+
|
|
634
|
+
data = ''
|
|
635
|
+
data << hash_lm
|
|
636
|
+
data << hash_nt
|
|
637
|
+
data << user + "\x00"
|
|
638
|
+
data << domain + "\x00"
|
|
639
|
+
data << self.native_os + "\x00"
|
|
640
|
+
data << self.native_lm + "\x00"
|
|
641
|
+
|
|
642
|
+
pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
|
|
643
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
644
|
+
|
|
645
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
646
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
647
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
648
|
+
pkt['Payload']['SMB'].v['WordCount'] = 13
|
|
649
|
+
pkt['Payload'].v['AndX'] = 255
|
|
650
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
651
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
652
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
653
|
+
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
|
|
654
|
+
pkt['Payload'].v['PasswordLenNT'] = hash_nt.length
|
|
655
|
+
pkt['Payload'].v['Capabilities'] = 64
|
|
656
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
657
|
+
pkt['Payload'].v['Payload'] = data
|
|
658
|
+
|
|
659
|
+
self.smb_send(pkt.to_s)
|
|
660
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
|
|
661
|
+
|
|
662
|
+
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
|
|
663
|
+
self.auth_user = user
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
|
|
667
|
+
|
|
668
|
+
info = ack['Payload'].v['Payload'].split(/\x00/)
|
|
669
|
+
|
|
670
|
+
self.peer_native_os = info[0]
|
|
671
|
+
self.peer_native_lm = info[1]
|
|
672
|
+
self.default_domain = info[2]
|
|
673
|
+
|
|
674
|
+
return ack
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
# Authenticate using extended security negotiation (NTLMv2)
|
|
678
|
+
def session_setup_ntlmv2(user = '', pass = '', domain = '', name = nil)
|
|
679
|
+
|
|
680
|
+
if (name == nil)
|
|
681
|
+
name = Rex::Text.rand_text_alphanumeric(16)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
blob = UTILS.make_ntlmv2_secblob_init(domain, name)
|
|
685
|
+
|
|
686
|
+
native_data = ''
|
|
687
|
+
native_data << self.native_os + "\x00"
|
|
688
|
+
native_data << self.native_lm + "\x00"
|
|
689
|
+
|
|
690
|
+
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
|
691
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
692
|
+
|
|
693
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
694
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
695
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
|
696
|
+
pkt['Payload']['SMB'].v['WordCount'] = 12
|
|
697
|
+
pkt['Payload'].v['AndX'] = 255
|
|
698
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
699
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
700
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
701
|
+
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
|
702
|
+
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
|
703
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
704
|
+
pkt['Payload'].v['Payload'] = blob + native_data
|
|
705
|
+
|
|
706
|
+
self.smb_send(pkt.to_s)
|
|
707
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
# The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
|
|
711
|
+
if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
|
|
712
|
+
return session_setup_ntlmv1(user, pass, domain)
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# Make sure the error code tells us to continue processing
|
|
716
|
+
if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
|
|
717
|
+
failure = XCEPT::ErrorCode.new
|
|
718
|
+
failure.word_count = ack['Payload']['SMB'].v['WordCount']
|
|
719
|
+
failure.command = ack['Payload']['SMB'].v['Command']
|
|
720
|
+
failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
|
|
721
|
+
raise failure
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
# Extract the SecurityBlob from the response
|
|
725
|
+
data = ack['Payload'].v['Payload']
|
|
726
|
+
blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
|
|
727
|
+
|
|
728
|
+
# Extract the native lanman and os strings
|
|
729
|
+
info = data.split(/\x00/)
|
|
730
|
+
self.peer_native_os = info[0]
|
|
731
|
+
self.peer_native_lm = info[1]
|
|
732
|
+
|
|
733
|
+
# Save the temporary UserID for use in the next request
|
|
734
|
+
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
|
735
|
+
|
|
736
|
+
# Extract the NTLM challenge key the lazy way
|
|
737
|
+
cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00")
|
|
738
|
+
|
|
739
|
+
if (cidx == -1)
|
|
740
|
+
raise XCEPT::NTLM2MissingChallenge
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# Store the challenge key
|
|
744
|
+
self.challenge_key = blob[cidx + 24, 8]
|
|
745
|
+
|
|
746
|
+
# Extract the address list from the blob
|
|
747
|
+
alist_len,alist_mlen,alist_off = blob[cidx + 40, 8].unpack("vvV")
|
|
748
|
+
alist_buf = blob[cidx + alist_off, alist_len]
|
|
749
|
+
|
|
750
|
+
while(alist_buf.length > 0)
|
|
751
|
+
atype, alen = alist_buf.slice!(0,4).unpack('vv')
|
|
752
|
+
break if atype == 0x00
|
|
753
|
+
addr = alist_buf.slice!(0, alen)
|
|
754
|
+
case atype
|
|
755
|
+
when 1
|
|
756
|
+
self.default_name = addr.gsub("\x00", '')
|
|
757
|
+
when 2
|
|
758
|
+
self.default_domain = addr.gsub("\x00", '')
|
|
759
|
+
when 3
|
|
760
|
+
self.dns_host_name = addr.gsub("\x00", '')
|
|
761
|
+
when 4
|
|
762
|
+
self.dns_domain_name = addr.gsub("\x00", '')
|
|
763
|
+
when 5
|
|
764
|
+
# unknown
|
|
765
|
+
when 7
|
|
766
|
+
# client time
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
# Generate a random client-side challenge
|
|
772
|
+
client_challenge = Rex::Text.rand_text(8)
|
|
773
|
+
|
|
774
|
+
# Generate the nonce
|
|
775
|
+
nonce = CRYPT.md5_hash(self.challenge_key + client_challenge)
|
|
776
|
+
|
|
777
|
+
# Generate the NTLM hash
|
|
778
|
+
if (pass.length == 65)
|
|
779
|
+
resp_ntlm = CRYPT.e_p24( [ pass.upcase()[33,65] ].pack('H42'), nonce[0, 8])
|
|
780
|
+
else
|
|
781
|
+
resp_ntlm = CRYPT.ntlm_md4(pass, nonce[0, 8])
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Generate the fake LANMAN hash
|
|
785
|
+
resp_lmv2 = client_challenge + ("\x00" * 16)
|
|
786
|
+
|
|
787
|
+
# Create the ntlmv2 security blob data
|
|
788
|
+
blob = UTILS.make_ntlmv2_secblob_auth(domain, name, user, resp_lmv2, resp_ntlm)
|
|
789
|
+
|
|
790
|
+
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
|
791
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
792
|
+
|
|
793
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
794
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
795
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
|
796
|
+
pkt['Payload']['SMB'].v['WordCount'] = 12
|
|
797
|
+
pkt['Payload']['SMB'].v['UserID'] = temp_user_id
|
|
798
|
+
pkt['Payload'].v['AndX'] = 255
|
|
799
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
800
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
801
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
802
|
+
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
|
803
|
+
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
|
804
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
805
|
+
pkt['Payload'].v['Payload'] = blob + native_data
|
|
806
|
+
|
|
807
|
+
self.smb_send(pkt.to_s)
|
|
808
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
|
|
809
|
+
|
|
810
|
+
# Make sure that authentication succeeded
|
|
811
|
+
if (ack['Payload']['SMB'].v['ErrorClass'] != 0)
|
|
812
|
+
if (user.length == 0)
|
|
813
|
+
return self.session_setup_ntlmv1(user, pass, domain)
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
failure = XCEPT::ErrorCode.new
|
|
817
|
+
failure.word_count = ack['Payload']['SMB'].v['WordCount']
|
|
818
|
+
failure.command = ack['Payload']['SMB'].v['Command']
|
|
819
|
+
failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
|
|
820
|
+
raise failure
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
|
|
824
|
+
|
|
825
|
+
if (ack['Payload'].v['Action'] != 1 and user.length > 0)
|
|
826
|
+
self.auth_user = user
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
return ack
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
# An exploit helper function for sending arbitrary SPNEGO blobs
|
|
834
|
+
def session_setup_ntlmv2_blob(blob = '')
|
|
835
|
+
native_data = ''
|
|
836
|
+
native_data << self.native_os + "\x00"
|
|
837
|
+
native_data << self.native_lm + "\x00"
|
|
838
|
+
|
|
839
|
+
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
|
840
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
841
|
+
|
|
842
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
843
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
844
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
|
845
|
+
pkt['Payload']['SMB'].v['WordCount'] = 12
|
|
846
|
+
pkt['Payload']['SMB'].v['UserID'] = 0
|
|
847
|
+
pkt['Payload'].v['AndX'] = 255
|
|
848
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
849
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
850
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
851
|
+
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
|
852
|
+
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
|
853
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
854
|
+
pkt['Payload'].v['Payload'] = blob + native_data
|
|
855
|
+
|
|
856
|
+
self.smb_send(pkt.to_s)
|
|
857
|
+
self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, false)
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
# Authenticate using extended security negotiation (NTLMv2), but stop half-way, using the temporary ID
|
|
862
|
+
def session_setup_ntlmv2_temp(domain = '', name = nil)
|
|
863
|
+
|
|
864
|
+
if (name == nil)
|
|
865
|
+
name = Rex::Text.rand_text_alphanumeric(16)
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
blob = UTILS.make_ntlmv2_secblob_init(domain, name)
|
|
869
|
+
|
|
870
|
+
native_data = ''
|
|
871
|
+
native_data << self.native_os + "\x00"
|
|
872
|
+
native_data << self.native_lm + "\x00"
|
|
873
|
+
|
|
874
|
+
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
|
875
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
876
|
+
|
|
877
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
878
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
879
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
|
880
|
+
pkt['Payload']['SMB'].v['WordCount'] = 12
|
|
881
|
+
pkt['Payload'].v['AndX'] = 255
|
|
882
|
+
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
|
883
|
+
pkt['Payload'].v['MaxMPX'] = 2
|
|
884
|
+
pkt['Payload'].v['VCNum'] = 1
|
|
885
|
+
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
|
886
|
+
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
|
887
|
+
pkt['Payload'].v['SessionKey'] = self.session_id
|
|
888
|
+
pkt['Payload'].v['Payload'] = blob + native_data
|
|
889
|
+
|
|
890
|
+
self.smb_send(pkt.to_s)
|
|
891
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
|
|
892
|
+
|
|
893
|
+
# The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
|
|
894
|
+
if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
|
|
895
|
+
return session_setup_ntlmv1(user, pass, domain)
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
# Make sure the error code tells us to continue processing
|
|
899
|
+
if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
|
|
900
|
+
failure = XCEPT::ErrorCode.new
|
|
901
|
+
failure.word_count = ack['Payload']['SMB'].v['WordCount']
|
|
902
|
+
failure.command = ack['Payload']['SMB'].v['Command']
|
|
903
|
+
failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
|
|
904
|
+
raise failure
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
# Extract the SecurityBlob from the response
|
|
908
|
+
data = ack['Payload'].v['Payload']
|
|
909
|
+
blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
|
|
910
|
+
|
|
911
|
+
# Extract the native lanman and os strings
|
|
912
|
+
info = data.split(/\x00/)
|
|
913
|
+
self.peer_native_os = info[0]
|
|
914
|
+
self.peer_native_lm = info[1]
|
|
915
|
+
|
|
916
|
+
# Save the temporary UserID for use in the next request
|
|
917
|
+
self.auth_user_id = ack['Payload']['SMB'].v['UserID']
|
|
918
|
+
|
|
919
|
+
# Extract the NTLM challenge key the lazy way
|
|
920
|
+
cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00")
|
|
921
|
+
|
|
922
|
+
if (cidx == -1)
|
|
923
|
+
raise XCEPT::NTLM2MissingChallenge
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
# Store the challenge key
|
|
927
|
+
self.challenge_key = blob[cidx + 24, 8]
|
|
928
|
+
|
|
929
|
+
return ack
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
# Connect to a specified share with an optional password
|
|
933
|
+
def tree_connect(share = 'IPC$', pass = '')
|
|
934
|
+
|
|
935
|
+
data = [ pass, share, '?????' ].collect{ |a| a + "\x00" }.join('');
|
|
936
|
+
|
|
937
|
+
pkt = CONST::SMB_TREE_CONN_PKT.make_struct
|
|
938
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
939
|
+
|
|
940
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_CONNECT_ANDX
|
|
941
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
942
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
943
|
+
pkt['Payload']['SMB'].v['WordCount'] = 4
|
|
944
|
+
pkt['Payload'].v['AndX'] = 255
|
|
945
|
+
pkt['Payload'].v['PasswordLen'] = pass.length + 1
|
|
946
|
+
pkt['Payload'].v['Capabilities'] = 64
|
|
947
|
+
pkt['Payload'].v['Payload'] = data
|
|
948
|
+
|
|
949
|
+
self.smb_send(pkt.to_s)
|
|
950
|
+
|
|
951
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_TREE_CONNECT_ANDX)
|
|
952
|
+
|
|
953
|
+
self.last_tree_id = ack['Payload']['SMB'].v['TreeID']
|
|
954
|
+
# why bother?
|
|
955
|
+
# info = ack['Payload'].v['Payload'].split(/\x00/)
|
|
956
|
+
|
|
957
|
+
return ack
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
# Disconnect from the current tree
|
|
961
|
+
def tree_disconnect(tree_id = self.last_tree_id)
|
|
962
|
+
|
|
963
|
+
pkt = CONST::SMB_TREE_DISCONN_PKT.make_struct
|
|
964
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
965
|
+
|
|
966
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_DISCONNECT
|
|
967
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
968
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
969
|
+
pkt['Payload']['SMB'].v['WordCount'] = 0
|
|
970
|
+
pkt['Payload']['SMB'].v['TreeID'] = tree_id
|
|
971
|
+
|
|
972
|
+
self.smb_send(pkt.to_s)
|
|
973
|
+
|
|
974
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_TREE_DISCONNECT)
|
|
975
|
+
|
|
976
|
+
if (tree_id == self.last_tree_id)
|
|
977
|
+
self.last_tree_id = 0
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
return ack
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
# Returns a SMB_CREATE_RES response for a given named pipe
|
|
984
|
+
def create_pipe(filename, disposition = 1, impersonation = 2)
|
|
985
|
+
self.create(filename)
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
# Creates a file or opens an existing pipe
|
|
989
|
+
def create(filename, disposition = 1, impersonation = 2)
|
|
990
|
+
|
|
991
|
+
pkt = CONST::SMB_CREATE_PKT.make_struct
|
|
992
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
993
|
+
|
|
994
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
|
|
995
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
996
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
997
|
+
pkt['Payload']['SMB'].v['WordCount'] = 24
|
|
998
|
+
|
|
999
|
+
pkt['Payload'].v['AndX'] = 255
|
|
1000
|
+
pkt['Payload'].v['FileNameLen'] = filename.length
|
|
1001
|
+
pkt['Payload'].v['CreateFlags'] = 0x16
|
|
1002
|
+
pkt['Payload'].v['AccessMask'] = 0x02000000 # Maximum Allowed
|
|
1003
|
+
pkt['Payload'].v['ShareAccess'] = 7
|
|
1004
|
+
pkt['Payload'].v['CreateOptions'] = 0
|
|
1005
|
+
pkt['Payload'].v['Impersonation'] = impersonation
|
|
1006
|
+
pkt['Payload'].v['Disposition'] = disposition
|
|
1007
|
+
pkt['Payload'].v['Payload'] = filename + "\x00"
|
|
1008
|
+
|
|
1009
|
+
self.smb_send(pkt.to_s)
|
|
1010
|
+
|
|
1011
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_NT_CREATE_ANDX)
|
|
1012
|
+
|
|
1013
|
+
# Save off the FileID
|
|
1014
|
+
if (ack['Payload'].v['FileID'] > 0)
|
|
1015
|
+
self.last_file_id = ack['Payload'].v['FileID']
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
return ack
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
# Deletes a file from a share
|
|
1022
|
+
def delete(filename, tree_id = self.last_tree_id)
|
|
1023
|
+
|
|
1024
|
+
pkt = CONST::SMB_DELETE_PKT.make_struct
|
|
1025
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1026
|
+
|
|
1027
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_DELETE
|
|
1028
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1029
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1030
|
+
pkt['Payload']['SMB'].v['TreeID'] = tree_id
|
|
1031
|
+
pkt['Payload']['SMB'].v['WordCount'] = 1
|
|
1032
|
+
|
|
1033
|
+
pkt['Payload'].v['SearchAttributes'] = 0x06
|
|
1034
|
+
pkt['Payload'].v['BufferFormat'] = 4
|
|
1035
|
+
pkt['Payload'].v['Payload'] = filename + "\x00"
|
|
1036
|
+
|
|
1037
|
+
self.smb_send(pkt.to_s)
|
|
1038
|
+
|
|
1039
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_DELETE)
|
|
1040
|
+
|
|
1041
|
+
return ack
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
# Opens an existing file or creates a new one
|
|
1045
|
+
def open(filename, mode = 0x12, access = 0x42)
|
|
1046
|
+
|
|
1047
|
+
pkt = CONST::SMB_OPEN_PKT.make_struct
|
|
1048
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1049
|
+
|
|
1050
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_OPEN_ANDX
|
|
1051
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1052
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1053
|
+
pkt['Payload']['SMB'].v['WordCount'] = 15
|
|
1054
|
+
|
|
1055
|
+
pkt['Payload'].v['AndX'] = 255
|
|
1056
|
+
pkt['Payload'].v['Access'] = access
|
|
1057
|
+
pkt['Payload'].v['SearchAttributes'] = 0x06
|
|
1058
|
+
pkt['Payload'].v['OpenFunction'] = mode
|
|
1059
|
+
pkt['Payload'].v['Payload'] = filename + "\x00"
|
|
1060
|
+
|
|
1061
|
+
self.smb_send(pkt.to_s)
|
|
1062
|
+
|
|
1063
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_OPEN_ANDX)
|
|
1064
|
+
|
|
1065
|
+
# Save off the FileID
|
|
1066
|
+
if (ack['Payload'].v['FileID'] > 0)
|
|
1067
|
+
self.last_file_id = ack['Payload'].v['FileID']
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
return ack
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
# Closes an open file handle
|
|
1074
|
+
def close(file_id = self.last_file_id, tree_id = self.last_tree_id)
|
|
1075
|
+
|
|
1076
|
+
pkt = CONST::SMB_CLOSE_PKT.make_struct
|
|
1077
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1078
|
+
|
|
1079
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
|
|
1080
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1081
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1082
|
+
pkt['Payload']['SMB'].v['TreeID'] = tree_id
|
|
1083
|
+
pkt['Payload']['SMB'].v['WordCount'] = 3
|
|
1084
|
+
|
|
1085
|
+
pkt['Payload'].v['FileID'] = file_id
|
|
1086
|
+
pkt['Payload'].v['LastWrite'] = -1
|
|
1087
|
+
|
|
1088
|
+
self.smb_send(pkt.to_s)
|
|
1089
|
+
|
|
1090
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_CLOSE)
|
|
1091
|
+
|
|
1092
|
+
return ack
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
# Writes data to an open file handle
|
|
1097
|
+
def write(file_id = self.last_file_id, offset = 0, data = '')
|
|
1098
|
+
|
|
1099
|
+
pkt = CONST::SMB_WRITE_PKT.make_struct
|
|
1100
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1101
|
+
|
|
1102
|
+
data_offset = pkt.to_s.length - 4
|
|
1103
|
+
|
|
1104
|
+
filler = EVADE.make_offset_filler(evasion_opts['pad_data'], 4096 - data.length - data_offset)
|
|
1105
|
+
|
|
1106
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
|
|
1107
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1108
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1109
|
+
pkt['Payload']['SMB'].v['WordCount'] = 14
|
|
1110
|
+
|
|
1111
|
+
pkt['Payload'].v['AndX'] = 255
|
|
1112
|
+
pkt['Payload'].v['FileID'] = file_id
|
|
1113
|
+
pkt['Payload'].v['Offset'] = offset
|
|
1114
|
+
pkt['Payload'].v['Reserved2'] = -1
|
|
1115
|
+
pkt['Payload'].v['WriteMode'] = 8
|
|
1116
|
+
pkt['Payload'].v['Remaining'] = data.length
|
|
1117
|
+
# pkt['Payload'].v['DataLenHigh'] = (data.length / 65536).to_i
|
|
1118
|
+
pkt['Payload'].v['DataLenLow'] = (data.length % 65536).to_i
|
|
1119
|
+
pkt['Payload'].v['DataOffset'] = data_offset + filler.length
|
|
1120
|
+
pkt['Payload'].v['Payload'] = filler + data
|
|
1121
|
+
|
|
1122
|
+
self.smb_send(pkt.to_s)
|
|
1123
|
+
|
|
1124
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_WRITE_ANDX)
|
|
1125
|
+
|
|
1126
|
+
return ack
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
# Reads data from an open file handle
|
|
1131
|
+
def read(file_id = self.last_file_id, offset = 0, data_length = 64000)
|
|
1132
|
+
|
|
1133
|
+
pkt = CONST::SMB_READ_PKT.make_struct
|
|
1134
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1135
|
+
|
|
1136
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
|
|
1137
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1138
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1139
|
+
pkt['Payload']['SMB'].v['WordCount'] = 10
|
|
1140
|
+
|
|
1141
|
+
pkt['Payload'].v['AndX'] = 255
|
|
1142
|
+
pkt['Payload'].v['FileID'] = file_id
|
|
1143
|
+
pkt['Payload'].v['Offset'] = offset
|
|
1144
|
+
# pkt['Payload'].v['MaxCountHigh'] = (data_length / 65536).to_i
|
|
1145
|
+
pkt['Payload'].v['MaxCountLow'] = (data_length % 65536).to_i
|
|
1146
|
+
pkt['Payload'].v['MinCount'] = data_length
|
|
1147
|
+
pkt['Payload'].v['Reserved2'] = -1
|
|
1148
|
+
|
|
1149
|
+
self.smb_send(pkt.to_s)
|
|
1150
|
+
|
|
1151
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_READ_ANDX, true)
|
|
1152
|
+
|
|
1153
|
+
err = ack['Payload']['SMB'].v['ErrorClass']
|
|
1154
|
+
|
|
1155
|
+
# Catch some non-fatal error codes
|
|
1156
|
+
if (err != 0 && err != CONST::SMB_ERROR_BUFFER_OVERFLOW)
|
|
1157
|
+
failure = XCEPT::ErrorCode.new
|
|
1158
|
+
failure.word_count = ack['Payload']['SMB'].v['WordCount']
|
|
1159
|
+
failure.command = ack['Payload']['SMB'].v['Command']
|
|
1160
|
+
failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
|
|
1161
|
+
raise failure
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
return ack
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
# Perform a transaction against a named pipe
|
|
1169
|
+
def trans_named_pipe(file_id, data = '', no_response = nil)
|
|
1170
|
+
pipe = EVADE.make_trans_named_pipe_name(evasion_opts['pad_file'])
|
|
1171
|
+
self.trans(pipe, '', data, 2, [0x26, file_id].pack('vv'), no_response)
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
# Perform a mailslot write over SMB
|
|
1175
|
+
# Warning: This can kill srv.sys unless MS06-035 is applied
|
|
1176
|
+
def trans_mailslot (name, data = '')
|
|
1177
|
+
# Setup data must be:
|
|
1178
|
+
# Operation: 1 (write)
|
|
1179
|
+
# Priority: 0
|
|
1180
|
+
# Class: Reliable
|
|
1181
|
+
self.trans_maxzero(name, '', data, 3, [1, 0, 1].pack('vvv'), true )
|
|
1182
|
+
end
|
|
1183
|
+
|
|
1184
|
+
# Perform a transaction against a given pipe name
|
|
1185
|
+
def trans(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = nil)
|
|
1186
|
+
|
|
1187
|
+
# Null-terminate the pipe parameter if needed
|
|
1188
|
+
if (pipe[-1,1] != "\x00")
|
|
1189
|
+
pipe << "\x00"
|
|
1190
|
+
end
|
|
1191
|
+
|
|
1192
|
+
pkt = CONST::SMB_TRANS_PKT.make_struct
|
|
1193
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1194
|
+
|
|
1195
|
+
# Packets larger than mlen will cause XP SP2 to disconnect us ;-(
|
|
1196
|
+
mlen = 4200
|
|
1197
|
+
|
|
1198
|
+
# Figure out how much space is taken up by our current arguments
|
|
1199
|
+
xlen = pipe.length + param.length + body.length
|
|
1200
|
+
|
|
1201
|
+
filler1 = ''
|
|
1202
|
+
filler2 = ''
|
|
1203
|
+
|
|
1204
|
+
# Fill any available space depending on the evasion settings
|
|
1205
|
+
if (xlen < mlen)
|
|
1206
|
+
filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1207
|
+
filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
# Squish the whole thing together
|
|
1211
|
+
data = pipe + filler1 + param + filler2 + body
|
|
1212
|
+
|
|
1213
|
+
# Throw some form of a warning out?
|
|
1214
|
+
if (data.length > mlen)
|
|
1215
|
+
# XXX This call will more than likely fail :-(
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
# Calculate all of the offsets
|
|
1219
|
+
base_offset = pkt.to_s.length + (setup_count * 2) - 4
|
|
1220
|
+
param_offset = base_offset + pipe.length + filler1.length
|
|
1221
|
+
data_offset = param_offset + filler2.length + param.length
|
|
1222
|
+
|
|
1223
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
|
|
1224
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1225
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1226
|
+
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
|
|
1227
|
+
|
|
1228
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1229
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1230
|
+
pkt['Payload'].v['ParamCountMax'] = 1024
|
|
1231
|
+
pkt['Payload'].v['DataCountMax'] = 65504
|
|
1232
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1233
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1234
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1235
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1236
|
+
pkt['Payload'].v['SetupCount'] = setup_count
|
|
1237
|
+
pkt['Payload'].v['SetupData'] = setup_data
|
|
1238
|
+
|
|
1239
|
+
pkt['Payload'].v['Payload'] = data
|
|
1240
|
+
|
|
1241
|
+
if no_response
|
|
1242
|
+
pkt['Payload'].v['Flags'] = 2
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
response = self.smb_send(pkt.to_s)
|
|
1246
|
+
if no_response
|
|
1247
|
+
return response
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1250
|
+
return self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
# Perform a transaction against a given pipe name
|
|
1256
|
+
# Difference from trans: sets MaxParam/MaxData to zero
|
|
1257
|
+
# This is required to trigger mailslot bug :-(
|
|
1258
|
+
def trans_maxzero(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = nil)
|
|
1259
|
+
|
|
1260
|
+
# Null-terminate the pipe parameter if needed
|
|
1261
|
+
if (pipe[-1] != 0)
|
|
1262
|
+
pipe << "\x00"
|
|
1263
|
+
end
|
|
1264
|
+
|
|
1265
|
+
pkt = CONST::SMB_TRANS_PKT.make_struct
|
|
1266
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1267
|
+
|
|
1268
|
+
# Packets larger than mlen will cause XP SP2 to disconnect us ;-(
|
|
1269
|
+
mlen = 4200
|
|
1270
|
+
|
|
1271
|
+
# Figure out how much space is taken up by our current arguments
|
|
1272
|
+
xlen = pipe.length + param.length + body.length
|
|
1273
|
+
|
|
1274
|
+
filler1 = ''
|
|
1275
|
+
filler2 = ''
|
|
1276
|
+
|
|
1277
|
+
# Fill any available space depending on the evasion settings
|
|
1278
|
+
if (xlen < mlen)
|
|
1279
|
+
filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1280
|
+
filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1281
|
+
end
|
|
1282
|
+
|
|
1283
|
+
# Squish the whole thing together
|
|
1284
|
+
data = pipe + filler1 + param + filler2 + body
|
|
1285
|
+
|
|
1286
|
+
# Throw some form of a warning out?
|
|
1287
|
+
if (data.length > mlen)
|
|
1288
|
+
# XXX This call will more than likely fail :-(
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1291
|
+
# Calculate all of the offsets
|
|
1292
|
+
base_offset = pkt.to_s.length + (setup_count * 2) - 4
|
|
1293
|
+
param_offset = base_offset + pipe.length + filler1.length
|
|
1294
|
+
data_offset = param_offset + filler2.length + param.length
|
|
1295
|
+
|
|
1296
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
|
|
1297
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1298
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1299
|
+
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
|
|
1300
|
+
|
|
1301
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1302
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1303
|
+
pkt['Payload'].v['ParamCountMax'] = 0
|
|
1304
|
+
pkt['Payload'].v['DataCountMax'] = 0
|
|
1305
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1306
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1307
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1308
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1309
|
+
pkt['Payload'].v['SetupCount'] = setup_count
|
|
1310
|
+
pkt['Payload'].v['SetupData'] = setup_data
|
|
1311
|
+
|
|
1312
|
+
pkt['Payload'].v['Payload'] = data
|
|
1313
|
+
|
|
1314
|
+
if no_response
|
|
1315
|
+
pkt['Payload'].v['Flags'] = 2
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
response = self.smb_send(pkt.to_s)
|
|
1319
|
+
if no_response
|
|
1320
|
+
return response
|
|
1321
|
+
end
|
|
1322
|
+
|
|
1323
|
+
return self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
# Perform a transaction against a given pipe name (no null terminator)
|
|
1328
|
+
def trans_nonull(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = nil)
|
|
1329
|
+
|
|
1330
|
+
pkt = CONST::SMB_TRANS_PKT.make_struct
|
|
1331
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1332
|
+
|
|
1333
|
+
# Packets larger than mlen will cause XP SP2 to disconnect us ;-(
|
|
1334
|
+
mlen = 4200
|
|
1335
|
+
|
|
1336
|
+
# Figure out how much space is taken up by our current arguments
|
|
1337
|
+
xlen = pipe.length + param.length + body.length
|
|
1338
|
+
|
|
1339
|
+
filler1 = ''
|
|
1340
|
+
filler2 = ''
|
|
1341
|
+
|
|
1342
|
+
# Fill any available space depending on the evasion settings
|
|
1343
|
+
if (xlen < mlen)
|
|
1344
|
+
filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1345
|
+
filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
|
|
1346
|
+
end
|
|
1347
|
+
|
|
1348
|
+
# Squish the whole thing together
|
|
1349
|
+
data = pipe + filler1 + param + filler2 + body
|
|
1350
|
+
|
|
1351
|
+
# Throw some form of a warning out?
|
|
1352
|
+
if (data.length > mlen)
|
|
1353
|
+
# XXX This call will more than likely fail :-(
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1356
|
+
# Calculate all of the offsets
|
|
1357
|
+
base_offset = pkt.to_s.length + (setup_count * 2) - 4
|
|
1358
|
+
param_offset = base_offset + pipe.length + filler1.length
|
|
1359
|
+
data_offset = param_offset + filler2.length + param.length
|
|
1360
|
+
|
|
1361
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
|
|
1362
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1363
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1364
|
+
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
|
|
1365
|
+
|
|
1366
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1367
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1368
|
+
pkt['Payload'].v['ParamCountMax'] = 0
|
|
1369
|
+
pkt['Payload'].v['DataCountMax'] = 0
|
|
1370
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1371
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1372
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1373
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1374
|
+
pkt['Payload'].v['SetupCount'] = setup_count
|
|
1375
|
+
pkt['Payload'].v['SetupData'] = setup_data
|
|
1376
|
+
|
|
1377
|
+
pkt['Payload'].v['Payload'] = data
|
|
1378
|
+
|
|
1379
|
+
if no_response
|
|
1380
|
+
pkt['Payload'].v['Flags'] = 2
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1383
|
+
response = self.smb_send(pkt.to_s)
|
|
1384
|
+
if no_response
|
|
1385
|
+
return response
|
|
1386
|
+
end
|
|
1387
|
+
|
|
1388
|
+
return self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
|
|
1389
|
+
end
|
|
1390
|
+
|
|
1391
|
+
# Perform a transaction2 request using the specified subcommand, parameters, and data
|
|
1392
|
+
def trans2(subcommand, param = '', body = '')
|
|
1393
|
+
|
|
1394
|
+
setup_count = 1
|
|
1395
|
+
setup_data = [subcommand].pack('v')
|
|
1396
|
+
|
|
1397
|
+
data = param + body
|
|
1398
|
+
|
|
1399
|
+
pkt = CONST::SMB_TRANS2_PKT.make_struct
|
|
1400
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1401
|
+
|
|
1402
|
+
base_offset = pkt.to_s.length + (setup_count * 2) - 4
|
|
1403
|
+
param_offset = base_offset
|
|
1404
|
+
data_offset = param_offset + param.length
|
|
1405
|
+
|
|
1406
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
|
1407
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1408
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1409
|
+
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
|
|
1410
|
+
|
|
1411
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1412
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1413
|
+
pkt['Payload'].v['ParamCountMax'] = 1024
|
|
1414
|
+
pkt['Payload'].v['DataCountMax'] = 65504
|
|
1415
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1416
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1417
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1418
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1419
|
+
pkt['Payload'].v['SetupCount'] = setup_count
|
|
1420
|
+
pkt['Payload'].v['SetupData'] = setup_data
|
|
1421
|
+
|
|
1422
|
+
|
|
1423
|
+
pkt['Payload'].v['Payload'] = data
|
|
1424
|
+
|
|
1425
|
+
self.smb_send(pkt.to_s)
|
|
1426
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_TRANSACTION2)
|
|
1427
|
+
|
|
1428
|
+
return ack
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
# Perform a nttransaction request using the specified subcommand, parameters, and data
|
|
1433
|
+
def nttrans(subcommand, param = '', body = '', setup_count = 0, setup_data = '')
|
|
1434
|
+
|
|
1435
|
+
data = param + body
|
|
1436
|
+
|
|
1437
|
+
pkt = CONST::SMB_NTTRANS_PKT.make_struct
|
|
1438
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1439
|
+
|
|
1440
|
+
base_offset = pkt.to_s.length + (setup_count * 2) - 4
|
|
1441
|
+
param_offset = base_offset
|
|
1442
|
+
data_offset = param_offset + param.length
|
|
1443
|
+
|
|
1444
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT
|
|
1445
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1446
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1447
|
+
pkt['Payload']['SMB'].v['WordCount'] = 19 + setup_count
|
|
1448
|
+
|
|
1449
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1450
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1451
|
+
pkt['Payload'].v['ParamCountMax'] = 1024
|
|
1452
|
+
pkt['Payload'].v['DataCountMax'] = 65504
|
|
1453
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1454
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1455
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1456
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1457
|
+
pkt['Payload'].v['SetupCount'] = setup_count
|
|
1458
|
+
pkt['Payload'].v['SetupData'] = setup_data
|
|
1459
|
+
pkt['Payload'].v['Subcommand'] = subcommand
|
|
1460
|
+
|
|
1461
|
+
pkt['Payload'].v['Payload'] = data
|
|
1462
|
+
|
|
1463
|
+
self.smb_send(pkt.to_s)
|
|
1464
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT)
|
|
1465
|
+
return ack
|
|
1466
|
+
end
|
|
1467
|
+
|
|
1468
|
+
# Perform a nttransaction request using the specified subcommand, parameters, and data
|
|
1469
|
+
def nttrans_secondary(param = '', body = '')
|
|
1470
|
+
|
|
1471
|
+
data = param + body
|
|
1472
|
+
|
|
1473
|
+
pkt = CONST::SMB_NTTRANS_SECONDARY_PKT.make_struct
|
|
1474
|
+
self.smb_defaults(pkt['Payload']['SMB'])
|
|
1475
|
+
|
|
1476
|
+
base_offset = pkt.to_s.length - 4
|
|
1477
|
+
param_offset = base_offset
|
|
1478
|
+
data_offset = param_offset + param.length
|
|
1479
|
+
|
|
1480
|
+
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT_SECONDARY
|
|
1481
|
+
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
|
1482
|
+
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
|
1483
|
+
pkt['Payload']['SMB'].v['WordCount'] = 18
|
|
1484
|
+
|
|
1485
|
+
pkt['Payload'].v['ParamCountTotal'] = param.length
|
|
1486
|
+
pkt['Payload'].v['DataCountTotal'] = body.length
|
|
1487
|
+
pkt['Payload'].v['ParamCount'] = param.length
|
|
1488
|
+
pkt['Payload'].v['ParamOffset'] = param_offset
|
|
1489
|
+
pkt['Payload'].v['DataCount'] = body.length
|
|
1490
|
+
pkt['Payload'].v['DataOffset'] = data_offset
|
|
1491
|
+
|
|
1492
|
+
pkt['Payload'].v['Payload'] = data
|
|
1493
|
+
|
|
1494
|
+
self.smb_send(pkt.to_s)
|
|
1495
|
+
ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT_SECONDARY)
|
|
1496
|
+
return ack
|
|
1497
|
+
end
|
|
1498
|
+
|
|
1499
|
+
def queryfs(level)
|
|
1500
|
+
parm = [level].pack('v')
|
|
1501
|
+
|
|
1502
|
+
begin
|
|
1503
|
+
resp = trans2(CONST::TRANS2_QUERY_FS_INFO, parm, '')
|
|
1504
|
+
|
|
1505
|
+
pcnt = resp['Payload'].v['ParamCount']
|
|
1506
|
+
dcnt = resp['Payload'].v['DataCount']
|
|
1507
|
+
poff = resp['Payload'].v['ParamOffset']
|
|
1508
|
+
doff = resp['Payload'].v['DataOffset']
|
|
1509
|
+
|
|
1510
|
+
# Get the raw packet bytes
|
|
1511
|
+
resp_rpkt = resp.to_s
|
|
1512
|
+
|
|
1513
|
+
# Remove the NetBIOS header
|
|
1514
|
+
resp_rpkt.slice!(0, 4)
|
|
1515
|
+
|
|
1516
|
+
resp_parm = resp_rpkt[poff, pcnt]
|
|
1517
|
+
resp_data = resp_rpkt[doff, dcnt]
|
|
1518
|
+
return resp_data
|
|
1519
|
+
|
|
1520
|
+
rescue ::Exception
|
|
1521
|
+
raise $!
|
|
1522
|
+
end
|
|
1523
|
+
end
|
|
1524
|
+
|
|
1525
|
+
def symlink(src,dst)
|
|
1526
|
+
parm = [513, 0x00000000].pack('vV') + src + "\x00"
|
|
1527
|
+
|
|
1528
|
+
begin
|
|
1529
|
+
resp = trans2(CONST::TRANS2_SET_PATH_INFO, parm, dst + "\x00")
|
|
1530
|
+
|
|
1531
|
+
pcnt = resp['Payload'].v['ParamCount']
|
|
1532
|
+
dcnt = resp['Payload'].v['DataCount']
|
|
1533
|
+
poff = resp['Payload'].v['ParamOffset']
|
|
1534
|
+
doff = resp['Payload'].v['DataOffset']
|
|
1535
|
+
|
|
1536
|
+
# Get the raw packet bytes
|
|
1537
|
+
resp_rpkt = resp.to_s
|
|
1538
|
+
|
|
1539
|
+
# Remove the NetBIOS header
|
|
1540
|
+
resp_rpkt.slice!(0, 4)
|
|
1541
|
+
|
|
1542
|
+
resp_parm = resp_rpkt[poff, pcnt]
|
|
1543
|
+
resp_data = resp_rpkt[doff, dcnt]
|
|
1544
|
+
return resp_data
|
|
1545
|
+
|
|
1546
|
+
rescue ::Exception
|
|
1547
|
+
raise $!
|
|
1548
|
+
end
|
|
1549
|
+
end
|
|
1550
|
+
|
|
1551
|
+
# Obtains allocation information on the mounted tree
|
|
1552
|
+
def queryfs_info_allocation
|
|
1553
|
+
data = queryfs(CONST::SMB_INFO_ALLOCATION)
|
|
1554
|
+
head = %w{fs_id sectors_per_unit unit_total units_available bytes_per_sector}
|
|
1555
|
+
vals = data.unpack('VVVVv')
|
|
1556
|
+
info = { }
|
|
1557
|
+
head.each_index {|i| info[head[i]]=vals[i]}
|
|
1558
|
+
return info
|
|
1559
|
+
end
|
|
1560
|
+
|
|
1561
|
+
# Obtains volume information on the mounted tree
|
|
1562
|
+
def queryfs_info_volume
|
|
1563
|
+
data = queryfs(CONST::SMB_INFO_VOLUME)
|
|
1564
|
+
vals = data.unpack('VCA*')
|
|
1565
|
+
return {
|
|
1566
|
+
'serial' => vals[0],
|
|
1567
|
+
'label' => vals[2][0,vals[1]].gsub("\x00", '')
|
|
1568
|
+
}
|
|
1569
|
+
end
|
|
1570
|
+
|
|
1571
|
+
# Obtains file system volume information on the mounted tree
|
|
1572
|
+
def queryfs_fs_volume
|
|
1573
|
+
data = queryfs(CONST::SMB_QUERY_FS_VOLUME_INFO)
|
|
1574
|
+
vals = data.unpack('VVVVCCA*')
|
|
1575
|
+
return {
|
|
1576
|
+
'create_time' => (vals[1] << 32) + vals[0],
|
|
1577
|
+
'serial' => vals[2],
|
|
1578
|
+
'label' => vals[6][0,vals[3]].gsub("\x00", '')
|
|
1579
|
+
}
|
|
1580
|
+
end
|
|
1581
|
+
|
|
1582
|
+
# Obtains file system size information on the mounted tree
|
|
1583
|
+
def queryfs_fs_size
|
|
1584
|
+
data = queryfs(CONST::SMB_QUERY_FS_SIZE_INFO)
|
|
1585
|
+
vals = data.unpack('VVVVVV')
|
|
1586
|
+
return {
|
|
1587
|
+
'total_alloc_units' => (vals[1] << 32) + vals[0],
|
|
1588
|
+
'total_free_units' => (vals[3] << 32) + vals[2],
|
|
1589
|
+
'sectors_per_unit' => vals[4],
|
|
1590
|
+
'bytes_per_sector' => vals[5]
|
|
1591
|
+
}
|
|
1592
|
+
end
|
|
1593
|
+
|
|
1594
|
+
# Obtains file system device information on the mounted tree
|
|
1595
|
+
def queryfs_fs_device
|
|
1596
|
+
data = queryfs(CONST::SMB_QUERY_FS_DEVICE_INFO)
|
|
1597
|
+
vals = data.unpack('VV')
|
|
1598
|
+
return {
|
|
1599
|
+
'device_type' => vals[0],
|
|
1600
|
+
'device_chars' => vals[1],
|
|
1601
|
+
}
|
|
1602
|
+
end
|
|
1603
|
+
|
|
1604
|
+
# Obtains file system attribute information on the mounted tree
|
|
1605
|
+
def queryfs_fs_attribute
|
|
1606
|
+
data = queryfs(CONST::SMB_QUERY_FS_ATTRIBUTE_INFO)
|
|
1607
|
+
vals = data.unpack('VVVA*')
|
|
1608
|
+
return {
|
|
1609
|
+
'fs_attributes' => vals[0],
|
|
1610
|
+
'max_file_name' => vals[1],
|
|
1611
|
+
'fs_name' => vals[3][0, vals[2]].gsub("\x00", '')
|
|
1612
|
+
}
|
|
1613
|
+
end
|
|
1614
|
+
|
|
1615
|
+
# Enumerates a specific path on the mounted tree
|
|
1616
|
+
def find_first(path)
|
|
1617
|
+
files = { }
|
|
1618
|
+
parm = [
|
|
1619
|
+
26, # Search for ALL files
|
|
1620
|
+
512, # Maximum search count
|
|
1621
|
+
6, # Resume and Close on End of Search
|
|
1622
|
+
260, # Level of interest
|
|
1623
|
+
0, # Storage type is zero
|
|
1624
|
+
].pack('vvvvV') + path + "\x00"
|
|
1625
|
+
|
|
1626
|
+
begin
|
|
1627
|
+
resp = trans2(CONST::TRANS2_FIND_FIRST2, parm, '')
|
|
1628
|
+
|
|
1629
|
+
pcnt = resp['Payload'].v['ParamCount']
|
|
1630
|
+
dcnt = resp['Payload'].v['DataCount']
|
|
1631
|
+
poff = resp['Payload'].v['ParamOffset']
|
|
1632
|
+
doff = resp['Payload'].v['DataOffset']
|
|
1633
|
+
|
|
1634
|
+
# Get the raw packet bytes
|
|
1635
|
+
resp_rpkt = resp.to_s
|
|
1636
|
+
|
|
1637
|
+
# Remove the NetBIOS header
|
|
1638
|
+
resp_rpkt.slice!(0, 4)
|
|
1639
|
+
|
|
1640
|
+
resp_parm = resp_rpkt[poff, pcnt]
|
|
1641
|
+
resp_data = resp_rpkt[doff, dcnt]
|
|
1642
|
+
|
|
1643
|
+
# search id, search count, end of search, error offset, last name offset
|
|
1644
|
+
sid, scnt, eos, eoff, loff = resp_parm.unpack('v5')
|
|
1645
|
+
|
|
1646
|
+
didx = 0
|
|
1647
|
+
while (didx < resp_data.length)
|
|
1648
|
+
info_buff = resp_data[didx, 70]
|
|
1649
|
+
break if info_buff.length != 70
|
|
1650
|
+
info = info_buff.unpack(
|
|
1651
|
+
'V'+ # Next Entry Offset
|
|
1652
|
+
'V'+ # File Index
|
|
1653
|
+
'VV'+ # Time Create
|
|
1654
|
+
'VV'+ # Time Last Access
|
|
1655
|
+
'VV'+ # Time Last Write
|
|
1656
|
+
'VV'+ # Time Change
|
|
1657
|
+
'VV'+ # End of File
|
|
1658
|
+
'VV'+ # Allocation Size
|
|
1659
|
+
'V'+ # File Attributes
|
|
1660
|
+
'V'+ # File Name Length
|
|
1661
|
+
'V'+ # Extended Attr List Length
|
|
1662
|
+
'C'+ # Short File Name Length
|
|
1663
|
+
'C' # Reserved
|
|
1664
|
+
)
|
|
1665
|
+
name = resp_data[didx + 70 + 24, info[15]].sub!(/\x00+$/, '')
|
|
1666
|
+
files[name] =
|
|
1667
|
+
{
|
|
1668
|
+
'type' => (info[14] & 0x10) ? 'D' : 'F',
|
|
1669
|
+
'attr' => info[14],
|
|
1670
|
+
'info' => info
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
break if info[0] == 0
|
|
1674
|
+
didx += info[0]
|
|
1675
|
+
end
|
|
1676
|
+
|
|
1677
|
+
last_search_id = sid
|
|
1678
|
+
|
|
1679
|
+
rescue ::Exception
|
|
1680
|
+
raise $!
|
|
1681
|
+
end
|
|
1682
|
+
|
|
1683
|
+
return files
|
|
1684
|
+
end
|
|
1685
|
+
|
|
1686
|
+
# TODO: Finish this method... requires search_id, resume_key, and filename from first
|
|
1687
|
+
=begin
|
|
1688
|
+
def find_next(path, sid = last_search_id)
|
|
1689
|
+
|
|
1690
|
+
parm = [
|
|
1691
|
+
sid, # Search ID
|
|
1692
|
+
512, # Maximum search count
|
|
1693
|
+
260, # Level of interest
|
|
1694
|
+
0, # Resume key from previous
|
|
1695
|
+
1, # Close search if end of search
|
|
1696
|
+
].pack('vvvVv') + path + "\x00"
|
|
1697
|
+
|
|
1698
|
+
return files
|
|
1699
|
+
end
|
|
1700
|
+
=end
|
|
1701
|
+
|
|
1702
|
+
# Creates a new directory on the mounted tree
|
|
1703
|
+
def create_directory(name)
|
|
1704
|
+
files = { }
|
|
1705
|
+
parm = [0].pack('V') + name + "\x00"
|
|
1706
|
+
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
# public read/write methods
|
|
1710
|
+
attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security, :read_timeout, :evasion_opts
|
|
1711
|
+
|
|
1712
|
+
# public read methods
|
|
1713
|
+
attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
|
|
1714
|
+
attr_reader :default_domain, :default_name, :auth_user, :auth_user_id
|
|
1715
|
+
attr_reader :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
|
|
1716
|
+
attr_reader :dns_host_name, :dns_domain_name
|
|
1717
|
+
attr_reader :security_mode, :server_guid
|
|
1718
|
+
|
|
1719
|
+
# private methods
|
|
1720
|
+
attr_writer :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
|
|
1721
|
+
attr_writer :default_domain, :default_name, :auth_user, :auth_user_id
|
|
1722
|
+
attr_writer :dns_host_name, :dns_domain_name
|
|
1723
|
+
attr_writer :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
|
|
1724
|
+
attr_writer :security_mode, :server_guid
|
|
1725
|
+
|
|
1726
|
+
attr_accessor :socket
|
|
1727
|
+
|
|
1728
|
+
|
|
1729
|
+
end
|
|
1730
|
+
end
|
|
1731
|
+
end
|
|
1732
|
+
end
|
|
1733
|
+
|