Skip to content

Commit 5de127a

Browse files
authored
Merge pull request #1845 from beefproject/fix_broken_admin_ui_access
Fix broken admin ui access
2 parents f5de5eb + 28b5eef commit 5de127a

File tree

7 files changed

+192
-65
lines changed

7 files changed

+192
-65
lines changed

config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ beef:
5151
# Reverse Proxy / NAT
5252
# If you want BeEF to be accessible behind a reverse proxy or NAT,
5353
# set both the publicly accessible hostname/IP address and port below:
54+
# NOTE: Allowing the reverse proxy will enable a vulnerability where the ui/panel can be spoofed
55+
# by altering the X-FORWARDED-FOR ip address in the request header.
56+
allow_reverse_proxy: false
5457
#public: "" # public hostname/IP address
5558
#public_port: "" # public port (experimental)
5659

core/bootstrap.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Core
1212
## @note Include the BeEF router
1313
require 'core/main/router/router'
1414
require 'core/main/router/api'
15+
require 'core/main/router/error_responses'
1516

1617

1718
## @note Include http server functions for beef
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
module BeEF
2+
module Core
3+
module Router
4+
5+
config = BeEF::Core::Configuration.instance
6+
7+
APACHE_HEADER = { "Server" => "Apache/2.2.3 (CentOS)",
8+
"Content-Type" => "text/html; charset=UTF-8" }
9+
APACHE_BODY = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" +
10+
"<html><head>" +
11+
"<title>404 Not Found</title>" +
12+
"</head><body>" +
13+
"<h1>Not Found</h1>" +
14+
"<p>The requested URL was not found on this server.</p>" +
15+
"<hr>" +
16+
"<address>Apache/2.2.3 (CentOS)</address>" +
17+
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
18+
"</body></html>"
19+
IIS_HEADER = {"Server" => "Microsoft-IIS/6.0",
20+
"X-Powered-By" => "ASP.NET",
21+
"Content-Type" => "text/html; charset=UTF-8"}
22+
IIS_BODY = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" +
23+
"<HTML><HEAD><TITLE>The page cannot be found</TITLE>" +
24+
"<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=Windows-1252\">" +
25+
"<STYLE type=\"text/css\">" +
26+
" BODY { font: 8pt/12pt verdana } " +
27+
" H1 { font: 13pt/15pt verdana }" +
28+
" H2 { font: 8pt/12pt verdana }" +
29+
" A:link { color: red }" +
30+
" A:visited { color: maroon }" +
31+
"</STYLE>" +
32+
"</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>" +
33+
"<h1>The page cannot be found</h1>" +
34+
"The page you are looking for might have been removed, had its name changed, or is temporarily unavailable." +
35+
"<hr>" +
36+
"<p>Please try the following:</p>" +
37+
"<ul>" +
38+
"<li>Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.</li>" +
39+
"<li>If you reached this page by clicking a link, contact" +
40+
" the Web site administrator to alert them that the link is incorrectly formatted." +
41+
"</li>" +
42+
"<li>Click the <a href=\"javascript:history.back(1)\">Back</a> button to try another link.</li>" +
43+
"</ul>" +
44+
"<h2>HTTP Error 404 - File or directory not found.<br>Internet Information Services (IIS)</h2>" +
45+
"<hr>" +
46+
"<p>Technical Information (for support personnel)</p>" +
47+
"<ul>" +
48+
"<li>Go to <a href=\"http://go.microsoft.com/fwlink/?linkid=8180\">Microsoft Product Support Services</a> and perform a title search for the words <b>HTTP</b> and <b>404</b>.</li>" +
49+
"<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr)," +
50+
"and search for topics titled <b>Web Site Setup</b>, <b>Common Administrative Tasks</b>, and <b>About Custom Error Messages</b>.</li>" +
51+
"</ul>" +
52+
"</TD></TR></TABLE>" +
53+
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
54+
"</BODY></HTML>"
55+
NGINX_HEADER = {"Server" => "nginx",
56+
"Content-Type" => "text/html"}
57+
NGINX_BODY = "<html>\n"+
58+
"<head><title>404 Not Found</title></head>\n" +
59+
"<body bgcolor=\"white\">\n" +
60+
"<center><h1>404 Not Found</h1></center>\n" +
61+
"<hr><center>nginx</center>\n" +
62+
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
63+
"</body>\n" +
64+
"</html>\n"
65+
66+
end
67+
end
68+
end

