In this lab, we will demonstrate the basics of applied information security.
In this lab you will learn how to
ist346-labs
PS > cd ist346-labs
PS ist346-labs> $Env:COMPOSE_CONVERT_WINDOWS_PATHS=1
PS ist346-labs> git pull origin master
lab-J
folder:PS ist346-labs> cd lab-J
The first line of defense for any system is a firewall on the host. Firewalls limit traffic to ports defined by the system administrator. So while the services might be running, the firewall might contain a rule which blocks certain hosts from accessing the service. Potentially, this stops malicious users from accessing the system through ports opened by our services. On Linux many system administrators utilize IPTables to create a firewall. Due to the complexity and flexibility of Iptables we will be using a simplified version called UFW or Uncomplicated Firewall. UFW really does live up to its name it is much easier than IPTables.
Lets begin by bring up our first docker environment. It a network consisting of a simple webserver, nginx
and two clients. On client is legitimate and the other is a hacker!
+--------------+
| client |---+
| 172.44.10.103| |
+--------------+ |
|
+--------------+ |
| hacker |---+
| 172.44.10.104| |
+--------------+ |
| +---------------+ ++++++++++++++
+---| host computer |-----| Internet |
| +---------------+ ++++++++++++++
+--------------+ |
| webserver |---+
| 172.44.10.102|
+--------------+
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml up -d
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml ps
client
, hacker
, and webserver
containers running, all in the Up
status.PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml logs webserver
webserver_1
if you do not, wait a minute and try again.PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml exec webserver bash
root@webserver:/# ufw default deny
root@webserver:/# ufw enable
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml logs webserver
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml exec client bash
root@client:/#
command prompt, use the curl
command to attempt to access the website. curl
is a command line web browser of sorts, allow us to issue HTTP requests from the command line. Try this:
root@client:/# curl -m 3 http://webserver
This command attempts to connect to our webserver on the well-known port of TCP/80, timing out after 3 seconds. You get this error message:
curl: (28) Connection timed out a 3000 milliseconds.
root@webserver:/#
let’s create the rule:
root@webserver:/# ufw allow 80
root@webserver:/# ufw reload
root@client:/#
command prompt, and try our curl command again:
root@client:/# curl -m 3 http://webserver
root@client:/# exit
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml logs webserver
172.44.1.104 - - [15/Oct/2018:14:50:34 +0000] "POST /admin HTTP/1.1" 404 153 "-" "curl/7.47.0" "-"
172.44.10.104
, which is visible at the beginning of the line on the logs. Lets block that person by inserting a new rule at position number 1, UFW evaluates rules in order, so we need to make sure our block rule is before the allow all rule for port 80. The status command shows the current rules. Back at the root@webserver:/#
prompt, type:
root@webserver:/# ufw insert 1 deny from 172.44.10.104 to any
root@webserver:/# ufw reload
root@webserver:/# ufw status numbered
You should now see this output:Status: active
To Action From
-- ------ ----
[ 1] Anywhere DENY IN 172.44.1.104
[ 2] 80 ALLOW IN Anywhere
[ 3] 80 (v6) ALLOW IN Anywhere (v6)
PS C:\ist346\Lab-J> docker-compose -f ufw-example.yml logs webserver
Let’s clean up from this part:
root@webserver:/#
Type:root@webserver:/# exit
PS C:\ist346\Lab-J> docker-compose -f .\ufw-example.yml down
Firewalls on the host are just one layer of security. Another thing to consider is disabling the individual features of a service that we do not require as part of our application. This is referred to as service hardening. When you do this to every service on the server, its server hardening.
One of the most used and exposed services on a server is the webserver, it’s the most used service on the Internet and is utilized by almost every organization. Being so popular and widely used means, webservers are popular attack targets primary But regardless of the webserver they all need to be configured to protect against attacks.
Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications. XSS enables attackers to inject client-side scripts into web pages viewed by other users. A cross-site scripting vulnerability may be used by attackers to bypass access controls such as the same-origin policy. XSS attacks are performed by an attacker inputting a script into a form field on a website such as a comment field on a blog or forum.
These types of attacks can be mitigated by the webserver, by configuring it to informing the client browser to not download resources from external sources that you do not control.
Let’s spin up the lab:
PS C:\ist346\Lab-J> docker-compose -f vulnerable-website.yml up -d
Two webservices are started, one is our website named webserver
which is available to host computer on port 80
and the other is a server that a malicious user controls called badsite
available to the host on port 8080
. Normally these would be different domains, but for this example they will use different ports.
+-------------+
| badsite:8080|---+
+-------------+ |
| +---------------+ ++++++++++++++
+---| host computer |-----| Internet |
| +---------------+ ++++++++++++++
+-------------+ |
| webserver:80|---+
+-------------+
Open your browser and go to the demo site. http://localhost
You will see the message: Your site has been hacked!
The malicious user has compromised your site by loading an external script in a comment box! The origin of the script is from the site http://localhost:8080. Is that website the hacker’s website? If they are a smart hacker, no. More than likely the hacker has hacked that site too and it using badsite
to launch attacks.
This hacker’s script only displays a message, but in reality the hacker could capture and collect any data you share with the legitimate site!
Lets fix our webserver so these attacks do not effect our good users. We are also going to add some other security measures that help harden our webserver.
PS C:\ist346\Lab-J> docker-compose -f vulnerable-website.yml exec webserver bash
root@webserver:/# nano /etc/nginx/conf.d/default.conf
default.conf
file. Paste it below the line: server_name localhost;
# don't send the nginx version number in error pages and Server header
server_tokens off;
# config to don't allow the browser to render the page inside an frame or iframe
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
# this particular website if it was disabled by the user.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";
# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
# you can tell the browser that it can only download content from the domains you explicitly allow in our example we are allowing google analytics.
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com";
default.conf
file should look like the one below:
server {
listen 80;
server_name localhost;
# don't send the nginx version number in error pages and Server header
server_tokens off;
# config to don't allow the browser to render the page inside an frame or iframe
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
# this particular website if it was disabled by the user.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";
# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
# you can tell the browser that it can only download content from the domains you explicitly allow in our example we are allowing google analytics.
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com";
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
CTRL
+x
and selecting Y
to saving over the modified butter, overwriting the current file.root@webserver:/# nginx -s reload
2018/10/16 10:45:59 [notice] 28#28: signal process started
NOTE: If you get an error you may have to edit the contents of default.conf
to fix the error.
NOTE: If you still see the error message, it is because the hacker’s malicious script is still in your browser cache. You should clear your browser’s history to remove the script from the cache.
This is just the beginning of what needs to be done to harden a webserver, check the NGINX documentation for other recommended security practices.
https://docs.nginx.com/nginx/admin-guide/security-controls/
root@webserver:/# exit
PS C:\ist346\Lab-J> docker-compose -f vulnerable-website.yml down
Key pairs allow you to login to a remote host via SSH (Secure Shell) without using your password. This is generally seen as a more secure practice than using a password because users have a tendency to use password that are too simple or to use the same password on multiple hosts. In this part of the lab we will demonstrate how to set this up.
PS C:\ist346\Lab-J> docker-compose -f ssh-keys.yml up -d
PS C:\ist346\Lab-J> docker-compose -f ssh-keys.yml ps
ssh-client
and ssh-server
should be Up
ssh-server
:PS C:\ist346\Lab-J> docker-compose -f ssh-keys.yml exec ssh-server bash
root@ssh-server:/#
prompt. Let’s create a new user called myuser
: root@ssh-server:/# adduser myuser
myuser
to testing123
and making up values for the rest of the prompts.ssh-server
as myuser
for this we need to open another powershell window and make Lab-J
the current directory.ssh-client
container:
PS C:\ist346\Lab-J> docker-compose -f ssh-keys.yml exec ssh-client bash
root@ssh-client:/#
prompt, let’s try to ssh
into the server:root@ssh-client:/# ssh myuser@ssh-server
yes
and press ENTER
.
The authenticity of host 'ssh-server (172.20.0.2)' can't be established.
ECDSA key fingerprint is SHA256:iV9FBVXcbAK0fFuM2ecIOuExbnpP1apuejmsNXuPcvk.
Are you sure you want to continue connecting (yes/no)? yes
testing123
:Warning: Permanently added 'ssh-server,172.20.0.2' (ECDSA) to the list of known hosts.
myuser@ssh-server's password:
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.9.93-linuxkit-aufs x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
myuser@ssh-server:~$
myuser@ssh-server:~$ exit
ssh-server
and return to the client.Now that we have a user account and we know it can log in to ssh-server
its time to generate a keypair so that we can login without using the password. When you generate a key pair, one is public the other is private. The private key is the important one and must be kept safe. How does this work? An analogy with real locks and keys is the private key creates unique padlocks every time which can only be unlocked with the public key.
We must keep the private key safe for if a hacker gets a copy of the private key she too can create unique padlocks on other systems which will be naively unlocked with any system that has the public key. At this point your encryption is useless!
ssh-client
:root@ssh-client:/# ssh-keygen -t rsa
ENTER
three times. We don’t want a password to login! The output:Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:qzIS1bPB+Yf+nnNHgXmbp8N00C6W+cMpVj4ox3dU6/Q root@ssh-client
The key's randomart image is:
+---[RSA 2048]----+
| |
| |
| o . o . |
| . * o + o|
| . =S. . Oo|
| . . o.. X+=|
| . ... .=BBo|
| . o .. .o.*=OE|
| . o. o+o+.o.+|
+----[SHA256]-----+
.ssh
directory on the server from the client: root@ssh-client:/# ssh myuser@ssh-server mkdir -p .ssh
testing123
to execute this command on the remote server.authorized_keys
, type:root@ssh-client:/# cat /root/.ssh/id_rsa.pub | ssh myuser@ssh-server 'cat >> .ssh/authorized_keys'
testing123
password./home/myuser/.ssh
, type:
root@ssh-server:/# ls /home/myuser/.ssh
authorized_keys
root@ssh-server:/# cat /home/myuser/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuYmc6toOzLNga95izpfQBNhn3psUoYFpVa4wHPDBwLMQchsp3DjXdVDbBn6clpZB0zCA8M28gKSqKPutc1KAXjDVqoC8/GYE9hlnwMmLgICuEzMSPZYuKQNrzQoQ+o47hviQIJkJEXcd+DsuDm8E7Rjw00DxRG0/ohClhJ57
WxHiSYjMB+Tw/0pYLQ0d6kuoI7ClHWaYhxMhLFoN8Qlw66Qy/F7LIDOaWPBM6wJWuB9dV40x+Ehi77JYtExa+RvOUpvlDvZWYVPtJWGxicVt5mWBqV8BJgvMQQhn0f0uFmPml5VqAERM7Hju3b4X+zm5yOveliqJRPDcU4TfLRAD root@ssh-client
authorized_keys
file so only the myuser
account user can access it. Password-less ssh will work without this step, but its definately more secure when you do it:root@ssh-server:# chmod 700 /home/myuser/.ssh; chmod 640 /home/myuser/.ssh/authorized_keys
ssh-client
try:root@ssh-client:/# ssh myuser@ssh-server
8. From the `myuser@ssh-server:~$` prompt, return to the client:
`myuser@ssh-server:~$ exit`
logout Connection to ssh-server closed.
9. Where this is really useful is in scenairios where you need to execute remote commands on the server from the client. For example, let's get the IP address of the server:
`root@ssh-client:/# ssh myuser@ssh-server ifconfig eth0`
You should see this output:
eth0 Link encap:Ethernet HWaddr 02:42:ac:1a:00:02 inet addr:172.26.0.2 Bcast:172.26.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:414 errors:0 dropped:0 overruns:0 frame:0 TX packets:302 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:67067 (67.0 KB) TX bytes:68943 (68.9 KB) ```
root@ssh-client:/# exit
root@ssh-server:/# exit
docker-compose -f ufw-example.yml exec webserver bash
What is the command we are executing? What is the host name?nginx
for example so important to being able to administer and secure that service?cat /etc/passwd
as user hacker
on server government
?