前言:在之前的一篇关于JAVA SQL注入测试 文章中记录了一些JAVA SQL注入代码审计及测试过程及注意点,近来回头看觉得那仅仅只能作为一个普通场景,漏掉了很多的特殊场景,因此又进一步进行了学习,现将相关笔记内容整理如下。
一、Mybatis 注入审计常见流程:
1、选择.xml文件,全局搜索${
2、从${逆向找到Controller思路(大体思路,实际作为参考):
.xml中的id——>mapper(DAO)——>serviceImpl——>Controller层
技巧:利用Free MyBatis Tool插件在xml文件id与mapper之间进行快速跳转,后续使用Ctrl+鼠标左键跳转,找到Controller层
3、正向查找
Controller——>调用函数——>mapper(DAO)——>.xml中的id——>SQL语句
有时候会有中间多层封装
二、JDBC注入
1、order by排序中存在的SQL注入
Order by后面可以跟字段名,也可以跟列名。
@RequestMapping("/order")
public String order(@RequestParam String key) throws SQLException {
StringBuilder result = new StringBuilder();
Connection connection = getConnection.getCon();
String sql = "select * from user order by " + key + " asc;";
POC:
报错注入:
key=id and(updatexml(1,concat(0x7e,(select database())),0))
基于时间的延迟注入:
key=id AND (SELECT 9315 FROM (SELECT(SLEEP(5)))SVTn)
修复方法:不能使用预编译的方式进行修复,使用预编译后,输入被当作字符串处理了,不能当作字段名,不能进行正确的排序。
正确的方法:对key进行过滤,示例代码如下:
@RequestMapping("/order")
public String order(@RequestParam String key) throws SQLException {
StringBuilder result = new StringBuilder();
Connection connection = getConnection.getCon();
//对用户输入进行过滤
String newkey = keyFilter.orderByFilter(key);
String sql = "select * from user order by ? asc ;";
System.out.println(sql);
过滤器代码示例:
public class keyFilter {
public static String orderByFilter(String key){
String orderbyField;
switch (key){
case "id":
orderbyField = "id";break;
case "username":
orderbyField = "username";break;
default:
orderbyField = "id";
}
return orderbyField;
}
}
2、like语句模糊查询
Like语句直接给出修复的代码示例:
//修复方式一:在setString的时候进行拼接
String sql = "select * from user where username like ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"%"+user+"%");
ResultSet resultSet = preparedStatement.executeQuery();
//修复方式二:在SQL语句中使用concat()函数
String sql = "select * from user where username like concat('%',?,'%')";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,user);
ResultSet resultSet = preparedStatement.executeQuery();
3、In查询语句
In查询语句用于配合where进行条件查询,如下:
修复方法:
String sql = "select * from user where username in (?,?);";
System.out.println(sql);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,key1);
preparedStatement.setString(2,key2);
ResultSet resultSet = preparedStatement.executeQuery();
三、MyBatis特殊场景注入
1、普通查询,如根据id进行查询
1)存在注入的情况:
Mapper.xml配置:
<select parameterType="string" resultType="org.example.entity.User">
select * from user where id = ${id}
</select>
Controller代码示例:
@RequestMapping("/id")
public String getListById(@RequestParam String id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(id);
return user.toString();
}
访问测试:
2)代码修复:
使用#{}替换${},如下:
<select parameterType="string" resultType="org.example.entity.User">
select * from user where id = #{id}
</select>
2、like语句查询
1)存在注入的情况:
Mapper.xml配置:
<select parameterType="string" resultType="org.example.entity.User">
select * from user where username like '%${user}%'
</select>
Controller代码示例:
@RequestMapping("/like")
public String like(@RequestParam String user){
StringBuilder result = new StringBuilder();
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userlist = userMapper.getUserLike(user);
for (User u : userlist) {
System.out.println(u);
result.append(u);
}
return result.toString();
}
访问:
2)代码修复:
Mapper.xml配置:
<select parameterType="string" resultType="org.example.entity.User">
select * from user where username like concat('%',#{user},'%')
</select>
注意:不能直接写为select * from user where username like '%#{user}%',正常访问时都会报错。
3、order by语句查询
1)存在注入的情况:
Mapper.xml配置:
<select resultType="org.example.entity.User">
select * from user order by ${key}
</select>
Controller代码示例:
@RequestMapping("/orderby")
public String orderBy(@RequestParam String key){
StringBuilder result = new StringBuilder();
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserOrderBy(key);
for (User u : userList) {
System.out.println(u);
result.append(u);
}
return result.toString();
}
访问:
2)代码修复:
修复方式同JDBC场景下使用order by的方式,将用户输入参数进行过滤,仅对特定的字段进行排序处理,此处不再进行赘述。
4、in 语句查询
1)存在注入的情况:
接口层的编写注意事项:多个参数传入,按照下面的写法,否则会报找不到绑定的参数
List<User> getUserIn(@Param("key1")String key1, @Param("key2")String key2);
Mapper.xml配置:
<select resultType="org.example.entity.User">
select * from user where username in ('${key1}','${key2}')
</select>
Controller代码示例:
@RequestMapping("/in")
public String in(@Param("key1")String key1, @Param("key2")String key2){
StringBuilder result = new StringBuilder();
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserIn(key1,key2);
for (User u : userList) {
System.out.println(u);
result.append(u);
}
return result.toString();
}
访问:
用SQLMAP再跑一下:
2)修复代码
主要通过foreach进行遍历取数,foreach的参数collection有三种不同的取值,array,list,map,三者均可以使用,如下分三种不同的方法进行举例:
方法一:使用array数组的方式
Mapper.xml配置:
<select resultType="org.example.entity.User">
select * from user where username in
<foreach collection="array" item="key" open="(" close=")" separator=",">
#{key}
</foreach>
</select>
接口层:
List<User> getUserInArray(String[] key1);
Controller代码示例:
//http://localhost:8080/secure/secure/in?keys=user3,user4
@RequestMapping("/in")
public String in(@RequestParam String[] keys){
StringBuilder result = new StringBuilder();
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserInArray(keys);
for (User u : userList) {
System.out.println(u);
result.append(u);
}
return result.toString();
}
方法二:使用集合list的方式
Mapper.xml配置:
<select resultType="org.example.entity.User">
select * from user where username in
<foreach collection="list" item="key" open="(" close=")" separator=",">
#{key}
</foreach>
</select>
接口层:
List<User> getUserInList(List<String> key);
Controller代码示例:
//访问:http://localhost:8080/secure/secure/inlist?keys=user3,user4
@RequestMapping("/inlist")
public String inlist(@RequestParam List<String> keys){
StringBuilder result = new StringBuilder();
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserInList(keys);
for (User u : userList) {
System.out.println(u);
result.append(u);
}
return result.toString();
}
方法三:使用map的方式
Mapper.xml配置:
<!-- collection的值与map的key保持一致,item与下面的别名#号中的内容保持一致-->
<select resultType="org.example.entity.User">
select * from user where username in
<foreach collection="key" item="keys" open="(" close=")" separator=",">
#{keys}
</foreach>
</select>
</mapper>
接口层:
List<User> getUserInMap(Map<String,Objects> keys);
Controller代码示例:
//http://localhost:8080/secure/secure/inmap?keys=user3,user4
@RequestMapping("/inmap")
public String inmap(@RequestParam String[] keys){
StringBuilder result = new StringBuilder();
Map map = new HashMap<String, Object>();
/**
* key要与collection保持一致
*/
map.put("key",keys);
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserInMap(map);
for (User u : userList) {
System.out.println(u);
result.append(u);
}
return result.toString();
}