黑马javaweb353集复杂的条件查询-参数名sql注入案例

Source

黑马javaweb353集复杂的条件查询-参数名sql注入案例

​ 因专业原因,被迫学起了java。不过还好有舍友给我推了黑马的学习资料,每个学期都稳过。

​ 在学习过程中,发现老师写的一处代码存在sql注入问题。而这个注入问题并不是常见的参数内容注入,而是因为参数名可控导致注入。

​ 因此记录一下,与大家分享。

https://www.bilibili.com/video/BV1qv4y1o79t?p=354

在353集中,老师演示了一个复杂的条件查询案例。

大致功能如下:(根据姓名,或籍贯、邮箱进行查询)

image-20210918100349974

实现分析

  1. 需要判断用户是否输入了姓名、籍贯等
  2. 如果不存在则直接执行原sql语句
  3. 如果存在则拼接sql语句(例:and name like ‘%test%’)

复杂条件分页查询

代码分析

list.jsp

将name等数据利用form表单发送到findUserByPageServlet

image-20210918101510340

findUserByPageServlet.java

将参数名和参数存入一个map集合中,发送到一个实现类UserServiceImpl的findUserByPage方法中

image-20210918102612093

UserServiceImpl.java

将map集合传入一个数据库操作实现类中(userDao)

image-20210918102911274

重点分析

UserDaoImpl.java

    public int findTotalCout(Map<String, String[]> condition) {
    
      
        String sql = "select count(*) from user where 1=1";

        StringBuilder sb = new StringBuilder(sql);
        //遍历map
        Set<String> keySets = condition.keySet();

        //定义一个参数的集合
        List<Object> params = new ArrayList<Object>();

        for (String key : keySets) {
    
      
            //排除分页条件参数
            if("currentPage".equals(key)||"rows".equals(key)){
    
      
                continue;
            }

            //获取value
            String value = condition.get(key)[0];
            //判断value是否有值
            if (value != null && !"".equals(value)){
    
      
                //有值
                sb.append(" and "+key+" like ?");
                params.add("%"+value+"%");
            }
        }

        return template.queryForObject(sb.toString(),Integer.class,params.toArray());
    }

​ 大概逻辑就是,利用for循环遍历map集合,然后将键名存入key中,如果key存在且参数值存在,则将key带入sql语句中拼接。

拼接语句:

select count(*) from user where 1=1 and name like '%?%';

而value做了预处理,不存在sql注入。

但是key没有做预处理,我们可以通过控制参数名拼接sql语句,所以出现sql注入。

image-20210918103352787

尝试利用漏洞

输入一些值进行查询

image-20210918104008589

利用burp抓包,正常查询

image-20210918104448277

当我通过burp修改参数名为**1 'and name **时,发现报错,看到了sql语句。因为tomcat本身原因,遇到特殊字符会修改成html编码传输到后台进行处理。

image-20210918104730791

而将参数名改成1 and name 又访问正常,说明存在注入。

且后台语句为:

select count(*) from user where 1=1 and 1 and name like "%text%"

查询时出现了聚合函数,且tomcat不允许带有特殊字符这个限制。所以照成不了显注,但是可以形成时间注入及报错注入。

image-20210918104942295

sqlmap跑一下

注意:将参数名利用*代替(表示sql会在那个地方载入payload)

POST /day17/findUserByPageServlet HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:92.0) Gecko/20100101 Firefox/92.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
Origin: http://localhost:8081
Connection: close
Referer: http://localhost:8081/day17/findUserByPageServlet?currentPage=1&rows=5
Cookie: JSESSIONID=84FF7F7AB1AAE9A01A9396DDC07E24CA; Idea-dab618b2=533e5dc9-f76f-4a1a-9ce3-526e903cd644
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
X-Forwarded-For: 127.0.0.4
X-Originating-IP: 127.0.0.4
X-Remote-IP: 127.0.0.4
X-Remote-Addr: 127.0.0.4

*=test&address=&email=
sqlmap -r "/Users/tiger/Desktop/未命名.txt" --dbs --batch

image-20210918105534766

加固思路

如果使用预处理去加固的话,会导致查询查不出

因为预处理相当于利用问号拼接,拼接后会在字段名两旁添加类似双引号的字符,导致查询不出结果

select * from user where 1=1  and 'name' like '%t%' limit 0,5

image-20210918111654805

不用预处理

image-20210918111803615

所以需要更换思路,过滤字符串

java项目中如何防止sql注入?

  static String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|"
            + "(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";

    static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);//表示忽略大小写

	@Override
    public List<User> finByPage(int start, int rows, Map<String, String[]> condition) {
    
      


        String sql = "select * from user where 1=1 ";

        StringBuilder sb = new StringBuilder(sql);
        //遍历map
        Set<String> keySets = condition.keySet();

        //定义一个参数的集合
        List<Object> params = new ArrayList<Object>();

        for (String key : keySets) {
    
      
            //排除分页条件参数
            if("currentPage".equals(key)||"rows".equals(key)){
    
      
                continue;
            }

            //获取value
            String value = condition.get(key)[0];
            //判断value是否有值
            if (value != null && !"".equals(value)){
    
      
                //判断是否为注入
                boolean sqlValid = isSqlValid(key);
                if (!sqlValid){
    
      
                    return null;
                }

                //有值
                sb.append(" and "+key+" like ?");

                params.add("%"+value+"%");
            }
        }

        //添加分页查询
        sb.append(" limit ?,?");
        //添加分页查询参数值
        params.add(start);
        params.add(rows);

        System.out.println(sb);
        System.out.println(params);

        return template.query(sb.toString(),new BeanPropertyRowMapper<User>(User.class),params.toArray());

    }


    public static boolean isSqlValid(String str) {
    
      

        Matcher matcher = sqlPattern.matcher(str);
        if (matcher.find()) {
    
      
            System.out.println("参数存在非法字符,请确认:"+matcher.group());//获取非法字符:or
            return false;
        }
        return true;
    }

在判断value值过后,加一层判断。

将键名带入isSqlValid方法中判断,如果是注入语句,将会返回一个false,并且直接返回一个空值。

image-20210918182429730

再利用sqlmap跑,已经失败了

image-20210918183125693