JAVA SQL注入代码审计

时间:2022-12-09 12:01:41

前言:在之前的一篇关于​​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)

修复方法:不能使用预编译的方式进行修复,使用预编译后,输入被当作字符串处理了,不能当作字段名,不能进行正确的排序

JAVA SQL注入代码审计

JAVA SQL注入代码审计

正确的方法:对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;

    }

}

JAVA SQL注入代码审计

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进行条件查询,如下:

JAVA SQL注入代码审计

修复方法:

        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();

    }

访问测试:

JAVA SQL注入代码审计

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();

    }

访问:

JAVA SQL注入代码审计

 2)代码修复:

 Mapper.xml配置:   

<select parameterType="string" resultType="org.example.entity.User">

        select * from user where username  like concat('%',#{user},'%')

    </select>

JAVA SQL注入代码审计

注意:不能直接写为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();

    }

访问:

JAVA SQL注入代码审计

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();

    }

访问:

JAVA SQL注入代码审计

用SQLMAP再跑一下:

JAVA SQL注入代码审计

 

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>

JAVA SQL注入代码审计

 接口层:

    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>

JAVA SQL注入代码审计

 接口层:

 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>

JAVA SQL注入代码审计

  接口层:

    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();

    }