From 998c3fd8b255c097739bcc2cd1649cfd71dae9a0 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:36:37 +1000 Subject: [PATCH 1/7] Added config option that Closes #1354 --- config.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.yaml b/config.yaml index f6018fc30..66540425a 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) From 9065fc9514c3234d2d1eb292c041a498cb8697c8 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:38:06 +1000 Subject: [PATCH 2/7] Check request IP before resolving the Admin UI --- extensions/admin_ui/classes/httpcontroller.rb | 67 +++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) 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 = '' From 35876694b0872953457aa69373403e023c7f2b93 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:40:29 +1000 Subject: [PATCH 3/7] Determines the correct IP if reverse proxy enabled --- .../admin_ui/controllers/authentication/authentication.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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.") From efad3c3d23d91c965f767a3cb2cf04305e3522e9 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:42:35 +1000 Subject: [PATCH 4/7] Made 404 error responses constants to use with UI --- core/main/router/error_responses.rb | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 core/main/router/error_responses.rb 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 From 1c4ef22947ec16261c495a50b0d3d633c135a37c Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:43:51 +1000 Subject: [PATCH 5/7] Changed references to constants to modify easily --- core/main/router/router.rb | 65 ++++---------------------------------- 1 file changed, 6 insertions(+), 59 deletions(-) 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" From a2de71c151e8f0f354bae9aa8ce03f4fb374d6f7 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:44:51 +1000 Subject: [PATCH 6/7] Added router/error_responses --- core/bootstrap.rb | 1 + 1 file changed, 1 insertion(+) 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 From 28b5eef7793a477c1cca064a4731b61da869eb36 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Fri, 10 Jan 2020 14:45:14 +1000 Subject: [PATCH 7/7] Tests to cover Admin UI access --- spec/beef/extensions/adminui_spec.rb | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 spec/beef/extensions/adminui_spec.rb 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