core/main/router/router.rb

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -24,61 +24,13 @@ class Router < Sinatra::Base
2424
case type
2525
when "apache"
2626
#response body
27-
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" +
28-
"<html><head>" +
29-
"<title>404 Not Found</title>" +
30-
"</head><body>" +
31-
"<h1>Not Found</h1>" +
32-
"<p>The requested URL was not found on this server.</p>" +
33-
"<hr>" +
34-
"<address>Apache/2.2.3 (CentOS)</address>" +
35-
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
36-
"</body></html>"
27+
BeEF::Core::Router::APACHE_BODY
3728
when "iis"
3829
#response body
39-
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" +
40-
"<HTML><HEAD><TITLE>The page cannot be found</TITLE>" +
41-
"<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=Windows-1252\">" +
42-
"<STYLE type=\"text/css\">" +
43-
" BODY { font: 8pt/12pt verdana } " +
44-
" H1 { font: 13pt/15pt verdana }" +
45-
" H2 { font: 8pt/12pt verdana }" +
46-
" A:link { color: red }" +
47-
" A:visited { color: maroon }" +
48-
"</STYLE>" +
49-
"</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>" +
50-
"<h1>The page cannot be found</h1>" +
51-
"The page you are looking for might have been removed, had its name changed, or is temporarily unavailable." +
52-
"<hr>" +
53-
"<p>Please try the following:</p>" +
54-
"<ul>" +
55-
"<li>Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.</li>" +
56-
"<li>If you reached this page by clicking a link, contact" +
57-
" the Web site administrator to alert them that the link is incorrectly formatted." +
58-
"</li>" +
59-
"<li>Click the <a href=\"javascript:history.back(1)\">Back</a> button to try another link.</li>" +
60-
"</ul>" +
61-
"<h2>HTTP Error 404 - File or directory not found.<br>Internet Information Services (IIS)</h2>" +
62-
"<hr>" +
63-
"<p>Technical Information (for support personnel)</p>" +
64-
"<ul>" +
65-
"<li>Go to <a href=\"http://go.microsoft.com/fwlink/?linkid=8180\">Microsoft Product Support Services</a> and perform a title search for the words <b>HTTP</b> and <b>404</b>.</li>" +
66-
"<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr)," +
67-
"and search for topics titled <b>Web Site Setup</b>, <b>Common Administrative Tasks</b>, and <b>About Custom Error Messages</b>.</li>" +
68-
"</ul>" +
69-
"</TD></TR></TABLE>" +
70-
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
71-
"</BODY></HTML>"
30+
BeEF::Core::Router::IIS_BODY
7231
when "nginx"
7332
#response body
74-
"<html>\n"+
75-
"<head><title>404 Not Found</title></head>\n" +
76-
"<body bgcolor=\"white\">\n" +
77-
"<center><h1>404 Not Found</h1></center>\n" +
78-
"<hr><center>nginx</center>\n" +
79-
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
80-
"</body>\n" +
81-
"</html>\n"
33+
BeEF::Core::Router::NGINX_BODY
8234
else
8335
"Not Found."
8436
end
@@ -93,16 +45,11 @@ class Router < Sinatra::Base
9345
type = config.get("beef.http.web_server_imitation.type")
9446
case type
9547
when "apache"
96-
headers "Server" => "Apache/2.2.3 (CentOS)",
97-
"Content-Type" => "text/html; charset=UTF-8"
98-
48+
headers BeEF::Core::Router::APACHE_HEADER
9949
when "iis"
100-
headers "Server" => "Microsoft-IIS/6.0",
101-
"X-Powered-By" => "ASP.NET",
102-
"Content-Type" => "text/html; charset=UTF-8"
50+
headers BeEF::Core::Router::IIS_HEADER
10351
when "nginx"
104-
headers "Server" => "nginx",
105-
"Content-Type" => "text/html"
52+
headers BeEF::Core::Router::NGINX_HEADER
10653
else
10754
headers "Server" => '',
10855
"Content-Type" => "text/html"

extensions/admin_ui/classes/httpcontroller.rb

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module AdminUI
1010
#
1111
# Handle HTTP requests and call the relevant functions in the derived classes
1212
#
13-
class HttpController
13+
class HttpController
1414

1515
attr_accessor :headers, :status, :body, :paths, :currentuser, :params
1616

@@ -26,8 +26,8 @@ def initialize(data = {})
2626
@status = 200 if data['status'].nil?
2727
@session = BeEF::Extension::AdminUI::Session.instance
2828

29-
config = BeEF::Core::Configuration.instance
30-
@bp = config.get "beef.extension.admin_ui.base_path"
29+
@config = BeEF::Core::Configuration.instance
30+
@bp = @config.get "beef.extension.admin_ui.base_path"
3131

3232
@headers = {'Content-Type' => 'text/html; charset=UTF-8'} if data['headers'].nil?
3333

