diff --git a/config.yaml b/config.yaml
index 528a8f751..ce6449269 100644
--- a/config.yaml
+++ b/config.yaml
@@ -51,6 +51,9 @@ beef:
# Reverse Proxy / NAT
# If you want BeEF to be accessible behind a reverse proxy or NAT,
# set both the publicly accessible hostname/IP address and port below:
+ # NOTE: Allowing the reverse proxy will enable a vulnerability where the ui/panel can be spoofed
+ # by altering the X-FORWARDED-FOR ip address in the request header.
+ allow_reverse_proxy: false
#public: "" # public hostname/IP address
#public_port: "" # public port (experimental)
diff --git a/core/bootstrap.rb b/core/bootstrap.rb
index 094213a7b..46bc79ab0 100644
--- a/core/bootstrap.rb
+++ b/core/bootstrap.rb
@@ -12,6 +12,7 @@ end
## @note Include the BeEF router
require 'core/main/router/router'
require 'core/main/router/api'
+require 'core/main/router/error_responses'
## @note Include http server functions for beef
diff --git a/core/main/router/error_responses.rb b/core/main/router/error_responses.rb
new file mode 100644
index 000000000..584e5ef80
--- /dev/null
+++ b/core/main/router/error_responses.rb
@@ -0,0 +1,68 @@
+module BeEF
+ module Core
+ module Router
+
+ config = BeEF::Core::Configuration.instance
+
+ APACHE_HEADER = { "Server" => "Apache/2.2.3 (CentOS)",
+ "Content-Type" => "text/html; charset=UTF-8" }
+ APACHE_BODY = "" +
+ "
" +
+ "404 Not Found" +
+ "" +
+ "Not Found
" +
+ "The requested URL was not found on this server.
" +
+ "
" +
+ "Apache/2.2.3 (CentOS)" +
+ ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
+ ""
+ IIS_HEADER = {"Server" => "Microsoft-IIS/6.0",
+ "X-Powered-By" => "ASP.NET",
+ "Content-Type" => "text/html; charset=UTF-8"}
+ IIS_BODY = "" +
+ "The page cannot be found" +
+ "" +
+ "" +
+ "" +
+ "The page cannot be found" +
+ "The page you are looking for might have been removed, had its name changed, or is temporarily unavailable." +
+ " " +
+ "Please try the following: " +
+ "" +
+ "- Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.
" +
+ "- If you reached this page by clicking a link, contact" +
+ " the Web site administrator to alert them that the link is incorrectly formatted." +
+ "
" +
+ "- Click the Back button to try another link.
" +
+ " " +
+ "HTTP Error 404 - File or directory not found. Internet Information Services (IIS)" +
+ " " +
+ "Technical Information (for support personnel) " +
+ "" +
+ "- Go to Microsoft Product Support Services and perform a title search for the words HTTP and 404.
" +
+ "- Open IIS Help, which is accessible in IIS Manager (inetmgr)," +
+ "and search for topics titled Web Site Setup, Common Administrative Tasks, and About Custom Error Messages.
" +
+ " " +
+ " |
" +
+ ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
+ ""
+ NGINX_HEADER = {"Server" => "nginx",
+ "Content-Type" => "text/html"}
+ NGINX_BODY = "\n"+
+ "404 Not Found\n" +
+ "\n" +
+ "404 Not Found
\n" +
+ "
nginx\n" +
+ ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
+ "\n" +
+ "\n"
+
+ end
+ end
+end
diff --git a/core/main/router/router.rb b/core/main/router/router.rb
index 7ea91f611..b092a5f48 100644
--- a/core/main/router/router.rb
+++ b/core/main/router/router.rb
@@ -24,61 +24,13 @@ module BeEF
case type
when "apache"
#response body
- "" +
- "" +
- "404 Not Found" +
- "" +
- "Not Found
" +
- "The requested URL was not found on this server.
" +
- "
" +
- "Apache/2.2.3 (CentOS)" +
- ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
- ""
+ BeEF::Core::Router::APACHE_BODY
when "iis"
#response body
- "" +
- "The page cannot be found" +
- "" +
- "" +
- "" +
- "The page cannot be found" +
- "The page you are looking for might have been removed, had its name changed, or is temporarily unavailable." +
- " " +
- "Please try the following: " +
- "" +
- "- Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.
" +
- "- If you reached this page by clicking a link, contact" +
- " the Web site administrator to alert them that the link is incorrectly formatted." +
- "
" +
- "- Click the Back button to try another link.
" +
- " " +
- "HTTP Error 404 - File or directory not found. Internet Information Services (IIS)" +
- " " +
- "Technical Information (for support personnel) " +
- "" +
- "- Go to Microsoft Product Support Services and perform a title search for the words HTTP and 404.
" +
- "- Open IIS Help, which is accessible in IIS Manager (inetmgr)," +
- "and search for topics titled Web Site Setup, Common Administrative Tasks, and About Custom Error Messages.
" +
- " " +
- " |
" +
- ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
- ""
+ BeEF::Core::Router::IIS_BODY
when "nginx"
#response body
- "\n"+
- "404 Not Found\n" +
- "\n" +
- "404 Not Found
\n" +
- "
nginx\n" +
- ("" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
- "\n" +
- "\n"
+ BeEF::Core::Router::NGINX_BODY
else
"Not Found."
end
@@ -93,16 +45,11 @@ module BeEF
type = config.get("beef.http.web_server_imitation.type")
case type
when "apache"
- headers "Server" => "Apache/2.2.3 (CentOS)",
- "Content-Type" => "text/html; charset=UTF-8"
-
+ headers BeEF::Core::Router::APACHE_HEADER
when "iis"
- headers "Server" => "Microsoft-IIS/6.0",
- "X-Powered-By" => "ASP.NET",
- "Content-Type" => "text/html; charset=UTF-8"
+ headers BeEF::Core::Router::IIS_HEADER
when "nginx"
- headers "Server" => "nginx",
- "Content-Type" => "text/html"
+ headers BeEF::Core::Router::NGINX_HEADER
else
headers "Server" => '',
"Content-Type" => "text/html"
diff --git a/extensions/admin_ui/classes/httpcontroller.rb b/extensions/admin_ui/classes/httpcontroller.rb
index d826b9d37..09b41db6c 100644
--- a/extensions/admin_ui/classes/httpcontroller.rb
+++ b/extensions/admin_ui/classes/httpcontroller.rb
@@ -10,7 +10,7 @@ module AdminUI
#
# Handle HTTP requests and call the relevant functions in the derived classes
#
- class HttpController
+ class HttpController
attr_accessor :headers, :status, :body, :paths, :currentuser, :params
@@ -26,8 +26,8 @@ module AdminUI
@status = 200 if data['status'].nil?
@session = BeEF::Extension::AdminUI::Session.instance
- config = BeEF::Core::Configuration.instance
- @bp = config.get "beef.extension.admin_ui.base_path"
+ @config = BeEF::Core::Configuration.instance
+ @bp = @config.get "beef.extension.admin_ui.base_path"
@headers = {'Content-Type' => 'text/html; charset=UTF-8'} if data['headers'].nil?
@@ -37,6 +37,60 @@ module AdminUI
@paths = data['paths']
end
end
+
+ #
+ # Authentication check. Confirm the request to access the UI comes from a permitted IP address
+ #
+ def authenticate_request(ip)
+ auth = BeEF::Extension::AdminUI::Controllers::Authentication.new
+ if !auth.permitted_source?(ip)
+ if @config.get("beef.http.web_server_imitation.enable")
+ type = @config.get("beef.http.web_server_imitation.type")
+ case type
+ when "apache"
+ @body = BeEF::Core::Router::APACHE_BODY
+ @status = 404
+ @headers = BeEF::Core::Router::APACHE_HEADER
+ return false
+ when "iis"
+ @body = BeEF::Core::Router::IIS_BODY
+ @status = 404
+ @headers = BeEF::Core::Router::IIS_HEADER
+ return false
+ when "nginx"
+ @body = BeEF::Core::Router::APACHE_BODY
+ @status = 404
+ @headers = BeEF::Core::Router::APACHE_HEADER
+ return false
+ else
+ @body = "Not Found."
+ @status = 404
+ @headers = {"Content-Type" => "text/html"}
+ return false
+ end
+ else
+ @body = "Not Found."
+ @status = 404
+ @headers = {"Content-Type" => "text/html"}
+ return false
+ end
+ else
+ return true
+ end
+ end
+
+ #
+ # Check if reverse proxy has been enabled and return the correct client IP address
+ #
+ def get_ip(request)
+ if !@config.get("beef.http.allow_reverse_proxy")
+ ua_ip = request.get_header('REMOTE_ADDR') # Get client remote ip address
+ else
+ ua_ip = request.ip # Get client x-forwarded-for ip address
+ end
+ ua_ip
+ end
+
#
# Handle HTTP requests and call the relevant functions in the derived classes
@@ -47,7 +101,12 @@ module AdminUI
# Web UI base path, like http://beef_domain//panel
auth_url = "#{@bp}/authentication"
-
+
+ # If access to the UI is not permitted for the request IP address return a 404
+ if !authenticate_request(get_ip(@request))
+ return
+ end
+
# test if session is unauth'd and whether the auth functionality is requested
if not @session.valid_session?(@request) and not self.class.eql?(BeEF::Extension::AdminUI::Controllers::Authentication)
@body = ''
diff --git a/extensions/admin_ui/controllers/authentication/authentication.rb b/extensions/admin_ui/controllers/authentication/authentication.rb
index 6fde20ed5..66c9065e5 100644
--- a/extensions/admin_ui/controllers/authentication/authentication.rb
+++ b/extensions/admin_ui/controllers/authentication/authentication.rb
@@ -44,9 +44,12 @@ class Authentication < BeEF::Extension::AdminUI::HttpController
config = BeEF::Core::Configuration.instance
@headers['Content-Type']='application/json; charset=UTF-8'
@headers['X-Frame-Options']='sameorigin'
- ua_ip = @request.ip # get client ip address
+ if !config.get("beef.http.allow_reverse_proxy")
+ ua_ip = @request.get_header('REMOTE_ADDR')
+ else
+ ua_ip = @request.ip # get client ip address
+ end
@body = '{ success : false }' # attempt to fail closed
-
# check if source IP address is permitted to authenticate
if not permitted_source?(ua_ip)
BeEF::Core::Logger.instance.register('Authentication', "IP source address (#{@request.ip}) attempted to authenticate but is not within permitted subnet.")
diff --git a/spec/beef/extensions/adminui_spec.rb b/spec/beef/extensions/adminui_spec.rb
new file mode 100644
index 000000000..72ec66057
--- /dev/null
+++ b/spec/beef/extensions/adminui_spec.rb
@@ -0,0 +1,46 @@
+#
+# Tests for handling access to the Admin UI
+#
+
+require 'extensions/admin_ui/classes/httpcontroller'
+require 'extensions/admin_ui/controllers/authentication/authentication'
+
+RSpec.describe 'BeEF Extension AdminUI' do
+ before(:all) do
+ @config = BeEF::Core::Configuration.instance
+ end
+
+ it 'loads configuration' do
+ expect(@config.get('beef.restrictions')).to have_key('permitted_ui_subnet')
+ end
+
+ it 'confirms that any ip address is permitted to view the admin ui' do
+ ui = BeEF::Extension::AdminUI::HttpController.new
+ expect(@config.set('beef.restrictions.permitted_ui_subnet',["0.0.0.0/0", "::/0"])).to eq true
+ expect(ui.authenticate_request("8.8.8.8")).to eq true
+ end
+
+ it 'confirms that an ip address is permitted to view the admin ui' do
+ ui = BeEF::Extension::AdminUI::HttpController.new
+ expect(@config.set('beef.restrictions.permitted_ui_subnet',["192.168.10.1"])).to eq true
+ expect(ui.authenticate_request("192.168.10.1")).to eq true
+ end
+
+ it 'confirms that an ip address is not permitted to view the admin ui' do
+ ui = BeEF::Extension::AdminUI::HttpController.new
+ expect(@config.set('beef.restrictions.permitted_ui_subnet',["10.10.10.1"])).to eq true
+ expect(ui.authenticate_request("8.8.8.8")).to eq false
+ end
+
+ it 'confirms that X-Forwarded-For cant be spoofed when reverse proxy is disabled' do
+ ui = BeEF::Extension::AdminUI::HttpController.new
+ expect(@config.set('beef.restrictions.permitted_ui_subnet',["192.168.0.10"])).to eq true
+ expect(@config.set('beef.http.allow_reverse_proxy',false)).to eq true
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/ui/authentication" }
+ request = Rack::Request.new(env)
+ request.add_header("HTTP_X_FORWARDED_FOR","192.168.0.10")
+ request.add_header("REMOTE_ADDR","192.168.0.20")
+ expect(ui.get_ip(request)).to eq "192.168.0.20"
+ end
+
+end
\ No newline at end of file