Stored
Low
This type of XSS involves malicious code being stored on the server and displayed to users when they access a specific page. At the first level we encounter a guestbook, where the relevant PHP code handles the user input and prepares it to be stored in a database. When you enter it, we observe that the system prints directly what has been entered in the name and message field.
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
By parsing the code the message and name are "sanitized" using mysqli_real_escape_string()
, which is a suitable function to protect against SQL injection but not necessarily against XSS. However, the stripslashes function is used to remove the slashes from a string with escaped quotes. This may not be sufficient to prevent a persistent XSS attack.
For this reason, we start again with the same tests as in the reflection, entering the following malicious code in the comment field:
<script>alert("low")</script>
This code will allow us to assess whether a persistent XSS vulnerability exists and determine whether we can execute malicious code in the application. When entering this code in the message field and sending it, the web page displays an alert with the message "low", then it is clear that the application is vulnerable to Cross-Site Scripting attacks. This is because the user input is not being properly validated or sanitized before being rendered on the web page.
Medium
At this level, we face greater complexity when trying to execute an XSS attack stored in the application. At the middle level, we note that the initial stored XSS vulnerability is no longer executed using the traditional method due to a mitigation measure implemented in the server's PHP code.
<script>alert("medium")</script>
Although our initial strategy was unsuccessful, we continued to explore new techniques to overcome the security measures implemented on the server.
We found that there is a limitation in the name field, restricted to 10 characters, which complicated the possibility of injecting a longer or more complex script.
A possible solution involves adjusting this constraint by modifying the maxlength attribute of the input field. Al cambiar este valor a uno mayor, como 100 caracteres, podemos permitir la entrada de una cantidad significativamente mayor de texto.
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
The proposed solution involves adjusting this constraint by modifying the maxlength attribute of the input field. By changing this value to a larger value, such as 100 characters, we can allow significantly more text to be entered. In addition, if the constraint is also present in the database, we can extend the size of the corresponding field to ensure that there are no additional constraints beyond those we have set in the frontend.
<SCRIPT>alert("medium")</SCRIPT>
And now we have our prize.
In situations where the input field limitation is controlled by JavaScript and the code is obfuscated, it can be tricky to find the validation point to bypass it. In such cases, we can take advantage of tools such as OWASP ZAP to intercept and modify requests before they reach the server, allowing us to effectively bypass them.
To use OWASP ZAP request interception, simply open the browser controlled from within OWASP ZAP by clicking on the browser icon, as shown in the following image highlighted in the red box.
From the browser that opens, we go to the website we need to intercept and activate the interception by clicking on the green icon, which we see highlighted in the image with the blue box.
After having entered the texts "title" and "message" in the corresponding input fields, we have the opportunity to modify these texts according to our needs before sending the request to the server. Once we have made the desired modifications, we simply click on the "Forward" button to send the request to the server.
The modified request will contain the new values we have entered in the "title" and "message" fields, allowing us to exploit any persistent XSS vulnerabilities that exist in the application. This process provides us with the ability to manipulate the data sent to the server and ultimately execute our desired XSS code.
This approach allows us to test and exploit persistent XSS vulnerabilities in a controlled manner, which helps us to better understand the potential security implications and develop effective mitigation strategies.
High
At the high level of our XSS analysis, we again apply the approach of starting from the simplest to the most complex attacks. We repeat the process used in the middle level, this time with the OWASP ZAP tool to take full advantage of its advanced request interception and modification capabilities.
In tackling the high level of our XSS analysis, we encountered an unexpected obstacle. Looking at the output of the application, we noticed a difference in the way the name and message fields are handled. When analyzing the relevant source code, we discovered that, while one of the fields is being properly sanitized, the other has a vulnerability identical to the one we found in the previously mentioned levels.
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
In this case we are going to do it with img. We are putting it in both because we don't really know the code that is running in the background.
And now we have achieved our XSS.
This discrepancy gives us a new opportunity to try to exploit the persistent XSS vulnerability using the techniques we have used in previous levels. By having a deeper understanding of the inner workings of the application and the security measures in place, we can repeat any of the attacks that proved effective in the past.
This process of continuous feedback and learning is fundamental in the field of computer security. By facing unexpected challenges and adapting to new circumstances, we strengthen our skills and develop more effective strategies to identify and mitigate vulnerabilities in web applications.