@@ -37,6 +37,60 @@ def initialize(data = {})
3737
@paths = data['paths']
3838
end
3939
end
40+
41+
#
42+
# Authentication check. Confirm the request to access the UI comes from a permitted IP address
43+
#
44+
def authenticate_request(ip)
45+
auth = BeEF::Extension::AdminUI::Controllers::Authentication.new
46+
if !auth.permitted_source?(ip)
47+
if @config.get("beef.http.web_server_imitation.enable")
48+
type = @config.get("beef.http.web_server_imitation.type")
49+
case type
50+
when "apache"
51+
@body = BeEF::Core::Router::APACHE_BODY
52+
@status = 404
53+
@headers = BeEF::Core::Router::APACHE_HEADER
54+
return false
55+
when "iis"
56+
@body = BeEF::Core::Router::IIS_BODY
57+
@status = 404
58+
@headers = BeEF::Core::Router::IIS_HEADER
59+
return false
60+
when "nginx"
61+
@body = BeEF::Core::Router::APACHE_BODY
62+
@status = 404
63+
@headers = BeEF::Core::Router::APACHE_HEADER
64+
return false
65+
else
66+
@body = "Not Found."
67+
@status = 404
68+
@headers = {"Content-Type" => "text/html"}
69+
return false
70+
end
71+
else
72+
@body = "Not Found."
73+
@status = 404
74+
@headers = {"Content-Type" => "text/html"}
75+
return false
76+
end
77+
else
78+
return true
79+
end
80+
end
81+
82+
#
83+
# Check if reverse proxy has been enabled and return the correct client IP address
84+
#
85+
def get_ip(request)
86+
if !@config.get("beef.http.allow_reverse_proxy")
87+
ua_ip = request.get_header('REMOTE_ADDR') # Get client remote ip address
88+
else
89+
ua_ip = request.ip # Get client x-forwarded-for ip address
90+
end
91+
ua_ip
92+
end
93+
4094

4195
#
4296
# Handle HTTP requests and call the relevant functions in the derived classes
@@ -47,7 +101,12 @@ def run(request, response)
47101

48102
# Web UI base path, like http://beef_domain/<bp>/panel
49103
auth_url = "#{@bp}/authentication"
50-
104+
105+
# If access to the UI is not permitted for the request IP address return a 404
106+
if !authenticate_request(get_ip(@request))
107+
return
108+
end
109+
51110
# test if session is unauth'd and whether the auth functionality is requested
52111
if not @session.valid_session?(@request) and not self.class.eql?(BeEF::Extension::AdminUI::Controllers::Authentication)
53112
@body = ''

extensions/admin_ui/controllers/authentication/authentication.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,12 @@ def login
4444
config = BeEF::Core::Configuration.instance
4545
@headers['Content-Type']='application/json; charset=UTF-8'
4646
@headers['X-Frame-Options']='sameorigin'
47-
ua_ip = @request.ip # get client ip address
47+
if !config.get("beef.http.allow_reverse_proxy")
48+
ua_ip = @request.get_header('REMOTE_ADDR')
49+
else
50+
ua_ip = @request.ip # get client ip address
51+
end
4852
@body = '{ success : false }' # attempt to fail closed
49-
5053
# check if source IP address is permitted to authenticate
5154
if not permitted_source?(ua_ip)
5255
BeEF::Core::Logger.instance.register('Authentication', "IP source address (#{@request.ip}) attempted to authenticate but is not within permitted subnet.")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#
2+
# Tests for handling access to the Admin UI
3+
#
4+
5+
require 'extensions/admin_ui/classes/httpcontroller'
6+
require 'extensions/admin_ui/controllers/authentication/authentication'
7+
8+
RSpec.describe 'BeEF Extension AdminUI' do
9+
before(:all) do
10+
@config = BeEF::Core::Configuration.instance
11+
end
12+
13+
it 'loads configuration' do
14+
expect(@config.get('beef.restrictions')).to have_key('permitted_ui_subnet')
15+
end
16+
17+
it 'confirms that any ip address is permitted to view the admin ui' do
18+
ui = BeEF::Extension::AdminUI::HttpController.new
19+
expect(@config.set('beef.restrictions.permitted_ui_subnet',["0.0.0.0/0", "::/0"])).to eq true
20+
expect(ui.authenticate_request("8.8.8.8")).to eq true
21+
end
22+
23+
it 'confirms that an ip address is permitted to view the admin ui' do
24+
ui = BeEF::Extension::AdminUI::HttpController.new
25+
expect(@config.set('beef.restrictions.permitted_ui_subnet',["192.168.10.1"])).to eq true
26+
expect(ui.authenticate_request("192.168.10.1")).to eq true
27+
end
28+
29+
it 'confirms that an ip address is not permitted to view the admin ui' do
30+
ui = BeEF::Extension::AdminUI::HttpController.new
31+
expect(@config.set('beef.restrictions.permitted_ui_subnet',["10.10.10.1"])).to eq true
32+
expect(ui.authenticate_request("8.8.8.8")).to eq false
33+
end
34+
35+
it 'confirms that X-Forwarded-For cant be spoofed when reverse proxy is disabled' do
36+
ui = BeEF::Extension::AdminUI::HttpController.new
37+
expect(@config.set('beef.restrictions.permitted_ui_subnet',["192.168.0.10"])).to eq true
38+
expect(@config.set('beef.http.allow_reverse_proxy',false)).to eq true
39+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/ui/authentication" }
40+
request = Rack::Request.new(env)
41+
request.add_header("HTTP_X_FORWARDED_FOR","192.168.0.10")
42+
request.add_header("REMOTE_ADDR","192.168.0.20")
43+
expect(ui.get_ip(request)).to eq "192.168.0.20"
44+
end
45+
46+
end

0 commit comments

Comments
 (0)