命令执行
从上往下依次是:
单独的进程中执行指定的字符串命令
单独的进程中执行指定的命令并传入变量
在指定环境的独立进程执行命令和传入变量
在指定环境和工作目录的独立进程执行中执行命令
在指定环境和工作目录的独立进程执行中执行命令和传入变量
有回显的命令
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Exec {
public static void main(String[] args) throws IOException {
BufferedReader br = null;
try{
Process p = Runtime.getRuntime().exec("ping 127.0.0.1");
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while((line= br.readLine())!=null){
sb.append(line+"\n");
}
System.out.println(sb.toString());
}catch (Exception e){
e.printStackTrace();
}finally {
if(br != null){
try{
br.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
进一步地,命令执行在不同系统下做一个判断
windows
String [] cmd = {"cmd","/c","calc.exe"};
Process ps = Runtime.getRuntime().exec(cmd);
linux
String [] cmd = {"/bin/sh","-c","ls"};
Process ps = Runtime.getRuntime().exec(cmd);
判断方法可以使用getProperty获取系统变量
查看getproperties描述可以看到可以获取很多系统变量的键值对
判断系统
if(property.contains(‘linux’))
if (properties.contains(“windows”)||properties.contains(“Windows”))
具有回显的命令,使用IO流读出来输出
Process p = Runtime.getRuntime().exec("");
InputStream io = p.getInputStream();//获得命令结果
InputStreamReader reader = new InputStreamReader(io);//读取结果
BufferedReader BuffRead = new BufferReader(reader);//创建缓冲器
StringBuilder sb = new StringBuilder();
String line = null;
while((line = BuffRead.readline())!=null){
sb.append(line);
System.out.println(line);
}
关闭流....
反射
反射里面的几个重要方法
forName 获取类的方法
newInstance 获取实例化类对象的方法
getMethod 获取函数的方法
invoke 执行函数的方法
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;
在获取类还有几种方式:
- 如果上下文存在某个类的实例a,可以通过**a.getClass()**获取
- 加载了某个类,获取它的java.lang.Class对象,访问它的属性class即可(不属于反射)
- 知道某个类的名字,获取这个类就可以使用forName来获取
forName有两个函数重载
Class<?> forName(String name)
Class<?> forName(String name, boolean initialize, ClassLoader loader)
第⼀个就是我们最常⻅的获取class的⽅式,其实可以理解为第⼆种⽅式的⼀个封装:
Class.forName(className) 等价于
Class.forName(className, true, currentLoader)
即 initialize=true
- 类加载器
java虚拟机加载类的时候,ClassLoader根据类名加载类,类名是类的完整路径,比如java.lang.Runtime
什么是ClassLoader呢,就是告诉jvm如何加载这个类
public class ClzLoad {
{
System.out.println("匿名代码块");// 第二个输出
}
static {
System.out.println("静态代码块");// 与类一同加载,所以最先输出,创建多个对象时,只在第一个对象里输出
}
public ClzLoad() {
System.out.println("构造方法");// 构造方法反而不是最先的
}
}
⾸先调⽤的是 static {} ,其次是 {} ,最后是构造函数。
其中, static {} 就是在“类初始化”的时候调⽤的,⽽ {} 中的代码会放在构造函数的 super() 后⾯,
但在当前构造函数内容的前⾯。
所以说, forName 中的 initialize=true 其实就是告诉Java虚拟机是否执⾏”类初始化“。
- 构造器
写payload的时候,newInstance()不成功原因有其一:1. 该类没有无参构造函数 2. 该类的构造函数时私有的
**getDeclaredConstructor()**获得构造方法,Runtime的构造方法是private,利用反射修改其访问权限
**getConstructor()**无论是否设置setAccessible(),都不可获取到类的私有构造器.
Constructor ct = a.getDeclaredConstructor();
ct.setAccessible(true);//修改访问权限 setAccessible
Object类是所有类的父类
- 实例化对象
在刚刚获得的构造器进行实例化一个对象
Object instance = ct.newInstance();
//等价于
Runtime rt = new Runtime();
- 获取方法
Method runMethod = a.getMethod("exec",String Class);
//获取当前类的所有成员方法
Method[] methods = class.getDeclaredMethod() ;
//获取指定类成员方法
Method method = class.getDeclaredMethod("func");
Method method = class.getDeclaredMethod("func",int,double,...)//详见重载
- 执行方法
Process ps = (Process) runMethod.invoke(instance,"clac")
method.invoke(instance,var1,var2…)
- 获取成员变量
Field var1 = class.getDeclaredField();
Field var2 = class.getDeclaredField("apple");//指定变量
Object obj = field.get(instance);//获取变量值
field.set(instance,setValue);//修改变量值
- 序列化
- 类要实现java.io.Serlalizable接口
- 所类的属性必须是可序列化,static,transient 修饰的变量不可被序列化
ObjectOutputStream类->writeObject():对参数指定的Obj文件序列化后写入一个ser文件
- 反序列化
ObjectInputStream类->readObject():从文件输入流读入,序列化成对象返回
当readObject()可控时,恶意构造后可以命令执行
一道简单的反序列化题
Recaf工具导入war包(java字节码,直接解压看不到源码)
上传的地方和读取的地方一致,考察文件上传木马了
Recaf将tools包导入进idea后,创建一个用来伪造cinfo的Test.java
成功进入,上传一句话木马
但是展示图片是:showpic.form?file=showpic.jsp
直接改为showpic.form?file=1.jsp出现404,看本地文件夹是传进了resource文件夹了,没有权限去调用resource,于是需要目录穿越